zOs/war/rexx4

}¢--- A540769.WK.REXX(T1) cre=2011-05-20 mod=2011-05-20-13.53.29 A540769 -------
/* copx tstZos begin **************************************************/
tstZOs:
    call sqlIni
    call tstSql
    call tstSqlStmt
    call tstSqlStmts
    call tstSqlO1
    call tstSqlO2
    call tstSqls1
    call tstSqlO
    call tstTotal
    return
endProcedure tstZOs

tstWshBatch:
    call adrTso 'alloc dd(WSH) shr dsn(WK.TEXT(WSHBATCH))'
    call wshBatch
    return adrTso('free dd(WSH)')

tstLmdTiming:
parse arg lev
    say timing() lev
    call lmdBegin abc, lev
    c = 0
    do while lmdNext(abc, st.)
        c = c + st.0
        end
    call lmdEnd   abc
    say timing() lev 'with group - without reading' c
    call adrIsp 'lmdinit listid(lmdId) level('lev')'
    do c=0 while adrIsp('lmdlist listid(&lmdId) dataset(abc)', 8) = 0
        end
    call adrIsp 'lmdfree listid(&lmdId)'
    say timing() lev 'with list' c
    return
endProcedure lmdTiming

tstCsi: procedure expose m.
    if 0 then do
        call lmd 'A540769.*K'
        call tstCsiCla 'A540769.WK.REXX'
        call tstCsiCla 'A540769.AAA.DATASETS'
        call tstCsiCla 'A540769.RRR.DATASETS'
        end
    if 0 then do
        call tstCsiOpNx 'A540769.WK.*E*'
        call tstCsiOpNx 'A540769.AAA.DATASETS'
        call tstCsiOpNx 'A540769.RRR.DATASETS'
        end
    if 1 then do
        call tstCsiNxCl 'A540769.WK.**'
        call tstCsiNxCl 'DBTF.M*.**'
        call tstCsiNxCl 'DBTF.BE*.**'
        end
    return
tstCsi: procedure expose m.
 /* call lmd 'A540769.*K' */
    call tstCsi1   'A540769.WK.REXX'
    call tstCsi1   'A540769.AAA.DATASETS'
    call tstCsi1   'A540769.RRR.DATASETS'
    return

tstCsiCla:
parse arg ds
    say ds '-->' csiCla(ds)
    return

tstCsiOpNx: procedure expose m.
parse arg ds
    m = 'NUE123'
    s = 'res89'
    flds = 'devtyp volser mgmtclas comudsiz NOBYTTRK UDATASIZ HARBA'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    do while csiNext(m, s)
        say m.s     'dev' c2x(m.s.devTyp) ,
                    'vol' m.s.volSer 'cla' m.s.mgmtclas,
                    'comuDsiz' m.s.comuDsiz 'noBytTrk' m.s.noBytTrk,
                    'udatasiz' c2x(m.s.udatasiz) ,
                    'harba' c2x(m.s.harba)
        end
    return

tstCsiNxCl: procedure expose m.
parse arg ds
    m = 'ABC123'
    s = 'efg89'
    flds = 'devtyp volser mgmtclas'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    say timing() 'begin'
    do i=1 while csiNext(m, s)
        nn = csiArcTape(m.s.volser, m.s.mgmtClas, m.s.devtyp, m.s)
/*      oo = csiCla(strip(m.s))
        if oo <> nn then
            say nn '<>' oo m.s
 */     if i // 1000 = 0 then
            say timing() i nn m.s
        end
    say timing() (i-1) nn m.s
    return
endProcedure tstCsiNxCl

listCatClass: procedure expose m.
parse upper arg dsn
    call outtrap x., '*'
    call adrTso "listcat volume entry('"dsn"')", 4
    rt = rc
    call outtrap off
    /* say 'listct rc =' rt 'lines' x.0 */
    cl = ''
    vo = ''
    if word(x.1, 3) \== dsn then
        call out 'err ??? for dsn' dsn 'bad first line' x.1
    else if pos('NOT FOUND', x.1) > 0 then
        return 'notFound'
    else if word(x.1, 1)\== 'NONVSAM' then
        call out 'err ??? for dsn' dsn 'bad first line' x.1
    do x=2 to x.0 while vo = '' & left(x.x, 1) = ' '
     /* say x.x */
        p = pos('MANAGEMENTCLASS-', x.x)
        if p > 0 then
            vo = strip(word(substr(x.x, p+16), 1), 'l', '-')
        p = pos('VOLSER--', x.x)
        if p > 0 then
            vo = strip(word(substr(x.x, p+6), 1), 'l', '-')
        p = pos('DEVCLASS--', x.x)
            dt = strip(word(substr(x.x, p+8), 1), 'l', '-')
        end
   /*  say 'lc' cl 'vo' vo 'dt' dt 'dsn' dsn */
    if vo = '' then
        call out '??? err no volume for dsn' dsn
    else if vo = 'ARCIVE' then
        res =  'arcive'
    else if cl = '' then
        res = 'tape'
    else
        res = cl
    if   abbrev(res, 'ar') \= abbrev(dt, "X'0") ,
       | abbrev(res, 'ta') \= abbrev(dt, "X'7") ,
       | (left(res, 1) >= 'A') \= abbrev(dt, "X'3") then
       call out 'err ??? mismatch cl' cl 'vo' vo 'dt' dt 'dsn' dsn
    return res
endProcedure listCatClass

/* copx tstZos end   **************************************************/
}¢--- A540769.WK.REXX(T2) cre=2011-05-20 mod=2011-05-20-13.53.41 A540769 -------
/* copx tstZos begin **************************************************/
tstZOs:
    call sqlIni
    call tstSql
    call tstSqlO
    call tstTotal
    return
endProcedure tstZOs

tstWshBatch:
    call adrTso 'alloc dd(WSH) shr dsn(WK.TEXT(WSHBATCH))'
    call wshBatch
    return adrTso('free dd(WSH)')

tstLmdTiming:
parse arg lev
    say timing() lev
    call lmdBegin abc, lev
    c = 0
    do while lmdNext(abc, st.)
        c = c + st.0
        end
    call lmdEnd   abc
    say timing() lev 'with group - without reading' c
    call adrIsp 'lmdinit listid(lmdId) level('lev')'
    do c=0 while adrIsp('lmdlist listid(&lmdId) dataset(abc)', 8) = 0
        end
    call adrIsp 'lmdfree listid(&lmdId)'
    say timing() lev 'with list' c
    return
endProcedure lmdTiming

tstCsi: procedure expose m.
    if 0 then do
        call lmd 'A540769.*K'
        call tstCsiCla 'A540769.WK.REXX'
        call tstCsiCla 'A540769.AAA.DATASETS'
        call tstCsiCla 'A540769.RRR.DATASETS'
        end
    if 0 then do
        call tstCsiOpNx 'A540769.WK.*E*'
        call tstCsiOpNx 'A540769.AAA.DATASETS'
        call tstCsiOpNx 'A540769.RRR.DATASETS'
        end
    if 1 then do
        call tstCsiNxCl 'A540769.WK.**'
        call tstCsiNxCl 'DBTF.M*.**'
        call tstCsiNxCl 'DBTF.BE*.**'
        end
    return
tstCsi: procedure expose m.
 /* call lmd 'A540769.*K' */
    call tstCsi1   'A540769.WK.REXX'
    call tstCsi1   'A540769.AAA.DATASETS'
    call tstCsi1   'A540769.RRR.DATASETS'
    return

tstCsiCla:
parse arg ds
    say ds '-->' csiCla(ds)
    return

tstCsiOpNx: procedure expose m.
parse arg ds
    m = 'NUE123'
    s = 'res89'
    flds = 'devtyp volser mgmtclas comudsiz NOBYTTRK UDATASIZ HARBA'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    do while csiNext(m, s)
        say m.s     'dev' c2x(m.s.devTyp) ,
                    'vol' m.s.volSer 'cla' m.s.mgmtclas,
                    'comuDsiz' m.s.comuDsiz 'noBytTrk' m.s.noBytTrk,
                    'udatasiz' c2x(m.s.udatasiz) ,
                    'harba' c2x(m.s.harba)
        end
    return

tstCsiNxCl: procedure expose m.
parse arg ds
    m = 'ABC123'
    s = 'efg89'
    flds = 'devtyp volser mgmtclas'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    say timing() 'begin'
    do i=1 while csiNext(m, s)
        nn = csiArcTape(m.s.volser, m.s.mgmtClas, m.s.devtyp, m.s)
/*      oo = csiCla(strip(m.s))
        if oo <> nn then
            say nn '<>' oo m.s
 */     if i // 1000 = 0 then
            say timing() i nn m.s
        end
    say timing() (i-1) nn m.s
    return
endProcedure tstCsiNxCl

listCatClass: procedure expose m.
parse upper arg dsn
    call outtrap x., '*'
    call adrTso "listcat volume entry('"dsn"')", 4
    rt = rc
    call outtrap off
    /* say 'listct rc =' rt 'lines' x.0 */
    cl = ''
    vo = ''
    if word(x.1, 3) \== dsn then
        call out 'err ??? for dsn' dsn 'bad first line' x.1
    else if pos('NOT FOUND', x.1) > 0 then
        return 'notFound'
    else if word(x.1, 1)\== 'NONVSAM' then
        call out 'err ??? for dsn' dsn 'bad first line' x.1
    do x=2 to x.0 while vo = '' & left(x.x, 1) = ' '
     /* say x.x */
        p = pos('MANAGEMENTCLASS-', x.x)
        if p > 0 then
            vo = strip(word(substr(x.x, p+16), 1), 'l', '-')
        p = pos('VOLSER--', x.x)
        if p > 0 then
            vo = strip(word(substr(x.x, p+6), 1), 'l', '-')
        p = pos('DEVCLASS--', x.x)
            dt = strip(word(substr(x.x, p+8), 1), 'l', '-')
        end
   /*  say 'lc' cl 'vo' vo 'dt' dt 'dsn' dsn */
    if vo = '' then
        call out '??? err no volume for dsn' dsn
    else if vo = 'ARCIVE' then
        res =  'arcive'
    else if cl = '' then
        res = 'tape'
    else
        res = cl
    if   abbrev(res, 'ar') \= abbrev(dt, "X'0") ,
       | abbrev(res, 'ta') \= abbrev(dt, "X'7") ,
       | (left(res, 1) >= 'A') \= abbrev(dt, "X'3") then
       call out 'err ??? mismatch cl' cl 'vo' vo 'dt' dt 'dsn' dsn
    return res
endProcedure listCatClass

/* copx tstZos end   **************************************************/
}¢--- A540769.WK.REXX(UNUSED) cre=2016-08-09 mod=2016-08-09-10.24.50 A540769 ---
/* copy unused begin *************************************************/

class2srcMap: procedure expose m.
parse arg m
    call mapReset m
    call mapPut m, m.class_v, 'v'
    call mapPut m, m.class_w, 'w'
    call mapPut m, m.class_o, 'o'
    return m
endProcedure class2srcMap

tstClass2src: procedure expose m.
/*
$</class2src/
$/class2src/
*/
    call jIni
    call tst t, 'class2src'
    done = class2SrcMap(tstClass2SrcMap)
    call class2src m.class_class, done, t
    call class2src m.class_jrw, done, t
    call class2src m.class_jrwLazy, done, t
    call tstEnd t
    return
endProcedure class2srcMap

class2src: procedure expose m.
parse arg cl, done, out
    res = mapGet(done, cl, '-')
    if res \== '-' then
        return res
    call mapPut done, cl, cl
    ty = m.cl
    res = 'class' cl':'
    if ty == 'u' then do
        if m.cl.name == '' then
            res = res 'u'
        else if right(m.cl.met, 1) \== '*' then
            res = res 'n' m.cl.name 'u'
        else
            res = res 'n*' left(m.cl.met, length(m.cl.met)-1)
        if m.cl.0 > 0 then do
            do cx=1 to m.cl.0
                res = res class2SrcEx(m.cl.cx, done, out)','
                end
            res = left(res, length(res)-1)
            end
        end
    else if ty == 'm' & m.cl.0 == 0 then
        res = res 'm' m.cl.name m.cl.met
    else
        res = res class2SrcEx(cl, done, out)
    call jWrite out, res
    return cl
endProcedure class2src

class2srcEx: procedure expose m.
parse arg cl, done, out
    res = ''
    ch = cl
    do forever
        g = mapGet(done, cl, '-')
        if g \== '-' then
            return strip(res g)
        else if m.ch == 'u' | m.ch == 'm' then
            return strip(res class2Src(ch, done, out))
        else if \ (length(m.ch) == 1 & pos(m.ch, 'fscr') >= 1,
                & m.ch.0 <= 1 & m.ch.met == '') then
            return err('class2src bad cl' ch 'ty='m.ch,
                     'name='m.ch.name '.0='m.ch.0 'met='m.ch.met)
        res = strip(res m.ch m.ch.name)
        if m.ch.0 = 0 then
            return res
        ch = m.ch.1
        end
endProcedure class2srcEx


/**********************************************************************
    lmd: catalog read ===> ersetzt durch csi
    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 tsoOpen grp, 'R'
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 tsoClose 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
tstLmdTiming:
parse arg lev
trace ?r
    lev = word(lev DSN    , 1)
    say timing() lev
    call lmdBegin abc, lev
    c = 0
    do while lmdNext(abc, st.)
        c = c + st.0
        end
    call lmdEnd   abc
    say timing() lev 'with group - without reading' c
    call adrIsp 'lmdinit listid(lmdId) level('lev')'
    do c=0 while adrIsp('lmdlist listid(&lmdId) dataset(abc)', 8) = 0
        end
    call adrIsp 'lmdfree listid(&lmdId)'
    say timing() lev 'with list' c
    return
endProcedure lmdTiming

/**********************************************************************
    ==> abgeloest mbrList: tso listDS "'"dsn"'" members
    member list of a pds:    ==> abgeloest mbrList tso
        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
/*--- find archived DSN's from listCat ------------------------------*/
listCatClass: procedure expose m.
parse upper arg dsn
    rt = adrTso("listcat volume entry('"dsn"')", 4)
    /* say 'listct rc =' rt 'lines' m.tso_trap.0 */
    cl = ''
    vo = ''
    if word(m.tso_trap.1, 3) \== dsn then
        call out 'err ??? for dsn' dsn 'bad first line' m.tso_trap.1
    else if pos('NOT FOUND', m.tso_trap.1) > 0 then
        return 'notFound'
    else if word(m.tso_trap.1, 1)\== 'NONVSAM' then
        call out 'err ??? for dsn' dsn 'bad first line' m.tso_trap.1
    do tx=2 to m.tso_trap.0 while vo = '' ,
              & left(m.tso_trap.tx, 1) = ' '
     /* say m.tso_trap.tx */
        p = pos('MANAGEMENTCLASS-', m.tso_trap.tx)
        if p > 0 then
            vo = strip(word(substr(m.tso_trap.tx, p+16), 1), 'l', '-')
        p = pos('VOLSER--', m.tso_trap.tx)
        if p > 0 then
            vo = strip(word(substr(m.tso_trap.tx, p+6), 1), 'l', '-')
        p = pos('DEVCLASS--', m.tso_trap.tx)
            dt = strip(word(substr(m.tso_trap.tx, p+8), 1), 'l', '-')
        end
   /*  say 'lc' cl 'vo' vo 'dt' dt 'dsn' dsn */
    if vo = '' then
        call out '??? err no volume for dsn' dsn
    else if vo = 'ARCIVE' then
        res =  'arcive'
    else if cl = '' then
        res = 'tape'
    else
        res = cl
    if   abbrev(res, 'ar') \= abbrev(dt, "X'0") ,
       | abbrev(res, 'ta') \= abbrev(dt, "X'7") ,
       | (left(res, 1) >= 'A') \= abbrev(dt, "X'3") then
       call out 'err ??? mismatch cl' cl 'vo' vo 'dt' dt 'dsn' dsn
    return res
endProcedure listCatClass

/**** sql stored procedures ******************************************/
/*--- sql call statement ---------------------------------------------
   old code: find procedure description in catalog
             and use it to create call statement --------------------*/
sqlStmtCall: procedure expose m.
parse arg src, retOk, opt
    s = scanSqlReset(scanSrc(sqlstmtcall, 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.ut_alfDot) 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 fTabAuto 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 jRead(rdr)
         a = m.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

tstSqlStored: procedure expose m.
    call sqlConnect 'DP4G'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "abc"
    call sqlExec "call SYSPROC.COMMAND ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.COMMAND'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStored

tstSqlStoredWK: procedure expose m.
    call sqlConnect 'DBAF'
 /* st = "direct wkUtiSub"
    rst = 'NO'
    say 'before call st='st 'rst='rst
    call sqlExec "call A540769.WKUTILSUB ( :st, :rst)"
    say 'after call st='st 'rst='rst
 */ rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    prc = 'DB2UTIL.DB2UTIL'
    st = "DA540769.A2*" /*  DA540769.A1*" */
    say  "call" prc "("st", ...)"
    call sqlExec "call" prc "(:st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE' prc
 /* say 'results' results  */
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say ''
    say '***** utility output'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say '***** end output'
    call sqlDisconnect
    return 0
endProcedure tstSqlStoredWK

tstSqlStoredSQL1: procedure expose m.
    call sqlConnect 'DBIA'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "DA540769.A2* DA540769.A1*"
    call sqlExec "call A540769.WKSQL1 ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE A540769.WKUTIL'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStoredSQL1

tstSqlUtils: procedure expose m.
    call sqlConnect 'DBAF'
    id = 'A540769.dsnUtils'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "TEMPLATE TCOPYD",
            "DSN('&SSID..&JO..&US..P&PART(2)..D&DATE(3)..T&TIME.')",
            "DATACLAS(NULL12) MGMTCLAS(COM#A011) STORCLAS(FAR$N)",
            "SPACE (150,3750) TRK UNCNT 59;",
         "listdef abc include tablespace DA540769.A002* partlevel;",
         "listdef mf  include tablespace MF01A1A.A110A partlevel;",
         "copy list abc copyddn(tcopyd) shrlevel change;"
    st = translate(st)
    call sqlExec "call SYSPROC.DSNUTILS ( :id, :rst,",
                   ":st,:retcode, :ANY" copies(',:e,:e,:z',12) ")"
    call sqlExec ,
         'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.DSNUTILS'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    exit
endProcedure tstSqlUtils

/*--- sql trigger timing --------------------------------------------*/
tstSqlTriggerTiming:
    parse upper arg tb ni
    m.noInsert = ni == 0
    cnt = 200000
    if tb = '' then
        TB = 'GDB9998.TWK511TRI'
    call sqlConnect dbaf
    say timing() 'noInsert' m.noInsert 'tb' tb
    call sql2St 'select max(pri) MX from' tb, cc
    if m.cc.1.mx == m.sqlNull then
        m.cc.1.mx = 0
    von = m.cc.1.mx + 1
    bis = m.cc.1.mx + cnt
    say m.cc.0 'max' m.cc.1.mx 'von' von 'bis' bis 'count' cnt tb
    if right(tb, 2) = 'A1' then do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long, par)' ,
                   'values (?, ?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    else do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long)' ,
                   'values (?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    /*  call sqlLn 5,,, 'select * from' tb 'where pri >=' von */
    call sqlCommit
    say timing()
    call sqlDisconnect
    return
endProcedure tstSqlTriggerTiming

/*******????? neu, noch versorgen ???????? ***************************/
tstRts: procedure expose m.
    call wshIni
    call sqlConnect dbaf
    call sqlQuery 3, "select * from sysibm.sysTableSpaceSTats" ,
                    "where dbName = 'MF01A1A' and name = 'A150A'",
                    "order by partition  asc"
    do while sqlFetch(3, rr)
        say f('@.DBNAME%-8C.@NAME%-8C @PARTITION %4C' ,rr)
        end
    call sqlDisconnect
endProcedure tstRts

tstWiki:
    call mapReset docs, 'k'
    call addFiles docs, 'n', '/media/wkData/literature/notes'
    call addFiles docs, 'd', '/media/wkData/literature/docs'

    in = jOpen(file('wiki.old'), '<')
    out = jOpen(file('wiki.new'), '>')
    abc = '(:abc: %l%'
    do cx=1 to length(m.ut_lc)
        c1 = substr(m.ut_lc, cx, 1)
        abc = abc '¢¢#'c1 '|' c1'!!'
        end
    call jWrite out, abc ':)'
    inTxt = 0
    li = m.i
    do lx=1 while jReadVar(in, i)
        if 0 then
            say length(m.i) m.i
        if m.i = '' then
            iterate
        li = m.i
        do forever
            bx = pos('¢=', li)
            if bx < 1 then
                leave
            ex = pos('=!', li)
            if ex <= bx then
                call err '=! before ¢= in' lx li
            li = left(li, bx-1)substr(li,bx+2, ex-bx-2)substr(li,ex+2)
            end
        li = strip(li)
        if abbrev(li, '|') then do
            w = word(substr(li, 2), 1)
            call jWrite out, '¢¢#'w'!! {$:abc}'
            call jWrite out, '|||' substr(li, 2)
            inTxt=1
            iterate
            end
        if \ inTxt then do
            call jWrite out, li
            iterate
            end
        if \ (abbrev(li, '->') | abbrev(li, '#') ,
                | abbrev(li, '¢')) then do
            call jWrite out, '-<' li
            iterate
            end
        cx = 1
        if substr(li, cx, 2) == '->' then
            cx = verify(li, ' ', 'n', cx+2)
        hasCross = substr(li, cx, 1) == '#'
        if hasCross then
            cx = verify(li, ' ', 'n', cx+1)
        ex = verify(li, '!:\, ', 'm', cx)
        ex = ex - (substr(li, ex, 1) \== '!')
        hasBr = substr(li, cx, 1) == '¢'
        if \ hasBr then
            w = substr(li, cx, ex+1-cx)
        else if substr(li, ex, 1) == '!' then
            w = substr(li, cx+1, ex-1-cx)
        else
            call err 'br not closed' substr(w, cx+1,ex-1-cx) 'in' lx li
        hasPdf = right(w, 4) == '.pdf'
        if hasPdf then
            w = left(w, length(w)-4)
        if verify(w, '#?', 'm') > 0 then do
            w = translate(w, '__', '#?')
            say '*** changing to' w 'in' lx li
            end
        o = '-< {def+'w'}'
        o = '-< ¢¢'w'!!'
        k = translate(w)
        if k.k == 1 then
            say '*** doppelter key' k 'in:' lx left(li,80)
        k.k = 1
        dT = ''
        if mapHasKey(docs, k) then do
            parse value mapGet(docs, k) with dT dC dN
            call mapPut docs, k, dT (dC+1) dN
            do tx=1 to length(dT)
                t1 = substr(dT, tx, 1)
                o = o '¢¢Lit'translate(t1)':'word(dN, tx) '|' t1 '!!'
                end
            end
        qSeq = 'nd'
        qq = left(qSeq, 1)
        qx = 0
        do forever
            qx = pos('@'qq, li, qx+1)
            if qx < 1 then do
                qq = substr(qSeq, 1 + pos(qq, qSeq), 1)
                qx=0
                if qq = '' then
                    leave
                else
                    iterate
                end
            if pos(qq, dT) < 1 then do
                say '*** @'qq 'document not found:' lx li
                iterate
                end
            do qb = qx-1 by -1 while substr(li, qb, 1) == ' '
                end
            do qe = qx+2 by 1 while substr(li, qe, 1) == ' '
                end
            if substr(li, qb, 1) == '.' & substr(li, qe, 1) == '.' then
                li = left(li, qb)substr(li, qe+1)
            else
                li = left(li, qb) substr(li, qe)
            end
        o = o':' strip(substr(li, ex+1+(substr(li,ex+1,1)==':')))
        if 0 then say left(li, 30) '==>' left(o, 30)
        call jWrite out, o
        end
    dk = mapKeys(docs)
    do dx=1 to m.dk.0
        parse value mapGet(docs, m.dk.dx) with dT dC dN
        if dC < 1 then
            say '*** document not used:' dT dC dn
        end
    call jClose in
    call jClose out
    return
endProcedure tstWiki

addFiles: procedure expose m.
parse arg m, ty, file
    fl = jOpen(fileList(file(file)), '<')
    do while jRead(fl)
        nm = substr(m.fl, lastPos('/', m.fl)+1)
        k = translate(left(nm, pos('.', nm)-1))
        if \ mapHasKey(m, k) then do
            call mapAdd m, k, ty 0 nm
            end
        else do
            parse value mapGet(m, k) with dT dC dN
            call mapPut m, k, dT || ty 0 dN nm
            end
        end
    call jClose fl
    return
endProcedure addFiles

/* copU fiLinux begin ************************************************/
/*--- send ggShCmd to shell bash,
        fail if rc <> 0 or not listed in ggRet ----------------------*/
adrSh: procedure expose m.  /* really no need for variables???? */
    parse arg ggShCmd, ggRet
    address 'bash' ggShCmd
    if rc == 0                     then return 0
    else if ggRet == '*'           then return rc
    else if wordPos(rc, ggRet) > 0 then return rc
    else
        call err 'adrSh rc' rc 'for' ggShCmd
endProcedure adrSh

fileLinuxReset: procedure expose m.
parse arg m, nm
    m.m.spec = nm
    if abbrev(nm, '&') then do
        if nm == '&in' then do
            m.m.stream = .input
            m.m.jReading = 1
            end
        else if nm == '&out' then do
            m.m.stream = .output
            m.m.jWriting = 1
            end
        else do
            call err 'bad spec' nm
            end
        end
    else do
        m.m.stream = .Stream%%new(nm)
        m.m.stream%%init(m.m.stream%%qualify)
        end
    return m
endProcedure fileLinuxReset

fileLinuxOpen: procedure expose m.
parse arg m, opt
    if opt == m.j.cRead then do
        res = m.m.stream%%open(read shareread)
        m.m.jReading = 1
        end
    else do
        if opt == m.j.cApp then
            res = m.m.stream%%open(write append)
        else if opt == m.j.cWri then
            res = m.m.stream%%open(write replace)
        else
            call err 'fileLinuxOpen('m',' opt') with bad opt'
        m.m.jWriting = 1
        end
    if res \== 'READY:' then
        call err 'fileLinuxOpen fails' res':' opt ,
        "'"m.m.stream%%qualify"'"
    return m
endProcedure fileLinuxOpen

fileLinuxClose:
parse arg m
    res = m.m.stream%%close
    if res \== 'READY:' then
        call err 'fileLinuxClose' res':' m.m.stream%%qualify
    return m
endProcedure fileLinuxClose

fileLinuxRead: procedure expose m.
parse arg m, var
    res = m.m.stream%%lineIn
    if res == '' then
        if m.m.stream%%state \== 'READY' then
            return 0
    m.var = res
    m.o.o2c.var = m.class_V
    return 1
endProcedure fileLinuxRead

fileLinuxWrite: procedure expose m.
parse arg m, line
    if m.m.stream%%lineOut(line) then
        call err 'fileLinuxWrite'
    return
endProcedure fileLinuxWrite

fileLinuxRmDir: procedure expose m.
parse arg m, opt
    if opt == '' then
        return adrSh('rmdir' m.m.spec)
    else if opt == '-r' then
        return adrSh('rm -r' m.m.spec)
    else
        call err 'bad opt' opt 'in fileLinuxRmDir'
endProcedure fileLInuxRmDir

fileLinuxListReset: procedure expose m.
parse arg m, m.m.spec, o
    if o == 'r' then
        m.m.opt = 'S'
    else if o == '' then
        m.m.opt = ''
    else
        call err 'bad opt' o 'in fileLinuxListReset'
    m.m.rx = 'closed'
    return m
endProcedure fileLinuxListReset

fileLinuxListOpen: procedure expose m.
parse arg m
    if m \== translate(m) then
        call err 'bad m for fileLinuxList:' m
    if m.m.opt == '' then
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST')
    else
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST', m.m.opt)
    if rc \== 0 then
        call err 'rc' rc 'in sysFileTree('m.m.spec', m.'m'.LIST)'
    m.m.rx = 0
    m.m.jReading = 1
    return m
endProcedure fileLinuxListOpen

fileLinuxListRead: procedure expose m.
parse arg m, var
    x = m.m.rx + 1
    if x > m.m.list.0 then
        return 0
    m.var = substr(m.m.list.x, 43)
    m.m.rx = x
    call oMutate var, m.class_V
    return 1
endProcedure fileLinuxListRead

fileLinuxIni: procedure expose m.
    if m.fileLinux.ini == 1 then
        return
    m.fileLinux.ini = 1
    m.file.sep = '/'
    call jIni
    call classNew "n File u JRW", "m",
        , "jReset call fileLinuxReset m, arg",
        , "jOpen  call fileLinuxOpen m, opt",
        , "jClose call fileLinuxClose m",
        , "jRead return fileLinuxRead(m, var)",
        , "jWrite call fileLinuxWrite m, wStem",
        , "filePath return m.m.stream%%qualify",
        , "fileIsFile return sysIsFile(m.m.stream%%qualify)" ,
        , "fileIsDir return sysIsFileDirectory(m.m.stream%%qualify)" ,
        , "fileChild return file(m.m.stream%%qualify'/'name)",
        , "fileRm return adrSh(m.m.spec)",
        , "fileMkDir return adrSh('mkdir' m.m.stream%%qualify)" ,
        , "fileRmDir return fileLinuxRmDir(m, opt)"
    call classNew "n FileList u JRW", "m",
        , "jReset call fileLinuxListReset m, arg, arg2",
        , "jOpen  call fileLinuxListOpen m, opt",
        , "jClose m.m.rx = 'closed'",
        , "jRead return fileLinuxListRead(m, var)"
    return
endProcedure fileLinuxIni
/* copU fiLinux end   ************************************************/
/* copy unused end   *************************************************/
}¢--- A540769.WK.REXX(USSPIPE) cre=2011-06-28 mod=2011-06-28-17.47.56 A540769 ---
/* rexx */
call sysCall 'pipe pi.'
say 'descInp' pi.1 'descOut' pi.2
say 'o_nonblock' o_nonblock
call sysCall 'f_setfl' pi.1 4
r1 = 'not read'
rv = sysCall('read' pi.1 'r1 100')
say 'read1 retval' rv 'read' r1
l1 = 'write 1 to pipe'
call sysCall 'write' pi.2 'l1'
rv = sysCall('read' pi.1 'r1 100')
say 'read2 retVal' rv 'read' r1
call sysCall 'read' 99 'r1 100'
exit

sysCall:
parse arg ggSysCall, ggRet
    say '>>>sysCall' ggSysCall
    address sysCall ggSysCall
    ggRV = retVal
    if rc <> 0 | errNo <> 0 then do
        say 'rc='rc 'retVal' retVal 'errNo' errNo 'errNoJR' errNoJR ,
            'for syscall' ggSysCall
        address syscall 'strerror' errNo errNoJR  'ggSce.'
        do ggX=1 to ggSce.0
            say ' ' ggSce.ggX
            end
        end
    return ggRV
}¢--- A540769.WK.REXX(USSREAD) cre=2011-06-28 mod=2011-06-28-17.05.28 A540769 ---
/* rexx */
address syscall 'realpath rx2 pa'
say 'realpaht rx2 =' pa
say 'reading stdin'
address syscall 'read 0 line 20'
say 'retVal' retVal 'line' line
address syscall 'readfile //wk.clist inp.'
say 'syscall readfile rc' rc 'retval' retval 'errno' errno
say inp.0 '1='inp.1
y = inp.0
say inp.0 y'='inp.y
exit
}¢--- A540769.WK.REXX(USSRX1) cre=2011-06-28 mod=2011-06-28-16.38.08 A540769 ---
/* rexx */
say 'begin /u/a540769/rx1'
parse source s1
say 'source 1' s1
say 'ls ?????'
address sh 'ls -l'
exit 5
}¢--- A540769.WK.REXX(UT) cre=2016-10-26 mod=2016-10-26-09.51.12 A540769 -------
/* copy ut begin  ****************************************************/
utIni: procedure expose m.
    if m.ut_ini == 1 then
        return
    m.ut_ini = 1
    m.ut_Num    = '0123456789'
                /* 012345678901234567890123456789 */
    m.ut_lc     = 'abcdefghijklmnopqrstuvwxyz'
    m.ut_uc     = translate(m.ut_lc)
    m.ut_uc25   = left(m.ut_uc, 25)
    m.ut_alfa   = m.ut_lc || m.ut_uc
    m.ut_alfNum = m.ut_alfa || m.ut_Num
    m.ut_alfDot = m.ut_alfNum || '.'
    m.ut_alfId  = m.ut_alfNum'_' /* avoid rexx allowed @ # $ ¬ . | ? */
    m.ut_alfIdN1 = m.ut_Num       /* not as first character */
    m.ut_rxId   = m.ut_AlfNum'@#$?' /* charset puff mit ¬*/
    m.ut_rxDot  = '.'m.ut_rxId
    m.ut_rxN1   = '.0123456789'
    m.ut_space  = '05'x' '         /* with space: space and Tab char */
    m.ut_alfPrint = m.ut_alfNum'+-*/=()¢!{}<> .:,;?|''"%&#@$£\_'
    m.ut_numUc = m.ut_num || m.ut_uc
    m.ut_base64 = m.ut_uc || m.ut_lc || m.ut_Num'+-'
    m.ut_alfLC  = m.ut_lc   /* backward compatibility */
    m.ut_alfUC  = m.ut_uc   /* backward compatibility */
    return
endProcedure utIni
/*--- 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

/*--- strip and returnn first argument not space --------------------*/
firstNS: procedure expose m.
    do ax=1 to arg()
        if arg(ax) <> '' then
            return strip(arg(ax))
        end
    return ''
endProcedure firstNS

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

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

/*--- left without truncation ---------------------------------------*/
lefPad: procedure expose m
parse arg s, len
    if length(strip(s, 't')) >= len then
        return strip(s, 't')
    return left(s, len)
endProcedure lefPad

/*--- right without truncation --------------------------------------*/
rigPad: procedure expose m
parse arg s, len
    if length(strip(s, 'l')) >= len then
        return strip(s, 'l')
    return right(s, len)
endProcedure rigPad

/*--- 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

/*--- translate to lowercase ----------------------------------------*/
ut2lc: procedure expose m.
parse arg s
    return translate(s, m.ut_lc, m.ut_uc)

/*--- verify an id --------------------------------------------------*/
verifId: procedure expose m.
    parse arg src, extra
    if pos(left(src, 1), m.ut_alfIdN1) > 0 then
        return 1
    else
        return verify(src, m.ut_alfId || extra, 'n')

/*--- 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
    do ax = 2 by 2 to arg()
        src = repAl2(src, src, arg(ax), arg(ax+1))
        end
    return src
endProcedure repAll

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

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
/*--- character to decimal '0140'x --> 256+64=320 -------------------*/
utc2d: procedure expose m.
parse arg ch
    cx = length(ch) // 3
    if cx = 0 then
        cx = 3
    res = c2d(left(ch, cx))
    do cx=cx+1 by 3 to length(ch)
        res = res * 16777216 + c2d(substr(ch, cx, 3))
        end
    return res
endProcedure utc2d

utInter: procedure expose m.
    interpret arg(1)
    return
endProcedure utInter
/* copy ut end *******************************************************/
}¢--- A540769.WK.REXX(UTPUN) cre=2015-11-22 mod=2015-11-22-14.47.44 A540769 ----
/* rexx ****************************************************************
    utPun: modify punch file to
                resume no replace copyddn
                truncate on each character column
                further standard parameters
                copyDDL if requested

History
 7. 8.13 Walter: fixed uCount
27. 3.13 Walter: allow partlevel
 1. 1.13 Walter: neu
***********************************************************************/
parse arg a1, a2
say 'rexx ... cs.exec(utPun) with' arg() 'arguments:' a1',' a2
call errReset h
do ax=1 to arg()
    say '  arg' ax arg(ax)
    end
spx = 0
do px=1 while spx < 10
    parse external l1
    if l1 == '' then do
        spx = spx+1
        if spx > 10 then
            leave
        iterate
        end
    if spx <> 0 then do
       say spx '* empty lines'
       spx = 0
       end
    say px 'external l1:' l1'|'
    end
if listdsi('bpiipt file') = 0 then
    say 'dd bpiipt dsn' sysDsName
else
    say 'listdsi(bpiipt file) rc='rc 'reason='sysReason,
         'msgLvl1='msgLvl1  'MsgLvl2='sysMsgLvl2
pre  = getArg(a1, 'PRE')
tPun = getArg(a1, 'TPUN')
if \ abbrev(tPun, pre) then
    call err 'mismatch tPun' tPun 'pre' pre
cPun = getArg(a1, 'CPUN')
if \ abbrev(cPun, pre) then
    call err 'mismatch cPun' cPun 'pre' pre
call modifyPunch tPun, cPun
copyDDL = getArg(a1, 'COPYDDL', 1)
if length(copyDDL) > 10 then
    call copyDDl copyDDl
exit

getArg: procedure expose m.
parse arg all, kw, noFail
    wx = wordPos(kw, all)
    if wx < 1 then
        if noFail==1 then
            return ''
        else
            call err 'keyWord' kw 'missing in' all
    if wordPos(kw, all, wx+1) > 0 then
        call err 'keyWord' kw 'duplicate in' all
    res = word(all, wx+1)
    if res = '' then
        call err 'no value for keyword' kw 'in' all
    return res
endProcedure getArg

modifyPunch: procedure expose m.
parse arg iDsn, oDsn
    call readDsn iDsn, 'M.I.'
    m.o.0 = 0
    do ix=1 to m.i.0 while word(m.i.ix, 1) <> 'LOAD'
        call mAdd o, m.i.ix
        end
    ll = space(m.i.ix, 1)
    if abbrev(ll, 'LOAD DATA INDDN ') then
        call mAdd o, subword(m.i.ix, 1, 4),
                   , '  LOG NO RESUME NO REPLACE COPYDDN(TCOPYD)' ,
                   , '  DISCARDDN TDISC '
    else if abbrev(ll, 'LOAD DATA LOG ') then
        call mAdd o, subword(m.i.ix, 1, 2) 'LOG NO'
    else
        call err 'bad load line' ix':' m.i.ix
    call mAdd o, '  STATISTICS INDEX(ALL) UPDATE ALL'
    call mAdd o, '  DISCARDS 1 '
    call mAdd o, '  ERRDDN   TERRD '
    call mAdd o, '  MAPDDN   TMAPD '
    call mAdd o, '  WORKDDN  (TSYUTD,TSOUTD) '
    call mAdd o, '  SORTDEVT DISK '
    do ix=ix+1 to m.i.0
        if pos('CHAR(', m.i.ix) > 0 then
            call mAdd o, strip(m.i.ix, 't') 'TRUNCATE'
        else if word(m.i.ix, 1) word(m.i.ix, 3) == 'PART INDDN' then
            call mAdd o, m.i.ix,
                       , '  RESUME NO REPLACE COPYDDN(TCOPYD)' ,
                       , '  DISCARDDN TDISC '
        else
            call mAdd o, m.i.ix
        end
    call writeDsn oDsn ':~'iDsn, 'M.O.', , 1
    return
endProcedure modifyPunch

copyDDl: procedure expose m.
parse arg dsn ':' mgmt
   if mgmt \== '' then
       mgmt = 'mgmtclas('mgmt')'
   call readDDBegin BPIIPT
   call readDD      BPIIPT, i., '*'
   call readDDEnd   BPIIPT
   call writeDsn dsn '::f' mgmt, i., , 1
   return
endProcedure copyDDl

/* copylmmbegin,********************************************************
  wecuse 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

/*--- get m.a --------------------------------------------------------*/
mGet: procedure expose m.
parse arg a
    return m.a
endProcedure mGet

/*--- 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, fx , tx
    dx = m.dst.0
    if fx == '' then
        fx = 1
    if tx == '' then
        tx = m.src.0
    do sx = fx to tx
        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
    fmt = '%s%qn%s%qe%q^'fmt
    if m.st.0 < 1 then
        return ''
    res = f(fmt, m.st.1)
    do sx=2 to m.st.0
        res = res || f(fmt'%Qn', m.st.sx)
        end
    return res || f(fmt'%Qe')
endProcedure mCat

mIni: procedure expose m.
    if m.m.ini = 1 then
        return
    m.m.ini = 1
    m.mDigits = '0123456789'
    m.mAlfLC  = 'abcdefghijklmnopqrstuvwxyz'
    m.mAlfUC  = translate(m.mAlfLC)
    m.mAlfa   = m.mAlfLC || m.mAlfUC
    m.mAlfNum = m.mAlfa || m.mDigits
    m.mAlfDot = m.mAlfNum || '.'
    m.mBase64 = m.mAlfUC || m.mAlfLC || m.mDigits'+-'
    m.mId     = m.mAlfNum'_'   /* avoid rexx allowed @ # $ ¬ . | ? */
    m.mAlfRex1 = m.mAlfa'@#$?'  /* charset problem with ¬|  */
    m.mAlfRexR = m.mAlfRex1'.0123456789'
    m.mPrint = m.mAlfNum'+-*/=()¢!{}<> .:,;?|''"%&#@$£\_'
    m.m.area.0 = 0
    call mNewArea
    return
endProcedure mIni

verifId: procedure expose m.
    parse arg src, extra, sx
    if sx == '' then
        sx = 1
    if pos(substr(src, sx, 1), m.mDigits) > 0 then
        return sx
    else
        return verify(src, m.mId || extra, 'n', sx)
endProcedure verifId

/* copy m 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
    m.m.free = subword(dsnAlloc('dd('m.m.dd')' m.m.dsn), 2)
    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
    m.m.cx = m.m.cx + 1
    if m.m.cx <= m.m.0 then
        return m'.'m.m.cx
    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 stem of the curr line, '' at end --------------------*/
readNxCur: procedure expose m.
parse arg m
    if m.m.cx <= m.m.0 then
        return m'.'m.m.cx
    else
        return ''
endProcedure readNxCur

/*--- 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
    interpret m.m.free
    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
        dd = 'DD*'
    dd = tsoDD(dd)
    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

/*--- find a free dd with prefix dd ----------------------------------*/
tsoDD: procedure expose m.
parse arg dd, f
    if m.err.ispf then
        call adrIsp 'vget wshTsoDD shared', 0 8
    else if symbol('m.tso.tsoDD') == 'VAR' then
        wshTsoDD = m.tso.tsoDD
    else
        wshTsoDD = ''
    if f == '-' then do
        px = wordPos(dd, wshTsoDD)
        if px < 1 then
            call err 'tsoDD dd' dd 'not used' wshTsoDD
        wshTsoDD = strip(subword(wshTsoDD, 1, px-1) ,
                         subWord(wshTsoDD, px+1))
        end
    else do
        if right(dd, 1) = '*' then do
            dd = left(dd, length(dd)-1) || m.err.screen
            cx = lastPos(' 'dd, ' 'wshTsoDD)
            if cx < 1 then
                dd = dd'1'
            else do
                old = word(substr(wshTsoDD, cx), 1)
                if datatype(substr(old, length(dd)+1), 'n') then
                    dd = dd || (substr(old, length(dd)+1) + 1)
                else
                    call err 'tsoDD old' old 'suffix not numeric dd' dd
                end
            end
        if wordPos(dd, wshTsoDD) > 0 then
            call err 'tsoDD dd' dd 'already used' wshTsoDD
        wshTsoDD = strip(wshTsoDD dd)
        end
    if m.err.ispf then
        call adrIsp 'vPut wshTsoDD shared'
    m.tso.tsoDD = wshTsoDD
    return dd
endProcedure tsoDD

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 = 'UCOUNT(30)' /* 7.8.13 corrected attribute */
        say 'creating' dsn 'with 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'
    return al "DSORG("sysDSorg") MGMTCLAS("sysMgmtClass")",
        "DATACLAS("sysDataClass")" ,
        "RECFM("translate('1 2 3', ' 'sysREcFM, ' 123')")",
        "LRECL("SYSLRECL")",
        "SPACE("sysPrimary"," sysSeconds")" sysUnits mv
    /*  "blksize("sysBLkSIZE")" removed 3.4.13: let sms do the magic */
endProcedure tsoAtts

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

dsnCreateAtts: procedure expose m.
parse arg dsn, atts, forCsm
    forCsm = forCsm == 1
    aU = ' 'translate(atts)
    res = ''
    if dsn \== '' then
        res = "dataset('"dsnSetMbr(dsn)"')"
    if abbrev(atts, '~') then
        return res tsoAtts(substr(atts, 2))
    if abbrev(atts, ':') then do
        parse var atts a1 atts
        rl = substr(a1, 3)
        if abbrev(a1, ':F') then do
            if rl = '' then
                rl = 80
            recfm='f b'
            end
        else do
            if rl = '' then
                rl = 32756
            recfm = substr(a1, 2, 1) 'b'
            end
        res =  res "recfm("space(recfm, 1-forCsm)") lrecl("rl")"
        end
    if pos('(', dsn) > 0 & pos(' DSNTYPE(', aU) < 1 ,
                         & pos(' DSORG(',   aU) < 1 then
        res = res 'dsntype(library) dsorg(po)'
    if pos(' MGMTCLAS(', aU) < 1 then
        res = res 'mgmtclas(COM#A091)'
    if pos(' SPACE(', aU) < 1 then
        res = res 'space(10, 1000) cyl' || copies('inder', forCsm)
    return res atts
endProcedure dsnCreateAtts
/*--- check if a dataset is archive ------------------------------------
          returns 'ok'                    if dataset on disk
                  'not'                   if dataset is not catalogued
                  'arc'                   if dataset archived
                  listDsi errorMsg        otherwise ------------------*/
dsnArc: procedure expose m.
parse upper arg dsn
    lc = listDsi("'"strip(dsn)"' noRecall")
    if lc = 0 then
        return 'ok'
    else if lc=4 & sysReason = 19 then  /* multiple volumes */
        return 'ok'
    else if lc=16 & sysReason = 5 then
        return 'notCat'
    else if lc=16 & sysReason = 9 then
        return 'arc'
    else
        return 'listDsi cc='lc', sysReason='sysReason ,
                          'm2='sysMsgLvl2', m1='sysMsgLvl1
endProcedure dsnArc
/*--- 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 *** errorhandling, messages, help    ****************/
errIni: procedure expose m.
    if m.err.ini == 1 then
        return
    m.err.ini     = 1
    m.err.handler = ''
    m.err.cleanup = ';'
    m.err.opt     = ''
    parse source m.err.os .
    m.err.ispf    = 0
    m.err.screen  = ''
    if m.err.os \== 'LINUX' then
        if sysVar('sysISPF') = 'ACTIVE' then do
            m.err.ispf = 1
            address ispExec 'vget zScreen shared'
            m.err.screen = zScreen
            end
    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 & m.err.ispf 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
        interpret m.err.handler
    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

/*--- set rc for ispf: -------------------------------------------------
    if a cmd is run by ispStart, its RC is ignored,
         but ISPF passes the value of the shared variable zIspfRc
         back as return code
----------------------------------------------------------------------*/
errSetRc: procedure expose m.
parse arg zIspfRc
    if m.err.ispf then
        address ispExec vput 'zIspfRc' shared
    return zIspfRc
endProcedure errSetRc

/*--- error routine: user message cleanup exit -----------------------*/
errAddCleanup: procedure expose m.
parse arg code
    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
    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
    errCleanup = m.err.cleanup
    if errCleanup = ';' then
        return
    m.err.cleanup = ';'
    say 'err cleanup begin' errCleanup
    interpret errCleanup
    say 'err cleanup end' errCleanup
    return
endProcedure errCleanup

/*--- say an errorMessage msg with pref and linesplits --------------*/
errSay: procedure expose m.
parse arg msg, pref
    return saySt(errMsg(msg, pref))

/*--- prefix an errormessage with pref,
          split it into lines at \n to stem m.err -------------------*/
errMsg: 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 splitNl(err, msg)           /* split lines at \n */
endProcedure errMsg

splitNL: procedure expose m.
parse arg st, msg
    bx = 1
    do lx=1 to 20
        ex = pos('\n', msg, bx)
        if ex < bx then
            leave
        m.st.lx = substr(msg, bx, ex-bx)
        bx = ex+2
        end
    m.st.lx = substr(msg, bx)
    m.st.0 = lx
    return st
endProcedure splitNL

/*--- say (part of) the lines of a stem ----------------------------*/
saySt: procedure expose m.
parse arg st, fx, tx
    do lx=word(fx 1, 1) to word(tx m.st.0, 1)
        say m.st.lx
        end
    return st
endProcedure saySt

/*--- out (part of) the lines of a stem ----------------------------*/
outSt: procedure expose m.
parse arg st, fx, tx
    do lx=word(fx 1, 1) to word(tx m.st.0, 1)
        call out m.st.lx
        end
    return st
endProcedure outSt

/*--- say a trace message if m.trace is set --------------------------*/
debug: procedure expose m.
parse arg msg
    if m.debug == 1 then
        say 'debug:' msg
    return
endProcedure debug

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

/*--- assert that the passed rexx expression evaluates to true -------*/
assert:
    interpret 'assertRes =' arg(1)
    if assertRes \==1 then
        call err 'assert failed' arg(1) '==>' assertRes':' arg(2)
    return
endProcedure assert

/*--- 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

/*--- display the first comment block of the source as help ----------*/
help: procedure expose m.
    parse source . . s3 .
    say 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
            say '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
        say li
        end
    say right(' end help for rexx' s3, 79, '*')
    return 4
endProcedure help

/* copy err end   *****************************************************/
}¢--- A540769.WK.REXX(VSAMCHK) cre=2014-08-20 mod=2014-11-25-09.01.27 A540769 ---
$#@
$*(  vsamChk
         compare RTS with the VSAMs of a DB2 System
         report orphaned dataseets
             and differences between vsam huRba and RTS space
  25.11.14 Walter: partitionNr >= 1000, aber Instanz nur 1
$*)
$=wRBA = 1   $** with haRBA and huRRA, 0 = without (faster)
$=dbSys = DP4G
say timing() 'start vsamCheck' $dbSys f('%t E')
call sqlConnect $dbSys
numeric digits(20)
$<>
$<=¢
with s as
(
  select s.dbName db , s.name sp, r.partition pa, s.name ts
      , s.partitions parts, s.pgSize pgSz, iPrefix, r.nActive act
    from sysibm.sysTableSpace s join sysibm.sysTableSpaceStats r
      on s.dbName = r.dbName and s.name = r.name
      join sysibm.sysTablePart p
          on s.dbName = p.dbName and s.name = p.tsname
              and r.partition = p.partition
         and s.dbId = r.dbId and s.psId = r.psid
 $**  where s.dbName = $"'$MAREC'"
  union all select i.dbName db , i.indexSpace sp, r.partition pa
      , s.name ts
      , s.partitions parts , i.pgSize pgSz, iPrefix, r.nActive act
    from sysibm.sysIndexes    i
      join sysibm.sysIndexSpaceStats r
        on i.creator = r.creator and i.name = r.name
           and i.dbId = r.dbId and i.isoBid = r.isoBid
      join sysibm.systables t
        on i.tbCreator = t.creator and i.tbName = t.name
      join sysibm.systableSpace s
        on t.dbName = s.dbName and t.tsName = s.name
      join sysibm.sysIndexPart p
        on i.creator = p.ixcreator and i.name = p.ixname
              and r.partition = p.partition
 $**  where i.dbName = $"'$MAREC'"
)
select strip(db) db, strip(sp) sp, pa, strip(ts) ts
      , parts, pgSz, strip(iPrefix) iPre, act
    from s
$@ if $dbSys == 'DVBP' then $@=¢
    where not db in
         ( select db from s100447.telardrop
              where kind='disp req' and nm in ('disp07', 'disp08#2')
         )
$!
    order by 1, 2, 3
$!
call sql2St scanSqlIn2Stmt(), dbs
$<>
say timing() 'selected' m.dbs.0 'parts'
do dx=1 to m.dbs.0
    dy = 'DBS.'dx
    d1 = m.dy.db
    s1 = m.dy.sp
    t1 = m.dy.ts
    p1 = 0 + m.dy.pa
    n = d1'.'s1'#'p1
    m.s2x.d1.s1.p1 = dx
    m.dy.cDs = 0
    m.dy.hu  = 0
    m.dy.ha  = 0
    if \ datatype(m.dy.pgSz, 'n') then do
          say 'bad pgSize' m.dy.pgSz n
          m.dy.pgSz = 4
          end
    if \ datatype(m.dy.act, 'n') then do
          if m.dy.act == m.sqlNull then
              say 'rts null' m.dy.act n
          else
              say 'bad nActive ' m.dy.act n
          m.dy.act = 0
          m.dy.noRts = 1
          end
    end
say timing() 'check1' m.dbs.0 'parts'
m.bad.0 = 0
do dx=1 to m.dbs.0          $** check partitions
    dy = 'DBS.'dx
    d1 = m.dy.db
    s1 = m.dy.sp
    t1 = m.dy.ts
    p1 = 0 + m.dy.pa
    n = d1'.'s1'#'p1
    if s1 == t1 then do /* tablePart */
        if m.dy.parts <> 0 then do
            if p1 <= 0 | p1 > m.dy.parts then
                call err 'part' p1 'in' m.dy.parts 'parts ts' n
            else do px=1 to m.dy.parts
                if symbol('m.s2x.d1.s1.px') \== 'VAR' then
                    call err 'part' px 'missing in ts' n
                end
            end
        else if p1 <> 0 then
            call err 'part' p1 'in nopartitioned ts' n
        end
    else do
        px = m.dy.parts > 0
        if symbol('m.s2x.d1.t1.px') \== 'VAR' then
            call err 'ts' t1'.'px 'missing for ix' n
        if m.dy.parts <> 0 then do
            if p1 < 0 | p1 > m.dy.parts then
                call err 'part' p1 'in' m.dy.parts 'parts ix' n
            else if p1 <> 0 then do
                if symbol('m.s2x.d1.sp.0') == 'VAR' then
                    call err 'part' p1 '& 0 in' m.dy.parts 'parts ix' n
                if p1 > 1 & symbol('m.s2x.d1.s1.1') \== 'VAR' then
                    call err 'part 1 missing in ix' n
                px = p1-1
                if px > 0 & symbol('m.s2x.d1.s1.px') \== 'VAR' then
                    call err 'part' px 'missing in ix' n
                end
            end
        else if p1 <> 0 then
            call err 'part' p1 'in nopartitioned ix' n
        end
    end
say timing() 'check2' m.dbs.0 'parts'
call csiOpen csi, $dbSys'*.DSNDBD.**',
               , copies('XHARBADS XHURBADS', $wRBA)
cNoDb = 0
aUse = 0
aAlc = 0
nUse = 0
nAlc = 0
do c=0 while csiNext(csi, nx)
    if $wRBA then $@¢
$** say c2x(m.nx.Xharbads) c2x(m.nx.Xhurbads) m.nx
    if m.nx.xhaRBAds == 'ffffffffffffffff'x ,
     | m.nx.xhuRBAds == 'ffffffffffffffff'x then do
        a1 = 0
        u1 = 0
        say 'bad rba' m.nx
        end
    else do
        a1 = c2d(m.nx.xharbads)
        u1 = c2d(m.nx.xhurbads)
        end
    aUse = aUse + u1
    aAlc = aAlc + a1
    $!
    ee = ''
    parse var m.nx with . '.' . '.' d1 '.' s1 '.' l5 '.' l6
    if translate(l5, 'I', 'J') \== 'I0001' then
        ee = 'l5'
    else if translate(l6, '000000000AAAA', '123456789BCDE') ,
            \== 'A000' then
        ee = 'l6'
    else do
        if \ datatype(translate(l6, '01234', 'ABCDE'), 'n') then do
            trace ?r
            say 'l6' l6 translate(l6, '01234', 'ABCDE')
            say m.nx
            end
        p1 = translate(l6, '01234', 'ABCDE') + 0
    $** say d1'?'s1'==>'m.nx
        if symbol('m.s2x.d1.s1.p1') == 'VAR' then
            dy = 'DBS.'m.s2x.d1.s1.p1
        else if symbol('m.s2x.d1.s1.0') \== 'VAR' then
            ee = 'notInDb2'
        else do
            dy = 'DBS.'m.s2x.d1.s1.0
            if m.dy.ts == s1 & m.dy.parts > 0 then
                ee = 'part0inPartTS'
            end
        end
    if ee == '' then
        if \ abbrev(l5, m.dy.iPre) then
            ee = 'iPrefix'
        else if m.dy.noRts == 1 then
            ee = 'noRTS'
    if ee \== '' then do
        if ee \==  'noRTS' then
            say left(ee, 20) '==>' m.nx
        if symbol('m.bad.ee') \== 'VAR' then do
            bx = m.bad.0 + 1
            m.bad.0 = bx
            m.bad.ee = bx
            m.bad.bx.type = ee
            m.bad.bx.cDs = 0
            m.bad.bx.hu  = 0
            m.bad.bx.ha  = 0
            end
        dy = 'BAD.'m.bad.ee
        end
$**    say m.nx '==>' dy m.dy.db m.dy.sp m.dy.pa m.dy.iPre
    m.dy.cDs = m.dy.cDs + 1
    m.dy.hu  = m.dy.hu + u1
    m.dy.ha  = m.dy.ha + a1
    end
say timing() 'after' c 'DSNs'
bc = 0
do bx=1 to m.bad.0
    say f('bad @TYPE%20c @CDS%7i @HU%kbiB @HA%kbiB', 'BAD.'bx)
    bc = bc + m.bad.bx.cDs
    end
say bc 'bad Dsns'
cDi = 0
sDs = 0
sHu = 0
sHa = 0
sRt = 0
do dx=1 to m.dbs.0          $** check partitions
    dy = 'DBS.'dx
    d1 = m.dy.db
    s1 = m.dy.sp
    t1 = m.dy.ts
    p1 = 0 + m.dy.pa
    n = d1'.'s1'#'p1
    sDs = sDs + m.dy.cDs
    sHu = sHu + m.dy.Hu
    sHa = sHa + m.dy.Ha
    rSz = m.dy.act * m.dy.pgSz
    if m.dy.pgSz < 100 then
        rSz = rSz * 1024
    sRt = sRt + rSz
    if rSz <> m.dy.hu then do
        say f('%kbiB %+kbiB @3.HU%kbiB @HA%kbiB @CDS%4i %c' ,
             , rSz, m.dy.hu - rSz, dy, n) ,
             m.dy.cDs (m.dy.noRTS==1) m.dy.pgsz
        cDi = cDi + 1
        end
    end
say cDi 'differences in' m.dbs.0 'parts with' sDs 'DSNs'
say f('rts %kbiB, huRBA %kbiB, haRba %kbiB', sRt, sHu, sHa)
say timing() 'vsamCheck end' $dbSys f('%t E')
$#out                                              20141125 08:20:53
}¢--- A540769.WK.REXX(VSAMRENA) cre=2015-09-14 mod=2015-09-14-07.12.45 A540769 ---
//* rename vsam
//STOSTA  EXEC PGM=IKJEFT01,DYNAMNBR=100
//SYSTSPRT  DD SYSOUT=*
//SYSTSIN   DD *
DSN SYSTEM(DBOF)
 -STOP  DATABASE(SN01A1P) SPACE(A100H)
 -START DATABASE(SN01A1P) SPACE(A100H)
END
//*
//RENAME  EXEC PGM=IDCAMS
//SYSPRINT  DD SYSOUT=*
//SYSIN     DD *
   ALTER      DBOF.DSNDBC.SN01A1P.A100H.J0001.A001 -
      NEWNAME(DBOF.DSNDBC.SN01A1P.A100H.DEFKT.A001)

   ALTER      DBOF.DSNDBD.SN01A1P.A100H.J0001.A001 -
      NEWNAME(DBOF.DSNDBD.SN01A1P.A100H.DEFKT.A001)
//*
}¢--- A540769.WK.REXX(VW) cre=2010-04-13 mod=2010-04-13-15.04.13 A540769 -------
/* rexx  EDIT MACRO to add creator to create view statements
         for dba cdl
**************************************************************/


call errReset 'hi'
call adrEdit 'macro'
nS = 9e9
nV = 9e9
cnt = 0
call adrEdit 'cursor =  .zl'
do forever
    call adrEdit 'cursor =' if(nV = 9e9,  '.zl', nV-1)
    if adrEdit("find prev view word", 4) <> 0 then
            leave
    call adrEdit "(nV) = cursor"
    call adrEdit "(lV) = line" nV
    vx = wordPos('VIEW', translate(lV))
    if vx <= 1 then
        iterate
    if translate(word(lV, vx-1)) \= 'CREATE' then
        iterate
    nm = word(lV, vx+1)
    nx = wordIndex(lV, vx+1)
    if pos('(', nm) > 0 then
        nm = left(nm, pos('(', nm) -1)
    if pos('.', nm) > 0 then
        iterate
    if nV <= nS then do
        call adrEdit 'cursor = 'nV
        if adrEdit("find prev schema word", 4) <> 0 then
            call err 'no scheam found'
        call adrEdit "(nS) = cursor"
        call adrEdit "(lS) = line" nS
        sx = wordPos('SCHEMA', translate(lS))
        if word(lS, sx+1) \== '=' then
            call err 'bad schema' nS':' left(lS, 80)
        sch = word(lS, sx+2)
        if left(sch, 1) = "'" then
            sch = substr(sch, 2)
        if right(sch, 1) = "'" then
            sch = left(sch, length(sch)-1)
        end
    cx = wordIndex(lV, vx+1)
    cy = cx+length(nm)
    new0 = left(lV, cx-1) sch'.'nm
    new1 = strip(overlay('', lV, 9, cy-9), 't')
    call adrEdit "line      " nV " = (new0)"
    call adrEdit "line_after" nV " = (new1)"
    cnt = cnt + 1
    end
say cnt 'views change'
exit
/* rexx ****************************************************************
     wsh
     compiler directives $# ('|' | '<')? <kind>
                         $# ( 'end' | 'out' )
     field access for getVars mit |
     kind # mit filter (c=cut, j=strip and join ...)
     inline Data mit $#</ und filter wie oben
     Ideen: writeFramed: eliminieren von rdr abhängig machen ?|
     Ideen: String --> ref mit Prefix done
             buf mit copy semantics bufR mit refs noch implementieren
             block mit lokalen geschachtelten Variabeln
             run von JRW wegnehmen --> nein,
                 braeuchte wieder Fallunterscheidung in run
     mapVia: eliminieren oder besser unterstützen?
     pipe aus rexx (kürzer als pipeBegin ... pipeLast ... pipeEnd)
     pipeAllFramed richtig testen (auch nested)
     cat optimieren mit recursive nextRdr (DelegationsKette kürzen)
--- history ------------------------------------------------------------
14. 1.10 w.keller batch interface no longer dies on openIfNotYet
                  CSM.RZ1.P0.EXEC korrigiert
***********************************************************************/
/*--- main code wsh --------------------------------------------------*/
    call errReset 'hI'
    parse arg spec
    os = errOS()
    if spec = '' & os == 'TSO' then do    /* z/OS edit macro */
        parse value wshEditMacro() with done spec
        if done then
            return
        end
    spec = wshFun(spec)
    if spec == '$' then
        return
    call wshIni

    inp = ''
    out = ''
    if os == 'TSO' then do
        if sysvar('sysEnv') = 'FORE' then do
            end
        else do
            inp = '-wsh'
            useOut = listDsi('OUT FILE')
            if \ (useOut = 16 & sysReason = 2) then
                out = '-out'
            end
        end
    else if os == 'LINUX' then do
        inp = '&in'
        out = '&out'
        end
    else
        call err 'implemnt wsh for os' os
    call compRun spec, inp, out
exit 0

wshFun: procedure expose m.
parse arg fun rest
    call scanIni
    f1 = translate(fun)
    sx = verify(f1, m.scan.alfNum)
    if sx = 2 | sx = 1 then do
        f1 = left(f1, 1)
        rest = substr(fun, 2) rest
        end
    if f1 = 'T' then
        call wshTst rest
    else if f1 = 'I' then
        call wshInter rest
    else if f1 = '?' then
        return 'call pipePreSuf' rest '$<$#='
    else
        return arg(1)
    return '$'
endProcedure wshFun

tstSqlO1: procedure expose m.
    call sqlOIni
    call sqlConnect dbaf
    sq = sqlSel("select strip(name) from sysibm.sysTables",
                     "where creator='SYSIBM' and name like 'SYSTABL%'",
                     "order by 1")
    do 2
    call jOpen sq, m.j.cRead
    do while jRead(sq, abc)
        call outO abc
        end
    call jClose sq
    end
    call sqlDisconnect
    return 0
endProcedure tstSqlO1
/*--- actual test case ----------------------------------------------*/
wshIni: procedure expose m.
    call compIni
    call sqlOIni
    return
endProcedure wshIni

wshTst: procedure expose m.
parse arg rest
    if rest = '' then do /* default */
        call tstSqlO1
        return 0
        end
    c = ''
    do wx=1 to words(rest)
        c = c 'call tst'word(rest, wx)';'
        end
    if wx > 2 then
        c = c 'call tstTotal;'
    say 'wsh interpreting' c
    interpret c
    return 0
endProcedure wshTst

/*--- compRun: compile shell or data from inp and
             run it to output out -----------------------------------*/
compRun: procedure expose m.
parse arg spec, inp, out
    return compRunO(spec, s2oNull(inp), s2oNull(out))
endProcedure compRun

compRunO: procedure expose m.
parse arg spec, inO, ouO
    cmp = comp(o2File(inO))
    r = compile(cmp, spec)
    if ouO \== '' then
        call pipeBeLa '>' ouO
    call oRun r
    if ouO \== '' then
        call pipeEnd
    return 0
endProcedure compRun
/*--- interpret user input: rexx, expr, data or shell ---------------*/
wshInter: procedure expose m.
parse arg inp
    call wshIni
    inp = strip(inp)
    mode = '*'
    do forever
        if pos(left(inp, 1), '|:*@.-=') > 0 then
            parse var inp mode 2 inp
        if mode == '|' then
            return
        if inp <> '' then do
            say 'as' mode 'interpreting' inp
            if mode = ':' then
                interpret inp
            else if mode = '*' then
                interpret 'say' inp
            else do
                call errReset 'h', 'say "******* intercepting error";',
                    'call errSay ggTxt; return "";'
                call oRun compile(comp(jBuf(inp)),
                           , translate(mode, 'ds', 'DS'))
                call errReset 'h'
                end
            end
        say 'enter' mode 'expression,  | for end, : or * for Rexx' ,
                                                 '@ . - =  for wsh'
        parse pull inp
        end
endProcedure wshInter

/*--- batch under tso: input dd(WSH), output dd(OUT) if allocated ---*/
wshBatchTSO: procedure expose m.
parse upper arg ty
    useOut = listDsi('OUT FILE')
    if \ (useOut = 16 & sysReason = 2) then
        out = '-out'
    else
        out = ''
    call wshBatch ty, '-wsh', out
    return 0
endProcedure wshBatchTso

/*--- if we are called
        not as editmacro return 0
        as an editmacro with arguments: return 0 arguments
        without arguments: run editMacro interface ------------------*/
wshEditMacro: procedure expose m.
    if sysvar('sysISPF') \= 'ACTIVE' then
        return 0
    if adrEdit('macro (mArgs) NOPROCESS', '*') \== 0 then
        return 0
    spec = wshFun(mArgs)
    if spec == '$' then
        return 1
    if spec == '' & dsnSetMbr(d, m) = 'A540769.WK.REXX(WSH)' then do
        call tstAct
        return 0
        end
    call wshIni
    o = jOpen(jBuf(), '>')
    call adrEdit '(d) = dataset'
    call adrEdit '(m) = member'
    pc = adrEdit("process dest range Q", 0 4 8 12 16)
    if pc = 16 then
        call err 'bad range must be q'
    if pc = 0 | pc = 8 then do
        call adrEdit "(rFi) = lineNum .zFrange"
        call adrEdit "(rLa) = lineNum .zLrange"
        say 'range' rFi '-' rLa
        end
    else do
        rFi = ''
        say 'no range'
        end
    if pc = 0 | pc = 4 then do
        call adrEdit "(dst) = lineNum .zDest"
        say 'dest' dst
        end
    else do
        dst = ''
        say 'no dest'
        if adrEdit("find first '$#out' 1", 4) = 0 then do
            call adrEdit "(dst) = cursor"
            call adrEdit "(li) = line" dst
            call jWrite o, left(li, 50) date('s') time()
            end
        end
    if rFi == '' then do
        call adrEdit "(zLa) = lineNum .zl"
        if adrEdit("find first '$#' 1", 4) = 0 then do
            call adrEdit "(rFi) = cursor"
            call adrEdit "(li) = line" rFi
            if abbrev(li, '$#out') | abbrev(li, '$#end') then
                rFi = 1
            if rFi < dst & dst \== '' then
                rLa = dst-1
            else
                rLa = zLa
            end
        else do
            rFi = 1
            rLa = zLa
            end
        end
    /*  say 'range' c1 'rc' pc':' rFi '-' rLa   'after' dst */

    i = jOpen(jBuf(), m.j.cWri)
    do lx=rFi to rLa
        call adrEdit "(li) = line" lx
        call jWrite i, li
        end
    cmp = comp(jClose(i))
    call errReset 'h',
             , 'return wshEditCompErrH(ggTxt, ' rFi',' rLa')'
    r = compile(cmp, spec)
    call errReset 'h',
             , 'return wshEditRunErrH(ggTxt, ' quote(o)',' dst')'
    call pipeBegin
    call oRun r
    call pipeLast '>' o
    do while inO(obj)
        call objOut(obj)
        end
    call pipeEnd
    lab = wshEditInsLinSt(dst, 0, , o'.BUF')
    if dst \= '' then
        call wshEditLocate max(1, dst-7)
    return 1
endProcedure wshEditMacro

wshEditLocate: procedure
parse arg ln
    call adrEdit '(la) = linenum .zl'
    if la < 40 then
        return
    if ln < 7 then
        ln = 1
    else
        ln = min(ln, la - 40)
    call adrEdit 'locate ' ln
    return
endProcedure wshEditLocate

wshEditCompErrH: procedure expose m.
parse arg ggTxt, rFi, rLa
    call errReset 'h'
    call outPush mCut(ggStem, 0)
    call errSay 'compErr' ggTxt
    call outPop
    do sx=1 to m.ggStem.0
        call out m.ggStem.sx
        end
    parse var m.ggStem.3 "pos " pos .  " in line " lin":"
    if pos = '' then do
        parse var m.ggStem.3 " line " lin":"
        pos = 0
        end
    lab = rFi + lin
    if pos \= '' then
        lab = wshEditInsLin(lab, 'msgline', right('*',pos))
    lab = wshEditInsLinSt((rFi+lin),0, 'msgline', ggStem)
    call wshEditLocate rFi+lin-25
    exit 0
endSubroutine wshEditCompErrH

wshEditRunErrH: procedure expose m.
parse arg ggTxt, so, dst
    call errReset 'h'
    call errSay ggTxt, '*** run error'
    lab = wshEditInsLinSt(dst, 1, , so'.BUF')
    call outPush mCut(ggStem, 0)
    call errSay ggTxt, '*** run error'
    call wshEditInsLinSt dst, 1, msgline, ggStem
    exit 0
endSubroutine wshEditRunErrH

wshEditInsLinCmd: procedure
parse arg wh
    if dataType(wh, 'n')  then do
        if adrEdit("label" wh "= .a", 0 8 12) \= 12 then
            return 'line_before .a ='
        else
            return 'line_after .zl ='
        end
    else if left(wh, 1) == '.' then
        return 'line_before' wh '='
    else
        return wh
endProcedure wshEditInsLinCmd

wshEditInsLin: procedure
parse arg wh, type
    cmd = wshEditInsLinCmd(wh)
    do ax=3 to arg()
        li = strip(arg(ax), 't')
        if li == '' then
            iterate
        if translate(type) = 'MSGLINE' then do while length(li) > 72
            sx = lastPos(' ', li, 72)
            if sx < 10 then
                sx = 72
            one = left(li, sx)
            li = '    'strip(substr(li, sx))
            call adrEdit cmd type "(one)"
            end
        call adrEdit cmd type "(li)", 0 4
        end
    return cmd
endProcedure wshEditInsLin

wshEditInsLinSt: procedure expose m.
parse arg wh, pl, type, st
    if wh == '' then do
        do ox=1 to m.st.0
            say m.st.ox
            end
        return ''
        end
    wh = wh + pl
    cmd = wshEditInsLinCmd(wh)
    do ax=1 to m.st.0
        call wshEditInsLin cmd, type, m.st.ax
        end
    return cmd
endProcedure wshEditInsLinSt
/*** end wsh, begin all copies ****************************************/

/* copy tstAll begin  *************************************************/
tstAll: procedure expose m.
    call tstBase
    call tstComp
    call tstDiv
    call tstZos
    return 0
endProcedure tstAll

/* copx tstZos begin **************************************************/
tstZOs:
    call sqlIni
    call tstSql    /* wkTst??? noch einbauen|||
    call tstSqlO
    call tstSqlEnv      */
    call tstTotal
    return
endProcedure tstZOs

tstWshBatch:
    call adrTso 'alloc dd(WSH) shr dsn(WK.TEXT(WSHBATCH))'
    call wshBatch
    return adrTso('free dd(WSH)')

tstLmdTiming:
parse arg lev
    say timing() lev
    call lmdBegin abc, lev
    c = 0
    do while lmdNext(abc, st.)
        c = c + st.0
        end
    call lmdEnd   abc
    say timing() lev 'with group - without reading' c
    call adrIsp 'lmdinit listid(lmdId) level('lev')'
    do c=0 while adrIsp('lmdlist listid(&lmdId) dataset(abc)', 8) = 0
        end
    call adrIsp 'lmdfree listid(&lmdId)'
    say timing() lev 'with list' c
    return
endProcedure lmdTiming

tstCsi: procedure expose m.
    if 0 then do
        call lmd 'A540769.*K'
        call tstCsiCla 'A540769.WK.REXX'
        call tstCsiCla 'A540769.AAA.DATASETS'
        call tstCsiCla 'A540769.RRR.DATASETS'
        end
    if 0 then do
        call tstCsiOpNx 'A540769.WK.*E*'
        call tstCsiOpNx 'A540769.AAA.DATASETS'
        call tstCsiOpNx 'A540769.RRR.DATASETS'
        end
    if 1 then do
        call tstCsiNxCl 'A540769.WK.**'
        call tstCsiNxCl 'DBTF.M*.**'
        call tstCsiNxCl 'DBTF.BE*.**'
        end
    return
tstCsi: procedure expose m.
 /* call lmd 'A540769.*K' */
    call tstCsi1   'A540769.WK.REXX'
    call tstCsi1   'A540769.AAA.DATASETS'
    call tstCsi1   'A540769.RRR.DATASETS'
    return

tstCsiCla:
parse arg ds
    say ds '-->' csiCla(ds)
    return

tstCsiOpNx: procedure expose m.
parse arg ds
    m = 'NUE123'
    s = 'res89'
    flds = 'devtyp volser mgmtclas comudsiz NOBYTTRK UDATASIZ HARBA'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    do while csiNext(m, s)
        say m.s     'dev' c2x(m.s.devTyp) ,
                    'vol' m.s.volSer 'cla' m.s.mgmtclas,
                    'comuDsiz' m.s.comuDsiz 'noBytTrk' m.s.noBytTrk,
                    'udatasiz' c2x(m.s.udatasiz) ,
                    'harba' c2x(m.s.harba)
        end
    return

tstCsiNxCl: procedure expose m.
parse arg ds
    m = 'ABC123'
    s = 'efg89'
    flds = 'devtyp volser mgmtclas'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    say timing() 'begin'
    do i=1 while csiNext(m, s)
        nn = csiArcTape(m.s.volser, m.s.mgmtClas, m.s.devtyp, m.s)
/*      oo = csiCla(strip(m.s))
        if oo <> nn then
            say nn '<>' oo m.s
 */     if i // 1000 = 0 then
            say timing() i nn m.s
        end
    say timing() (i-1) nn m.s
    return
endProcedure tstCsiNxCl

listCatClass: procedure expose m.
parse upper arg dsn
    call outtrap x., '*'
    call adrTso "listcat volume entry('"dsn"')", 4
    rt = rc
    call outtrap off
    /* say 'listct rc =' rt 'lines' x.0 */
    cl = ''
    vo = ''
    if word(x.1, 3) \== dsn then
        call out 'err ??? for dsn' dsn 'bad first line' x.1
    else if pos('NOT FOUND', x.1) > 0 then
        return 'notFound'
    else if word(x.1, 1)\== 'NONVSAM' then
        call out 'err ??? for dsn' dsn 'bad first line' x.1
    do x=2 to x.0 while vo = '' & left(x.x, 1) = ' '
     /* say x.x */
        p = pos('MANAGEMENTCLASS-', x.x)
        if p > 0 then
            vo = strip(word(substr(x.x, p+16), 1), 'l', '-')
        p = pos('VOLSER--', x.x)
        if p > 0 then
            vo = strip(word(substr(x.x, p+6), 1), 'l', '-')
        p = pos('DEVCLASS--', x.x)
            dt = strip(word(substr(x.x, p+8), 1), 'l', '-')
        end
   /*  say 'lc' cl 'vo' vo 'dt' dt 'dsn' dsn */
    if vo = '' then
        call out '??? err no volume for dsn' dsn
    else if vo = 'ARCIVE' then
        res =  'arcive'
    else if cl = '' then
        res = 'tape'
    else
        res = cl
    if   abbrev(res, 'ar') \= abbrev(dt, "X'0") ,
       | abbrev(res, 'ta') \= abbrev(dt, "X'7") ,
       | (left(res, 1) >= 'A') \= abbrev(dt, "X'3") then
       call out 'err ??? mismatch cl' cl 'vo' vo 'dt' dt 'dsn' dsn
    return res
endProcedure listCatClass

/* copx tstZos end   **************************************************/
/* copx tstZos begin **************************************************/
tstZOs:
    call sqlIni
    call tstSql
    call tstSqlO
    call tstSqlEnv
    call tstTotal
    return
endProcedure tstZOs

tstWshBatch:
    call adrTso 'alloc dd(WSH) shr dsn(WK.TEXT(WSHBATCH))'
    call wshBatch
    return adrTso('free dd(WSH)')

tstLmdTiming:
parse arg lev
    say timing() lev
    call lmdBegin abc, lev
    c = 0
    do while lmdNext(abc, st.)
        c = c + st.0
        end
    call lmdEnd   abc
    say timing() lev 'with group - without reading' c
    call adrIsp 'lmdinit listid(lmdId) level('lev')'
    do c=0 while adrIsp('lmdlist listid(&lmdId) dataset(abc)', 8) = 0
        end
    call adrIsp 'lmdfree listid(&lmdId)'
    say timing() lev 'with list' c
    return
endProcedure lmdTiming

tstCsi: procedure expose m.
    if 0 then do
        call lmd 'A540769.*K'
        call tstCsiCla 'A540769.WK.REXX'
        call tstCsiCla 'A540769.AAA.DATASETS'
        call tstCsiCla 'A540769.RRR.DATASETS'
        end
    if 0 then do
        call tstCsiOpNx 'A540769.WK.*E*'
        call tstCsiOpNx 'A540769.AAA.DATASETS'
        call tstCsiOpNx 'A540769.RRR.DATASETS'
        end
    if 1 then do
        call tstCsiNxCl 'A540769.WK.**'
        call tstCsiNxCl 'DBTF.M*.**'
        call tstCsiNxCl 'DBTF.BE*.**'
        end
    return
tstCsi: procedure expose m.
 /* call lmd 'A540769.*K' */
    call tstCsi1   'A540769.WK.REXX'
    call tstCsi1   'A540769.AAA.DATASETS'
    call tstCsi1   'A540769.RRR.DATASETS'
    return

tstCsiCla:
parse arg ds
    say ds '-->' csiCla(ds)
    return

tstCsiOpNx: procedure expose m.
parse arg ds
    m = 'NUE123'
    s = 'res89'
    flds = 'devtyp volser mgmtclas comudsiz NOBYTTRK UDATASIZ HARBA'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    do while csiNext(m, s)
        say m.s     'dev' c2x(m.s.devTyp) ,
                    'vol' m.s.volSer 'cla' m.s.mgmtclas,
                    'comuDsiz' m.s.comuDsiz 'noBytTrk' m.s.noBytTrk,
                    'udatasiz' c2x(m.s.udatasiz) ,
                    'harba' c2x(m.s.harba)
        end
    return

tstCsiNxCl: procedure expose m.
parse arg ds
    m = 'ABC123'
    s = 'efg89'
    flds = 'devtyp volser mgmtclas'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    say timing() 'begin'
    do i=1 while csiNext(m, s)
        nn = csiArcTape(m.s.volser, m.s.mgmtClas, m.s.devtyp, m.s)
/*      oo = csiCla(strip(m.s))
        if oo <> nn then
            say nn '<>' oo m.s
 */     if i // 1000 = 0 then
            say timing() i nn m.s
        end
    say timing() (i-1) nn m.s
    return
endProcedure tstCsiNxCl

listCatClass: procedure expose m.
parse upper arg dsn
    call outtrap x., '*'
    call adrTso "listcat volume entry('"dsn"')", 4
    rt = rc
    call outtrap off
    /* say 'listct rc =' rt 'lines' x.0 */
    cl = ''
    vo = ''
    if word(x.1, 3) \== dsn then
        call out 'err ??? for dsn' dsn 'bad first line' x.1
    else if pos('NOT FOUND', x.1) > 0 then
        return 'notFound'
    else if word(x.1, 1)\== 'NONVSAM' then
        call out 'err ??? for dsn' dsn 'bad first line' x.1
    do x=2 to x.0 while vo = '' & left(x.x, 1) = ' '
     /* say x.x */
        p = pos('MANAGEMENTCLASS-', x.x)
        if p > 0 then
            vo = strip(word(substr(x.x, p+16), 1), 'l', '-')
        p = pos('VOLSER--', x.x)
        if p > 0 then
            vo = strip(word(substr(x.x, p+6), 1), 'l', '-')
        p = pos('DEVCLASS--', x.x)
            dt = strip(word(substr(x.x, p+8), 1), 'l', '-')
        end
   /*  say 'lc' cl 'vo' vo 'dt' dt 'dsn' dsn */
    if vo = '' then
        call out '??? err no volume for dsn' dsn
    else if vo = 'ARCIVE' then
        res =  'arcive'
    else if cl = '' then
        res = 'tape'
    else
        res = cl
    if   abbrev(res, 'ar') \= abbrev(dt, "X'0") ,
       | abbrev(res, 'ta') \= abbrev(dt, "X'7") ,
       | (left(res, 1) >= 'A') \= abbrev(dt, "X'3") then
       call out 'err ??? mismatch cl' cl 'vo' vo 'dt' dt 'dsn' dsn
    return res
endProcedure listCatClass

/* copx tstZos end   **************************************************/
/* copx tstDiv begin **************************************************/
tstDiv:
    call tstSorQ
    call tstSort
    call tstMatch
    call tstTotal
    return
endProcedure tstDiv

tstSorQ: procedure expose m.   /* wkTst??? remove once upon a time */
/*<<tstSorQ
    ### start tst tstSorQ #############################################
    sort 29  c ACHT DREI DREIZEHN EINS ELF FUENF M.I.25 M.I.26 M.I.27 M+
    ..I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI ZWOELF 0 1 1 1 2 2+
    . 3 3 4 4
    sort 22  c ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27 M.I.29 NEUN VIERZ+
    EHN ZEHN ZWOELF 0 1 1 1 2 2 3 3 4 4
    sort 15  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1 1 2 2 3 3 4 4
    sort 8  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1
    sort 1  M.I.29
tstSorQ */
/*<<tstSorQAscii
    ### start tst tstSorQAscii ########################################
    sort 29  0 1 1 1 2 2 3 3 4 4 ACHT DREI DREIZEHN EINS ELF FUENF M.I.+
    25 M.I.26 M.I.27 M.I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI Z+
    WOELF c
    sort 22  0 1 1 1 2 2 3 3 4 4 ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27+
    . M.I.29 NEUN VIERZEHN ZEHN ZWOELF c
    sort 15  0 1 1 1 2 2 3 3 4 4 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 8  0 1 1 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 1  M.I.29
tstSorQAscii */
    if errOS() == 'LINUX' then
        call tst t, "tstSorQAscii"
    else
        call tst t, "tstSorQ"
    call mAdd mCut(i, 0), eins, zwei, drei, vier, fuenf, sechs,
        ,sieben, acht, neun, zehn, elf, zwoelf, dreizehn, vierzehn
    call mAdd i, 1, 2, 3, 4, 4, 3, 2, 1, 0, 1,
        , 'M.I.25', 'M.I.26', 'M.I.27', 'c', 'M.I.29'

    do yy = m.i.0 by -1 to 1
        do x = 0 to yy
            m.i.0 = x
            call sort i, o
            m = ''
            la = ''
            if x <> m.o.0 then
                call err 'size mismatch' x '<>' m.o.0
            do y=1 to m.o.0
                m = m m.o.y
                if \ (la << m.o.y) then
                    call err 'sort mismatch' yy x y '\' la '<<' m.o.y
                end
            end
        if yy // 7 = 1 then
           call tstOut t, 'sort' yy m
        do x = 2 to yy
            x1 = x-1
            m.i.x1 = m.i.x
            end
        end
    call tstEnd t
    return
endProcedure tstSorQ

tstSort: procedure expose m.
    call tstSortComp
    call tstSortComp '<<='
    call tstSortComp 'm.aLe <<= m.aRi'
    call tstSortComp 'if 1 then cmp = m.aLe <<= m.aRi; else call err sd'
    return
endProcedure tstSort

tstSortComp: procedure expose m.
parse arg cmp
/*<<tstSort
    ### start tst tstSort #############################################
    sort 29  c ACHT DREI DREIZEHN EINS ELF FUENF M.I.25 M.I.26 M.I.27 M+
    ..I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI ZWOELF 0 1 1 1 2 2+
    . 3 3 4 4
    sort 22  c ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27 M.I.29 NEUN VIERZ+
    EHN ZEHN ZWOELF 0 1 1 1 2 2 3 3 4 4
    sort 15  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1 1 2 2 3 3 4 4
    sort 8  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1
    sort 1  M.I.29
tstSort */
/*<<tstSortAscii
    ### start tst tstSortAscii ########################################
    sort 29  0 1 1 1 2 2 3 3 4 4 ACHT DREI DREIZEHN EINS ELF FUENF M.I.+
    25 M.I.26 M.I.27 M.I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI Z+
    WOELF c
    sort 22  0 1 1 1 2 2 3 3 4 4 ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27+
    . M.I.29 NEUN VIERZEHN ZEHN ZWOELF c
    sort 15  0 1 1 1 2 2 3 3 4 4 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 8  0 1 1 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 1  M.I.29
tstSortAscii */
    say '### start with comparator' cmp '###'
    if errOS() == 'LINUX' then
        call tst t, "tstSortAscii"
    else
        call tst t, "tstSort"
    call mAdd mCut(i, 0), eins, zwei, drei, vier, fuenf, sechs,
        ,sieben, acht, neun, zehn, elf, zwoelf, dreizehn, vierzehn
    call mAdd i, 1, 2, 3, 4, 4, 3, 2, 1, 0, 1,
        , 'M.I.25', 'M.I.26', 'M.I.27', 'c', 'M.I.29'

    do yy = m.i.0 by -1 to 1
        do x = 0 to yy
            m.i.0 = x
            call sort i, o, cmp
            m = ''
            la = ''
            if x <> m.o.0 then
                call err 'size mismatch' x '<>' m.o.0
            do y=1 to m.o.0
                m = m m.o.y
                if \ (la << m.o.y) then
                    call err 'sort mismatch' yy x y '\' la '<<' m.o.y
                end
            end
        if yy // 7 = 1 then
           call tstOut t, 'sort' yy m
        do x = 2 to yy
            x1 = x-1
            m.i.x1 = m.i.x
            end
        end
    call tstEnd t
    return
endProcedure tstSort
tstMatch: procedure expose m.
/*<<tstMatch
    ### start tst tstMatch ############################################
    match(eins, e?n*) 1 1 2,i,s trans(E?N*) EiNs
    match(eins, eins) 1 1 0 trans(EINS) EINS
    match(e1nss, e?n*) 1 1 2,1,ss trans(?*) 1ss
    match(eiinss, e?n*) 0 0 -9
    match(einss, e?n *) 0 0 -9
    match(ein s, e?n *) 1 1 2,i,s trans(E?N *) EiN s
    match(ein abss  , ?i*b*) 1 1 3,e,n a,ss   trans(?I*B*) eIn aBss  .
    match(ein abss wie gehtsssxdirx und auch , ) 0 0 -9
    match(ies000, *000) 1 1 1,ies trans(*000) ies000
    match(xx0x0000, *000) 1 1 1,xx0x0 trans(*000) xx0x0000
    match(000x00000xx, 000*) 1 1 1,x00000xx trans(000*) 000x00000xx
    match(000xx, *0*) 1 1 2,00,xx trans(ab*cd*ef) ab00cdxxef
tstMatch */
    call tst t, "tstMatch"
    call tstOut t, matchTest1('eins', 'e?n*'                        )
    call tstOut t, matchTest1('eins', 'eins'                        )
    call tstOut t, matchTest1('e1nss', 'e?n*', '?*'                 )
    call tstOut t, matchTest1('eiinss', 'e?n*'                      )
    call tstOut t, matchTest1('einss', 'e?n *'                      )
    call tstOut t, matchTest1('ein s', 'e?n *'                      )
    call tstOut t, matchTest1('ein abss  ', '?i*b*'                 )
    call tstOut t, matchTest1('ein abss wie gehtsssxdirx und auch ' )
    call tstOut t, matchTest1('ies000', '*000'                      )
    call tstOut t, matchTest1('xx0x0000', '*000'                    )
    call tstOut t, matchTest1('000x00000xx', '000*'                 )
    call tstOut t, matchTest1('000xx', '*0*', 'ab*cd*ef'            )
    call tstEnd t
return

matchTest1:
parse arg w, m, m2
    r = 'match('w',' m')' match(w, m) match(w, m, vv) m.vv.0
    do x=1 to m.vv.0
        r = r','m.vv.x
        end
    if m2 = '' then
        m2 = translate(m)
    if m.vv.0 >= 0 then
        r = r 'trans('m2')' matchTrans(m2, vv)
    return r
endProcedure matchTest1
/* copx tstDiv end   **************************************************/

/* copx tstSql begin **************************************************/
tstSqlUtils: procedure expose m.
    call sqlConnect 'DBAF'
    id = 'A540769.dsnUtils'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "TEMPLATE TCOPYD",
            "DSN('&SSID..&JO..&US..P&PART(2)..D&DATE(3)..T&TIME.')",
            "DATACLAS(NULL12) MGMTCLAS(COM#A011) STORCLAS(FAR$N)",
            "SPACE (150,3750) TRK UNCNT 59;",
         "listdef abc include tablespace DA540769.A002* partlevel;",
         "listdef mf  include tablespace MF01A1A.A110A partlevel;",
         "copy list abc copyddn(tcopyd) shrlevel change;"
    st = translate(st)
    call sqlExec "call SYSPROC.DSNUTILS ( :id, :rst,",
                   ":st,:retcode, :ANY" copies(',:e,:e,:z',12) ")"
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.DSNUTILS'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    exit
endProcedure tstSqlUtils

tstSqlStored: procedure expose m.
    call sqlConnect 'DBAF'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "abc"
    call sqlExec "call SYSPROC.COMMAND ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.COMMAND'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStored

tstSqlStoredWK: procedure expose m.
    call sqlConnect 'DBAF'
 /* st = "direct wkUtiSub"
    rst = 'NO'
    say 'before call st='st 'rst='rst
    call sqlExec "call A540769.WKUTILSUB ( :st, :rst)"
    say 'after call st='st 'rst='rst
 */ rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    prc = 'DB2UTIL.DB2UTIL'
    st = "DA540769.A2*" /*  DA540769.A1*" */
    say  "call" prc "("st", ...)"
    call sqlExec "call" prc "(:st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE' prc
 /* say 'results' results  */
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say ''
    say '***** utility output'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say '***** end output'
    call sqlDisconnect
    return 0
endProcedure tstSqlStoredWK

tstSqlStoredSQL1: procedure expose m.
    call sqlConnect 'DBIA'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "DA540769.A2* DA540769.A1*"
    call sqlExec "call A540769.WKSQL1 ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE A540769.WKUTIL'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStoredSQL1

tstSqlTriggerTiming:
    parse upper arg tb ni
    m.noInsert = ni == 0
    cnt = 200000
    if tb = '' then
        TB = 'GDB9998.TWK511TRI'
    call sqlConnect dbaf
    say timing() 'noInsert' m.noInsert 'tb' tb
    call sql2St 49, '*', cc, 'select max(pri) MX from' tb
    if m.cc.1.mx == m.sqlNull then
        m.cc.1.mx = 0
    von = m.cc.1.mx + 1
    bis = m.cc.1.mx + cnt
    say m.cc.0 'max' m.cc.1.mx 'von' von 'bis' bis 'count' cnt tb
    if right(tb, 2) = 'A1' then do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long, par)' ,
                   'values (?, ?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    else do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long)' ,
                   'values (?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    /*  call sqlLn 5,,, 'select * from' tb 'where pri >=' von */
    call sqlExImm 'commit'
    say timing()
    call sqlDisconnect
    return
endProcedure tstSqlTriggerTiming

tstSql: procedure expose m.
    cx = 2
    call sqlIni
    call jIni
/*<<tstSql
    ### start tst tstSql ##############################################
    *** err: sqlCode -204 A540769.SYSDUMMY IS AN UNDEFINED NAME .
    .    e 1: warnings
    .    e 2: state 42704
    .    e 3: stmt =  execSql prepare s7 from :src
    .    e 4: with src = select * from sysdummy
    fetchA 1 ab= m.abcdef.123.AB abc ef= efg
    fetchA 0 ab= m.abcdef.123.AB abc ef= efg
    sqlVars :M.STST.A :M.STST.A.SQLIND, :M.STST.B :M.STST.B.SQLIND, :M.+
    STST.C :M.STST.C.SQLIND
    1 all from dummy1
    a=a b=2 c=0
    sqlVarsNull 1
    a=a b=2 c=---
    fetchBT 1 SYSTABLES
    fetchBT 0 SYSTABLES
    fetchBI 1 SYSINDEXES
    fetchBI 0 SYSINDEXES
    opAllCl 3
    fetchC 1 SYSTABLES
    fetchC 2 SYSTABLESPACE
    fetchC 3 SYSTABLESPACESTATS
    PreAllCl 3
    fetchD 1 SYSIBM.SYSTABLES
    fetchD 2 SYSIBM.SYSTABLESPACE
    fetchD 3 SYSIBM.SYSTABLESPACESTATS
tstSql */
    call tst t, "tstSql"
    call sqlConnect 'DBAF'
    src = 'select * from sysdummy'
    call sqlExec 'prepare s7 from :src'
    call sqlPrepare cx, "select 'abc' , 'efg'",
                            'from sysibm.sysDummy1'
    call sqlExec 'declare c'cx 'cursor for s'cx
    call sqlOpen cx
    a = 'abcdef'
    b = 123
    do i=1 to 2
        call out 'fetchA' sqlFetchInto(cx, ':m.a.b.ab, :m.a.b.ef') ,
            'ab= m.'a'.' || b'.'ab m.a.b.ab 'ef=' m.a.b.ef
        end
    call sqlClose cx
    drop stst a b c m.stst.a m.stst.b m.stst.c
    sv = sqlVars('M.STST',  A B C , 1)
    call out 'sqlVars' sv
    call out sqlPreAllCl(cx,
           , "select 'a', 2, case when 1=0 then 1 else null end ",
                 "from sysibm.sysDummy1",
           , stst, sv) 'all from dummy1'
    call out 'a='m.stst.a 'b='m.stst.b 'c='m.stst.c
    call out 'sqlVarsNull' sqlVarsNull(stst,   A B C)
    call out 'a='m.stst.a 'b='m.stst.b 'c='m.stst.c
    call sqlPreDeclare cx, "select name" ,
                            "from sysibm.sysTables" ,
                            "where creator = 'SYSIBM' and name = ?"
    call sqlOpen cx, 'SYSTABLES'
    call out 'fetchBT' sqlFetchInto(cx, ':NM') nm
    call out 'fetchBT' sqlFetchInto(cx, ':NM') nm
    call sqlClose cx
    call sqlOpen cx, 'SYSINDEXES'
    a = 'a b c'
    b = 1234565687687234
    call out 'fetchBI' sqlFetchInto(cx, ':NM') nm
    call out 'fetchBI' sqlFetchInto(cx, ':NM') nm
    call sqlClose cx
    src = "select name" ,
              "from sysibm.sysTables" ,
              "where creator = 'SYSIBM' and name like 'SYSTABLES%'",
              "fetch first 3 rows only"
     call sqlPreDeclare cx, src
     st = 'wie geht'' s'
     call out 'opAllCl' sqlOpAllCl(cx, st, ':M.ST.SX.NAME')
     do x=1 to m.st.0
         call out 'fetchC' x m.st.x.name
         end
     st = 'auch noch'
     src = "select strip(creator) || '.' || strip(name)" substr(src,12)
     call out 'PreAllCl' sqlPreAllCl(cx+11, src, st, ':M.ST.SX.NAME')
     do x=1 to m.st.0
         call out 'fetchD' x m.st.x.name
         end
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSql

tstSqlO: procedure expose m.
    call tst t, "tstSqlO",
       ,  "*** err: sqlCode -204 A540769.SYSDUMMY IS AN UNDEFINED NAM",
       || "E ",
       ,  "    e 1: warnings",
       ,  "    e 2: state 42704",
       ,  "    e 3: stmt =  execSql prepare s7 from :src",
       ,  "    e 4: with src = select * from sysdummy",
       ,  "REQD=Y col=123 case=--- col5=anonym",
       ,  "NAME            T DBNAME          TSNAME         ",
       ,  "SYSTABAUTH      T DSNDB06         SYSDBASE       ",
       ,  "SYSTABCONST     T DSNDB06         SYSOBJ         ",
       ,  "SYSTABLEPART    T DSNDB06         SYSDBASE       ",
       ,  "SYSTABLEPART_HI T DSNDB06         SYSHIST        ",
       ,  "SYSTABLES       T DSNDB06         SYSDBASE       ",
       ,  "NAME              T DBNAME  TSNAME  ",
       ,  "SYSTABAUTH        T DSNDB06 SYSDBASE",
       ,  "SYSTABCONST       T DSNDB06 SYSOBJ  ",
       ,  "SYSTABLEPART      T DSNDB06 SYSDBASE",
       ,  "SYSTABLEPART_HIST T DSNDB06 SYSHIST ",
       ,  "SYSTABLES         T DSNDB06 SYSDBASE"
    call sqlConnect 'DBAF'
    src = 'select * from sysdummy'
    call sqlExec 'prepare s7 from :src'
    call sql2Cursor 13,
          , 'select d.*, 123, current timestamp "jetzt und heute",',
                    'case when 1=0 then 1 else null end caseNull,',
                    "'anonym'" ,
               'from sysibm.sysdummy1 d'
    call sqlOpen 13
    do while sqlFetch(13, abc)
        call out 'REQD='m.ABC.IBMREQD 'col='m.ABC.col2,
                  'case='m.ABC.CASENULL,
                  'col5='m.ABC.col5
        je    = 'jetzt'
        jetzt = m.ABC.je
        dd = date('s')
        dd = left(dd, 4)'-'substr(dd, 5, 2)'-'right(dd, 2)'-' ,
                || left(time(), 2)'.'
        say 'jetzt='jetzt 'date time' dd
        if \ abbrev(jetzt, dd) then
            call err 'date mismatch abbrev' dd
        end
    call sqlClose 13
    call sql2Cursor 13 ,
            , 'select name, class, dbName, tsName'           ,
                              /* ,alteredTS, obid, cardf'*/ ,
                 'from sysibm.systables'                    ,
                 "where creator = 'SYSIBM' and name like 'SYSTA%'"  ,
                 "fetch first 5 rows only",
            , , 'sl<15'
    call sqlOpen 13
    call out fmtFldTitle(m.sql.13.fmt)
    do while sqlFetchLn(13, li)
        call out m.li
        end
    call sqlClose 13
    call sqlGenFmt m.sql.13.fmt, 13, 'sst'
    call sqlOpen 13
    do ix=1 while sqlFetch(13, fe.ix)
        end
    m.fe.0 = ix-1
    call fmtFldSquash sqFmt, sqlClass(13), fe
    call out fmtFldTitle(sqFmt)
    do ix=1 to m.fe.0
        call out oFldCat(sqlClass(13), fe.ix, sqFmt)
        end
    call sqlClose 13
    if 0 then do
        call sql2Cursor 13 ,
            , 'select *',
                 'from sysibm.systables'                    ,
                 "where creator = 'SYSIBM' and name like 'SYSTA%'"  ,
                 "fetch first 1 rows only",
            , , 'sl<15'
        call sqlOpen 13
        call out fmtFldTitle(m.sql.13.fmt)
        do while sqlFetchLn(13, li)
            call out m.li
            end
        call sqlClose 13
        end
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSqlO

tstSqlEnv: procedure expose m.
    call tst t, "tstSqlEnv",
       ,  "REQD=Y COL2=123 case=--- COL5=anonym",
       ,  "sql fmtFldRw sl<15",
       ,  "NAME            T DBNAME          TSNAME         ",
       ,  "SYSTABAUTH      T DSNDB06         SYSDBASE       ",
       ,  "SYSTABCONST     T DSNDB06         SYSOBJ         ",
       ,  "SYSTABLEPART    T DSNDB06         SYSDBASE       ",
       ,  "SYSTABLEPART_HI T DSNDB06         SYSHIST        ",
       ,  "SYSTABLES       T DSNDB06         SYSDBASE       ",
       ,  "sql fmtFldSquashRW",
       ,  "NAME              T DBNAME  TSNAME  ",
       ,  "SYSTABAUTH        T DSNDB06 SYSDBASE",
       ,  "SYSTABCONST       T DSNDB06 SYSOBJ  ",
       ,  "SYSTABLEPART      T DSNDB06 SYSDBASE",
       ,  "SYSTABLEPART_HIST T DSNDB06 SYSHIST ",
       ,  "SYSTABLES         T DSNDB06 SYSDBASE",
       ,  "sqlLn  sl=",
       ,  "COL1          T DBNAME                   COL4    ",
       ,  "SYSTABAUTH    T DSNDB06                  SYSDBASE"
    call mAdd t.cmp,
       ,  "SYSTABCONST   T DSNDB06                  SYSOBJ  ",
       ,  "SYSTABLEPART  T DSNDB06                  SYSDBASE",
       ,  "SYSTABLEPART_ T DSNDB06                  SYSHIST ",
       ,  "SYSTABLES     T DSNDB06                  SYSDBASE",
       ,  "sqlLn  ---",
       ,  "NAME              T DBNAME  TSNAME  ",
       ,  "SYSTABAUTH        T DSNDB06 SYSDBASE",
       ,  "SYSTABCONST       T DSNDB06 SYSOBJ  ",
       ,  "SYSTABLEPART      T DSNDB06 SYSDBASE",
       ,  "SYSTABLEPART_HIST T DSNDB06 SYSHIST ",
       ,  "SYSTABLES         T DSNDB06 SYSDBASE"
    call sqlConnect 'DBAF'
    call pipeBegin
    call out 'select d.*, 123, current timestamp "jetzt und heute",'
    call out        'case when 1=0 then 1 else null end caseNull,'
    call out        "'anonym'"
    call out   'from sysibm.sysdummy1 d'
    call pipe
    call sql 13
    call pipeLast
    do while envRead(abc)
        call out 'REQD='envGet('ABC.IBMREQD'),
                  'COL2='envGet('ABC.COL2'),
                  'case='envGet('ABC.CASENULL'),
                  'COL5='envGet('ABC.COL5')
        jetzt = envGet('ABC.jetzt')
        say 'jetzt='jetzt
        dd = date('s')
        dd = left(dd, 4)'-'substr(dd, 5, 2)'-'right(dd, 2)'-' ,
                || left(time(), 2)'.'
        if \ abbrev(jetzt, dd) then
            call err 'date mismatch abbrev' dd
        end
    call pipeEnd
    call out 'sql fmtFldRw sl<15'
    call pipeBegin
    call out 'select name, class, dbName, tsName'
                                  /* ,alteredTS, obid, cardf'*/
    call out     'from sysibm.systables'
    call out     "where creator = 'SYSIBM' and name like 'SYSTA%'"
    call out     "fetch first 5 rows only"
    call pipe
    call sql 13
    call pipeLast
    call fmtFldRW sqlGenFmt(fmtAbc, 13, 'sl<15')
    call pipeEnd
    call out 'sql fmtFldSquashRW'
    call pipeBegin
    call out 'select name, class, dbName, tsName'
                                  /* ,alteredTS, obid, cardf'*/
    call out     'from sysibm.systables'
    call out     "where creator = 'SYSIBM' and name like 'SYSTA%'"
    call out     "fetch first 5 rows only"
    call pipe
    call sql 13
    call pipeLast
    call fmtFldSquashRW
    call pipeEnd
    call out 'sqlLn  sl='
    call pipeBegin
    call out 'select char(name, 13),  class, dbName, char(tsName, 8)'
                                  /* ,alteredTS, obid, cardf'*/
    call out     'from sysibm.systables'
    call out     "where creator = 'SYSIBM' and name like 'SYSTA%'"
    call out     "fetch first 5 rows only"
    call pipeLast
    call sqlLn 13, , ,'sl='
    call pipeEnd
    call out 'sqlLn  ---'
    call pipeBegin
    call out 'select name,  class, dbName, tsName'
                                  /* ,alteredTS, obid, cardf'*/
    call out     'from sysibm.systables'
    call out     "where creator = 'SYSIBM' and name like 'SYSTA%'"
    call out     "fetch first 5 rows only"
    call pipeLast
    call sqlLn 13
    call pipeEnd
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSqlEnv
/* copx tstSql end  ***************************************************/
/* copx tstComp begin **************************************************
    test the wsh compiler
***********************************************************************/
tstComp: procedure expose m.
    call compIni
    call tstCompDataConst
    call tstCompDataVars
    call tstCompShell
    call tstCompPrimary
    call tstCompExpr
    call tstCompFile
    call tstCompStmt
    call tstCompDir
    call tstCompObj
    call tstCompDataIO
    call tstCompPipe
    call tstCompRedir
    call tstCompComp
    call tstCompSyntax
    call tstTotal
    return
endProcedure tstComp

tstComp1: procedure expose m.
parse arg ty nm cnt
    c1 = 0
    if cnt = 0 |cnt = '+' then do
        c1 = cnt
        cnt = ''
        end
    call jIni
    src = jBuf()
    call jOpen src, m.j.cWri
    do sx=2 to arg()
        call jWrite src, arg(sx)
        end
    call tstComp2 nm, ty, jClose(src), , c1, cnt
    return
endProcedure tstComp1

tstComp2: procedure expose m.
parse arg nm, spec, src, compSt
    call compIni
    call tst t, nm, compSt
    if src == '' then do
        src = jBuf()
        call tst4dp src'.BUF', mapInline(nm'Src')
        end
    m.t.moreOutOk = abbrev(strip(arg(5)), '+')
    cmp = comp(src)
    call out 'compile' spec',' (m.src.buf.0) 'lines:' m.src.buf.1
    r = compile(cmp, spec)
    noSyn = m.t.errHand = 0
    coErr = m.t.err
    say if( noSyn, "compiled", "*** syntaxed") r ":" objMet(r, 'oRun')
    cnt = 0
    do ax = 5 to max(arg(), 5) while m.t.err = coErr & noSyn
        a1 = strip(arg(ax))
        if a1 == '' & arg() >= 5 then
            iterate
        if abbrev(a1, '+') then do
            m.t.moreOutOk = 1
            a1 = strip(substr(a1, 2))
            end
        if datatype(a1, 'n') then
            cnt = a1
        else if a1 \== '' then
            call err 'tstComp2 bad arg('ax')' arg(ax)
        if cnt = 0 then do
            call mCut 'T.IN', 0
            call out "run without input"
            end
        else  do
            call mAdd mCut('T.IN', 0),
                ,"eins zwei drei", "zehn elf zwoelf?",
                , "zwanzig 21 22 23 24 ... 29|"
            do lx=4 to cnt
                call mAdd 'T.IN', left(lx "line" lx, lx+9, '-')';'
                end
            call out "run with" cnt "inputs"
            end
        m.t.inIx = 0
        call oRun r
        end
    call tstEnd t
    return
endProcedure tstComp2

tstCompDataConst: procedure expose m.
/*<<tstCompDataConst
    ### start tst tstCompDataConst ####################################
    compile =, 8 lines:       Lline one, $** asdf
    run without input
    .      Lline one, .
    line two..
    line threecontinued on 4
    line five  fortsetzung
    line six   fortsetzung
tstCompDataConst */
    call tstComp1 '= tstCompDataConst',
        , '      Lline one, $** asdf',
        , 'line two.',
        , 'line three$*+ bla bla' ,
        , 'continued on 4',
        , 'line five $*( und so',
        , 'weiter $abc $? $''$*)'' $"$*)" und weiter $*) fortsetzung',
        , 'line six  $*( und $*( $** $*( so',
        , 'weiter $abc $? $*)'' $"$*)" und weiter $*) fortsetzung'

/*<<tstCompDataConstBefAftComm1
    ### start tst tstCompDataConstBefAftComm1 #########################
    compile =, 3 lines:     $*(anfangs com.$*)       $*(plus$*) $** x
    run without input
    the only line;
tstCompDataConstBefAftComm1 */
    call tstComp1 '= tstCompDataConstBefAftComm1',
        , '    $*(anfangs com.$*)       $*(plus$*) $** x',
        , 'the only line;',
        , '      $*(end kommentar$*)              '

/*<<tstCompDataConstBefAftComm2
    ### start tst tstCompDataConstBefAftComm2 #########################
    compile =, 11 lines:     $*(anfangs com.$*)       $*(plus$*) $*+ x
    run without input
    the first non empty line;
    .      .
    befor an empty line with comments;
tstCompDataConstBefAftComm2 */

    call tstComp1 '= tstCompDataConstBefAftComm2',
        , '    $*(anfangs com.$*)       $*(plus$*) $*+ x',
        , '    $*(forts Zeile com.$*)       $*(plus$*) $** x',
        , ' $*(comment 2. Zeile$*) $*(plus$*) $*+ x',
        , ' $*(forts Zeile com.$*) $*(plus$*) $** x',
        , 'the first non empty line;',
        , '      ',
        , 'befor an empty line with comments;',
        , ' $*(comment 4. Zeile$*) $*(plus$*) $*+ x',
        , ' $*(forts 4.Zeile com.$*) $*(plus$*) $** x',
        , '      $*(end kommentar$*)              $*+',
        , ' $*(forts end com.$*) $*(plus$*) $** x'
     return
endProcedure tstCompDataComm

tstCompDataVars: procedure expose m.
/*<<tstCompDataVars
    ### start tst tstCompDataVars #####################################
    compile =, 5 lines:       Lline one, $** asdf
    run without input
    .      Lline one, .
    lline zwei output
    lline 3 .
    variable v1 = valueV1 ${v1}= valueV1; .
    .      $-$.{""$v1} = valueV1; .
tstCompDataVars */
    call tstComp1 '= tstCompDataVars',
        , '      Lline one, $** asdf',
        , '   $$ lline zwei output',
        , 'lline 3 $=v1= valueV1 ' ,
        , 'variable v1 = $v1 $"${v1}=" ${  v1  }; ',
        , '      $"$-$.{""""$v1} =" $-$.{""$v1}; '
    return
endProcedure tstCompDataVars

tstCompShell: procedure expose m.
/*<<tstCompShell
    ### start tst tstCompShell ########################################
    compile @, 12 lines:   $$  Lline one, $** asdf
    run without input
    Lline one,
    lline zwei output
    v1 = valueV1 ${v1}= valueV1|
    REXX OUT L5 CONTINUED L6 CONTINUED L7
    L8 ONE
    L9 TWO
    valueV1
    valueV1 valueV2
    out  valueV1 valueV2
    SCHLUSS
tstCompShell */
    call tstComp1 '@ tstCompShell',
        , '  $$  Lline one, $** asdf',
        , '   $$ lline zwei output',
        , '        $=v1= valueV1 ' ,
        , '$$       v1 = $v1 $"${v1}=" ${  v1  }| ' ,
        , 'call out rexx out l5, ' ,
        , '     continued l6 , ' ,
        , '     continued l7   ' ,
        , 'call out l8 one    ' ,
        , 'call out l9 two$=v2=valueV2  ',
        , '$$- $v1  $$- $v1 $v2   ',
        , 'call out   "out "     $v1 $v2   ',
        , '$$-   schluss    '
/*<<tstCompShell2
    ### start tst tstCompShell2 #######################################
    compile @, 13 lines: $@do j=0 to 1 $@¢ $$ do j=$j
    run without input
    do j=0
    after if 0 $@¢ $!
    after if 0 $=@¢ $!
    do j=1
    if 1 then $@¢ a
    a2
    if 1 then $@=¢ b
    b2
    after if 1 $@¢ $!
    after if 1 $=@¢ $!
    end
tstCompShell2 */
    call tstComp1 '@ tstCompShell2',
        , '$@do j=0 to 1 $@¢ $$ do j=$j' ,
        ,     'if $j then $@¢ ',
        ,          '$$ if $j then $"$@¢" a $$a2' ,
        ,          '$!',
        ,     'if $j then $@=¢ ',
        ,          '$$ if $j then $"$@=¢" b $$b2' ,
        ,          '$!',
        ,     'if $j then $@¢ $!' ,
        ,     '$$ after if $j $"$@¢ $!"' ,
        ,     'if $j then $@=¢ $!' ,
        ,     '$$ after if $j $"$=@¢ $!"' ,
        ,     '$!',
        , '$$ end'
    return
endProcedure tstCompShell

tstCompPrimary: procedure expose m.
    call compIni
/*<<tstCompPrimary
    ### start tst tstCompPrimary ######################################
    compile =, 16 lines: Strings $"$""$""""$""" $'$''$''''$'''
    run without input
    Strings $"$""$" $'$''$'
    rexx $-{ 3 * 5 } = 15
    rexx $-¢ 5 * 7 $! = 35
    rexx $-// 7 * 11 $// = 77
    rexx $-/abcEf/ 11 * 13 $/abcEf/ = 143
    data  line three line four  bis hier
    shell line five line six bis hier
    var get   v1 value Eins, v1 value Eins .
    var isDef v1 1, v2 0 .
    #jIn eof 1#
    var read  >1 0 rr undefined
    #jIn eof 2#
    var read  >2 0 rr undefined
    no call abc$-{4*5} $-{efg$-{6*7} abc20 EFG42
    brackets $-{$-{1+9}*$-¢7+3$!*$-/b/5+5$/b/} 1000
    run with 3 inputs
    Strings $"$""$" $'$''$'
    rexx $-{ 3 * 5 } = 15
    rexx $-¢ 5 * 7 $! = 35
    rexx $-// 7 * 11 $// = 77
    rexx $-/abcEf/ 11 * 13 $/abcEf/ = 143
    data  line three line four  bis hier
    shell line five line six bis hier
    var get   v1 value Eins, v1 value Eins .
    var isDef v1 1, v2 0 .
    #jIn 1# eins zwei drei
    var read  >1 1 rr eins zwei drei
    #jIn 2# zehn elf zwoelf?
    var read  >2 1 rr zehn elf zwoelf?
    no call abc$-{4*5} $-{efg$-{6*7} abc20 EFG42
    brackets $-{$-{1+9}*$-¢7+3$!*$-/b/5+5$/b/} 1000
tstCompPrimary */
    call envRemove 'v2'
    call tstComp1 '= tstCompPrimary 3',
        , 'Strings $"$""$""""$"""' "$'$''$''''$'''",
        , 'rexx $"$-{ 3 * 5 } =" $-{ 3 * 5 }' ,
        , 'rexx $"$-¢ 5 * 7 $! =" $-¢ 5 * 7 $!' ,
        , 'rexx $"$-// 7 * 11 $// =" $-// 7 * 11 $//' ,
        , 'rexx $"$-/abcEf/ 11 * 13 $/abcEf/ ="',
            '$-/abcEf/ 11 * 13 $/abcEf/' ,
        , 'data $-=¢ line three',
        , 'line four $! bis hier'  ,
        , 'shell $-@¢ $$ line five',
        , '$$ line six $! bis hier' ,
        , '$= v1  =   value Eins  $=rr=undefined $= eins = 1 ',
        , 'var get   v1 $v1, v1 ${  v1  } ',
        , 'var isDef v1 ${? v${  eins  }  }, v2 ${?v2 } ',
        , 'var read  >1 ${> rr} rr $rr' ,
        , 'var read  >2 ${> rr} rr $rr',
        , 'no call $"abc$-{4*5} $-{efg$-{6*7}"',
            'abc$-{4*5} $-{efg$-{6*7}}',
        , 'brackets $"$-{$-{1+9}*$-¢7+3$!*$-/b/5+5$/b/}"',
            '$-{$-{1+9}*$-¢7+3$!*$-/b/5+5$/b/}'
    return
endProcedure tstCompPrimary

tstCompExpr: procedure expose m.
    call compIni
/*<<tstCompExprStr
    ### start tst tstCompExprStr ######################################
    compile -, 3 lines: $=vv=vvStr
    run without input
    vv=vvStr
    o2String($.$vv)=vvStr
tstCompExprStr */
    call tstComp1 '- tstCompExprStr',
        , '$=vv=vvStr' ,
        , '"vv="$vv' ,
        , '$"o2String($.$vv)="o2String($.$vv)'
/*<<tstCompExprObj
    ### start tst tstCompExprObj ######################################
    compile ., 5 lines: $=vv=vvStr
    run without input
    vv=
    vvStr
    s2o($.$vv)=
    vvStr
tstCompExprObj */
    call tstComp1 '. tstCompExprObj',
        , '$=vv=vvStr' ,
        , '"!vv="', '$vv',
        , '$"s2o($.$vv)="', 's2o($-$vv)'
/*<<tstCompExprDat
    ### start tst tstCompExprDat ######################################
    compile =, 4 lines: $=vv=vvDat
    run without input
    vv=vvDat
    $.$vv= !vvDat
    $.$-{"abc"}=!abc
tstCompExprDat */
    call tstComp1 '= tstCompExprDat',
        , '$=vv=vvDat' ,
        , 'vv=$vv',
        , '$"$.$vv=" $.$vv',
        , '$"$.$-{""abc""}="$.$-{"abc"}'

/*<<tstCompExprRun
    ### start tst tstCompExprRun ######################################
    compile @, 3 lines: $=vv=vvRun
    run without input
    vv=vvRun
    o2string($.$vv)=vvRun
tstCompExprRun */
    call tstComp1 '@ tstCompExprRun',
        , '$=vv=vvRun' ,
        , 'call out "vv="$vv',
        , 'call out $"o2string($.$vv)="o2string($.$vv)'
/*<<tstCompExprCon
tstCompExprCon */
/* wkTst sinnvolle Erweiterung ???
    call tstComp1 '# tstCompExprCon',
        , '$=vv=vvCon' ,
        , 'call out "vv="$vv',
        , 'call out $"o2string($.$vv)="o2string($.$vv)'
*/
    return
endProcedure tstCompExpr

tstCompStmt: procedure expose m.
/*<<tstCompStmt1
    ### start tst tstCompStmt1 ########################################
    compile @, 8 lines: $= v1 = value eins  $= v2  =- 3*5*7 .
    run without input
    data v1 value eins v2 105
    eins
    zwei
    drei
    vier
    fuenf
    elf
    zwoelf  dreiZ
    . vierZ .
    fuenfZ
    lang v1 value eins v2 945
    oRun ouput 1
tstCompStmt1 */
    call pipeIni
    call envPutO 'oRun', oRunner('call out "oRun ouput" (1*1)')
    call envRemove 'v2'
    call tstComp1 '@ tstCompStmt1',
        , '$= v1 = value eins  $= v2  =- 3*5*7 ',
        , '$$ data v1 $v1 v2 ${   v2  }',
        , '$$eins $@¢$$ zwei $$ drei  ',
        , '   $@¢   $! $@{   } $@//   $// $@/q r s /   $/q r s /',
             '       $@/eins/ $@¢ $$vier $! $/eins/ $! $$fuenf',
        , '$$elf $@=¢$@={ zwoelf  dreiZ  }  ',
        , '   $@=¢   $! $@=¢ $@=¢ vierZ $! $! $! $$fuenfZ',
        , '$$- "lang v1" $v1 "v2" ${v2}*9',
        , '$@$oRun""' /* String am schluss -> $$ "" statment||||| */

/*<<tstCompStmt2
    ### start tst tstCompStmt2 ########################################
    compile @, 1 lines: $@for qq $$ loop qq $qq
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    loop qq eins zwei drei
    #jIn 2# zehn elf zwoelf?
    loop qq zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    loop qq zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
tstCompStmt2 */
    call tstComp1 '@ tstCompStmt2 3',
        , '$@for qq $$ loop qq $qq'

/*<<tstCompStmt3
    ### start tst tstCompStmt3 ########################################
    compile @, 9 lines: $$ 1 begin run 1
    2 ct zwei
    ct 4 mit assign .
    run without input
    1 begin run 1
    3 run 3 ctV = ct 4 assign ctV|
    run 5 procCall $@$prCa
    out in proc at 8
    run 6 vor call $@prCa()
    out in proc at 8
    9 run end
    run with 3 inputs
    1 begin run 1
    3 run 3 ctV = ct 4 assign ctV|
    run 5 procCall $@$prCa
    out in proc at 8
    run 6 vor call $@prCa()
    out in proc at 8
    9 run end
tstCompStmt3 */
    call tstComp1 '@ tstCompStmt3 3',
        , '$$ 1 begin run 1',
        , '$@ct $$ 2 ct zwei',
        , '$$ 3 run 3 ctV = $ctV|',
        , '$@ct $@=¢ct 4 mit assign $=ctV = ct 4 assign ctV $!',
        , '$$ run 5 procCall $"$@$prCa" $@$prCa',
        , '$$ run 6 vor call $"$@prCa()"',
        , '$@prCa()',
        , '$@proc prCa $$out in proc at 8',
        , '$$ 9 run end'

/*<<tstCompStmtDo
    ### start tst tstCompStmtDo #######################################
    compile @, 2 lines: ti=0$@do y=3 to 4 $@do 2 $@¢
    run without input
    y=3 ti1 z=7
    y=3 ti1 z=8
    y=3 ti2 z=7
    y=3 ti2 z=8
    y=4 ti3 z=7
    y=4 ti3 z=8
    y=4 ti4 z=7
    y=4 ti4 z=8
tstCompStmtDo */
    call tstComp1 '@ tstCompStmtDo', 'ti=0$@do y=3 to 4 $@do 2 $@¢',
     ,    'ti = ti + 1',
        '$@do $*(sdf$*) z $*(sdf$*) = 7 to 8 $$ y=$y ti$-{ti} z=$z $!'

/*<<tstCompStmtDo2
    ### start tst tstCompStmtDo2 ######################################
    compile @, 7 lines: $$ $-=/sqlSel/
    run without input
    select 1 abc select 2 abc after table .
tstCompStmtDo2 */
    call tstComp1 '@ tstCompStmtDo2',
         , '$$ $-=/sqlSel/',
         ,     '$=ty = abc ',
         ,     '$@do tx=1 to 2 $@=/table/',
         ,          'select $tx $ty',
         , '$/table/',
         ,     '$=ty = abc',
         ,     'after table',
         '$/sqlSel/'
    return
endProcedure tstCompStmt

tstCompSyntax: procedure expose m.
    call tstCompSynPrimary
    call tstCompSynAss
    call tstCompSynRun
    return
endProcedure tstCompSyntax

tstCompSynPrimary: procedure expose m.

/*<<tstCompSynPri1
    ### start tst tstCompSynPri1 ######################################
    compile @, 1 lines: a $ =
    *** err: scanErr pipe or $; expected: compile shell  stopped before+
    . end of input
    .    e 1: last token  scanPosition $ =
    .    e 2: pos 3 in line 1: a $ =
tstCompSynPri1 */
    call tstComp1 '@ tstCompSynPri1 +', 'a $ ='

/*<<tstCompSynPri2
    ### start tst tstCompSynPri2 ######################################
    compile @, 1 lines: a $. {
    *** err: scanErr objRef expected after $. expected
    .    e 1: last token  scanPosition  {
    .    e 2: pos 5 in line 1: a $. {
tstCompSynPri2 */
    call tstComp1 '@ tstCompSynPri2 +', 'a $. {'

/*<<tstCompSynPri3
    ### start tst tstCompSynPri3 ######################################
    compile @, 1 lines: b $-  ¢  .
    *** err: scanErr objRef expected after $- expected
    .    e 1: last token  scanPosition   ¢
    .    e 2: pos 5 in line 1: b $-  ¢
tstCompSynPri3 */
    call tstComp1 '@ tstCompSynPri3 +', 'b $-  ¢  '

/*<<tstCompSynPri4
    ### start tst tstCompSynPri4 ######################################
    compile @, 1 lines: a ${ $*( sdf$*) } =
    *** err: scanErr var name expected
    .    e 1: last token  scanPosition } =
    .    e 2: pos 17 in line 1: a ${ $*( sdf$*) } =
tstCompSynPri4 */
    call tstComp1 '@ tstCompSynPri4 +', 'a ${ $*( sdf$*) } ='

/*<<tstCompSynFile
    ### start tst tstCompSynFile ######################################
    compile @, 1 lines: $@$.<$*( co1 $*) $$abc
    *** err: scanErr block or expr expected for file expected
    .    e 1: last token  scanPosition $$abc
    .    e 2: pos 18 in line 1: $@$.<$*( co1 $*) $$abc
tstCompSynFile */
    call tstComp1 '@ tstCompSynFile +', '$@$.<$*( co1 $*) $$abc'

    return
endProcedure tstCompSynPrimary

tstCompSynAss: procedure expose m.

/*<<tstCompSynAss1
    ### start tst tstCompSynAss1 ######################################
    compile @, 1 lines: $=
    *** err: scanErr variable name after $= expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 3 in line 1: $=
tstCompSynAss1 */
    call tstComp1 '@ tstCompSynAss1 +', '$='

/*<<tstCompSynAss2
    ### start tst tstCompSynAss2 ######################################
    compile @, 2 lines: $=   .
    *** err: scanErr variable name after $= expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $=
tstCompSynAss2 */
    call tstComp1 '@ tstCompSynAss2 +', '$=   ', 'eins'

/*<<tstCompSynAss3
    ### start tst tstCompSynAss3 ######################################
    compile @, 2 lines: $=   $$
    *** err: scanErr variable name after $= expected
    .    e 1: last token  scanPosition $$
    .    e 2: pos 6 in line 1: $=   $$
tstCompSynAss3 */
    call tstComp1 '@ tstCompSynAss3 +', '$=   $$', 'eins'

/*<<tstCompSynAss4
    ### start tst tstCompSynAss4 ######################################
    compile @, 1 lines: $=   eins
    *** err: scanErr = expected after $= "eins"
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $=   eins
tstCompSynAss4 */
    call tstComp1 '@ tstCompSynAss4 +', '$=   eins'

/*<<tstCompSynAss5
    ### start tst tstCompSynAss5 ######################################
    compile @, 1 lines: $=  abc eins $$ = x
    *** err: scanErr = expected after $= "abc eins"
    .    e 1: last token  scanPosition $$ = x
    .    e 2: pos 14 in line 1: $=  abc eins $$ = x
tstCompSynAss5 */
    call tstComp1 '@ tstCompSynAss5 +', '$=  abc eins $$ = x'

/*<<tstCompSynAss6
    ### start tst tstCompSynAss6 ######################################
    compile @, 1 lines: $=  abc =
    *** err: scanErr block or expression after $= "abc" = expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $=  abc =
tstCompSynAss6 */
    call tstComp1 '@ tstCompSynAss6 +', '$=  abc ='

/*<<tstCompSynAss7
    ### start tst tstCompSynAss7 ######################################
    compile @, 1 lines: $=  abc =..
    *** err: scanErr block or expression after $= "abc" = expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 11 in line 1: $=  abc =..
tstCompSynAss7 */
    call tstComp1 '@ tstCompSynAss7 +', '$=  abc =.'
    return
endProcedure tstCompSynAss

tstCompSynRun: procedure expose m.

/*<<tstCompSynRun1
    ### start tst tstCompSynRun1 ######################################
    compile @, 1 lines: $@
    *** err: scanErr objRef expected after $@ expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 3 in line 1: $@
tstCompSynRun1 */
    call tstComp1 '@ tstCompSynRun1 +', '$@'

/*<<tstCompSynRun2
    ### start tst tstCompSynRun2 ######################################
    compile @, 1 lines: $@=
    *** err: scanErr objRef expected after $@ expected
    .    e 1: last token  scanPosition =
    .    e 2: pos 3 in line 1: $@=
tstCompSynRun2 */
    call tstComp1 '@ tstCompSynRun2 +', '$@='

/*<<tstCompSynRun3
    ### start tst tstCompSynRun3 ######################################
    compile @, 1 lines: $@ =
    *** err: scanErr objRef expected after $@ expected
    .    e 1: last token  scanPosition  =
    .    e 2: pos 3 in line 1: $@ =
tstCompSynRun3 */
    call tstComp1 '@ tstCompSynRun3 +', '$@ ='

/*<<tstCompSynFor4
    ### start tst tstCompSynFor4 ######################################
    compile @, 1 lines: $@for
    *** err: scanErr variable name after $@for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $@for
tstCompSynFor4 */
    call tstComp1 '@ tstCompSynFor4 +', '$@for'

/*<<tstCompSynFor5
    ### start tst tstCompSynFor5 ######################################
    compile @, 2 lines: $@for
    *** err: scanErr variable name after $@for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $@for
tstCompSynFor5 */
    call tstComp1 '@ tstCompSynFor5 +', '$@for', a

/*<<tstCompSynFor6
    ### start tst tstCompSynFor6 ######################################
    compile @, 2 lines: a
    *** err: scanErr variable name after $@for expected
    .    e 1: last token  scanPosition $$q
    .    e 2: pos 12 in line 2:  b $@for   $$q
tstCompSynFor6 */
    call tstComp1 '@ tstCompSynFor6 +', 'a', ' b $@for   $$q'

/*<<tstCompSynFor7
    ### start tst tstCompSynFor7 ######################################
    compile @, 3 lines: a
    *** err: scanErr statement after $@for "a" expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 11 in line 2:  b $@for a
tstCompSynFor7 */
    call tstComp1 '@ tstCompSynFor7 +', 'a', ' b $@for a', '  $$q'

/*<<tstCompSynCt8
    ### start tst tstCompSynCt8 #######################################
    compile @, 3 lines: a
    *** err: scanErr ct statement expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 8 in line 2:  b $@ct
tstCompSynCt8 */
    call tstComp1 '@ tstCompSynCt8 +', 'a', ' b $@ct', '  $$q'

/*<<tstCompSynProc9
    ### start tst tstCompSynProc9 #####################################
    compile @, 2 lines: a
    *** err: scanErr proc name expected
    .    e 1: last token  scanPosition $$q
    .    e 2: pos 12 in line 2:  b $@proc  $$q
tstCompSynProc9 */
    call tstComp1 '@ tstCompSynProc9 +', 'a', ' b $@proc  $$q'

/*<<tstCompSynProcA
    ### start tst tstCompSynProcA #####################################
    compile @, 2 lines: $@proc p1
    *** err: scanErr proc statement expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $@proc p1
tstCompSynProcA */
    call tstComp1 '@ tstCompSynProcA +', '$@proc p1', '  $$q'

/*<<tstCompSynCallB
    ### start tst tstCompSynCallB #####################################
    compile @, 1 lines: $@call (roc p1)
    *** err: scanErr procCall, for, do, ct, proc or objRef expected aft+
    er $@
    .    e 1: last token  scanPosition  (roc p1)
    .    e 2: pos 7 in line 1: $@call (roc p1)
tstCompSynCallB */
    call tstComp1 '@ tstCompSynCallB +', '$@call (roc p1)'

/*<<tstCompSynCallC
    ### start tst tstCompSynCallC #####################################
    compile @, 1 lines: $@call( roc p1 )
    *** err: scanErr closing ) expected after $@call(
    .    e 1: last token  scanPosition roc p1 )
    .    e 2: pos 9 in line 1: $@call( roc p1 )
tstCompSynCallC */
    call tstComp1 '@ tstCompSynCallC +', '$@call( roc p1 )'

/*<<tstCompSynCallD
    ### start tst tstCompSynCallD #####################################
    compile @, 2 lines: $@call( $** roc
    *** err: scanErr closing ) expected after $@call(
    .    e 1: last token  scanPosition .
    .    e 2: pos 16 in line 1: $@call( $** roc
tstCompSynCallD */
    call tstComp1 '@ tstCompSynCallD +',
        ,'$@call( $** roc' , ' $*( p1 $*) )'
    return
endProcedure tstCompSynRun

tstCompObj: procedure expose m.
    call tstReset t
    call oIni
    cl = classNew('n tstCompCla u v, f FEINS v, f FZWEI v')
    do rx=1 to 10
        o = oNew(cl)
        m.tstComp.rx = o
        m.o = 'o'rx
        if rx // 2 = 0 then do
            m.o.fEins = 'o'rx'.1'
            m.o.fZwei = 'o'rx'.fZwei'rx
            end
        else do
            m.o.fEins = 'o'rx'.fEins'
            m.o.fZwei = 'o'rx'.2'
            end
        call mAdd 'T.TRANS', m.tstComp.rx '<o'rx'>'
        end

/*<<tstCompObjRef
    ### start tst tstCompObjRef #######################################
    compile @, 13 lines: o1=m.tstComp.1
    run without input
    out .$"string" o1
    string
    out . o1
    tstR: @<o1> isA :tstCompCla union = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .{ o2 }
    tstR: @<o2> isA :tstCompCla union = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    out .¢ o3 $!
    tstR: @<o3> isA :tstCompCla union = o3
    tstR:  .FEINS = o3.fEins
    tstR:  .FZWEI = o3.2
    out .¢ o4 $!
    tstR: @<o4> isA :tstCompCla union = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    out ./-/ o5 $/-/
    tstR: @<o5> isA :tstCompCla union = o5
    tstR:  .FEINS = o5.fEins
    tstR:  .FZWEI = o5.2
tstCompObjRef */

    call tstComp1 '@ tstCompObjRef' ,
        , 'o1=m.tstComp.1',
        , 'o2 = m.tstComp.2' ,
        , '$$ out $".$""string""" o1 $$.$"string"',
        , '$$ out . o1 $$. o1',
        , '$$ out .{ o2 } $$.{ o2 }',
        , '$$ out .¢ o3 $"$!" $$.¢ ', ' m.tstComp.3 ', ' $!',
        , '$$ out .¢ o4 $"$!" $$.¢ ', ' m.tstComp.4 ', ' $!',
        , '$$ out ./-/ o5 $"$/-/" $$./-/  m.tstComp.5 ', ' $/-/'

/*<<tstCompObjRefPri
    ### start tst tstCompObjRefPri ####################################
    compile @, 9 lines: $$ out .$"$.{o1}" $$.$.{ m.tstComp.1 }
    run without input
    out .$.{o1}
    tstR: @<o1> isA :tstCompCla union = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .$.-{o2}
    <o2>
    out .$.={o3}
    m.tstComp.3
    out .$.@{out o4}
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o4> isA :tstCompCla union = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    tstWriteO kindOf ORun oRun end   >>>
    out .$.@¢$$abc $$efg$!
    tstWriteO kindOf ORun oRun begin <<<
    abc
    efg
    tstWriteO kindOf ORun oRun end   >>>
    out .$.@¢o5$!
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o5> isA :tstCompCla union = o5
    tstR:  .FEINS = o5.fEins
    tstR:  .FZWEI = o5.2
    abc
    tstWriteO kindOf ORun oRun end   >>>
tstCompObjRefPri */


    call tstComp1 '@ tstCompObjRefPri' ,
        , '$$ out .$"$.{o1}" $$.$.{ m.tstComp.1 }',
        , '$$ out .$"$.-{o2}" $$.$.-{ m.tstComp.2 }',
        , '$$ out .$"$.={o3}" $$.$.={ m.tstComp.3 }',
        , '$$ out .$"$.@{out o4}" $$.$.@{ call outO m.tstComp.4 }',
    , '$$ out .$"$.@¢$$abc $$efg$!" $$.$.@¢ $$abc ', ' ', ' $$efg $!',
        , '$$ out .$"$.@¢o5$!" $$.$.@¢ $$.m.tstComp.5', '$$abc $!'

/*<<tstCompObjRefFile
    ### start tst tstCompObjRefFile ###################################
    compile @, 7 lines: $$ out .$".<.¢o1!" $$.$.<.¢ m.tstComp.1 $!
    run without input
    out ..<.¢o1!
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @LINE isA :tstCompCla union = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .<$.-{o2}
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @LINE isA :tstCompCla union = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$.<{o3}
    tstWriteO kindOf JRW jWriteNow begin <<<
    m.tstComp.3
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$.<@{out o4}
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @LINE isA :tstCompCla union = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$<@¢$$abc $$efg$!
    tstWriteO kindOf JRW jWriteNow begin <<<
    abc
    efg
    tstWriteO kindOf JRW jWriteNow end   >>>
tstCompObjRefFile */

    call tstComp1 '@ tstCompObjRefFile' ,
        , '$$ out .$".<.¢o1!" $$.$.<.¢ m.tstComp.1 $!',
        , '$$ out .$"<$.-{o2}" $$.$.<.{ m.tstComp.2 }',
        , '$$ out .$"$.<{o3}" $$.$.<{ m.tstComp.3 }',
        , '$$ out .$"$.<@{out o4}" $$.$.<@{ call outO m.tstComp.4 }',
    , '$$ out .$"$<@¢$$abc $$efg$!" $$.$.<@¢ $$abc ', ' ', ' $$efg $!'

/*<<tstCompObjRun
    ### start tst tstCompObjRun #######################################
    compile @, 4 lines: $$ out .$"$@¢o1!" $$.$.@¢ $$. m.tstComp.1 $!
    run without input
    out .$@¢o1!
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o1> isA :tstCompCla union = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstWriteO kindOf ORun oRun end   >>>
    out .$<@¢$$abc $$efg$!
    tstWriteO kindOf JRW jWriteNow begin <<<
    abc
    efg
    tstWriteO kindOf JRW jWriteNow end   >>>
tstCompObjRun */
    call tstComp1 '@ tstCompObjRun' ,
        , '$$ out .$"$@¢o1!" $$.$.@¢ $$. m.tstComp.1 $!',
    , '$$ out .$"$<@¢$$abc $$efg$!" $$.$.<@¢ $$abc ', ' ', ' $$efg $!'

    m.t.trans.0 = 0
    return
/*<<tstCompObj
    ### start tst tstCompObj ##########################################
    compile @, 8 lines: o1=m.tstComp.1
    run without input
    out . o1
    tstR: @<o1> isA :tstCompCla union = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .{ o2 }
    tstR: @<o2> isA :tstCompCla union = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei
    out .¢ o1, o2!
    tstR: @<o1> isA :tstCompCla union = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstR: @<o2> isA :tstCompCla union = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei
tstCompObj */
    call tstComp1 '@ tstCompObj' ,
        , 'o1=m.tstComp.1',
        , 'o2 = m.tstComp.2' ,
        , '$$ out . o1 $$. o1',
        , '$$ out .{ o2 } $$.{ o2 }',
    , '$$ out .¢ o1, o2!$; $@<.¢  m.tstComp.1  ', '  m.tstComp.2  $!'
    return
    m.t.trans.0 = 0
endProcedure tstCompObj

tstCompDataIO: procedure expose m.
/*<<tstCompDataHereData
    ### start tst tstCompDataHereData #################################
    compile =, 13 lines:  herdata $@#/stop/    .
    run without input
    . herdata .
    heredata 1 $x
    heredata 2 $y
    nach heredata
    . herdata ¢ .
    heredata 1 xValue
    heredata 2 yValueY
    nach heredata ¢
    . herdata { .
    HEREDATA 1 xValue
    heredata 2 yValueY
    nach heredata {
tstCompDataHereData */
    call tstComp1 '= tstCompDataHereData',
        , ' herdata $@#/stop/    ',
        , 'heredata 1 $x',
        , 'heredata 2 $y',
        , '$/stop/ $$ nach heredata',
        , ' herdata ¢ $@=/stop/    ',
        , '$=x=xValue$=y=yValueY',
        , 'heredata 1 $x',
        , 'heredata 2 $y',
        , '$/stop/ $$ nach heredata ¢',
        , ' herdata { $@/st/',
        , '; call out heredata 1 $x',
        , '$$heredata 2 $y',
        , '$/st/ $$ nach heredata {'
/*<<tstCompDataIO
    ### start tst tstCompDataIO #######################################
    compile =, 5 lines:  input 1 $@$.<$dsn $*+.
    run without input
    . input 1 .
    readInp line 1                       .
    readInp line 2                       .
    . nach dsn input und nochmals mit & .
    readInp line 1                       .
    readInp line 2                       .
    . und schluiss..
tstCompDataIO */
    dsn = word(tstPdsMbr(tstFilename('lib37', 'r'), 'readInp'), 1)
    dsnFB = dsn tstFB('::F37', 0)
    b = jBuf(tstFB('readInp line 1', 37),
            ,tstFB('readInp line 2', 37))
    extFd = tstFB('&dsn('dsn') dd(xyz)', 0)
    extFd = tstFB(dsn 'dd(xyz) ::f', 0)
    if extFd = '' then
        extFd = dsn
    call jCat file(dsnFB), b
    call envPut 'dsn', dsn
    call tstComp1 '= tstCompDataIO',
        , ' input 1 $@$.<$dsn $*+',
        , tstFB('::f', 0),
        , ' nach dsn input und nochmals mit & ' ,
        , '         $@$.<' extFD,
        , ' und schluiss.'
    return
endProcedure tstCompDataIO

tstObjVF: procedure expose m.
parse arg v, f
    obj  = oNew(classNew('n TstClassVF u v, f FLD1 v'))
    m.obj = if(f=='','val='v, v)
    m.obj.fld1 = if(f=='','FLD1='v, f)
    return obj
endProcedure tstObjVF

tstCompFile: procedure expose m.
/*<<tstCompFileBloSrc
    $=vv=value-of-vv
    ###file from empty # block
    $@<#¢
        $!
    ###file from 1 line # block
    $@<#¢
    the only $ix+1/0 line $vv
    $!
    ###file from 2 line # block
    $@<#¢
        first line /0 $*+ no comment
        second and last line $$ $wie
    $!
    ===file from empty = block
    $@<=¢     $*+ comment
        $!
    ===file from 1 line = block
    $@<=¢ the only line $!
    ===file from 2 line = block
    $@<=¢ first line$** comment
        second and last line  $!
    ---file from empty - block
    $@<-/s/
        $/s/
    ---file from 1 line - block
    $@<-/s/ the only "line" (1*1) $/s/
    ---file from 2 line = block
    $@<-// first "line" (1+0)
        second   and   "last  line" (1+1)  $//
    ...file from empty . block
    $@<.¢
        $!
    ...file from 1 line . block
    $@<.¢ tstObjVF('v-Eins', '1-Eins') $!
    ...file from 2 line . block
    $@<.¢ tstObjVF('v-Elf', '1-Elf')
        tstObjVF('zwoelf')  $!
    ...file from 3 line . block
    $@<.¢ tstObjVF('einUndDreissig')
            s2o('zweiUndDreissig' o2String($vv))
            tstObjVF('dreiUndDreissig')  $!
    @@@file from empty @ block
    $@<@¢
        $!
    $=noOutput=before
    @@@file from nooutput @ block
    $@<@¢ nop
        $=noOutput = run in block $!
    @@@nach noOutput=$noOutput
    @@@file from 1 line @ block
    $@<@¢ $$. tstObjVF('w-Eins', 'w1-Eins') $!
    @@@file from 2 line @ block
    $@<@¢ $$.tstObjVF('w-Elf', 'w1-Elf')
        y='zwoelf' $$-y  $!
    @@@file from 3 line @ block
    $@<@¢ $$.tstObjVF('w einUndDreissig')    $$ +
    zweiUndDreissig $$ 33 $vv$!
    {{{ empty { block
    $@<{      }
    {{{ empty { block with comment
    $@<{    $*+.
          }
    {{{ one line { block
    $@<{ the only $"{...}" line $*+.
        $vv  }
    {{{ one line -{ block
    $@<-{ the only $"-{...}"  "line" $vv  }
    {{{ empty #{ block
    $@<#{            }
    {{{ one line #{ block
    $@<#{ the only $"-{...}"  "line" $vv ${vv${x}}  }
tstCompFileBloSrc */
/*<<tstCompFileBlo
    ### start tst tstCompFileBlo ######################################
    compile =, 70 lines: $=vv=value-of-vv
    run without input
    ###file from empty # block
    ###file from 1 line # block
    the only $ix+1/0 line $vv
    ###file from 2 line # block
    first line /0 $*+ no comment
    second and last line $$ $wie
    ===file from empty = block
    ===file from 1 line = block
    . the only line .
    ===file from 2 line = block
    . first line
    second and last line  .
    ---file from empty - block
    ---file from 1 line - block
    THE ONLY line 1
    ---file from 2 line = block
    FIRST line 1
    SECOND AND last  line 2
    ...file from empty . block
    ...file from 1 line . block
    tstR: @LINE isA :TstClassVF union = v-Eins
    tstR:  .FLD1 = 1-Eins
    ...file from 2 line . block
    tstR: @LINE isA :TstClassVF union = v-Elf
    tstR:  .FLD1 = 1-Elf
    tstR: @LINE isA :TstClassVF union = val=zwoelf
    tstR:  .FLD1 = FLD1=zwoelf
    ...file from 3 line . block
    tstR: @LINE isA :TstClassVF union = val=einUndDreissig
    tstR:  .FLD1 = FLD1=einUndDreissig
    zweiUndDreissig value-of-vv
    tstR: @LINE isA :TstClassVF union = val=dreiUndDreissig
    tstR:  .FLD1 = FLD1=dreiUndDreissig
    @@@file from empty @ block
    @@@file from nooutput @ block
    @@@nach noOutput=run in block
    @@@file from 1 line @ block
    tstR: @LINE isA :TstClassVF union = w-Eins
    tstR:  .FLD1 = w1-Eins
    @@@file from 2 line @ block
    tstR: @LINE isA :TstClassVF union = w-Elf
    tstR:  .FLD1 = w1-Elf
    zwoelf
    @@@file from 3 line @ block
    tstR: @LINE isA :TstClassVF union = val=w einUndDreissig
    tstR:  .FLD1 = FLD1=w einUndDreissig
    zweiUndDreissig
    33 value-of-vv
    {{{ empty { block
    {{{ empty { block with comment
    {{{ one line { block
    the only {...} line value-of-vv
    {{{ one line -{ block
    THE ONLY -{...} line value-of-vv
    {{{ empty #{ block
    .            .
    {{{ one line #{ block
    . the only $"-{...}"  "line" $vv ${vv${x}}  .
tstCompFileBlo */
    call tstComp2 'tstCompFileBlo', '='
    m.t.trans.0 = 0

/*<<tstCompFileObjSrc
    $=vv=value-vv-1
    $=fE=.$.<¢ $!
    $=f2=.$.<.¢s2o("f2 line 1" o2String($vv))
         tstObjVF("f2 line2") $!
    ---empty file $"$@<$fE"
    $@$fE
    ---file with 2 lines $"$@<$f2"
    $@<.$f2
    $=vv=value-vv-2
    ---file with 2 lines $"$@<$f2"
    $@<.$f2
    $= dsn =- word(tstPdsMbr(tstFilename('libvb', 'r'), 'fileObj'),1) +
                tstFB('::V', 0)
    $@¢
        fi=jOpen(file($dsn),'>')
        call jWrite fi, 'line one on' $"$dsn"
        call jWrite fi, 'line two on' $"$dsn"
        call jClose fi
    $!
    ---file on disk out
    $@$.<$dsn
tstCompFileObjSrc */
/*<<tstCompFileObj
    ### start tst tstCompFileObj ######################################
    compile =, 20 lines: $=vv=value-vv-1
    run without input
    ---empty file $@<$fE
    ---file with 2 lines $@<$f2
    f2 line 1 value-vv-1
    tstR: @LINE isA :TstClassVF union = val=f2 line2
    tstR:  .FLD1 = FLD1=f2 line2
    ---file with 2 lines $@<$f2
    f2 line 1 value-vv-2
    tstR: @LINE isA :TstClassVF union = val=f2 line2
    tstR:  .FLD1 = FLD1=f2 line2
    ---file on disk out
    line one on $dsn
    line two on $dsn
tstCompFileObj */
    call tstComp2 'tstCompFileObj', '='

    return
endProcedure tstCompFile

tstCompPipe: procedure expose m.
/*<<tstCompPipe1
    ### start tst tstCompPipe1 ########################################
    compile @, 1 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    (1 eins zwei drei 1)
    #jIn 2# zehn elf zwoelf?
    (1 zehn elf zwoelf? 1)
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    (1 zwanzig 21 22 23 24 ... 29| 1)
    #jIn eof 4#
tstCompPipe1 */
    call tstComp1 '@ tstCompPipe1 3',
        , ' call pipePreSuf "(1 ", " 1)"'
/*<<tstCompPipe2
    ### start tst tstCompPipe2 ########################################
    compile @, 2 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    ¢2 (1 eins zwei drei 1) 2!
    ¢2 (1 zehn elf zwoelf? 1) 2!
    ¢2 (1 zwanzig 21 22 23 24 ... 29| 1) 2!
tstCompPipe2 */
    call tstComp1 '@ tstCompPipe2 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| call pipePreSuf "¢2 ", " 2!"'

/*<<tstCompPipe3
    ### start tst tstCompPipe3 ########################################
    compile @, 3 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <3 ¢2 (1 eins zwei drei 1) 2! 3>
    <3 ¢2 (1 zehn elf zwoelf? 1) 2! 3>
    <3 ¢2 (1 zwanzig 21 22 23 24 ... 29| 1) 2! 3>
tstCompPipe3 */
    call tstComp1 '@ tstCompPipe3 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| call pipePreSuf "¢2 ", " 2!"',
        , ' $| call pipePreSuf "<3 ", " 3>"'

/*<<tstCompPipe4
    ### start tst tstCompPipe4 ########################################
    compile @, 7 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <3 ¢222 ¢221 ¢21 ¢20 (1 eins zwei drei 1) 20! 21! 221! 222! 3>
    <3 ¢222 ¢221 ¢21 ¢20 (1 zehn elf zwoelf? 1) 20! 21! 221! 222! 3>
    <3 ¢222 ¢221 ¢21 ¢20 (1 zwanzig 21 22 23 24 ... 29| 1) 20! 21! 221!+
    . 222! 3>
tstCompPipe4 */
    call tstComp1 '@ tstCompPipe4 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| $@¢    call pipePreSuf "¢20 ", " 20!"',
        ,        ' $| call pipePreSuf "¢21 ", " 21!"',
        ,        ' $| $@¢      call pipePreSuf "¢221 ", " 221!"',
        ,                 ' $| call pipePreSuf "¢222 ", " 222!"',
        ,     '$!     $! ',
        , ' $| call pipePreSuf "<3 ", " 3>"'
    return
endProcedure tstCompPipe

tstCompRedir: procedure expose m.
/*<<tstCompRedir
    ### start tst tstCompRedir ########################################
    compile @, 6 lines:  $>}eins $@for vv $$<$vv> $; .
    run without input
    #jIn eof 1#
    output eins .
    output piped zwei .
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    output eins <eins zwei drei> <zehn elf zwoelf?> <zwanzig 21 22 23 2+
    4 ... 29|>
    output piped zwei ab<eins zwei drei>yz ab<zehn elf zwoelf?>yz ab<zw+
    anzig 21 22 23 24 ... 29|>yz
tstCompRedir */
    call pipeIni
    call envRemove 'eins'  /* alte Variable loswerden */
    dsn = word(tstPdsMbr(tstFilename('libvb', 'r'), 'redir1'), 1)
    call envPut 'dsn', dsn
    call tstComp1 '@ tstCompRedir 3' ,
        , ' $>}eins $@for vv $$<$vv> $; ',
        , ' $$ output eins $-=¢$@$eins$!$; ',
        , ' $@for ww $$b${ww}y ',
        , '     $>$-{ $dsn } 'tstFB('::v', 0),
        ,         '$| call pipePreSuf "a", "z" $<}eins',
        , ' $; $$ output piped zwei $-=¢$@<$dsn$! '
    return
endProcedure tstCompRedir

tstCompComp: procedure expose m.
/*<<tstCompCompShell
    ### start tst tstCompCompShell ####################################
    compile @, 5 lines: $$compiling shell $; $= rrr =. $.compile@ $<#/+
    aaa/
    run without input
    compiling shell
    running einmal
    RUN 1 COMPILED einmal
    #jIn eof 1#
    running zweimal
    RUN 1 COMPILED zweimal
    #jIn eof 2#
    run with 3 inputs
    compiling shell
    running einmal
    RUN 1 COMPILED einmal
    #jIn 1# eins zwei drei
    compRun eins zwei dreieinmal
    #jIn 2# zehn elf zwoelf?
    compRun zehn elf zwoelf?einmal
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    compRun zwanzig 21 22 23 24 ... 29|einmal
    #jIn eof 4#
    running zweimal
    RUN 1 COMPILED zweimal
    #jIn eof 5#
tstCompCompShell */
    call tstComp1 '@ tstCompCompShell 3',
        ,  "$$compiling shell $; $= rrr =. $.compile@ $<#/aaa/",
        ,  "call out run 1*1*1 compiled $cc; $@for v $$ compRun $v$cc",
        ,  "$/aaa/ $;",
        ,  "$=cc=einmal $$ running $cc $@$rrr",
        ,  "$=cc=zweimal $$ running $cc $@$rrr"
/*<<tstCompCompData
    ### start tst tstCompCompData #####################################
    compile @, 5 lines: $$compiling data $; $= rrr =. $.compile=  +
        $<#/aaa/
    run without input
    compiling data
    running einmal
    call out run 1*1*1 compiled einmal
    running zweimal
    call out run 1*1*1 compiled zweimal
    run with 3 inputs
    compiling data
    running einmal
    call out run 1*1*1 compiled einmal
    running zweimal
    call out run 1*1*1 compiled zweimal
tstCompCompData */
    call tstComp1 '@ tstCompCompData 3',
        ,  "$$compiling data $; $= rrr =. $.compile=  $<#/aaa/",
        ,  "call out run 1*1*1 compiled $cc",
        ,  "$/aaa/ $;",
        ,  "$=cc=einmal $$ running $cc $@$rrr",
        ,  "$=cc=zweimal $$ running $cc $@$rrr"
    return
endProcedure tstCompComp

tstCompDir: procedure expose m.
/*<<tstCompDirSrc
  'in src v1='$v1
  $#@ call out 'src @ out v1='$v1
  $#. s2o('src . v1=')
       $v1
  $#- 'src - v1='$v1
  $#= src = v1=$v1
tstCompDirSrc  */
/*<<tstCompDir
    ### start tst tstCompDir ##########################################
    compile call out 'before v1='$v1 $=v1=eins $#. s2o('. v1='$-$v1) $#+
    @ call out '@ v1='$v1 $#= = v1=$v1 $#- '- v1='$v1, 6 lines: 'in src+
    . v1='$v1
    run without input
    before v1=v1Before
    .. v1=eins
    @ v1=eins
    . = v1=eins .
    - v1=eins
    in src v1=eins
    src @ out v1=eins
    src . v1=
    eins
    src - v1=eins
    . src = v1=eins
tstCompDir */
    call envPut 'v1', 'v1Before'
    call tstComp2 'tstCompDir', "call out 'before v1='$v1 $=v1=eins" ,
        "$#. s2o('. v1='$-$v1) $#@ call out '@ v1='$v1" ,
        "$#= = v1=$v1 $#- '- v1='$v1"
/*<<tstCompDirPiSrc
  zeile 1 v1=$v1
  zweite Zeile vor $"$@$#-"
  $@$#-
  $'zeile drei nach $@$#- v1='v1
  vierte und letzte Zeile
tstCompDirPiSrc  */
/*<<tstCompDirPi
    ### start tst tstCompDirPi ########################################
    compile call pipePreSuf '<','>' $=v1=eiPi $<.$.$#=, 5 lines: zeile +
    1 v1=$v1
    run without input
    <zeile 1 v1=eins>
    <zweite Zeile vor $@$#->
    <zeile drei nach $@$#- v1=V1>
    <VIERTE UND LETZTE ZEILE>
tstCompDirPi */
    call tstComp2 'tstCompDirPi',
            , "call pipePreSuf '<','>' $=v1=eiPi $<.$.$#="
    return
endProcedure tstCompDir
/* copx tstComp end   *************************************************/
/* copx tstBase begin **************************************************
     test the basic classes
***********************************************************************/
tstBase: procedure expose m.
    call tstTstSay
    call oIni
    call tstM
    call tstMap
    call tstMapVia
    call tstClass
    call tstClass2
    call tstO
    call jIni
    call tstJSay
    call tstJ
    call tstJ2
    call catIni
    call tstCat
       call pipeIni
    CALL TstEnv
    CALL TstEnvCat
    call tstPipe
    call tstEnvVars
    call tstTotal
    call tstPipeLazy
    call tstEnvClass
    call tstFile /* reimplent zOs ||| */
    call tstFileList
    call tstFmt
    call tstTotal
    call scanIni
    call tstScan
    call ScanReadIni
    call tstScanRead
    call tstScanWin
    call tstScanSQL
    call tstTotal
    return
endProcedure tstBase

/*--- test the tst Module, check the output visually  ----------------*/
tstTstSay: procedure expose m.
    call tstIni
    oldErr = m.tst.err
    oldNames = m.tst.errNames
    say '+++ tstTstSay start with' oldErr 'totErrs and',
            m.tst.tests 'tests'
/*<<tstTstSayEins
    ### start tst tstTstSayEins #######################################
    test eins einzige testZeile
tstTstSayEins */

    call tst x, 'tstTstSayEins'
    call tstOut x, "test eins einzige testZeile"
    call tstEnd x, 'err 0'

/*<<tstTstSayZwei
    ### start tst tstTstSayZwei #######################################
    zwei 1. testZeile
    zwei 2. und letzte testZeile
tstTstSayZwei */

    call tst x, 'tstTstSayZwei'
    call tstOut x, "zwei 1. testZeile"
    call tstOut x, "zwei 2. und letzte testZeile"
    call tstEnd x, 'err 0'

    call tst x, 'tstTstSayZwei'
    call tstOut x, "zwei 1. testZeile "    /* ein space zuviel */
    call tstOut x, "zwei 2. und letzte testZeile"
    call tstOut x, "zwei 3. zuviel"
    call tstEnd x, 'err 3'

/*<<tstTstSayDrei
    ### start tst tstTstSayDrei #######################################
    drei 1. testZeile vor empty Zeile
    ..
    drei 3. testZeile vor 10 space
    .          .
    drei 5. testZeile ziemlich lang 66                                 +
    .                                77  5   10   15++++++++++++++++++++
    .+++++++++++++++++++++++++++++++++++++++++++.
tstTstSayDrei */

    call tst y, 'tstTstSayDrei'
    call tstOut y, 'drei 1. testZeile vor empty Zeile'
    call tstOut y, ''
    call tstOut y, 'drei 3. testZeile vor 10 space'
    call tstOut y, left('', 10)
    call tstOut y, 'drei 5. testZeile ziemlich lang',
                left(66, 66) left('77  5   10   15', 77, '+')
    call tstEnd y, 'err 0'
    if m.y.err <> 0 then
        call err '+++ tstTstSay errs' m.x.err 'expected' 0
    if m.tst.err <> oldErr + 3 then
        call err '+++ tstTstSay totErrs' m.tst.err 'expected' 3
    say '+++ tstTstSay end Ok with' m.tst.err 'totErrs and' ,
        m.tst.tests 'tests'
    m.tst.err = oldErr
    m.tst.errNames = oldNames
    return
endProcedure tstTstSay

tstM: procedure expose m.
/*<<tstM
    ### start tst tstM ################################################
    symbol m.b LIT
    mInc b 2 m.b 2
    symbol m.a LIT
    mAdd a A.2
    mAdd a A.3
    m.a: 3: 1=eins 2=zwei 3=drei 4=M.A.4
    m.c: 5: 1=c vorAddSt a 2=eins 3=zwei 4=drei 5=c nacAddSt a 6=M.C.6
    tstMSubj1 tstMSubj1 added listener 1
    tstMSubj1 notified list1 1 arg tstMSubj1 registered list
    tstMSubj1 notified list1 1 arg tstMSubj1 notify 11
    tstMSubj1 tstMSubj1 added listener 2
    tstMSubj1 notified list2 2 arg tstMSubj1 registered list
    tstMSubj1 notified list1 1 arg tstMSubj1 notify 12
    tstMSubj1 notified list2 2 arg tstMSubj1 notify 12
    tstMSubj2 tstMSubj2 added listener 1
    tstMSubj2 notified list1 1 arg tstMSubj2 registered list
    tstMSubj2 tstMSubj2 added listener 2
    tstMSubj2 notified list2 2 arg tstMSubj2 registered list
    tstMSubj1 notified list1 1 arg tstMSubj1 notify 13
    tstMSubj1 notified list2 2 arg tstMSubj1 notify 13
    tstMSubj2 notified list1 1 arg tstMSubj2 notify 24
    tstMSubj2 notified list2 2 arg tstMSubj2 notify 24
tstM */


    drop m.b m.a m.a.0 m.a.1 m.a.2
    call tst t, 'tstM'
    call tstOut t, 'symbol m.b' symbol('m.b')
    m.b = 1
    call tstOut t, 'mInc b' mInc(b) 'm.b' m.b
    call tstOut t, 'symbol m.a' symbol('m.a')
    call tstOut t, 'mAdd a' mAdd(mCut(a, 0), 'eins', 'zwei')
    call tstOut t, 'mAdd a' mAdd(a, 'drei')
    call tstOut t, 'm.a:' m.a.0': 1='m.a.1 '2='m.a.2 '3='m.a.3 '4='m.a.4
    call mAdd mCut(c, 0), 'c vorAddSt a'
    call mAddSt c, a
    call mAdd c, 'c nacAddSt a'
    call tstOut t, 'm.c:' m.c.0': 1='m.c.1 '2='m.c.2 '3='m.c.3,
                    '4='m.c.4 '5='m.c.5 '6='m.c.6
    s1 = 'tstMSubj1'
    s2 = 'tstMSubj2'
    /* we must unregister for the second test */
    drop m.m.subLis.s1 m.m.subLis.s1.0 m.m.subLis.s2 m.m.subLis.s2.0
    call mRegisterSubject s1,
        , 'call tstOut t, "'s1'" subject "added listener" listener;',
            'call mNotify1 "'s1'", listener, "'s1' registered list"'
    call mRegister s1,
        , 'call tstOut t, subject "notified list1" listener "arg" arg'
    call mNotify s1, s1 'notify 11'
    call mRegister s1,
        , 'call tstOut t, subject "notified list2" listener "arg" arg'
    call mRegister s2,
        , 'call tstOut t, subject "notified list1" listener "arg" arg'
    call mRegister s2,
        , 'call tstOut t, subject "notified list2" listener "arg" arg'
    call mNotify s1, s1 'notify 12'
    call mRegisterSubject s2,
        , 'call tstOut t, "'s2'" subject "added listener" listener;',
            'call mNotify1 "'s2'", listener, "'s2' registered list"'
    call mNotify s1, s1 'notify 13'
    call mNotify s2, s2 'notify 24'

    call tstEnd t
    return
endProcedure tstM

tstMap: procedure expose m.
/*<<tstMap
    ### start tst tstMap ##############################################
    mapNew m keys m-keys 0
    map m zwei --> 2
    map m Zwei is not defined
    map stem m-keys 4
    map m eins --> 1
    map m zwei --> 2
    map m drei --> 3
    map m vier --> 4
    *** err: duplicate key eins in map m
    map m zwei is not defined
    q 2 zw dr
    map stem Q 2
    map Q zw --> 2Q
    map Q dr --> 3Q
    map stem m 3
    map m eins --> 1
    map m zwei --> 2PUT
    map m vier --> 4PUT
    *** err: duplicate key zwei in map m
    tstMapLong eins keys 3
    tstMapLong zweiMal keys 48
    tstMapLong dreiMal keys 93
    tstMapLong vier    keys 138
    tstMapLong <fuenf> keys 188
    tstMap clear keys 0
    inline1 3
    inline1 1 ==    inline1 eins==
    inline1 2 ====
    inline1 3 ==    inline1 drei==
    inline2 1 1 ==    inline2 eins==
    inline3 nicht gefunden
tstMap */
/*<<tstMapInline1
    inline1 eins

    inline1 drei
tstMapInline1 */
/*<<tstMapInline2
    inline2 eins
tstMapInline2 */

    call tst t, 'tstMap'
    m = mapNew('K')
    ky = mapKeys(m)
    call mAdd t'.TRANS', m 'm', ky 'm-keys'
    call tstOut t, 'mapNew' m 'keys' ky m.ky.0
    call mapAdd m, 'eins', 1
    call mapAdd m, 'zwei', 2
    call mapAdd m, 'drei', 3
    call mapAdd m, 'vier', 4
    call tstMapShow m, 'zwei'
    call tstMapShow m, 'Zwei'
    call tstMapShowSt m, mapKeys(m)
    call mapAdd m, 'eins', 1
    call mapReset m, '='
    call tstMapShow m, 'zwei'
    call mapAdd m, 'eins', 1
    call mapAdd m, 'zwei', 2
    call mapPut m, 'zwei', 2Put
    call mapPut m, 'vier', 4Put
    call mapReset q, '='
    call mapAdd q, 'zw', 2q
    call mapAdd q, 'dr', 3q
    call tstOut t, 'q' m.q.0 m.q.1 m.q.2
    call tstMapShowSt q, mapKeys(q)
    call tstMapShowSt m, mapKeys(m)
    call mapAdd m, 'zwei', 2addDup
    call tstMapLong m, 'eins'      ,201, 2000, -2, 2
    call tstMapLong m, 'zweiMal'   ,201, 2000, -2, 2
    call tstMapLong m, 'dreiMal'   ,201, 2000,  2,-2
    call tstMapLong m, 'vier   '   ,2010, 201, -2, 2
    call tstMapLong m, '<fuenf>'   ,2010, 201,  2,-2
    call mapClear m
    keys = mapKeys(m)
    call tstOut t, 'tstMap clear keys' m.keys.0
    i = mapInline('tstMapInline1')
    call tstOut t, 'inline1' m.i.0
    do x=1 to m.i.0
        call tstOut t, 'inline1' x '=='m.i.x'=='
        end
    i = mapInline('tstMapInline2')
    call tstOut t, 'inline2' m.i.0 '1 =='m.i.1'=='
    call tstOut t, 'inline3' mapInline('tstMapInline3',
              , 'nicht gefunden')
    call tstEnd t
    return
endProcedure tstMap

tstMapLong: procedure expose m.
parse arg m, w, f1, t1, f2, t2
    if f1 < t1 then
        b1 = 201
    else
        b1 = -201
    if f2 < t2 then
        b2 = 1
    else
        b2 = -1
    lo = copies(w, 2100 % length(w))
    keys = mapKeys(m)
    keyCn = m.keys.0
    call tstOut t, 'tstMapLong' w 'keys' keyCn
    do x = f1 by b1 to t1
        do y = x+f2 by b2 to x+t2
            k = left(lo, y)
            if mapHasKey(m, k) then
                call err 'mapLong hasKey before' w y
            call mapAdd m, k, w y
            if \ mapHasKey(m, k) then
                call err 'mapLong \ hasKey after' w y
            if mapGet(m, k) \== w y then
                call err 'mapLong \ get <> ' w y
            keys = mapKeys(m)
            if keyCn + 1 \= m.keys.0 then
                call err 'mapLong keys .0 <> ' w y
            keyCn = m.keys.0
            if k \== m.keys.keyCn then
                call err 'mapLong keys . ' keyCn '<>' w y
            end
        end
    return
endProcedure tstMapLong

tstMapVia: procedure expose m.
/*<<tstMapVia
    ### start tst tstMapVia ###########################################
    map M K --> A
    mapVia(m, K)      A
    *** err: missing m.A at 3 in mapVia(M, K|)
    mapVia(m, K|)     M.A
    mapVia(m, K|)     valAt m.a
    mapVia(m, K|)     valAt m.a
    *** err: missing m.A.aB at 5 in mapVia(M, K|aB)
    mapVia(m, K|aB)   M.A.aB
    mapVia(m, K|aB)   valAt m.A.aB
    *** err: missing m.valAt m.a at 4 in mapVia(M, K||)
    mapVia(m, K||)    M.valAt m.a
    mapVia(m, K||)    valAt m.valAt m.a
    mapVia(m, K||F)   valAt m.valAt m.a.F
tstMapVia */
    call tst t, 'tstMapVia'
    u = 'A.aB'
    v = 'valAt m.a'
    drop m.a m.u m.v m.v.f
    call mapReset m, 'K'
    call mapAdd m, k, a
    call tstMapShow m, k
    call tstOut t, 'mapVia(m, K)     ' mapVia(m, 'K')
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    m.a = v
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    call tstOut t, 'mapVia(m, K|aB)  ' mapVia(m, 'K|aB')
    m.u = 'valAt m.'u
    call tstOut t, 'mapVia(m, K|aB)  ' mapVia(m, 'K|aB')
    call tstOut t, 'mapVia(m, K||)   ' mapVia(m, 'K||')
    v = m.a
    m.v = 'valAt m.'v
    m.v.f = 'valAt m.'v'.F'
    call tstOut t, 'mapVia(m, K||)   ' mapVia(m, 'K||')
    call tstOut t, 'mapVia(m, K||F)  ' mapVia(m, 'K||F')
    call tstEnd t
    return
endProcedure tstMapVia

tstMapShow: procedure expose m.
parse arg a, key
    if mapHasKey(a, key) then
        call tstOut t, 'map' a key '-->' mapGet(a, key)
    else
        call tstOut t, 'map' a key 'is not defined'
    return
endProcedure tstMapShow

tstMapShowSt: procedure expose m.
parse arg a, st
    call tstOut t, 'map stem' st m.st.0
    do wx=1 to m.st.0
        call tstMapShow a, m.st.wx
        end
    return
endProcedure tstMapShow

tstClass2: procedure expose m.
/*<<tstClass2old
    ### start tst tstClass2 ###########################################
    @CLASS.8 isA :class union
    . choice n union
    .  .NAME = class
    .  .CLASS refTo @CLASS.7 :class union
    .   choice u stem 9
    .    .1 refTo @CLASS.15 :class union
    .     choice c union
    .      .NAME = v
    .      .CLASS refTo @CLASS.3 :class union
    .       choice v = v
    .    .2 refTo @CLASS.16 :class union
    .     choice c union
    .      .NAME = r
    .      .CLASS refTo @CLASS.11 :class union
    .       choice f union
    .        .NAME = CLASS
    .        .CLASS refTo @CLASS.10 :class union
    .         choice r .CLASS refTo @CLASS.8 done :class @CLASS.8
    .    .3 refTo @CLASS.17 :class union
    .     choice c union
    .      .NAME = s
    .      .CLASS refTo @CLASS.11 done :class @CLASS.11
    .    .4 refTo @CLASS.19 :class union
    .     choice c union
    .      .NAME = u
    .      .CLASS refTo @CLASS.18 :class union
    .       choice s .CLASS refTo @CLASS.10 done :class @CLASS.10
    .    .5 refTo @CLASS.20 :class union
    .     choice c union
    .      .NAME = f
    .      .CLASS refTo @CLASS.12 :class union
    .       choice u stem 2
    .        .1 refTo @CLASS.9 :class union
    .         choice f union
    .          .NAME = NAME
    .          .CLASS refTo @CLASS.3 done :class @CLASS.3
    .        .2 refTo @CLASS.11 done :class @CLASS.11
    .    .6 refTo @CLASS.21 :class union
    .     choice c union
    .      .NAME = n
    .      .CLASS refTo @CLASS.12 done :class @CLASS.12
    .    .7 refTo @CLASS.22 :class union
    .     choice c union
    .      .NAME = c
    .      .CLASS refTo @CLASS.12 done :class @CLASS.12
    .    .8 refTo @CLASS.23 :class union
    .     choice c union
    .      .NAME = m
    .      .CLASS refTo @CLASS.14 :class union
    .       choice u stem 2
    .        .1 refTo @CLASS.9 done :class @CLASS.9
    .        .2 refTo @CLASS.13 :class union
    .         choice f union
    .          .NAME = MET
    .          .CLASS refTo @CLASS.3 done :class @CLASS.3
    .    .9 refTo @CLASS.26 :class union
    .     choice c union
    .      .NAME = w
    .      .CLASS refTo @CLASS.25 :class union
    .       choice n union
    .        .NAME = w
    .        .CLASS refTo @CLASS.24 :class union
    .         choice r .CLASS refTo @CLASS.3 done :class @CLASS.3
tstClass2old */
/*<<tstClass2
    ### start tst tstClass2 ###########################################
    @CLASS.13 isA :class union
    . choice n union
    .  .NAME = class
    .  .CLASS refTo @CLASS.12 :class union
    .   choice u stem 10
    .    .1 refTo @CLASS.20 :class union
    .     choice c union
    .      .NAME = v
    .      .CLASS refTo @CLASS.3 :class union
    .       choice v = v
    .    .2 refTo @CLASS.22 :class union
    .     choice c union
    .      .NAME = w
    .      .CLASS refTo @CLASS.21 :class union
    .       choice w } LASS.21
    .    .3 refTo @CLASS.23 :class union
    .     choice c union
    .      .NAME = o
    .      .CLASS refTo @CLASS.10 :class union
    .       choice o obj has no class @o
    .    .4 refTo @CLASS.24 :class union
    .     choice c union
    .      .NAME = r
    .      .CLASS refTo @CLASS.16 :class union
    .       choice f union
    .        .NAME = CLASS
    .        .CLASS refTo @CLASS.15 :class union
    .         choice r .CLASS refTo @CLASS.13 done :class @CLASS.13
    .    .5 refTo @CLASS.25 :class union
    .     choice c union
    .      .NAME = s
    .      .CLASS refTo @CLASS.16 done :class @CLASS.16
    .    .6 refTo @CLASS.27 :class union
    .     choice c union
    .      .NAME = u
    .      .CLASS refTo @CLASS.26 :class union
    .       choice s .CLASS refTo @CLASS.15 done :class @CLASS.15
    .    .7 refTo @CLASS.28 :class union
    .     choice c union
    .      .NAME = f
    .      .CLASS refTo @CLASS.17 :class union
    .       choice u stem 2
    .        .1 refTo @CLASS.14 :class union
    .         choice f union
    .          .NAME = NAME
    .          .CLASS refTo @CLASS.3 done :class @CLASS.3
    .        .2 refTo @CLASS.16 done :class @CLASS.16
    .    .8 refTo @CLASS.29 :class union
    .     choice c union
    .      .NAME = n
    .      .CLASS refTo @CLASS.17 done :class @CLASS.17
    .    .9 refTo @CLASS.30 :class union
    .     choice c union
    .      .NAME = c
    .      .CLASS refTo @CLASS.17 done :class @CLASS.17
    .    .10 refTo @CLASS.31 :class union
    .     choice c union
    .      .NAME = m
    .      .CLASS refTo @CLASS.19 :class union
    .       choice u stem 2
    .        .1 refTo @CLASS.14 done :class @CLASS.14
    .        .2 refTo @CLASS.18 :class union
    .         choice f union
    .          .NAME = MET
    .          .CLASS refTo @CLASS.3 done :class @CLASS.3
tstClass2 */

    call oIni
    call tst t, 'tstClass2'
    call classOut , m.class.class
    call tstEnd t
    return
endProcedure tstClass2

tstClass: procedure expose m.
/*<<tstClass
    ### start tst tstClass ############################################
    Q n =className= tstClassTf12
    Q.eins.zwei v ==> M.Q.eins.zwei
    *** err: basicClass v end of Exp expected: v tstClassTf12 .
    R n =className= uststClassTf12
    R n =className= uststClassTf12in
    R n =className= tstClassTf12
    R.eins.zwei v ==> M.R.eins.zwei
    R s =stem.0= 2
    R.1 r ==> M.R.1
    R.1 n =className= tstClassTf12
    R.1.eins.zwei v ==> M.R.1.eins.zwei
    R.2 r ==> M.R.2
    R.2 n =className= tstClassTf12
    R.2.eins.zwei v ==> M.R.2.eins.zwei
    S s =stem.0= 2
    S.1.eins v ==> M.S.1.eins
    S.1 m =met-metA--> say "metA"
    S.1 m =met-metB--> say "metB"
    S.2.zwei v ==> M.S.2.zwei
    S.2 m =met-metA--> say "metA"
    S.2 m =met-metB--> say "metB"
    class of mutate qq tstClassTf12
tstClass */

    f = 'eins.zwei'
    e = 'eins'
    z = 'zwei'
    drop m.q.f m.r.f m.r.0 m.r.1 m.r.1.f m.r.2 m.r.2.f
    drop m.s.0 m.s.1 m.s.1.e m.s.2 m.s.2.z
    call classIni
    call tst t, 'tstClass'
    t1  =  classNew('n tstClassTf12 f eins f zwei v')
    call tstClassOut t, t1, q
    z = m.class.0
    errDef = 'n tstClassB n tstClassC u tstClassTf12,' ,
        's u v tstClassTf12'
    if class4name(errDef, ' ') == ' ' then
        t2 = classNew(errDef)
    else    /* the second time we do not get the error anymore,
                because the err did not abend | */
        call tstOut t,'*** err: basicClass v' ,
             'end of Exp expected: v tstClassTf12 '
    t2 = classNew('n uststClassTf12 n uststClassTf12in u tstClassTf12',
        , classNew('s u r, tstClassTf12'))
    m.r.0 = 2
    call tstClassOut t, t2, r
    t3 = classNew('s u c 1 f eins v, c 2 f zwei v',
        ,'m', 'metA say "metA"', 'metB say "metB"')
    m.s.0 = 2
    m.s.1 = 1
    m.s.2 = 2
    call tstClassOut t, t3, s
    call oMutate qq, 'tstClassTf12'
    tt = objClass(qq)
    call tstOut t, 'class of mutate qq' m.tt.name
    call tstEnd t
    return
endProcedure tstClass

tstClassOut: procedure expose m.
parse arg o, t, a
    if pos(m.t, 'vr') > 0 then
        return tstOut(o, a m.t '==>' m.a)
    if m.t == 'n' then do
        call tstOut o, a m.t '=className=' m.t.name
        return tstClassOut(o, m.t.class, a)
        end
    if m.t == 'f' then
        return tstClassOut(o, m.t.class, a'.'m.t.name)
    if m.t = 'u' then do
        do ux=1 to m.t.0
            call tstClassOut o, m.t.ux, a
            end
        return 0
        end
    if m.t = 's' then do
        call tstOut o, a m.t '=stem.0=' m.a.0
        do ux=1 to m.a.0
            call tstClassOut o, m.t.class, a'.'ux
            end
        return 0
        end
    if m.t = 'c' then do
        if m.t.name = m.a then
            call tstClassOut o, m.t.class, a
         return 0
        end
    if m.t = 'm' then
        return tstOut(o, a m.t '=met-'m.t.name'-->' m.t.met)
    call err 'bad class type' m.t
endProcedure tstClassOut

tstO: procedure expose m.
/*<<tstO
    ### start tst tstO ################################################
    class method calls of TstOEins
    .  met Eins.eins M
     FLDS of <obj e of TstOEins> .FEINS, .FZWEI
     methodcalls of object e of TstOEins
    .  met Eins.eins <obj e of TstOEins>
    .  met Eins.zwei <obj e2 of TstOEins>
    *** err: no method nein in class TstOEins of object <obj e+
    . of TstOEins>
    *** err: no class found for object noObj
    class method calls of TstOEins
    .  met Elf.zwei M
    FLDS of <obj f of TstOElf> .FEINS, .FZWEI, .FELF
    methodcalls of object f of TstOElf
    .  met Eins.eins <obj f of TstOElf>
    .  met Elf.zwei <obj f of TstOElf>
    .  met Elf.drei <obj f of TstOElf>
    methodcalls of object f cast To TstOEins
    .  met Eins.eins <obj f of TstOElf>
    .  met Eins.zwei <obj f of TstOElf>
    FLDS of <cast(f, TstOEins)> .FEINS, .FZWEI
    oCopy c1 of class TstOEins, c2
    C1 n =className= TstOEins
    C1.FEINS v ==> M.C1.FEINS
    C1.FZWEI v ==> M.C1.FZWEI
    C1 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C1 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    C2 n =className= TstOEins
    C2.FEINS v ==> M.C1.FEINS
    C2.FZWEI v ==> M.C1.FZWEI
    C2 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C2 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    oCopy c3 of class TstOElf, c4
    C4 n =className= TstOElf
    C4 n =className= TstOEins
    C4.FEINS v ==> M.C3.FEINS
    C4.FZWEI v ==> M.C3.FZWEI
    C4 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C4 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    C4.FELF r ==> M.C3.FELF
    C4 m =met-zwei--> call tstOut t, "  met Elf.zwei" m
    C4 m =met-drei--> call tstOut t, "  met Elf.drei" m
    tEinsDop <class TstOEins>
    oRun 7*3 21
    oRun 12*12 144
tstO */

    call tst t, 'tstO'
    tEins = classNew('n TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
    call mAdd t.trans, tEins '<class TstOEins>'
    call tstOut t, 'class method calls of TstOEins'
    interpret classMet('TstOEins', 'eins')
    e = oNew('TstOEins')
    e2 = oNew('TstOEins')
    call mAdd t.trans, e '<obj e of TstOEins>'
    call mAdd t.trans, e2 '<obj e2 of TstOEins>'
    tEins = classNew('n TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
    call tstOut t, 'FLDS of' e mCat(oFlds(e), ', ')
    call tstOut t, 'methodcalls of object e of TstOEins'
    call tstOmet e, 'eins'
    call tstOmet e2, 'zwei'
    call tstOmet e, 'nein'
    call tstOmet 'noObj', 'nein'
    tElf = classNew('n TstOElf u TstOEins, f FELF r', 'm',
        , 'zwei call tstOut t, "  met Elf.zwei" m',
        , 'drei call tstOut t, "  met Elf.drei" m')
    call tstOut t, 'class method calls of TstOEins'
    interpret classMet('TstOElf', 'zwei')
    f = oNew('TstOElf')
    call mAdd t.trans, f '<obj f of TstOElf>'
    call tstOut t, 'FLDS of' f mCat(oFlds(f), ', ')
    call tstOut t, 'methodcalls of object f of TstOElf'
    call tstOmet f, 'eins'
    call tstOmet f, 'zwei'
    call tstOmet f, 'drei'
    call tstOut t, 'methodcalls of object f cast To TstOEins'
    call tstOmet oCast(f, 'TstOEins'), 'eins'
    call tstOmet oCast(f, 'TstOEins'), 'zwei'
    call tstOut t, 'FLDS of <cast(f, TstOEins)>',
        mCat(oFlds(oCast(f, 'TstOEins')), ', ')

    call oMutate c1, 'TstOEins'
    call tstOut t, 'oCopy c1 of class TstOEins, c2'
    call tstClassOut t, tEins, c1
    call oCopy c1, c2
    call tstClassOut t, tEins, c2
    call tstOut t, 'oCopy c3 of class TstOElf, c4'
    call oMutate c3, 'TstOElf'
    call oCopy c3, c4
    call tstClassOut t, tElf, c4

    tEinsDop = classNew('n TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
    call tstOut t, 'tEinsDop' tEinsDop
    e3 = oNew('TstOEins')
    if e3 <<= e | e3 <<= e2 then
        call err 'doppelt reinitialised objects'

    rr = oRunner('return 7 * 3')
    call tstOut t, 'oRun 7*3' oRun(rr)
    r12 = oRunner('return 12 * 12')
    call tstOut t, 'oRun 12*12' oRun(r12)
    call tstEnd t
    return
endProcedure tstO

tstOmet: procedure expose m.
parse arg m, met
    interpret objMet(m, met)
    return
endProcedure tstOmet

tstJSay: procedure expose m.
/*<<tstJSay
    ### start tst tstJSay #############################################
    *** err: call of abstract method jOpen(<obj j of JRW>, openArg)
    *** err: jWrite(<obj j of JRW>, writeArg) but not opened w
    *** err: can only write JRWOut.jOpen(<obj s of JRWOut>, open<Arg)
    *** err: jWrite(<obj s of JRWOut>, write s vor open) but not opened+
    . w
    *** err: can only read JRWEof.jOpen(<obj e of JRWEof>, open>Arg)
    *** err: jRead(<obj e of JRWEof>, XX) but not opened r
    read e vor open 0 m.xx valueBefore
    read e nach open 0 m.xx M.XX
    out eins
    #jIn 1# tst in line 1 eins ,
    out zwei in 1 vv=readAdrVV
    #jIn 2# tst in line 2 zwei ;   .
    out drei in 1 vv=readAdrVV Schluss
tstJSay */

    call jIni
    call tst t, 'tstJSay'
    j = oNew('JRW')
    call mAdd t'.TRANS', j '<obj j of JRW>'
    call jOpen j, 'openArg'
    call jWrite j, 'writeArg'
    s = oNew('JRWOut')
    call mAdd t'.TRANS', s '<obj s of JRWOut>'
    call jOpen s, 'open<Arg'
    call jWrite s, 'write s vor open'
    call jOpen s, '>'
    call jWrite s, 'write s nach open'
    e = oNew('JRWEof')
    call mAdd t'.TRANS', e '<obj e of JRWEof>'
    call jOpen e, 'open>Arg'
    m.xx = 'valueBefore'
    call tstOut t, 'read e vor open' jRead(e, xx) 'm.xx' m.xx
    call jOpen e
    call tstOut t, 'read e nach open' jRead(e, xx) 'm.xx' m.xx
    call out 'out eins'
    vv = 'readAdrVV'
    m.vv = 'readAdrVVValueBefore'
    call out 'out zwei in' in(vv) 'vv='vv
    m.vv = 'readAdrVVValueBefore'
    call out 'out drei in' in(vv) 'vv='vv 'Schluss'
    call tstEnd t
    return
endProcedure tstJSay

tstJ: procedure expose m.
/*<<tstJ
    ### start tst tstJ ################################################
    out eins
    #jIn 1# tst in line 1 eins ,
    1 in() tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    2 in() tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    3 in() tst in line 3 drei .schluss..
    #jIn eof 4#
    in() 3 reads vv VV
    *** err: already opened jOpen(<buf b>, <)
    line buf line one
    line buf line two
    line buf line three
    line buf line four
    *** err: jWrite(<buf b>, buf line five while reading) but not opene+
    d w
tstJ */

    call jIni
    call tst t, "tstJ"
    b = jOpen(jBuf(), '>')
    call mAdd t'.TRANS', b '<buf b>'
    call out 'out eins'
    do lx=1 by 1 while in(var)
        call out lx 'in()' m.var
        end
    call out 'in()' (lx-1) 'reads vv' vv
    call jWrite b, 'buf line one'
    call mAdd m.b.stem, 'buf line two', 'buf line three'
    call jWrite b, 'buf line four'
    call jOpen b, '<'
    call jClose b
    call jOpen b, '<'
    do while (jRead(b, line))
        call out 'line' m.line
        end
    call jWrite b, 'buf line five while reading'
    call jClose b
    call tstEnd t
    return
endProcedure tstJ

tstJ2: procedure expose m.
/*<<tstJ2
    ### start tst tstJ2 ###############################################
    class1 <Tst?1 class> <Tst?1 name>
    class2 <Tst?1 class> <Tst?1 name>
    class3 <Tst?1 class> <Tst?1 name>
    b read EINS feld eins, ZWEI feld zwei, DREI feld drei
    b read EINS feld eins, ZWEI feld zwei 2, DREI feld drei
    c read EINS feld eins, ZWEI feld zwei, DREI drei cat 1
    tstR: @CCC isA :<Tst?1 name> union
    tstR:  .EINS = feld eins
    tstR:  .ZWEI = feld zwei
    tstR:  .DREI = drei cat 1
    c read EINS feld eins, ZWEI feld zwei 2, DREI drei cat 2
    tstR: @CCC isA :<Tst?1 name> union
    tstR:  .EINS = feld eins
    tstR:  .ZWEI = feld zwei 2
    tstR:  .DREI = drei cat 2
tstJ2 */

    call tst t, "tstJ2"
    ty = classNew('n Tst* u f EINS v, f ZWEI v, f DREI v')
    call mAdd t'.TRANS', ty '<Tst?1 class>'
    call mAdd t'.TRANS', m.ty.name '<Tst?1 name>'
    call tstOut t, 'class1' ty m.ty.name
    ty = classNew('n Tst* u f EINS v, f ZWEI v, f DREI v')
    call tstOut t, 'class2' ty m.ty.name
    ty = classNew('n Tst*  u f  EINS v, f  ZWEI v, f  DREI v')
    call tstOut t, 'class3' ty m.ty.name
    call oMutate qq, m.ty.name
    m.qq.eins = 'feld eins'
    m.qq.zwei = 'feld zwei'
    m.qq.drei = 'feld drei'
    b = jBuf()
    call jOpen b, '>'
    call jWriteO b, qq
    m.qq.zwei = 'feld zwei 2'
    call jWriteO b, qq
    call jOpen jClose(b), '<'
    c = jOpen(jBuf(), '>')
    do xx=1 while jReadO(b, res)
        call out 'b read EINS' m.res.eins', ZWEI' m.res.zwei,
                                     || ', DREI' m.res.drei
        m.res.drei = 'drei cat' xx
        call jWriteO c, res
        end
    call jOpen jClose(c), '<'
    do while jReadO(c, ccc)
        call out 'c read EINS' m.ccc.eins', ZWEI' m.ccc.zwei,
                                     || ', DREI' m.ccc.drei
        call outO ccc
        end
    call tstEnd t
    return
endProcedure tstJ2

tstCat: procedure expose m.
/*<<tstCat
    ### start tst tstCat ##############################################
    catRead 1 line 1
    catRead 2 line 2
    catRead 3 line 3
    appRead 1 line 1
    appRead 2 line 2
    appRead 3 line 3
    appRead 4 append 4
    appRead 5 append 5
tstCat */
    call catIni
    call tst t, "tstCat"
    i = cat(jBuf('line 1', 'line 2'), jBuf('line 3'))
    call jOpen i, m.j.cRead
    do lx=1 by 1 while jRead(i, v)
        call tstOut t, 'catRead' lx m.v
        end
    call jOpen jClose(i), m.j.cApp
    call jWrite i, 'append 4'
    call jWrite i, 'append 5'
    call jOpen jClose(i), m.j.cRead
    do lx=1 by 1 while jRead(i, v)
        call tstOut t, 'appRead' lx m.v
        end
    call tstEnd t
    return
endProcedure tstCat

tstEnv: procedure expose m.
    call pipeIni
/*<<tstEnv
    ### start tst tstEnv ##############################################
    before pipeBeLa
    after pipeEnd
    *** err: jWrite(<jBuf c>, write nach pop) but not opened w
    #jIn 1# tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    before writeNow 2 c --> std
    before writeNow 1 b --> c
    b line eins
    b zwei |
    nach writeNow 1 b --> c
    add nach pop
    after push c only
    tst in line 1 eins ,
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    nach writeNow 2 c --> std
    *** err: jWrite(<jBuf c>, ) but not opened w
tstEnv */

    call tst t, "tstEnv"
    c = jBuf()
    call mAdd t'.TRANS', c '<jBuf c>'
    call out 'before pipeBeLa'
    b = jBuf("b line eins", "b zwei |")
    call pipeBeLa '<' b, '>' c
    call out 'before writeNow 1 b --> c'
    call pipeWriteNow
    call out 'nach writeNow 1 b --> c'
    call pipeEnd
    call out 'after pipeEnd'
    call jWrite c, 'write nach pop'
    call mAdd c'.BUF', 'add nach pop'
    call pipeBeLa '>>' c
    call out 'after push c only'
    call pipeWriteNow
    call pipeEnd
    call pipeBeLa '<' c
    call out 'before writeNow 2 c --> std'
    call pipeWriteNow
    call out 'nach writeNow 2 c --> std'
    call pipeEnd
    call jWrite c
    call tstEnd t
    return
endProcedure tstEnv

tstEnvCat: procedure expose m.
    call pipeIni
/*<<tstEnvCat
    ### start tst tstEnvCat ###########################################
    c1 contents
    c1 line eins |
    before writeNow 1 b* --> c*
    b1 line eins|
    b2 line eins
    b2 zwei |
    c2 line eins |
    after writeNow 1 b* --> c*
    c2 contents
    c2 line eins |
tstEnvCat */

    call tst t, "tstEnvCat"

    b0= jBuf()
    b0= jBuf()
    b1= jBuf("b1 line eins|")
    b2 = jBuf("b2 line eins", "b2 zwei |")
    c1 = jBuf("c1 line eins |")
    c2 = jBuf("c2 line eins |")
    call pipeBeLa '<' b0, '<' b1, '<' b2, '<' c2,'>>' c1

    call out 'before writeNow 1 b* --> c*'
    call pipeWriteNow
    call out 'after writeNow 1 b* --> c*'
    call pipeEnd
    call out 'c1 contents'
    call pipeBeLa '<' c1
    call pipeWriteNow
    call pipeEnd
    call pipeBeLa '<' c2
    call out 'c2 contents'
    call pipeWriteNow
    call pipeEnd
    call tstEnd t
    return
endProcedure tstEnvCat

tstPipe: procedure expose m.
    call pipeIni
/*<<tstPipe
    ### start tst tstPipe #############################################
    .+0 vor pipeBegin
    #jIn 1# tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    .+7 nach pipeLast
    ¢7 +6 nach pipe 7!
    ¢7 +2 nach pipe 7!
    ¢7 +4 nach nested pipeLast 7!
    ¢7 (4 +3 nach nested pipeBegin 4) 7!
    ¢7 (4 (3 +1 nach pipeBegin 3) 4) 7!
    ¢7 (4 (3 tst in line 1 eins , 3) 4) 7!
    ¢7 (4 (3 tst in line 2 zwei ;    3) 4) 7!
    ¢7 (4 (3 tst in line 3 drei .schluss. 3) 4) 7!
    ¢7 (4 (3 +1 nach writeNow vor pipe 3) 4) 7!
    ¢7 (4 +3 nach preSuf vor nested pipeLast 4) 7!
    ¢7 +4 nach preSuf vor nested pipeEnd 7!
    ¢7 +5 nach nested pipeEnd vor pipe 7!
    ¢7 +6 nach writeNow vor pipeLast 7!
    .+7 nach writeNow vor pipeEnd
    .+8 nach pipeEnd
tstPipe */

    say 'x0' m.pipe.0
    call tst t, 'tstPipe'
    call out '+0 vor pipeBegin'
    say 'x1' m.pipe.0
    call pipeBegin
    call out '+1 nach pipeBegin'
    call pipeWriteNow
    call out '+1 nach writeNow vor pipe'
    call pipe
    call out '+2 nach pipe'
    call pipeBegin
    call out '+3 nach nested pipeBegin'
    call pipePreSuf '(3 ', ' 3)'
    call out '+3 nach preSuf vor nested pipeLast'
    call pipeLast
    call out '+4 nach nested pipeLast'
    call pipePreSuf '(4 ', ' 4)'
    call out '+4 nach preSuf vor nested pipeEnd'
    call pipeEnd
    call out '+5 nach nested pipeEnd vor pipe'
    call pipe
    call out '+6 nach pipe'
    call pipeWriteNow
    say 'out +6 nach writeNow vor pipeLast'
    call out '+6 nach writeNow vor pipeLast'
    call pipeLast
    call out '+7 nach pipeLast'
    call pipePreSuf '¢7 ', ' 7!'
    call out '+7 nach writeNow vor pipeEnd'
    call pipeEnd
    call out '+8 nach pipeEnd'
    say 'xx' m.pipe.0
    call tstEnd t
    return
endProcedure tstPipe

tstEnvVars: procedure expose m.
    call pipeIni
/*<<tstEnvVars
    ### start tst tstEnvVars ##########################################
    put v1 value eins
    v1 hasKey 1 get value eins
    v2 hasKey 0
    via v1.fld via value
    one to theBur
    two to theBuf
tstEnvVars */
    call tst t, "tstEnvVars"
    call envRemove 'v2'
    m.tst.adr1 = 'value eins'
    put1 = envPutO('v1', oMutate(tst'.'adr1, m.class.classV))
    call tstOut t, 'put v1' m.put1
    call tstOut t, 'v1 hasKey' envHasKey('v1') 'get' envGet('v1')
    call tstOut t, 'v2 hasKey' envHasKey('v2')
    if 0 then
        call tstOut t, 'v2 get'    envGet('v2')
    m.put1.fld = 'via value'
    call tstOut t, 'via v1.fld' envVia('v1|FLD')
    call pipeBeLa '>' s2o('}theBuf')
    call out 'one to theBur'
    call out 'two to theBuf'
    call pipeEnd
    call pipeBeLa '<' s2o('}theBuf')
    call pipeWriteNow
    call pipeEnd
    call tstEnd t
    return
endProcedure tstEnvVars

tstPipeLazy: procedure expose m.
    call pipeIni
/*<<tstPipeLazy
    ### start tst tstPipeLazy #########################################
    a1 vor pipeBegin loop lazy 0 writeNow *** <class TstPipeLazyBuf>
    bufOpen <
    bufClose
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    a5 vor 2 writeNow in inIx 4
    a2 vor writeNow jBuf
    jBuf line 1
    jBuf line 2
    a3 vor writeNow in inIx 1
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    a4 vor barLast inIx 4
    a6 vor barEnd inIx 4
    a7 nach barEnd lazy 0 writeNow ***
    b1 vor barBegin lazy 0 writeNow *** <class TstPipeLazyRdr>
    RdrOpen <
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    rdrClose
    b4 vor writeNow
    b2 vor writeNow rdr inIx 2
    jRead lazyRdr
    tst in line 3 drei .schluss..
    jRead lazyRdr
    b3 vor barLast inIx 4
    b5 vor barEnd inIx 4
    b6 nach barEnd lazy 0 writeNow ***
    a1 vor pipeBegin loop lazy 1 writeAllFramed *** +
        .<class TstPipeLazyBuf>
    a5 vor 2 writeAllFramed in inIx 0
    a2 vor writeAllFramed jBuf
    bufOpen <
    jBuf line 1
    jBuf line 2
    bufClose
    a3 vor writeAllFramed in inIx 0
    #jIn 1# tst in line 1 eins ,
    tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    #jIn eof 4#
    a4 vor barLast inIx 0
    a6 vor barEnd inIx 4
    a7 nach barEnd lazy 1 writeAllFramed ***
    b1 vor barBegin lazy 1 writeAllFramed *** <class TstPipeLazyRdr>
    b4 vor writeAllFramed
    b2 vor writeAllFramed rdr inIx 1
    RdrOpen <
    *** err: jWrite(PIPE.framedNoOut, jRead lazyRdr) but not opened w
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    *** err: jWrite(PIPE.framedNoOut, jRead lazyRdr) but not opened w
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    *** err: jWrite(PIPE.framedNoOut, jRead lazyRdr) but not opened w
    #jIn eof 4#
    rdrClose
    b3 vor barLast inIx 1
    b5 vor barEnd inIx 4
    b6 nach barEnd lazy 1 writeAllFramed ***
tstPipeLazy */
    call tst t, "tstPipeLazy"
    do lz=0 to 1
        if lz then
            w = 'writeAllFramed'
        else
            w = 'writeNow'
        m.t.inIx = 1-lz
        ty = classNew('n TstPipeLazyBuf u JBuf', 'm',
            , 'jOpen call tstOut "T", "bufOpen" opt;',
                'return jOpen(oCast(m, "JBuf"), opt)',
            , 'jClose call tstOut "T", "bufClose";',
                'return jClose(oCast(m, "JBuf"), opt)')
        if \ lz then
             call mAdd t'.TRANS', ty '<class TstPipeLazyBuf>'
        call out 'a1 vor pipeBegin loop lazy' lz w '***' ty
        call pipeBegin
        call out 'a2 vor' w 'jBuf'
        b = oMutate(jBuf('jBuf line 1','jBuf line 2'),
                ,'TstPipeLazyBuf')
        interpret 'call pipe'w 'b'
        call out 'a3 vor' w 'in inIx' m.t.inIx
        interpret 'call pipe'w
        call out 'a4 vor barLast inIx' m.t.inIx
        call pipeLast
        call out 'a5 vor 2' w 'in inIx' m.t.inIx
        interpret 'call pipe'w
        call out 'a6 vor barEnd inIx' m.t.inIx
        call pipeEnd
        call out 'a7 nach barEnd lazy' lz w '***'

        ty = classNew('n TstPipeLazyRdr u JRW', 'm',
            , 'jOpen call tstOut "T", "RdrOpen" opt;m.m.jReading=1',
            , 'jRead call out "jRead lazyRdr"; return in(var);',
            , 'jClose call tstOut "T",  "rdrClose";')
        if \ lz then
             call mAdd t'.TRANS', ty '<class TstPipeLazyRdr>'

        r = oNew('TstPipeLazyRdr')
         if lz then
             call mAdd t'.TRANS', r '<lazyRdr>'
     m.t.inIx = 2-lz
        call out 'b1 vor barBegin lazy' lz w '***' ty
     call pipeBegin
        if lz then
             call mAdd t'.TRANS', m.j.out '<barBegin out>'
     call out 'b2 vor' w 'rdr inIx' m.t.inIx
     interpret 'call pipe'w 'r'
        call out 'b3 vor barLast inIx' m.t.inIx
     call pipeLast
        call out 'b4 vor' w
        interpret 'call pipe'w
        call out 'b5 vor barEnd inIx' m.t.inIx
        call pipeEnd
     call out 'b6 nach barEnd lazy' lz w '***'
        end
    call tstEnd t
    return
endProcedure tstPipeLazy

tstEnvClass: procedure expose m.
    call pipeIni
/*<<tstEnvClass
    ### start tst tstEnvClass #########################################
    a0 vor pipeBegin loop lazy 0 writeNow *** TY
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    a5 vor writeNow
    a1 vor jBuf()
    a2 vor writeNow b
    tstR: @LINE isA :TstEnvClass10 union
    tstR:  .f11 = M.<o20 of TstEnvClass10>.f11
    tstR:  .F12 = value F12 of o1 <o20 of TstEnvClass10>
    tstR:  .f13 = M.<o20 of TstEnvClass10>.f13
    WriteO o2
    tstR: @LINE isA :TstEnvClass20 union = valueO2Lazy0
    tstR:  .f24 = M.<o20 of TstEnvClass20>.f24
    tstR:  .F25 = value F25 of o20 <o20 of TstEnvClass20>
    a3 vor writeNow
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    a4 vor barLast inIx 4
    a6 vor barEnd
    a7 nach barEnd lazy 0 writeNow ***
    a0 vor pipeBegin loop lazy 1 writeAllFramed *** TY
    a5 vor writeAllFramed
    a1 vor jBuf()
    a2 vor writeAllFramed b
    tstR: @LINE isA :TstEnvClass10 union
    tstR:  .f11 = M.<o21 of TstEnvClass10>.f11
    tstR:  .F12 = value F12 of o1 <o21 of TstEnvClass10>
    tstR:  .f13 = M.<o21 of TstEnvClass10>.f13
    WriteO o2
    tstR: @LINE isA :TstEnvClass20 union = valueO2Lazy1
    tstR:  .f24 = M.<o21 of TstEnvClass20>.f24
    tstR:  .F25 = value F25 of o21 <o21 of TstEnvClass20>
    a3 vor writeAllFramed
    #jIn 1# tst in line 1 eins ,
    tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    #jIn eof 4#
    a4 vor barLast inIx 0
    a6 vor barEnd
    a7 nach barEnd lazy 1 writeAllFramed ***
tstEnvClass */

    call tst t, "tstEnvClass"
    do lz=0 to 1
        if lz then
            w = 'writeAllFramed'
        else
            w = 'writeNow'
        m.t.inIx = 1-lz
        t10 = classNew('n TstEnvClass10 u f f11 v, f F12 v, f f13 v')
        t20 = classNew('n TstEnvClass20 u v, f f24 v, f F25 v')
        call out 'a0 vor pipeBegin loop lazy' lz w '***' ty
        call pipeBegin
        call out 'a1 vor jBuf()'
        b = jOpen(jBuf(), m.j.cWri)
        o1 = oNew('TstEnvClass10')
        m.o1.F12 = 'value F12 of o1' o1
        call mAdd t'.TRANS', o1 '<o2'lz 'of TstEnvClass10>'
        call jWriteO b, o1
        call jWrite b, 'WriteO o2'
        o2 = oNew('TstEnvClass20')
        m.o2 = 'valueO2Lazy'lz
        m.o2.F25 = 'value F25 of o2'lz o2
        oc = oCopyNew(oCopyNew(o2))
        call mAdd t'.TRANS', o2 '<o2'lz 'of TstEnvClass20>'
        call mAdd t'.TRANS', oc '<oc'lz 'of TstEnvClass20>'
        call jWriteO b, oc
        call out 'a2 vor' w 'b'
        interpret 'call pipe'w jClose(b)
        call out 'a3 vor' w
        interpret 'call pipe'w
        call out 'a4 vor barLast inIx' m.t.inIx
        call pipeLast
        call out 'a5 vor' w
        interpret 'call pipe'w
        call out 'a6 vor barEnd'
        call pipeEnd
        call out 'a7 nach barEnd lazy' lz w '***'
        end
    call tstEnd t
    m.t.trans.0 = 0
    return
endProcedure tstEnvClass

tstFile: procedure expose m.
    call catIni
/*<<tstFile
    ### start tst tstFile #############################################
    write read 0 last 10 vor anfang
    write read 1 last 80  links1 1   und rechts |  ..
    write read 2 last 80 liinks2 2   und rechts |  ..
    write read 5 last 80 links5 5 rechts5
    write read 99 last 80 links99 99 rechts
    write read 100 last 80 links100 100 rechts
    write read 101 last 80 links101 101 rechts
    write read 999 last 80 links999 999 rechts
    write read 1000 last 80 links1000 1000 rechts
    write read 1001 last 80 links1001 1001 rechts
    write read 2109 last 80 links2109 2109 rechts
    out > eins 1                                                       +
    .             .
    out > eins 2 schluss.                                              +
    .             .
    buf eins
    buf zwei
    buf drei
    out > zwei mit einer einzigen Zeile                                +
    .             .
    . links1 1   und rechts |  .                                       +
    .              .
tstFile */
    call tst t, "tstFile"
    pds = tstFilename('lib', 'r')
    call tstFileWr pds, 0, ' links0', '  und rechts |  .  '
    call tstFileWr pds, 1, ' links1', '  und rechts |  .  '
    call tstFileWr pds, 2, 'liinks2', '  und rechts |  .  '
    call tstFileWr pds, 5, 'links5', 'rechts5'
    call tstFileWr pds, 99, 'links99', 'rechts'
    call tstFileWr pds, 100, 'links100', 'rechts'
    call tstFileWr pds, 101, 'links101', 'rechts'
    call tstFileWr pds, 999, 'links999', 'rechts'
    call tstFileWr pds, 1000, 'links1000', 'rechts'
    call tstFileWr pds, 1001, 'links1001', 'rechts'
    call tstFileWr pds, 2109, 'links2109', 'rechts'
    pd2 = tstFilename('li2', 'r')
    call pipeIni
    call pipeBeLa '>' s2o(tstPdsMbr(pd2, 'eins'))
    call out tstFB('out > eins 1') /* simulate fixBlock on linux */
    call out tstFB('out > eins 2 schluss.')
    call pipeEnd
    call pipeBeLa '>' s2o(tstPdsMbr(pd2, 'zwei'))
    call out tstFB('out > zwei mit einer einzigen Zeile')
    call pipeEnd
    b = jBuf("buf eins", "buf zwei", "buf drei")
    call pipeBeLa '<' s2o(tstPdsMbr(pd2, 'eins')), '<' b,
                    ,'<' jBuf(),
                    ,'<' s2o(tstPdsMbr(pd2, 'zwei')),
                    ,'<' s2o(tstPdsMbr(pds, 'wr0')),
                    ,'<' s2o(tstPdsMbr(pds, 'wr1'))
    call pipeWriteNow
    call pipeEnd
    call tstEnd t
    return
endProcedure tstFile

/*--- simulate fixBlock 80 on linux ---------------------------------*/
tstFB: procedure expose m.
parse arg line, recL
    if errOS() \== 'LINUX' then
        return line
    else if recL == '' then
        return left(line, 80)
    else
        return left(line, recL)
endProcedure tstFB

tstPdsMbr: procedure expose m.
parse arg pds, mbr
    os = errOS()
    if os = 'TSO' then
        return pds'('mbr') ::F'
    if os = 'LINUX' then
        return pds'.'mbr
    call err 'tstPdsMbr implement os' os
endProcedure tstPdsMbr

tstFileWR: procedure expose m.
parse arg dsn, num, le, ri
    io = file(tstPdsMbr(dsn, 'wr'num))
    call jOpen io, m.j.cWri
    do x = 1 to num /* simulate fixBlock 80 on LINUX*/
        call jWrite io, tstFB(le x ri)
        end
    call jClose io
    if num > 100 then
        call jReset io, tstPdsMbr(dsn, 'wr'num)

    call jOpen io, m.j.cRead
    m.vv = 'vor anfang'
    do x = 1 to num
        if \ jRead(io, vv) then
            call err x 'not jRead'
        else if m.vv <> le x ri then
            call err x 'read mismatch' m.vv
        end
    if jRead(io, vv) then
        call err x 'jRead but should be eof 1'
    if jRead(io, vv) then
        call err x'+1 jjRead but should be eof 2'
    call jClose io
    call tstOut t, 'write read' num 'last' length(m.vv) strip(m.vv,'t')
    return
endProcedure tstFileRW

tstFileList: procedure expose m.
    call catIni
/*<<tstFileList
    ### start tst tstFileList #########################################
    empty dir
    filled dir
    <<pref 2 List>>drei
    <<pref 2 List>>eins
    <<pref 2 List>>zwei
    <<pref 2 List>>vier
    filled dir recursive
    <<pref 2 List>>drei
    <<pref 2 List>>eins
    <<pref 2 List>>zwei
    <<pref 2 List>>vier
    <<pref 1 vier>>drei
    <<pref 1 vier>>eins
    <<pref 1 vier>>zwei
tstFileList */
/*<<tstFileListTSO
    ### start tst tstFileListTSO ######################################
    empty dir
    filled dir
    <<pref 2 LIST>>DREI
    <<pref 2 LIST>>EINS
    <<pref 2 LIST>>ZWEI
    filled dir recursive
    <<pref 2 LIST>>DREI
    <<pref 2 LIST>>EINS
    <<pref 1 VIER>>DREI
    <<pref 1 VIER>>EINS
    <<pref 1 VIER>>ZWEI
    <<pref 2 LIST>>ZWEI
tstFileListTSO */
    if errOS() = 'TSO' then
        call tst t, "tstFileListTSO"
    else
        call tst t, "tstFileList"
    fi = file(tstFileName('FileList', 'r'))
    call fileMkDir fi
    fl = fileList(fi)
    call tstOut t, 'empty dir'
    call jWriteNow t, fl
    call tstFileListMake t, fi, 2
    call tstOut t, 'filled dir'
    call jWriteNow t, fl
    call tstOut t, 'filled dir recursive'
    call jWriteNow t, fileList(fi, 'r')
    call tstEnd t
    return
endProcedure tstFileList

tstFileListMake: procedure expose m.
parse arg t, fi, lev
    if \ fileIsDir(fi) then
        call fileMkDir fi
    call mAdd t'.TRANS', filePath(fi) || m.file.sep ,
                '<<pref' lev right(filePath(fi),4)'>>'
    call jCat fileChild(fi, 'eins','::F'),jBuf('zeile eins','eins' lev)
    call jCat fileChild(fi, 'zwei','::F'),jBuf('zeile zwei','zwei' lev)
    call jCat fileChild(fi, 'drei','::F'),jBuf('zeile drei','drei' lev)
    if lev > 1 then
        call tstFileListMake t, fileChild(fi, 'vier'), lev-1
    return
endProcedure tstFileListMake


tstFmt: procedure expose m.
    call pipeIni
/*<<tstFmt
    ### start tst tstFmt ##############################################
    =   a2i b3b   d4                       fl5          ex6
    -11 -11 b3    d4-11+d4++++    -111.1000000 -1.11000E-12
    -1  -10 b     d4-10+d4+++            null1        null3
    -    -9 b3b-9 d4-9+d4+++       -11.0000000 -1.11900E-10
    -8+  -8 b3b-  d4-8+d4++        -18.0000000 -1.18000E010
    -7   -7 b3b   d4-7+d4+          -7.0000000 -1.70000E-07
    -    -6 b3    d4-6+d4           -0.1111160 -6.00000E006
    -5+  -5 b     d4-5+d                 null2        null2
    -4   -4 b3b-4 d4-4+         -11114.0000000 -1.11140E008
    -    -3 b3b-  d4-3              -0.1130000 -1.13000E-04
    -2+  -2 b3b   d4-               -0.1200000 -1.20000E001
    -1   -1 b3    d4                -0.1000000 -1.00000E-02
    0     0 b     d                      null1        null1
    1+    1 b3    d4                 0.1000000  1.00000E-02
    2++   2 b3b   d42                0.1200000  1.20000E001
    3     3 b3b3  d43+               0.1130000  1.13000E-04
    4+    4 b3b4+ d44+d          11114.0000000  1.11140E008
    5++   5 b     d45+d4                 null2        null2
    6     6 b3    d46+d4+            0.1111160  1.11116E005
    7+    7 b3b   d47+d4++           0.1111117  7.00000E-08
    8++   8 b3b8  d48+d4+++          8.0000000  1.80000E009
    9     9 b3b9+ d49+d4++++         0.9000000  1.19000E-08
    10   10 b     d410+d4++++            null1        null3
    11+  11 b3    d411+d4+++++       0.1110000  1.00000E-12
    1    12 b3b   d412+d4++++++  11112.0000000  2.00000E012
    13   13 b3b1  d               1111.3000000  1.13000E-12
    14+  14 b3b14 d4            111111.0000000  1.40000E013
    1    15 b     d41                    null2        null1
    16   16 b3    d416               6.0000000  1.16000E003
    17+  17 b3b   d417+              0.7000000  1.11170E-03
    1    18 b3b1  d418+d            11.0000000  1.11800E003
    19   19 b3b19 d419+d4            0.1190000  9.00000E-05
    20+  20 b     d420+d4+               null1        null2
    2    21 b3    d421+d4++         11.1210000  1.11000E-05
    22   22 b3b   d422+d4+++     11111.2000000  2.00000E007
    23+  23 b3b2  d423+d4++++        0.1111123  1.11230E-09
    c3L      a2i drei  d4                 fl5          ex6
    -11 -1.10E01 b3    d4-11+d   -111.1000000 -1.11000E-12
    -1  -1.00E01 b     d4-10+d          null1        null3
    -   -9.00E00 b3b-9 d4-9+d4    -11.0000000 -1.11900E-10
    -8+ -8.00E00 b3b-  d4-8+d4    -18.0000000 -1.18000E010
    -7  -7.00E00 b3b   d4-7+d4     -7.0000000 -1.70000E-07
    -   -6.00E00 b3    d4-6+d4     -0.1111160 -6.00000E006
    -5+ -5.00E00 b     d4-5+d           null2        null2
    -4  -4.00E00 b3b-4 d4-4+   -11114.0000000 -1.11140E008
    -   -3.00E00 b3b-  d4-3        -0.1130000 -1.13000E-04
    -2+ -2.00E00 b3b   d4-         -0.1200000 -1.20000E001
    -1  -1.00E00 b3    d4          -0.1000000 -1.00000E-02
    0    0.00E00 b     d                null1        null1
    1+   1.00E00 b3    d4           0.1000000  1.00000E-02
    2++  2.00E00 b3b   d42          0.1200000  1.20000E001
    3    3.00E00 b3b3  d43+         0.1130000  1.13000E-04
    4+   4.00E00 b3b4+ d44+d    11114.0000000  1.11140E008
    5++  5.00E00 b     d45+d4           null2        null2
    6    6.00E00 b3    d46+d4+      0.1111160  1.11116E005
    7+   7.00E00 b3b   d47+d4+      0.1111117  7.00000E-08
    8++  8.00E00 b3b8  d48+d4+      8.0000000  1.80000E009
    9    9.00E00 b3b9+ d49+d4+      0.9000000  1.19000E-08
    10   1.00E01 b     d410+d4          null1        null3
    11+  1.10E01 b3    d411+d4      0.1110000  1.00000E-12
    1    1.20E01 b3b   d412+d4  11112.0000000  2.00000E012
    13   1.30E01 b3b1  d         1111.3000000  1.13000E-12
    14+  1.40E01 b3b14 d4      111111.0000000  1.40000E013
    1    1.50E01 b     d41              null2        null1
    16   1.60E01 b3    d416         6.0000000  1.16000E003
    17+  1.70E01 b3b   d417+        0.7000000  1.11170E-03
    1    1.80E01 b3b1  d418+d      11.0000000  1.11800E003
    19   1.90E01 b3b19 d419+d4      0.1190000  9.00000E-05
    20+  2.00E01 b     d420+d4          null1        null2
    2    2.10E01 b3    d421+d4     11.1210000  1.11000E-05
    22   2.20E01 b3b   d422+d4  11111.2000000  2.00000E007
    23+  2.30E01 b3b2  d423+d4      0.1111123  1.11230E-09
tstFmt */

    call tst t, "tstFmt"
    b = jBuf()
    st = b'.BUF'
    call pipeBeLa m.j.cWri b
    call tstDataClassOut '. c3 a2i i b3b c5 d4 c13 fl5 f8n2 ex6 e9n3',
        , -11, + 23
    call pipeEnd
    call fmtFWriteAll fmtFreset(abc), b
    call fmtFAddFlds fmtFReset(abc), oFlds(st'.'1)
    m.abc.1.tit = 'c3L'
    m.abc.2.fmt = 'e'
    m.abc.3.tit = 'drei'
    m.abc.4.fmt = 'l7'
    call fmtFWriteAll abc, b
    call tstEnd t
    return
endProcedure tstFmt

tstScan: procedure expose m.
/*<<tstScan.1
    ### start tst tstScan.1 ###########################################
    scan src a034,Und hr123sdfER"st1" 'str2''mit''apo''s' .
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    scan n tok 3: Und key  val .
    scan v tok 1:   key  val .
    scan n tok 10: hr123sdfER key  val .
    scan q tok 5: "st1" key  val st1
    scan v tok 1:   key  val st1
    scan a tok 19: 'str2''mit''apo''s' key  val str2'mit'apo's
    scan v tok 1:   key  val str2'mit'apo's
tstScan.1 */
    call tst t, 'tstScan.1'

    call tstScan1 ,'l"litEins"l"litZwei"ndv" "aq1' ,
        ,"a034,Und hr123sdfER""st1"" 'str2''mit''apo''s' "
    call tstEnd t

/*<<tstScan.2
    ### start tst tstScan.2 ###########################################
    scan src a034,Und hr123sdfER"st1" 'str2''mit''apo''s' .
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    scan n tok 3: Und key  val .
    scan b tok 0:  key  val .
    scan n tok 10: hr123sdfER key  val .
    scan " tok 5: "st1" key  val st1
    scan b tok 0:  key  val st1
    scan ' tok 19: 'str2''mit''apo''s' key  val str2'mit'apo's
    scan b tok 0:  key  val str2'mit'apo's
tstScan.2 */
    call tst t, 'tstScan.2'
    call tstScan1 , 'ndsb1' ,
        ,"a034,Und hr123sdfER""st1"" 'str2''mit''apo''s' "
    call tstEnd t

/*<<tstScan.3
    ### start tst tstScan.3 ###########################################
    scan src a034,'wie 789abc
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    *** err: scanErr ending Apostroph(') missing
    .    e 1: last token  scanPosition 'wie 789abc
    .    e 2: pos 6 in string a034,'wie 789abc
    scan ' tok 1: ' key  val .
    scan n tok 3: wie key  val .
    scan s tok 0:  key  val .
    *** err: scanErr illegal number end after 789
    .    e 1: last token 789 scanPosition abc
    .    e 2: pos 14 in string a034,'wie 789abc
    scan d tok 3: 789 key  val .
    scan n tok 3: abc key  val .
tstScan.3 */
    call tst t, 'tstScan.3'
    call tstScan1 , 'nds1' ,
        ,"a034,'wie 789abc"
    call tstEnd t

/*<<tstScan.4
    ### start tst tstScan.4 ###########################################
    scan src litEinsefr 23 sdfER'str1'litZwei "str2""mit quo"s .
    scan l tok 7: litEins key  val .
    scan n tok 3: efr key  val .
    scan b tok 0:  key  val .
    scan d tok 2: 23 key  val .
    scan b tok 0:  key  val .
    scan n tok 5: sdfER key  val .
    scan a tok 6: 'str1' key  val str1
    scan l tok 7: litZwei key  val str1
    scan b tok 0:  key  val str1
    scan q tok 15: "str2""mit quo" key  val str2"mit quo
    scan n tok 1: s key  val str2"mit quo
    scan b tok 0:  key  val str2"mit quo
tstScan.4 */
    call tst t, 'tstScan.4'
    call tstScan1 , 'l"litEins"l"litZwei"ndbaq1' ,
           ,"litEinsefr 23 sdfER'str1'litZwei ""str2""""mit quo""s "
    call tstEnd t

/*<<tstScan.5
    ### start tst tstScan.5 ###########################################
    scan src  aha;+-=f ab=cdEf eF='strIng' .
    scan b tok 0:  key  val .
    scan k tok 4:  no= key aha val def
    scan ; tok 1: ; key aha val def
    scan + tok 1: + key aha val def
    scan - tok 1: - key aha val def
    scan = tok 1: = key aha val def
    scan k tok 4:  no= key f val def
    scan k tok 4: cdEf key ab val cdEf
    scan b tok 4: cdEf key ab val cdEf
    scan k tok 8: 'strIng' key eF val strIng
    scan b tok 8: 'strIng' key eF val strIng
tstScan.5 */
    call tst t, 'tstScan.5'
    call tstScan1 , 'bk1'," aha;+-=f ab=cdEf eF='strIng' "
    call tstEnd t
    return
endProcedure tstScan

tstScanRead: procedure expose m.
/*<<tstScanRead
    ### start tst tstScanRead #########################################
    name erste
    space
    name Zeile
    space
    nextLine
    nextLine
    space
    name dritte
    space
    name Zeile
    space
    name schluss
    space
tstScanRead */
    call tst t, 'tstScanRead'
    b = jBuf('erste Zeile  ',,'  dritte Zeile  schluss  ')
    s = jOpen(scanRead(b))
    do while \scanAtEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanVerify(s, ' ') then call tstOut t, 'space'
        else if scanReadNL(s) then      call tstOut t, 'nextLine'
        else if \scanAtEnd(s) then      call scanErr s, 'cannot scan'
        end
    call jClose s
    call tstEnd t

/*<<tstScanReadMitSpaceLn
    ### start tst tstScanReadMitSpaceLn ###############################
    name erste
    spaceLn
    name Zeile
    spaceLn
    name dritte
    spaceLn
    name Zeile
    spaceLn
    name schluss
    spaceLn
tstScanReadMitSpaceLn */
    call tst t, 'tstScanReadMitSpaceLn'
    s = jOpen(scanRead(b))
    do forever
        if scanName(s) then         call out 'name' m.s.tok
        else if scanSpaceNL(s) then call out 'spaceLn'
        else if \scanAtEnd(s) then      call scanErr s, 'cannot scan'
        else                        leave
        end
    call jClose s
    call tstEnd t

/*<<tstScanJRead
    ### start tst tstScanJRead ########################################
    1 jRead n tok erste val .
    2 jRead s tok  val .
    3 jRead n tok Zeile val .
    4 jRead s tok  val .
    5 jRead n tok dritte val .
    6 jRead s tok  val .
    7 jRead n tok Zeile val .
    8 jRead s tok  val .
    9 jRead n tok schluss val .
    10 jRead s tok  val .
    11 jRead 0 tok 1 val 1
    12 jRead s tok  val 1
    13 jRead + tok + val 1
    14 jRead s tok  val 1
    15 jRead 0 tok 2. val 2..
    16 jRead s tok  val 2..
    17 jRead + tok + val 2..
    18 jRead . tok . val 2..
    19 jRead s tok  val 2..
    20 jRead 0 tok +.3 val +.3
    21 jRead 0 tok -45e-3 val -45E-3
    22 jRead s tok  val -45E-3
    23 jRead " tok "a""b" val a"b
    24 jRead s tok  val a"b
    25 jRead ' tok 'c''d' val c'd
    className 1: Scan 18: Scan
tstScanJRead */

    call tst t, 'tstScanJRead'
    call jWrite jOpen(b,'>>'), '1  + 2. +. +.3-45e-3 "a""b"' "'c''d'"
    s = jOpen(scanRead(jClose(b)))
    do x=1 while jRead(s, v.x)
        call out x 'jRead' m.v.x.type 'tok' m.v.x.tok 'val' m.v.x.val
        end
    call jClose s
    call out 'className 1:' className(objClass(v.1)),
                    '18:' className(objClass(v.18))
    call tstEnd t
    return
endProcedure tstScanRead

tstScanWin: procedure expose m.
/*<<tstScanWin
    ### start tst tstScanWin ##########################################
    info 0: last token  scanPosition erste     Zeile                 dr+
    itteZe\npos 1 in line 1: erste     Zeile
    name erste
    spaceNL
    name Zeile
    spaceNL
    name dritteZeeeile
    info 5: last token dritteZeeeile scanPosition    zeile4            +
    .    fuenfueberSechs\npos 1 in line 4:    zeile4
    spaceNL
    name zeile4
    spaceNL
    name fuenfueberSechsUnddSiebenUNDundUndUAcht
    spaceNL
    info 10: last token  scanPosition undZehnueberElfundNochWeiterZwoel+
    fundim1\npos 9 in line 10:         undZehn
    name undZehnueberElfundNochWeiterZwoelfundim13
    spaceNL
    name Punkt
    infoE 14: last token Punkt scanPosition \natEnd after line 13: im13+
    .      Punkt
tstScanWin */
    call scanWinIni
    call tst t, 'tstScanWin'
    b = jBuf('?erste     Zeile?',,'?  dritteZeeeile?', '?   zeile4 ',
       ,'?          fuenf?', '?ueberSechsUnddS?', '?iebenUNDundUndU?',
       ,'?Acht           ?', '?               ?', '?        undZehn?',
       ,'?ueberElfundNoch?', '?WeiterZwoelfund?', '?im13      Punkt?')
    s = jOpen(scanWin(b, , , 2, 15))
    call tstOut t, 'info 0:' scanInfo(s)
    do sx=1 while \scanAtEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanSpaceNL(s) then call tstOut t, 'spaceNL'
        else if \scanAtEnd(s) then      call scanErr s, 'cannot scan'
        if sx // 5 = 0 then
            call tstOut t, 'info' sx':' scanInfo(s)
        end
    call tstOut t, 'infoE' sx':' scanInfo(s)
    call tstEnd t

/*<<tstScanWinRead
    ### start tst tstScanWinRead ######################################
    info 0: last token  scanPosition erste     Zeile                z3 +
    com Ze\npos 1 in line 1: erste     Zeile
    name erste
    spaceNL
    name Zeile
    spaceNL
    name z3
    info 5: last token z3 scanPosition  com Zeeeile z4 come4          f+
    uenf\npos 4 in line 3:  z3 com Zeeeile
    spaceNL
    name z4
    spaceNL
    name fuenf
    spaceNL
    info 10: last token  scanPosition com    Sechs  com  sieben   comAc+
    ht  com\npos 15 in line 5:     fuenf     c
    name com
    spaceNL
tstScanWinRead */
    call tst t, 'tstScanWinRead'
    call mAdd t.cmp,
       ,  "name Sechs",
       ,  "spaceNL",
       ,  "name com",
       ,  "info 15: last token com scanPosition   sieben   comAcht  c",
       || "om com    com\npos 2 in line 7: m  sieben   com" ,
       ,  "spaceNL",
       ,  "name sieben",
       ,  "spaceNL",
       ,  "name Acht",
       ,  "spaceNL",
       ,  "info 20: last token  scanPosition ueberElfundNochWeit com ",
       || "elfundim13\npos 1 in line 11: ueberElfundNoch",
       ,  "name ueberElfundNochWeit",
       ,  "spaceNL",
       ,  "name im13",
       ,  "spaceNL",
       ,  "name Punkt",
       ,  "info 25: last token Punkt scanPosition \natEnd after line ",
       || "13: im13      Punkt",
       ,  "infoE 26: last token Punkt scanPosition \natEnd after line",
       || " 13: im13      Punkt"
    b = jBuf('?erste     Zeile?',,'? z3 com Zeeeile?', '? z4 come4 ',
       ,'?    fuenf     c?', '?om    Sechs  co?', '?m  sieben   com?',
       ,'?Acht  com com  ?', '?  com          ?', '?  com   undZehn?',
       ,'?ueberElfundNoch?', '?Weit com elfund?', '?im13      Punkt?')
    s = scanWin(b, , , 2, 15)
    call scanOpts s, , , 'com'
    call tstOut t, 'info 0:' scanInfo(jOpen(s))
    do sx=1 while \scanAtEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanSpaceNL(s) then call tstOut t, 'spaceNL'
        else if \scanAtEnd(s) then      call scanErr s, 'cannot scan'
        if sx // 5 = 0 then
            call tstOut t, 'info' sx':' scanInfo(s)
        end
    call tstOut t, 'infoE' sx':' scanInfo(s)
    call tstEnd t
    return
endProcedure tstScanWin

tstScanSql: procedure expose m.
    call scanWinIni
/*<<tstScanSqlId
    ### start tst tstScanSqlId ########################################
    sqlId ABC
    spaceNL
    sqlId AB__345EF
    spaceNL
tstScanSqlId */
    call tst t, 'tstScanSqlId'
    b = jBuf('abc  --  kommentar', right('ab_', 72), '_345ef-- kom')
    s = jOpen(scanSql(b))
    do sx=1 while \scanAtEnd(s)
        if scanSqlID(s) then       call tstOut t, 'sqlId' m.s.val
        else if scanSpaceNL(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*<<tstScanSqlDelimited
    ### start tst tstScanSqlDelimited #################################
    sqlDeId ABC
    spaceNL
    sqlDeId AB_3F
    spaceNL
    sqlDeId abc
    spaceNL
    sqlDeId ab_Ef
    spaceNL
tstScanSqlDelimited */
    call tst t, 'tstScanSqlDelimited'
    b = jBuf('abc  --  kommentar',,'  -- ',,right('ab_', 72),'3F-- kom',
           , '"abc"  --  ko', ' -- ',,right('"ab_', 72),'Ef"-- kom')
    s = jOpen(scanSql(b))
    do sx=1 while \scanAtEnd(s)
        if scanSqlDeID(s) then       call tstOut t, 'sqlDeId' m.s.val
        else if scanSpaceNL(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*<<tstScanSqlQualified
    ### start tst tstScanSqlQualified #################################
    sqlQuId ABC 1 ABC
    sqlQuId AB_3F 1 AB_3F
    sqlQuId abc 1 abc
    sqlQuId ab_Ef 1 ab_Ef
    sqlQuId EINS.Zwei.DREI 3 EINS
    sqlQuId vi er.fu  enf 2 vi er
tstScanSqlQualified */
    call tst t, 'tstScanSqlQualified'
    b = jBuf('abc  --  kommentar',,'  -- ',,right('ab_', 72),'3F-- kom',
           , '"abc"  --  ko', ' -- ',right('"ab_', 72),'Ef"-- kom',
           , 'eins."Zwei', '" -- com', ' . -- com', '  -- com',
           , 'drei -- ko', '"vi er"."fu  enf   " -- co')
    s = jOpen(scanSql(b))
    do sx=1 while \scanAtEnd(s)
        if scanSqlQuID(s) then
            call tstOut t, 'sqlQuId' m.s.val m.s.val.0 m.s.val.1
        else if scanSpaceNL(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*<<tstScanSqlNum
    ### start tst tstScanSqlNum #######################################
    sqlNum 1
    spaceNL
    sqlNum 2..
    spaceNL
    sqlNum .3
    spaceNL
    sqlNum 4.5
    spaceNL
    sqlNum +6
    spaceNL
    sqlNum +7.03
    spaceNL
    sqlNum -8
    spaceNL
    sqlNum -.9
    spaceNL
    sqlNum 1E2
    spaceNL
    sqlNum -2.E-2
    spaceNL
    sqlNum +.3E+3
    spaceNL
tstScanSqlNum */
    call tst t, 'tstScanSqlNum'
    b = jBuf('1 2. .3 4.5 +6 + --kom', , ' -- com ', , ' 7.03 -8 -  .9',
             '1e2 - 2.e-2 + .3e+3')
    s = jOpen(scanSql(b))
    do sx=1 while \scanAtEnd(s)
        if scanSqlNum(s) then
            call tstOut t, 'sqlNum' m.s.val
        else if scanSpaceNL(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*<<tstScanSqlNumUnit
    ### start tst tstScanSqlNumUnit ###################################
    sqlNumUnit 1 KB
    spaceNL
    sqlNumUnit .3 MB
    sqlNumUnit .5
    sqlNumUnit +6.E-5 B
    spaceNL
    sqlNumUnit -7
    char *
    spaceNL
    sqlNumUnit -.8
    char T
    char B
    spaceNL
    *** err: scanErr scanSqlNumUnit after +9. bad unit TB
    .    e 1: last token Tb scanPosition .
    .    e 2: pos 41 in line 1: 1 kb .3mB.5 +   6.e-5B -7* -.8 TB + 9.T+
    b
    sqlNumUnit +9..
    spaceNL
tstScanSqlNumUnit */
    call tst t, 'tstScanSqlNumUnit'
    b = jBuf('1 kb .3mB.5 +   6.e-5B -7* -.8 TB + 9.Tb')
    s = jOpen(scanSql(b))
    do sx=1 while \scanAtEnd(s)
        if scanSqlNumUnit(s, 0, 'B KB MB') then
            call tstOut t, 'sqlNumUnit' m.s.val
        else if scanSpaceNL(s) then call tstOut t, 'spaceNL'
        else if scanChar(s ,1) then call tstOut t, 'char' m.s.tok
        else call scanErr s, 'cannot scan'
        end
    call tstEnd t
    return
endProcedure tstScanSql

/*--- one single test scan with lines to scan in stem ln -------------*/
tstScan1:
parse arg sc, classs, ln
    if sc == '' then do
        call tstOut t, 'scan src' ln
        call scanSrc scanReset(s), ln
        end
    else do
        call tstOut t, 'scan scanner' sc
        s = sc
        end
    do forever
        x = tstScanType(s, classs)
        if x == '' then
           leave
        call tstOut t, 'scan' x 'tok' length(m.s.tok)':' m.s.tok ,
                  'key' m.s.key 'val' m.s.val
        end
    return
endProcedure tstScan1

tstScanType: procedure expose m.
parse arg s, opt
    cx = 1
    a2 = ''
    res = 0
    do while cx <= length(opt)
        f = substr(opt, cx, 1)
        cx = cx + 1
        if pos(substr(opt, cx, 1), "'""") > 0 then do
            m.tstScanType.src = opt
            m.tstScanType.pos = cx
            call scanString tstScanType
            a2 = m.tstScanType.val
            cx = m.tstScanType.pos
            end
        if      f == 'a' then
            res = scanString(s, "'")
        else if f == 'b' then
            res = scanSpaceNl(s)
        else if f == 'c' then
            res = scanChar(s, a2)
        else if f == 'd' then
            res = scanNat(s, a2)
        else if f == 'k' then
            res = scanKeyValue(s, 'def')
        else if f == 'l' then
            res = scanLit(s, a2)
        else if f == 'q' then
            res = scanString(s, '"')
        else if f == 'v' then
            res = scanVerify(s, a2)
        else if f == 'w' then
            res = scanWord(s)
        else if f == 'y' then
            res = scanVerify(s, a2, 'm')
        if res then
            return f
        end
    return scanType(s)
endProcedure tstScanType

/* copx tstBase end   *************************************************/

/* copx tst begin ****************************************************
      test infrastructure
***********************************************************************/
/*--- migrate all compares to new ones:
        tstCI input compare
        tstCO ouput migrated compares
        tstCIO input and output -------------------------------------*/
tstCI: procedure expose m.
parse arg m, nm
    m.m.CIO = 0
    signal tstCIwork
tstCIO: procedure expose m.
parse arg m, nm
    m.m.CIO = 1
tstCIwork:
    m.m.name = nm
    m.m.cmp.1 = left('### start tst' nm '', 67, '#')

    do ix=2 to arg()-1
        m.m.cmp.ix = arg(ix+1)
        end
    m.m.cmp.0 = ix-1
    if m.m.CIO then
        call tstCO m
    return

tstCO: procedure expose m.
parse arg m
    call tst2dpSay m.m.name, m'.CMP', 68
    return
/*--- initialise m as tester with name nm
        use inline input nm as compare lines -----------------------*/
tstReset: procedure expose m.
parse arg m, nm
    call tstIni
    m.m.name = nm
    m.m.inIx  = 0
    m.m.out.0 = 0
    m.m.err   = 0
    m.m.errHand = 0
    m.tst.act = m
    if \ datatype(m.m.trans.0, 'n') then
        m.m.trans.0 = 0
    m.m.trans.old = m.m.trans.0
    return
endProcedure tstReset

tst: procedure expose m.
parse arg m, nm, cmpSt
    call tstReset m, nm
    m.tst.tests = m.tst.tests+1
    if cmpSt == '' then do
        cmpSt = mCut(t'.CMP', 0)
        call tst4dp cmpSt, mapInline(nm)
        end
    m.m.cmp = cmpSt
    m.m.moreOutOk = 0
    call mAdd mCut(m'.IN', 0), 'tst in line 1 eins ,'    ,
                     , 'tst in line 2 zwei ;   ' ,
                     , 'tst in line 3 drei .schluss.'
    call tstOut m, left('### start tst' nm '', 67, '#')
    call errReset 'h', 'return tstErrHandler(ggTxt)'
    m.m.errCleanup = m.err.cleanup
    if m.tst.ini.j \== 1 then do
        call err implement outDest 'i', 'call tstOut' quote(m)', msg'
        end
    else do
        call oMutate m, 'Tst'
        m.m.jReading = 1
        m.m.jWriting = 1
        if m.tst.ini.e \== 1 then do
            m.m.oldJin = m.j.in
            m.m.oldOut = m.j.out
            m.j.in = m
            m.j.out = m
            end
        else do
            if m.pipe.0 <> 1 then
                call tstErr m, 'm.pipe.0' m.pipe.0 '<> 1'
            call pipeBeLa '<' m, '>' m
            end
        end
    return m
endProcedure tst

tstEnd: procedure expose m.
parse arg m, opt opt2
    cmp = m.m.cmp
    m.m.jReading = 0
    m.m.jWriting = 0
    if m.tst.ini.j == 1 then do
        m.m.jReading = 0
        m.m.jWriting = 0
           if m.tst.ini.e \== 1 then do
            m.j.in = m.m.oldJin
            m.j.out = m.m.oldOut
            end
        else do
            if m.j.in \== m | m.j.out \== m then
                call tstErr m, m.j.in '\==' m '|' m.j.out '\==' m
            call pipeEnd
            if m.pipe.0 <> 1 then
                call tstErr m, 'm.pipe.0' m.pipe.0 '<> 1'
            end
        end
    if m.m.err = 0 then
        if m.m.errCleanup \= m.err.cleanup then
            call tstErr m, 'err.Cleanup' m.err.cleanup '<> old',
                        m.m.errCleanup
    if m.m.out.0 \= m.cmp.0 & \ (m.m.moreOutOk,
            &  m.m.out.0 > m.cmp.0) then do
        call tstErr m, 'old' m.cmp.0 'lines \= new' m.m.out.0
        do nx = m.m.out.0 + 1 to min(m.m.out.0+10, m.cmp.0)
            say 'old -  ' m.cmp.nx
            end
        end
    call errReset 'h'
    m.tst.act = ''
    soll = 0
    if opt = 'err' then do
        soll = opt2
        if m.m.err \= soll then
            call err soll 'errors expected, but got' m.m.err
        end
    if m.m.err \= soll then do
        say 'new lines:' (m.m.out.0 - 1)
           call tst2dpSay m.m.name, m'.OUT', 68
        end
    say left('###' m.m.name 'end with' m.m.err 'errors ', 67, '#')

    if 1 & m.m.err \= soll then
        call err 'dying because of' m.m.err 'errors'
    m.m.trans.0 = m.m.trans.old
    return
endProcedure tstEnd

tst2dp: procedure expose m.
parse arg st, dp, ml
    dx = m.dp.0
    do sx=1 to m.st.0
        li = m.st.sx
        cx = 1
        do until cx > length(li)
            c = substr(li, cx, 1)
            ou = left('.', strip(c) == '' | pos(c, '.+') > 0)
            ex = min(length(li),cx+ml-length(ou)-2)
            ou = ou || substr(li, cx, ex+1-cx)
            dx = dx + 1
            c = right(ou, 1)
            if ex < length(li) then
                m.dp.dx = ou || '+'
            else if strip(c) == '' | pos(c, '.+') > 0 then
                m.dp.dx = ou || '.'
            else
                m.dp.dx = ou
            cx = ex +1
            end
        end
    m.dp.0 = dx
    return
endProcedure tst2dp

tst2dpSay: procedure expose m.
parse arg name, st, ml
    say '/*<<'name
    call tst2dp st, mCut('TST.TMP', 0), 68
    do nx=1 to m.tst.tmp.0
           say '   ' m.tst.tmp.nx
           end
     say name '*/'
return tst2dpSay

tst4dp: procedure expose m.
parse arg st, dp
    sx = m.st.0
    inData = 0
    data = ''
    do dx=1 to m.dp.0
        li = strip(m.dp.dx)
        if pos(left(li, 1), '.+') > 0 then
         li = substr(li, 2)
        if right(li, 1) == '+' then do
            inData = 1
            data = data || left(li, length(li)-1)
            iterate
            end
        if right(li, 1) == '.' then
            li = left(li, length(li)-1)
        sx = sx + 1
        m.st.sx = data || li
        inData = 0
        data = ''
        end
    m.st.0 = sx
    if inData then
        call err 'end inData'
    return
endProcedure tst4dp

/*--- write to test: say lines and compare them ----------------------*/
tstWrite: procedure expose m.
parse arg m, arg
    call tstOut m, 'out:' arg
    return
endProcedure tstWrite

tstOut: procedure expose m.
parse arg m, arg
    do tx=m.m.trans.0 by -1 to 1
        arg = repAll(arg, word(m.m.trans.tx, 1),
            , subword(m.m.trans.tx, 2))
        end
    call mAdd m'.OUT', arg
    nx = m.m.out.0
    cmp = m.m.cmp
    c = m.cmp.nx
    if nx > m.cmp.0 then do
        if nx = m.cmp.0+1 & \ m.m.moreOutOK then
            call tstErr m, 'more new Lines' nx
        end
    else if c \== arg then do
        do cx=1 to min(length(c), length(arg)) ,
             while substr(c, cx, 1) == substr(arg, cx, 1)
             end
         msg = 'old line' nx '<> new overnext, firstDiff' cx',',
                 'len old' length(c)', new' length(arg)

        if cx > 10 then
            msg = overlay('|', msg, cx-10)
        call tstErr m, msg
        say c
        end
    say arg
    return 0
endProcedure tstOut

tstWriteO: procedure expose m.
parse arg m, var
   if abbrev(var, m.class.escW) then do
        call tstOut t, o2String(var)
        end
   else if m.class.o2c.var == m.class.classV then do
        call tstOut t, m.var
        end
    else if oKindOf(var, 'JRW') then do
        call tstOut t, 'tstWriteO kindOf JRW jWriteNow begin <<<'
        call jWriteNow m, var
        call tstOut t, 'tstWriteO kindOf JRW jWriteNow end   >>>'
        end
    else if oKindOf(var, 'ORun') then do
        call tstOut t, 'tstWriteO kindOf ORun oRun begin <<<'
        call oRun var
        call tstOut t, 'tstWriteO kindOf ORun oRun end   >>>'
        end
    else do
        call classOut , var, 'tstR: '
        end
    return
endProcedure tstWriteO

tstReadO: procedure expose m.
parse arg m, arg
    ix = m.m.inIx + 1
    m.m.inIx = ix
    if ix <= m.m.in.0 then do
        m.arg = m.m.in.ix
        m.class.o2c.arg = m.class.classV
        call tstOut m, '#jIn' ix'#' m.arg
        return 1
        end
    call tstOut m, '#jIn eof' ix'#'
    return 0
endProcedure tstReadO

tstFilename: procedure
parse arg suf, opt
    os = errOS()
    if os == 'TSO' then do
        dsn = dsn2jcl('~tmp.tst.'suf)
        if opt = 'r' then do
            if sysDsn("'"dsn"'") \== 'DATASET NOT FOUND' then
                call adrTso "delete '"dsn"'"
            call csiOpen 'TST.CSI', dsn'.**'
            do while csiNext('TST.CSI', 'TST.FINA')
                say 'deleting csiNext' m.tst.fina
                call adrTso "delete '"m.tst.fina"'"
                end
            end
        return dsn
        end
    else if os == 'LINUX' then do
        if abbrev(suf, '/') then
            fn = suf
        else
            fn = .Stream%%new('~/tmp/tst/'suf)%%qualify /* full path */
        cx = lastPos('/', fn)
        if cx > 0 then do
            dir = left(fn, cx-1)
            if \sysIsFileDirectory(dir) then
                call adrSh "mkdir -p" dir
            if \sysIsFileDirectory(dir) then
                call err 'tstFileName could not create dir' dir
            end
        if opt \= 'r' then
            nop
        else if sysIsFile(fn) then
            call sysFileDelete fn
        else if sysIsFileDirectory(fn) then
            call adrSh 'rm -r' fn
        return fn
        end
    else
        call err 'tstFilename does not implement os' os
endProcedure tstFilename

/*--- say total errors and fail if not zero --------------------------*/
tstTotal: procedure expose m.
    say '######'
    say '######'
    say '######' m.tst.tests 'tests with' ,
                 m.tst.err 'errors in' m.tst.errNames
    say '######'
    say '######'
    if m.tst.err \== 0 then
        call err m.tst.err 'errors total'
    return
endProcedure tstTotal

/*--- test err: message, count it and continue -----------------------*/
tstErr: procedure expose m.
parse arg m, msg
    say '### error' msg
    m.m.err = m.m.err + 1
    m.tst.err = m.tst.err + 1
    nm = m.m.name
    if wordPos(nm, m.tst.errNames) < 1 then
        m.tst.errNames = m.tst.errNames nm
    return 0
endProcedure tstErr

/*--- tstErrHandler: intercept errors --------------------------------*/
tstErrHandler: procedure expose m.
parse arg ggTxt
    m = m.tst.act
    if m == '' then
        call err ggTxt
    m.m.errHand = m.m.errHand + 1
    m.tstErrHandler.0 = 0
    call outPush tstErrHandler
    call errSay ggTxt
    call outPop
    call tstOut m.tst.act, '*** err:' m.tstErrHandler.1
        do x=2 to m.tstErrHandler.0
            call tstOut m, '    e' (x-1)':' m.tstErrHandler.x
            end
    return 0
endSubroutine tstErrHandler

tstTrc: procedure expose m.
parse arg msg
    m.tst.trc = m.tst.trc + 1
    say 'tstTrc' m.tst.trc msg
    return m.tst.trc
endProcedure tstTrc

/*--- tstIni: global initialization ----------------------------------*/
tstIni: procedure expose m.
    if m.tst.ini \== 1 then do
        m.tst.ini = 1
        call mapIni
        m.tst.err = 0
        m.tst.trc = 0
        m.tst.errNames = ''
        m.tst.tests = 0
        m.tst.act = ''
        end
    if m.tst.ini.j \== 1 & m.j.ini == 1 then do
        m.tst.ini.j = 1
          call classNew 'n Tst u JRWO', 'm',
             , "jReadO return tstReadO(m, var)",
             , "jWrite call tstOut m, line",
             , "jWriteO call tstWriteO m, var"
        end
    if m.tst.ini.e \== 1 & m.pipe.ini == 1 then do
        m.tst.ini.e = 1
        end
    return
endProcedure tstIni
/* copx tst    end   **************************************************/
/* copx tstData begin *************************************************/
tstData: procedure expose m.
parse arg ty 2 le 'n' nu, l, r, num
    abs = abs(num)
    if nu \== '' then do
        if abs // 5 = 0 then
            return 'null' || (abs % 5 // nu + 1)
        end
    if ty = 'c' then do
        if le = '' then
            le = 8
        le = abs // le + 1
        if r = '' then
            r = '+'
        return left(l || num || r, le, right(r, 1))
        end
    if pos(ty, 'ief') < 1 then
        call err 'bad type' ty
    nn = abs
    if abbrev(num, '-') | abbrev(num, '+') then
        parse var num si 2 nn
    else
        si = ''
    if ty == 'e' then
        ex = 'e' || left('-', abs // 2) || (abs // 15)
    else
        ex = ''
    if le \== '' then
        nn = right(nn, abs // max(1, le - length(si||ex)) + 1, 1)
    if ty \== 'i' & (abs // 4) \= 0 & length(nn) > 1 then
            nn = overlay('.', nn, length(nn) - abs // length(nn))
     return si || nn || ex
endProcedure tstData

tstDataClassFo: procedure expose m.
parse arg flds
    ty = ''
    do fx=1 by 2 to words(flds)
        if word(flds, fx) = '.' then
            ty = ty', v'
        else
            ty = ty', f' word(flds, fx) 'v,'
        end
    t = classNew('n tstData* u' substr(ty, 2))
    fo = oNew(m.t.name)
    fs = oFlds(fo)
    do fx=1 to m.fs.0
        f = fo || m.fs.fx
        m.f = word(flds, 2*fx)
        end
    return fo
endProcedure tstDataClassFo


tstDataClassOut: procedure expose m.
parse arg flds, f, t
    fo = tstDataClassFo(flds)
    fs = oFlds(fo)
    do x=f to t
        o = oCopyNew(fo)
        do fx=1 to m.fs.0
            na = substr(m.fs.fx, 2)
            f = o || m.fs.fx
            m.f = tstData(m.f, na, '+'na'+', x)
            end
        call outO o
        end
    return
endProcedure tstDataClassOut
/* copx tstData end ***************************************************/
/* copy tstAll end   **************************************************/
/* copy fmt    begin **************************************************/
/*--- format the first arg by the format in the second ---------------*/
fmt: procedure
parse arg v, f 2 l
/* say 'fmt' v',' f l */
    if abbrev('-', f) then
        return v
    else if f == 'l' then
        return left(v, l)
    else if f == 'r' then
        return right(v, l)
    else if f == 'f' then do
        parse value l'.0.0.' with b '.' a '.' e '.'
        return format(v, b, a, e, 0)
        end
    else if f == 'e' then do
        parse var l b '.' a '.' e '.'
        if b == '' then b = 2
        if a == '' then a = 2
        if e == '' then e = 2
        res = format(v, b, a, e, 0)
         y = length(res)-e-1
        if substr(res, y) = '' then
            return left(res, y-1)left('E', e+1, 0)
        else if substr(res, y+1, 1) == '+' then
            return left(res, y)substr(res, y+2)
        else if substr(res, y+2, 1) == '0' then
            return left(res, y+1)substr(res, y+3)
        else
            call err 'formatoverflow' f || l 'for' v '-->' res
        end
     else if f = 's' then
        if l == '' then
            return strip(v, 't')
        else
            return strip(v, l)
    else if f == 'w' then do
        parse var l st ',' le
        return substr(v, st, le)
        end
    else
        call err 'bad format' f l 'for' v
endProcedure fmt

/*--- format special (title, null Value also for numbers) ------------*/
fmtS: procedure expose m.
parse arg v, ty 2 l
    if ty == 'f' then do
        if \ dataType(v, 'n') then do
            parse value l'.0.0.' with b '.' a '.' e '.'
            return right(v, b + a + (a \== 0) + e + 2 * (e > 0))
            end
        end
    else if ty == 'e' then do
        if \ dataType(v, 'n') then do
            parse var l b '.' a '.' e '.'
            if b == '' then b = 2
            if a == '' then a = 2
            if e == '' then e = 2
            return right(v, b + a + (a \== 0) + e + (e > 0))
            end
        end
    return fmt(v,  ty || l)
endProcedure fmtS
/* copy fmt    end   **************************************************/
/* copy fmtF   begin **************************************************/
fmtFCsvAll: procedure expose m.
parse arg fSep
    if fSep = '' then
        fSep = ','
    if \ inO(i) then
        return
    f = oFlds(i)
    li = ''
    do fx=1 to m.f.0
        li = li',' substr(m.f.fx, 2)
        end
    call out substr(li, 3)
    do until \ inO(i)
        li = ''
        do fx=1 to m.f.0
            if m.f.fx = '' then do
                li = li',' m.i
                end
            else do
                fld = substr(m.f.fx, 2)
                li = li',' m.i.fld
                end
            end
        call out substr(li, 3)
        end
    return
endProcedure fmtFCsvAll

fmtFAdd: procedure expose m.
parse arg m
    fx = m.m.0
    do ax=2 to arg()
        fx = fx + 1
        parse value arg(ax) with m.m.fx.fld m.m.fx.fmt m.m.fx.tit
        end
    m.m.0 = fx
    return m
endProcedure fmtFAdd

fmtFAddFlds: procedure expose m.
parse arg m, st
    fx = m.m.0
    do sx=1 to m.st.0
        fx = fx + 1
        parse value m.st.sx with m.m.fx.fld m.m.fx.fmt m.m.fx.tit
        end
    m.m.0 = fx
    return m
endProcedure fmtFAddFlds

fmtF: procedure expose m.
parse arg m, st
    if arg() >= 3 then
        mid = arg(3)
    else
        mid = ' '
    li = ''
    do fx=1 to m.m.0
        f = st || m.m.fx.fld
        li = li || mid || fmtS(m.f, m.m.fx.fmt)
        end
    return substr(li, 1 + length(mid))
endProcedure fmtF

fmtFReset: procedure expose m.
parse arg m
    m.m.0 = 0
    return m
endProcedure fmtFReset

fmtFWriteAll: procedure expose m.
parse arg m, rdr, wiTi
    b = env2buf(rdr)
    st = b'.BUF'
    if m.st.0 < 1 then
        return
    if m.m.0 < 1 then
        call fmtFAddFlds m, oFlds(st'.1')
    call fmtFDetect m, st
    if wiTi \== 0 then
        call out fmtFTitle(m)
    do sx=1 to m.st.0
        call out fmtF(m, st'.'sx)
        end
    return
fmtFWriteAll

fmtFTitle: procedure expose m.
parse arg m
    if arg() >= 2 then
        mid = arg(2)
    else
        mid = ' '
    li = ''
    do fx=1 to m.m.0
        if m.m.fx.tit \= '' then
            t = m.m.fx.tit
        else if m.m.fx.fld = '' then
            t = '='
        else
            t = substr(m.m.fx.fld, 1+abbrev(m.m.fx.fld, '.'))
        li = li || mid || fmtS(t, m.m.fx.fmt)
        end
    return substr(li, 1 + length(mid))

    res = ''
    fs = m.form.FLDS
    do ix=1 to m.fs.0
        res = res fmtS(m.fs.ix, m.form.ix)
        end
    return substr(res, 2)
endProcedure fmtFldTitle


fmtFldTitle: procedure expose m.
parse arg form
    res = ''
    fs = m.form.FLDS
    do ix=1 to m.fs.0
        res = res fmtS(m.fs.ix, m.form.ix)
        end
    return substr(res, 2)
endProcedure fmtFldTitle

fmtFld: procedure expose m.
parse arg form, st
    res = ''
    fs = m.form.FLDS
    do ix=1 to m.fs.0
        f = m.fs.ix
        res = res fmt(m.st.f, m.form.ix)
        end
    return substr(res, 2)
endProcedure fmtData

fmtFldSquash: procedure expose m.
parse arg newFo, class, src
    fs = oFlds(class)
    do fx = 1 to m.fs.0
        fd = m.fs.fx
        lMi = 9e9
        lMa = 0
        rMi = 9e9
        rMa = 0
        len = 0
        do sx = 1 to m.src.0
            x = verify(m.src.sx.fd, ' ', 'n')
            if x < 1 then
                iterate
            lMi = min(lMi, x)
            lMa = max(lMa, x)
            x = length(strip(m.src.sx.fd, 't'))
            rMi = min(rMi, x)
            rMa = max(rMa, x)
            end
        if rMa = 0  then
            m.newFo.fx = 'w1,1'len
        else
            m.newFo.fx = 'w'lMi',' || (rMa+1-lMi)
        end
    m.newFo.0 = m.fs.0
    m.newFo.flds = fs
    return newFo
endProcedure fmtFldSquash

fmtFDetect: procedure expose m.
parse arg m, st
    do fx=1 to m.m.0
        if m.m.fx.fmt = '' then
            m.m.fx.fmt = fmtFDetect1(st, m.m.fx.fld)
        end
    return m
endProcedure fmtDetect

fmtFDetect1: procedure expose m.
parse arg st, suf
    aMa = -1
    aCnt = 0
    aDiv = 0
    nCnt = 0
    nMi = ''
    nMa = ''
    nDi = -1
    nBe = -1
    nAf = -1
    eMi = ''
    eMa = ''
    do sx=1 to m.st.0
        f = st'.'sx || suf
        v = m.f
        aMa = max(aMa, length(v))
        if \ dataType(v, 'n') then do
            aCnt = aCnt + 1
            if aDiv <=3 then
                if aDiv.v \== 1 then do
                    aDiv.v = 1
                    aDiv = aDiv + 1
                    end
            iterate
            end
        nCnt = nCnt + 1
        if nMi == '' then
            nMi = v
        else
            nMi = min(nMi, v)
        if nMa == '' then
            nMa = v
        else
            nMa = max(nMa, v)
        parse upper var v man 'E' exp
        if exp \== '' then do
            en = substr(format(v, 2, 2, 9, 0), 7)
            if en = '' then
                en = exp
            if eMi == '' then
                eMi = en
            else
                eMi = min(eMi, en)
            if eMa == '' then
                eMa = en
            else
                eMa = max(eMa, en)
            end
        parse upper var man be '.' af
        nBe = max(nBe, length(be))
        nAf = max(nAf, length(af))
        nDi = max(nDi, length(be || af))
        end
    say 'suf' suf aCnt 'a len' aMa 'div' aDiv
    say '   ' nCnt 'n' nMi'-'nMa 'be' nBe 'af' nAf,
            'di' nDi 'ex' eMi'-'eMa
    if nCnt = 0 | aDiv > 3 then
        newFo = 'l'max(0, aMa)
    else if eMi \== '' then do
        eMa = max(eMa, substr(format(nMa, 2, 2, 9, 0), 7))
        newFo = 'e' || (1+(eMi < 0)) || '.' || (max(0, nDi-1))'.' ,
            || max(length(eMa+0), length(eMi+0))
        end
    else if nAf > 0 then
        newFo ='f'nBe'.'nAf
    else
        newFo ='f'nBe'.0'
    say '   ' newFo
   return newFo
endProcedure fmtFDetect1

fmtFldRW: procedure expose m.
parse arg fo
    ty = oGetClassPara(m.j.in)
    call assert 'oFlds(ty) == m.fo.flds', 'fo different flds than class'
    call out fmtFldTitle(fo)
    do while in(ii)
        call out fmtFld(fo, ii)
        end
    return
endProcedure fmtClassRW

fmtFldSquashRW: procedure expose m.
parse arg in, opCl
    if in = '' then
        in = m.j.in
    if opCl == 'opCl' then
        call jOpen in, 'r'
    ty = oGetClassPara(in)
    flds = oFlds(ty)
    st = 'FMT.CLASSAD'
    do ix=1 while jRead(in, st'.'ix)
        end
    m.st.0 = ix - 1
    fo = fmtFldSquash(sqFo, ty, st)
    call out fmtFldTitle(fo)
    do ix = 1 to m.st.0
        call out fmtFld(fo, st'.'ix)
        end
    if opCl == 'opCl' then
        call jClose in
    return
endProcedure fmtFldSquashRW
/* copy fmtF  end  * **************************************************/
/* copy sort begin ****************************************************/
sort: procedure expose m.
parse arg i, o, cmp
    if cmp == '' then
        m.sort.comparator = "cmp = m.l.l0 <<= m.r.r0"
    else if length(cmp) < 6 then
        m.sort.comparator = "cmp = m.l.l0" cmp "m.r.r0"
    else if pos(';', cmp) < 1 then
        m.sort.comparator = "aLe = l'.'l0; aRi = r'.'r0; cmp =" cmp
    else
        m.sort.comparator = "aLe = l'.'l0; aRi = r'.'r0;" cmp
    call sort1 i, 1, m.i.0, o, 1, sort.work, 1
    m.o.0 = m.i.0
    return
endProcedure sort

sort1: procedure expose m.
parse arg i, i0, le, o, o0, w, w0
    if le <= 3 then do
        if le = 3 then do
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, w, w0
            call sortMerge i, i0+2, i0+3, w, w0, w0+2, o, o0
            end
        else if le = 2 then
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, o, o0
        else if le = 1 then
            m.o.o0 = m.i.i0
        return
        end
    h = (le + 1) % 2
    call sort1 i, i0,   h,    o, o0+le-h, w, w0
    call sort1 i, i0+h, le-h, w, w0,      o, o0
    call sortMerge o, o0+le-h, o0+le, w, w0, w0+le-h, o, o0
    return
endProcedure sort1

sortMerge: procedure expose m.
parse arg l, l0, le, r, r0, re, o, o0
    do while l0 < le & r0 < re
        interpret m.sort.comparator
        if cmp then do
            m.o.o0 = m.l.l0
            l0 = l0 + 1
            end
        else do
            m.o.o0 = m.r.r0
            r0 = r0 + 1
            end
        o0 = o0 + 1
        end
    do while l0 < le
        m.o.o0 = m.l.l0
        l0 = l0 + 1
        o0 = o0 + 1
        end
    do while r0 < re
        m.o.o0 = m.r.r0
        r0 = r0 + 1
        o0 = o0 + 1
        end
    return
endProcedure sortMerge
/* copy sort end   ****************************************************/
/* 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 comp begin *****************************************************
    the shell compiler
         syntax and semantics of the shell language see javaDoc
***********************************************************************/
/*--- module initialisation ------------------------------------------*/
compIni: procedure expose m.
    if m.compIni = 1 then
        return
    m.compIni = 1
    call pipeIni
    call scanReadIni
    cc = classNew('n Compiler u')
    m.comp.stem.0 = 0
    m.comp.idChars = m.scan.alfNum'@_'
    return
endProcedure compIni

/*--- constructor of Compiler ----------------------------------------*/
comp: procedure expose m.
parse arg src
    nn = oNew('Compiler')
    m.nn.cmpRdr = src
    return nn
endProcedure comp

    m.nn.cmpRdr = scanRead(src)
    return compReset(nn, src)
compReset: procedure expose m.
parse arg m
    m.m.scan = scanRead(,,'|0123456789')
    m.m.chDol = '$'
    m.m.chSpa = ' ' || x2c('09')
    m.m.chNotBlock = '${}='
    m.m.chNotWord = m.m.chNotBlock || m.m.chSpa
    m.m.chKind = '.-=#@'
    m.m.chKinC = '.-=@'
    return m
endProcedure compReset

/*--- push an empty stem on the stack --------------------------------*/
compNewStem: procedure expose m.
parse arg m
    st = mAdd('COMP.STEM', '')
    do ix=1 to arg()-1
        m.st.ix = arg(ix+1)
        end
    m.st.0 = ix-1
    return st
endProcedure compNewStem

/*--- compile the source and return an oRunner with the code ---------*/
compile: procedure expose m.
parse arg m, spec
    call compReset m
    s = m.m.scan
    kind = '@'
    spec = strip(spec)
    do while pos(left(spec, 1), m.m.chKinC) > 0
       kind = left(spec, 1)
       spec = strip(substr(spec, 2))
       end
    call scanSrc s, spec
    call compSpComment m
    m.m.dirKind = kind
    m.m.compSpec = 1
    res = oRunner()
    nxt = res
    doClose = 0
    do cx=1 to 100
        m.m.dir = ''
        kind = m.m.dirKind
        if kind == '@' then do
            what = "shell"
            expec = "pipe or $;";
            call compSpNlComment m
            src = comp2Code(m, ';'compShell(m))
            end
        else do
            what = "data("kind")";
            expec = "sExpression or block";
            src = comp2Code(m, ';'compData(m, kind))
            end
        if m.m.dir == '' then
            call compDirective m
        if m.m.dir == '' then
           return scanErr(s, expec  "expected: compile" what ,
                                   " stopped before end of input")
        if abbrev(m.m.dir, '$#') then
            if \ scanLit(s, m.m.dir) then
                call scanErr m.m.scan 'directive' m.m.dir 'mismatch'
        if src \== '' then do
            call oRunnerCode nxt, src
            nxt = m.m.dirNext
            end
        if wordPos(m.m.dir, 'eof next $#end $#out') > 0 then do
            if doClose then
                call jClose s
            if m.m.dir \== 'next' | \ m.m.compSpec then
                return res
            call scanReadReset s, m.m.cmpRdr
            doClose = jOpenIfNotYet(s)
            m.m.compSpec = 0
            end
        end
    call scanErr s, 'loop in compile'
endProcedure compile

compDirective: procedure expose m.
parse arg m, ki
    if m.m.dir \== '' then
        return ''
    lk = scanLook(m.m.scan, 9)
    if abbrev(lk, '$#') then do
        if pos(substr(lk, 3, 1), m.m.chKinC) > 0 then do
            m.m.dirKind = substr(lk, 3, 1)
            m.m.dir = left(lk, 3)
            end
        else if abbrev(lk, '$#end') then do
            m.m.dir = 'eof'
            return ''
            end
        else
            call scanErr m.m.scan, 'bad directive after $#'
        end
    else if scanAtEnd(m.m.scan) then do
        if \ m.m.compSpec | m.m.cmpRdr == '' then do
            m.m.dir = 'eof'
            return ''
            end
        m.m.dir = 'next'
        end
    else do
        return ''
        end
    m.m.dirNext = oRunner()
    if ki == '@' then
        return "; call oRun '"m.m.dirNext"'"
    else
        return ". '"m.m.dirNext"'"
endProcedure compDirective

/*--- compile data lines return stmts or expr ------------------------*/
compData: procedure expose m.
parse arg m, ki
    s = m.m.scan
    lines = compNewStem(m)
    do forever
        state = 'f'
        do forever
            l = compExpr(m, 'd', ki)
            if \ scanReadNL(s) then
                state = 'l'
            if \ compIsEmpty(m, l) | (state=='' &\abbrev(l, 'c')) then
                call mAdd lines, l
            if state == 'l' then
                leave
            call compComment m
            state = ''
            end
        one = compStmt(m)
        if one == '' then
            leave
        call mAdd lines, one
        call compComment m
        end
    return 'l*' lines
endProcedure compData

/*--- compile shell and return code ----------------------------------*/
compShell: procedure expose m.
parse arg m
    res = ''
    do forever
        one = compPipe(m)
        if one \== '' then
            res = res || one
        if \ scanLit(m.m.scan, '$;') then
            return res
        call compSpNlComment m
        end
endProcedure compShell

/*--- compile an expression, type d=data, w=word, s=strip ------------*/
compExpr: procedure expose m.
parse arg m, type, ki
    s = m.m.scan
    if length(type) \== 1 | pos(type, 'dsb') < 1 then
        call scanErr s, 'bad type' type 'in compExpr(,' type','ki ')'
    if length(ki) \== 1 | pos(ki, m.m.chKind';') < 1 then
        call scanErr s, 'bad kind' ki 'in compExpr(,' type','ki ')'
    charsNot = if(type=='b', m.m.chNotBlock, m.m.chDol)
    laTx = 9e9
    st = compNewStem(m)
    gotCom = 0
    if pos(type, 'sb') > 0 then do
        call compSpComment m
        gotCom = gotCom | m.m.gotComment
        end
    ki2 = if(ki=='=', '-=', ki)
    do forever
        if scanVerify(s, charsNot, 'm') then do
            call mAdd st, ki2 m.s.tok
            laTx = min(laTx, m.st.0)
            end
        else do
            pr = compPrimary(m, ki)
            if pr = '' then
                leave
            call mAdd st, pr
            laTx = 9e9
            end
        gotCom = gotCom | compComment(m)
        end
    do rx = m.st.0 by -1 to laTx while m.st.rx = ki2
        end
    if pos(type, 'bs') > 0 then do
       if rx >= laTx then
           m.st.rx = strip(m.st.rx, 't')
       m.st.0 = rx
       end
   if ki == '=' then
       if m.st.0 < 1 then
           return 'e='
       else
           ki = '-'
    return substr('ce', 2-gotCom, rx < 1)ki'0*' st
endProcedure compExpr

/*--- transform abstract syntax tree to code ------------------------
  wkTst??? codeTree besser dokumentieren
           optimizer an/und/abschaltbar machen
                (test sollte laufen, allenfalls gehen rexx variabeln
                                       verloren)
        syntax tree is simple, only where
        * a transformation is needed from several places or
        * must be deferred for possible optimizations

sn = ops*                 syntax node            op or syntax function
    ( '=' constant                            none
    | '-' rexxExpr     yielding string            cast to string
    | '.' rexxExpr     yielding object            cast to object
    | '<' rexxExpr     yielding file            cast to file
    | ';' rexxStmts                            execute, write obj, Str
    | '*' stem         yielding multiple sn    none
    )

ops = '@'                                    cast to ORun
    | '|'                                    single
    | 'e'                                    empty = space only
    | 'c'                                    empty = including a comment
    | '0'                                    cat expression parts
    | 'l'                                    cat lines
    | '('                                    add ( ... ) or do ... end
---------------------------------------------------------------------*/

comp2Code: procedure expose m.
parse arg m, ki expr
    /* wkTst??? optimize: use stem with code and interpret */
    if expr = '' & pos(right(ki, 1), '@;=') < 1 then
        return scanErr(m.m.scan, 'comp2Code empty expr' ki expr)
    do forever
        ki = comp2CodeKind(m, ki)
        if length(ki) <= 1 then
            if pos(ki, m.m.chKind';<') > 0 then
                return expr
            else
                call err 'comp2Code bad return' ki expr
        fr = right(ki, 1)
        to = substr(ki, length(ki)-1, 1)
        opt = ''
        if pos(to, 'l0') > 0 then do
            opt = to
            to = substr(ki, length(ki)-2, 1)
            end
        nn = '||||'
        if fr == '*' then do
            if opt == '' then
                call scanErr m.m.scan, 'no sOp for * kind' ki expr
            cat = comp2CodeCat(m, expr, opt, to)
            parse var cat to nn
            end
        else if to == '-' then do
            if fr == '=' then
                 nn = quote(expr)
            else if abbrev(fr expr, '. envGetO(') then
                nn =  'envGet(' || substr(expr, 9)
            else if fr == ';' then
                nn = "o2String('"oRunner(expr)"')"
            else if pos(fr, '.<') > 0 then
                nn = "o2String("expr")"
            end
        else if to == '.' then do
            if fr == '=' then
                 nn = quote(s2o(expr))
            else if abbrev(fr expr, '- envGet(') then
                nn = 'envGetO('substr(expr, 8)
            else if fr == '-' then
                nn = 's2o('expr')'
            else if fr == '<' then
                 nn = expr
            else if fr == ';' then
                nn = quote(oRunner(expr))
            end
        else if to == '@' then do
            if fr == '.' then
                nn = 'call oRun' expr
            else if fr == '<' then
                nn = 'call pipeWriteAll' expr
            else if fr == ';' then
                nn = expr
            to = ';'
            end
        else if to == ';' then do
            if fr == '=' then
                nn = 'call out' quote(expr)
            else if fr == '-' then
                nn = 'call out' expr
            else if fr == '.' then
                nn = 'call outO' expr
            else if fr == '<' then
                nn = 'call pipeWriteAll ' expr
            end
        else if to == ':' then do
            if fr == '=' then
                 nn = quote(expr)
            else
                nn = expr
            to = ';'
            end
        else if to == '<' then do
            if fr == '-' then
                 nn = 'file('expr')'
            else if fr == '=' then
                 nn = "file("quote(expr)")"
            else if fr == '.' then
                nn = 'o2File('expr')'
            else if fr == ';' then
                nn = 'o2File('oRunner(expr)')'
            end
        else if to == '(' then do
            nn = compAddBracks(m, fr, expr)
            to = fr
            end
        if nn == '||||' then
            return scanErr(m.m.scan,
                ,'comp2code bad fr' fr 'to' to 'for' ki expr)
        ki = left(ki, length(ki)-2-length(opt))to
        expr = nn
        end
endProcedure comp2Code

/*--- optimize operands: eliminate duplicates and
                         identity transformations -------------------*/
comp2CodeKind: procedure expose m.
parse arg m, ki
    ki = '$'space(translate(ki, '  ', 'ce'), 0)
    fr.2 = '== -- .. << ;; (( -( .(  ;( (< @;  @@ ;@ $l $0'
    to.2 = '=   -  .  <  ;  ( (- (.  (; <  ;   @  @ $  $'
    fr.3 = ';0; ;l; -.- -;- .-. .;. ;<; <(; '
    to.3 = ' 0;  l;   -   -   .   .   ; <;  '
    do until ki = oldKi
        oldKi = ki
        do le=3 by-1 to 2
            do cx=1 while cx <= length(ki)+1-le
                wx = wordPos(substr(ki, cx, le), fr.le)
                if wx > 0 then
                    ki = left(ki, cx-1) || ,
                        word(to.le, wx) || substr(ki, cx+le)
                end
            end
        end
    return substr(ki, 2)
endProcedure comp2CodeKind

/*--- generate one codeString for one stem -------------------------*/
comp2CodeCat: procedure expose m.
parse arg m, st, sOp, trgt
    toCode = trgt == '@' | trgt == ';'
    if m.st.0 < 1 & trgt \== '<' then
        return trgt
    tr1 = trgt
    if \ toCode then do
                        /* check wether we need to evaluate statements
                            and cast the outptut to an object */
        maxTy = 0
         do x=1 to m.st.0
            maxTy = max(maxTy, pos(left(m.st.x, 1), '=-.<;@'))
            end
        if trgt \== '<' then do
            if maxTy >= 5 then do
                tr1 = ';'
                toCode = 1
                end
            end
        else do                        /* handle files */
            if maxTy > 1 then do    /* not constant */
                res = ';'
                do sx=1 to m.st.0
                    res = res';' comp2Code(m, ';'m.st.sx)
                    end
                return '<'res
                end
                                    /* constant file write to jBuf */
            buf = jOpen(jBuf(), m.j.cWri)
            do sx=1 to m.st.0
                call jWrite buf, substr(m.st.sx, 3)
                end
            return '<' quote(jClose(buf))
            end
        end

    if m.st.0 = 1 & trgt \== '<' then
        return trgt comp2Code(m, trgt || m.st.1)
    tr2 = tr1
    if toCode then do
        mc = '; '
        if sOp == 0 then do
            mc = ''
            tr2 = ':'
            end
        end
    else if sOp == '0' then
        mc = if(tr1 == '.' | tr1 == '-', '', ' || ')
    else if sOp == 'l' then
        mc = ' '
    else
        call scanErr m.m.scan, 'bad sOp' sOp ,
            'in comp2CodeCat('m',' st',' sOp',' trgt')'
    if symbol('m.st.1') \== 'VAR' then
        return err("bad m."st'.1')
    sep = if(tr1 == '.' | tr1 == '-' | tr1 == '=', ' || ', ' ')
    sep = if(sOp = 0, ' || ', ' ')
    tr3 = left(tr2, sOp \== 0)
    res = comp2Code(m, tr3 || m.st.1)
    do sx = 2 to m.st.0
        if (tr2 == '.' | tr2 == '-') ,
            & (m.st.sx = '-' | m.st.sx = '.') then do
                /* empty expr is simply a rexx syntax space */
            if right(res, 1) \== ' ' then
                res = res' '
            end
        else do
            act = comp2Code(m, tr3 || m.st.sx)
            res = compCatRexx(res, act, mc, sep)
            end
        end
    return copies(trgt || sOp, tr1 \== trgt)tr1 res
endProcedure comp2CodeCat

/*--- add expression brackets if necessary --------------------------*/
compAddBracks: procedure expose m.
parse arg m, ki, ex
    if ki == ';' then
         return 'do;' ex || left(';', ex \= '') 'end'
    if \ (ki == '.' | ki == '-') then
        return ex
    ex = strip(ex)
    e1 = left(ex, 1)
    if e1 == '(' & pos('(', ex, 2) = 0 & pos(')', ex) = length(ex) then
        return ex
    if pos(e1, '"''') > 0  & pos(e1, ex, 2) = length(ex) then
        return ex
    return '('ex')'
endProcedure compAddBracks

/*--- cat two rexx parts, avoid strange effects--------------------*/
compCatRexx: procedure expose m.
parse arg le, ri, mi, sep
    if mi \== '' then
        return le || mi || ri
    lr = right(le, 1)
    rl = left(ri, 1)
    if (lr == "'" | lr == '"') then do
        if rl == lr then                /* "a","b" -> "ab" */
            return left(le, length(le)-1) || substr(ri, 2)
        else if  rl == '(' then            /* "a",( -> "a" || ( */
            return le||sep||ri            /* avoid function call    */
        end
    else if pos(lr, m.comp.idChars) > 0 then
        if pos(rl, m.comp.idChars'(') > 0 then
            return le || sep || ri        /* a,b -> a || b */
    return le || mi || ri
endProcedure compCatRexx

/*--- compile a primary and return code ------------------------------*/
compPrimary: procedure expose m.
parse arg m, ki
    s = m.m.scan
    if \ scanLit(s, '$') then
        return ''
    if scanString(s) then     /*wkTst??? brauchts beides? */
        return translate(ki, '.--', '@;=')'=' m.s.val
    if scanLit(s, '.', '-') then do
        op = m.s.tok
        return op'('compCheckNN(m, compObj(m, op),
            , 'objRef expected after $'op)
        end
    if pos(ki, '.<') >= 1 then
        f = '. envGetO'
    else
        f = '- envGet'
    if scanLit(s, '{') then do
        if scanLit(s, '?') then
            f = '- envIsDefined'
        else if scanLit(s, '>') then
            f = '- envReadO'
        res = compCheckNE(m, compExpr(m, 'b', '='), 'var name')
        if \scanLit(s, '}') then
            call scanErr s, 'closing } missing after ${'
        return f'(' || comp2Code(m, '-'res)')'
        end
    if scanName(s) then
        return f"('"m.s.tok"')"
    call scanBack s, '$'
    return ''
endProcedure compPrimary

/*--- compile a pipe and return code ---------------------------------*/
compPipe: procedure expose m.
parse arg m
    s = m.m.scan
    ios = ''
    stmts = ''
    stmtLast = ''
    do forever
        io1 = compRedirIO(m, 1)
        if io1 \== '' then do
            ios = ios',' io1
               call compSpNlComment m
            end
        else do
            if stmtLast \== '' then do
                if \ scanLit(s, '$|') then
                    leave
                call compSpNlComment m
                end
            one = comp2code(m, ';'compStmts(m))
            if one == '' then do
                if stmtLast \== '' then
                    call scanErr s, 'stmts expected after $|'
                if ios == '' then
                    return ''
                leave
                end
           if stmtLast \== '' then
                stmts = stmts'; call pipe' || stmtLast
            stmtLast = ';' one
            end
        end
    if stmts \== '' then
        stmtLast = insert('Begin', stmts, pos('pipe;', stmts)+3) ,
                   || '; call pipeLast' stmtLast'; call pipeEnd'
    if ios \== '' then do
        if stmtLast == '' then
            stmtLast = '; call pipeWriteAll'
        stmtLast = '; call pipeBeLa 'substr(ios, 3) || stmtLast';' ,
                   'call pipeEnd'
        end
    return stmtLast
endProcedure compPipe

/*--- compile an io redirection, return
        if makeExpr then "option", expr
                    else code write inut to standard out -------------*/
compRedirIO: procedure expose m.
parse arg m
    s = m.m.scan
    if \ scanLit(s, '$<', '$>>', '$>') then
        return ''
    opt = substr(m.s.tok, 2)
    return "'"opt"'" comp2Code(m, compFile(m))
endProcedure compRedirIO

/*--- compile stmts (stmt or java) -----------------------------------*/
compStmts: procedure expose m.
parse arg m
    lst = compNewStem(m)
    do forever
        one = compStmt(m)
        if one == '' then do
            do forever
                la = compExpr(m, 's', ';')
                if compIsEmpty(m, la) then
                    leave
                la = strip(comp2code(m, ';'la))
                if right(la, 1) \== ',' then do
                    one = one la
                    leave
                    end
                one = one strip(left(la, length(la)-1))
                call compSpNlComment m
                end
             if one = '' then
                 return 'l*' lst
             one = ';' one
             end
        call mAdd lst, one
        call compSpNlComment m
        end
endProcedure compStmts
                        /* wkTst???syntax start */
/*--- compile a single statement -------------------------------------*/
compStmt: procedure expose m.
parse arg m
    s = m.m.scan
    if scanLit(s, "$=") then do
        nm = comp2Code(m, '-'compCheckNE(m,
            , compExpr(m, 'b', '='), "variable name after $="))
        if \ scanLit(s, "=") then
            call scanErr s, '= expected after $=' nm
        vl = compCheckNE(m, compBlockExpr(m, '='),
            , 'block or expression after $=' nm '=')
        if abbrev(vl, '-') then
            return '; call envPut' nm',' comp2Code(m, vl)
        else
            return '; call envPutO' nm',' comp2Code(m, '.'vl)
        end
    if scanLit(s, '$@') then do
        if \ scanName(s) then
            return 'l;' comp2Code(m,
                , '@'compCheckNN(m, compObj(m, '@'),
                , "objRef expected after $@"))
        fu = m.s.tok
        if fu == 'for' then do
            v = comp2Code(m, '-'compCheckNE(m, compExpr(m, 'b', '='),
                   , "variable name after $@for"))
            call compSpComment m
            st = comp2Code(m, ';'compCheckNN(m, compStmt(m),
                     , "statement after $@for" v))
            return '; do while envReadO('v');' st'; end'
            end
        if fu == 'do' then do
            call compSpComment m
            var = if(scanName(s), m.s.tok, '')
            pre = var
            call compSpComment m
            if scanLook(s, 1) \== '=' then
                var = ''
            suf = comp2Code(m, ':'compCheckNE(m, compExpr(m, 's', ';'),
                   , "$@do control construct"))
            call compSpComment m
            st = comp2Code(m, ";"compCheckNN(m, compStmt(m),
                     , "$@do statement"))
            return "; do" pre suf";",
                if(var \== "", "call envPut '"var"'," var";") st"; end"
            end
        if fu == 'ct' then do
            call compSpComment m
            call compInter(comp2Code(m, ';'compCheckNN(m, compStmt(m),
                , 'ct statement')));
            return '; '
            end
        if fu == 'proc' then do
            nm = compCheckNE(m, compExpr(m, 'b', '='), "proc name")
            call compSpComment m
            st = oRunner(comp2Code(m, ';'compCheckNN(m, compStmt(m),
                , 'proc statement')));
            call envPutO compInterEx(comp2Code(m, '-'nm)), st
            return '; '
            end
        if \ scanLit(s, '(') then
            call scanErr s, 'procCall, for, do, ct, proc' ,
                 'or objRef expected after $@'
        call compSpComment m
        if \ scanLit(s, ')') then
            call scanErr s, 'closing ) expected after $@'fu'('
        return '; call oRun envGetO("'fu'")'
        end
    if scanLit(s, '$$') then
        return  compCheckNN(m, compBlockExpr(m, '='),
            , 'block or expression expected after $$')
    return compDirective(m, '@')
endProcedure compStmt
                        /* wkTst???syntax end */

compInter: procedure expose m.
    interpret arg(1)
    return
endProcedure compInter

compInterEx: procedure expose m.
    interpret 'return' arg(1)
endProcedure compInterEx

compBlockExpr: procedure expose m.
parse arg m, ki
    s = m.m.scan
    res = compBlock(m, ki)
    if res \== '' then
        return res
    lk = scanLook(s, 1)
    if pos(lk, m.m.chKind) > 0 then
        call scanChar s, 1
    else
        lk = ki
    return compExpr(m, 's', lk)
endProcedure compBlockExpr

compObj: procedure expose m.
parse arg m, ki
    one = compPrimary(m, translate(ki, '.', '@'))
    if one \== '' then
        return one
    ki = translate(ki, ';', '@')
    one = compBlock(m, ki)
    if one \== '' then
           return ki || one
    s = m.m.scan
    if scanLit(s, '<') then
        return compFile(m)
    if scanLit(s, 'compile') then do
        if pos(scanLook(s, 1), m.m.chKind) < 1 then
            call scanErr s, 'compile kind expected'
        call scanChar s, 1
        return ki'. compile(comp(env2Buf()), "'m.s.tok'")'
        end
    return compDirective(m, ki)
endProcedure compObj

compFile: procedure expose m.
parse arg m
    res = compBlock(m, '=')
    if res \== '' then
        return '<;'res
    s = m.m.scan
    ki = scanLook(s, 1)
    if pos(ki, m.m.chKind) > 0 then do
        call scanLit s, ki
        end
    else do
        ki = '='
        res = compDirective(m, '.')
        if res \== '' then
            return '<'res
        end
    res = compCheckNE(m, compExpr(m, 's', ki),
        , 'block or expr expected for file')
    return '<'res
endProcedure compFile

compBlock: procedure expose m.
parse arg m, ki
    s = m.m.scan
    t2 = scanLook(s, 2)
    hasType = pos(left(t2, 1) , m.m.chKind) > 0
    start = substr(t2, hasType+1, 1)
    if pos(start, '{¢/') < 1 then
        return ''
    if hasType then
        ki = translate(left(t2, 1), ';', '@')
    if \ scanLit(s, left(t2, hasType+1)) then
        call scanErr s, 'compBlock internal 1'
    starter = start
    if start == '{' then
        stopper = '}'
    else if start == '¢' then
        stopper = '$!'
    else do
        call scanVerify s, '/', 'm'
        starter = '/'m.s.tok'/'
        stopper = '$'starter
        if \scanLit(s, '/') then
            call scanErr s, 'ending / after stopper' stopper 'expected'
        end
    if start == '{' then do
        res = compNewStem(m)
        if ki == '#' then do
            tx = '= '
            cb = 1
            do forever
                call scanVerify s, '{}', 'm'
                tx = tx || m.s.tok
                if scanLit(s, '{') then
                    cb = cb + 1
                else if scanLook(s, 1) \== '}' then
                    call scanErr s, 'closing } expected'
                else if cb <= 1 then
                    leave
                else if scanLit(s, '}') then
                    cb = cb - 1
                else
                    call scanErr s, 'closing } programming error'
                tx = tx || m.s.tok
                end
            call mAdd res, tx
            end
        else do
            one = compExpr(m, 'b', ki)
            if one \== '' & \ abbrev(one, 'e') then
                call mAdd res, one
            end
        res = 'l*' res
        end
    else if ki == '#' then do
        res = compNewStem(m)
        call compSpComment m
        if \ scanReadNl(s) then
            call scanErr s,
                , 'space nl expected in heredata after' starter
        do while \ abbrev(m.s.src, stopper)
            call mAdd res, '=' strip(m.s.src, 't')
            if \ scanReadNl(s, 1) then
                call scanErr s, 'eof in heredata after' starter
            end
        res = 'l*' res
        end
     else if ki == ';' then do
         call compSpNlComment m
         res = compShell(m)
         end
     else if ki == '@' then do
         call err 'compBlock bad ki' ki
         end
     else do
         res = compData(m, ki)
         if res == '' then
             res = 'l*' compNewStem(m)
         end
    if \ scanLit(s, stopper) then
         call scanErr s, 'ending' stopper 'expected after' starter
    if res = '' then
        return '('ki
    else
          return '('res
endProcedure compBlock

/*--- if va == null then issue an error with msg --------------------*/
compCheckNN: procedure expose m.
parse arg m, va, msg
    if va == '' then
        call scanErr m.m.scan, msg 'expected'
    return va
endProcedure compCheckNN

/*--- return true iff expression is empty ---------------------------*/
compIsEmpty: procedure expose m.
parse arg m, ex
    e1 = left(ex, 1)
    return ex = '' | pos(e1, 'ce') > 0 | e1 = ex
endProcedure compIsEmpty

/*--- if va == null or empty then issue an error with msg -----------*/
compCheckNE: procedure expose m.
parse arg m, ex, msg
    e1 = left(ex, 1)
    if compIsEmpty(m, ex) then
        call scanErr m.m.scan, msg 'expected'
    return ex
endProcedure compCheckNE

/*--- skip a comment. return 0 if there is none ----------------------*/
compComment: procedure expose m.
parse arg m
    s = m.m.scan
    res = 0
    do forever
        if scanLit(s, '$**') then
            m.s.pos = 1 + length(m.s.src) /* before next nl */
        else if scanLit(s, '$*+') then
            call scanReadNl s, 1
        else if scanLit(s, '$*(') then do
            do forever
                if scanVerify(s, m.m.chDol, 'm') then iterate
                if scanReadNl(s) then iterate
                if compComment(m) then iterate
                if \ scanLit(s, '$') then
                    call scanErr s, 'source end in comment'
                if scanLit(s, '*)') then
                    return 1
                if scanLit(s, '$') then iterate
                if scanString(s) then iterate
                end
            end
        else
            return res
        res = 1
        end
endProcedure compComment

/*--- skip spaces and comments ---------------------------------------*/
compSpComment: procedure expose m.
parse arg m
    sp = 0
    co = 0
    do forever
        if scanVerify(m.m.scan, m.m.chSpa) then
            sp = 1
        else if compComment(m) then
            co = 1
        else
            leave
        end
    m.m.gotComment = co
    return co | sp
endProcedure compSpComment

/*--- skip spaces, NLs and comments ----------------------------------*/
compSpNlComment: procedure expose m.
parse arg m
    found = 0
    do forever
        if compSpComment(m) then
            found = 1
        else if scanReadNl(m.m.scan) then
            found = 1
        else
            return found
        end
endProcedure compSpComment
/* copy comp end ******************************************************/
/* copy scan begin ****************************************************
Scan: scan an input:
    scanLine(m,ln) : begin scanning a single line (string)
    scanRead??(m,ln): begin scanning all lines of an opened reader
    scanAtEnd(m)   : returns whether we reached end of input
    scanLit(m,lit) : scan Literal lit if present or return 0
    scanChar(m,n)  : scan next n characters
    scanName(m)    : scan a name
    ScanNat(m)     : scan a natural number (without sign)
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanKeyValue(m): scan a key = value clause (with spaces)
    scanWord(m,u)  : scan a space delimited word or a string,
                          if u=1 then uppercase non-strings
    scanErr(m, txt): error with current scan location

    m is an adress, to store our state
    if a scan function succeeds, the scan posititon is moved

returns: true if scanned, false otherwise
         m.m.tok  ==> last token
         m.m.val  ==> last value for scanString/Word/KeyValue
         m.key    ==> key for scanKeyValue
         m.m.pos ==> scan position
         m.m.src ==> scan source
***********************************************************************/
scanIni: procedure expose m.
    if m.scan.ini == 1 then
        return
    m.scan.ini = 1
    m.scan.alfLC = 'abcdefghijklmnopqrstuvwxyz'
    m.scan.alfUC = translate(m.scan.alfLC)
    m.scan.alfa = m.scan.alfLC || m.scan.alfUC
    m.scan.alfNum = m.scan.alfa || '0123456789'
    return
endProcedure scanIni

scanReset: procedure expose m.
parse arg m, n1, np, co
    m.m.rdr = ''
    m.m.jReading = 0 /* if called without jReset */
    m.m.jWriting = 0
    return scanOpts(m, n1, np, co)
endProcedure scanReset

scanOpts: procedure expose m.
parse arg m, m.m.scanName1, namePlus, m.m.scanComment
    if m.m.scanName1 == '' then
        m.m.scanName1 = m.scan.alfa
    if namePlus == '' then
        m.m.scanNameR = m.m.scanName1 || '0123456789'
    else
        m.m.scanNameR = m.m.scanName1 || namePlus
    return m
endProcedure scanOpts


/*--- begin scanning a single line -----------------------------------*/
scanSrc: procedure expose m.
parse arg m, m.m.src
    return scanOpen(m)
endProcedure scanSrc

scanOpen: procedure expose m.
parse arg m
    m.m.tok = ''
    m.m.val = ''
    m.m.key = ''
    m.m.pos = 1
    m.m.atEnd = m.m.rdr == ''
    m.m.jReading = 1
    return m
endProcedure scanOpen

/*--- return the next len characters ---------------------------------*/
scanLook: procedure expose m.
parse arg m, len
    if len == '' then
        return substr(m.m.src, m.m.pos)
    else
        return substr(m.m.src, m.m.pos,
                     , min(len, 1 + length(m.m.src) - m.m.pos))
endProcedure scanLook

/*--- scan the literal lit ------------------------------------------*/
scanLit: procedure expose m.
parse arg m
    do ax=2 to arg()
        if abbrev(substr(m.m.src, m.m.pos), arg(ax)) then do
            m.m.tok = arg(ax)
            m.m.pos = m.m.pos + length(arg(ax))
            return 1
            end
        end
    m.m.tok = ''
    return 0
endProcedure scanLit

/*--- scan the next len characters -----------------------------------*/
scanChar: procedure expose m.
parse arg m, len
    nx = 1 + length(m.m.src)
    if len \= '' then
        nx = min(m.m.pos + len, nx)
    m.m.tok = substr(m.m.src, m.m.pos, nx - m.m.pos)
    m.m.pos = nx
    return m.m.tok \== ''
endProcedure scanChar

/*--- scan a string with quote char qu -------------------------------*/
scanString: procedure expose m.
parse arg m, prefs
    m.m.tok = ''
    bx = m.m.pos
    if prefs = '' then do
        qu = substr(m.m.src, bx, 1)
        if pos(qu, "'""") < 1 then
            return 0
        ax = bx + 1
        end
    else do
        do px=1 until abbrev(substr(m.m.src, bx), p1)
            p1 = word(prefs, px)
            if p1 = '' then
                return 0
            end
        qu = right(p1, 1)
        ax = bx + length(p1)
        end
    m.m.val = ''
    do forever
        qx = pos(qu, m.m.src, ax)
        if qx < 1 then
            return scanErr(m, 'ending Apostroph('qu') missing')
        m.m.val = m.m.val || substr(m.m.src, ax, qx-ax)
        if qx >= length(m.m.src) then
            leave
        else if substr(m.m.src, qx+1, 1) <> qu then
            leave
        ax = qx+2
        m.m.val = m.m.val || qu
        end
    m.m.tok = substr(m.m.src, bx, qx+1-bx)
    m.m.pos = qx+1
    return 1
endProcedure scanString

/*--- scan a Name, first char in *.scanName1, rest in *.scanNameR ----*/
scanName: procedure expose m.
parse arg m
    if pos(substr(m.m.src, m.m.pos, 1),
                 , m.m.scanName1) <= 0 then do
        m.m.tok = ''
        return 0
        end
    return scanVerify(m, m.m.scanNameR)
endProcedure scanName

/*--- scan with verify, vOpt is passed to verify ---------------------*/
scanVerify: procedure expose m.
parse arg m, alpha, vOpt, onlyIfMatch
    if vOpt == '' then   /* empty string does not take default */
        nx = verify(m.m.src, alpha, , m.m.pos)
    else
        nx = verify(m.m.src, alpha, vOpt, m.m.pos)
    if nx = 0 then
        if onlyIfMatch == 1 then
            nx = m.m.pos
        else
            nx = length(m.m.src) + 1
    m.m.tok = substr(m.m.src, m.m.pos, nx - m.m.pos)
    m.m.pos = nx
    return m.m.tok \== ''
endProcedure scanVerify

/*--- scan a natural number (no sign, decpoint ...) ------------------*/
scanNat: procedure expose m.
parse arg m, chEn
    if \ scanVerify(m, '0123456789') then
        return 0
    if chEn \== 0 then
        if pos(scanLook(m , 1), m.m.scanNameR) > 0 then
            call scanErr m, 'illegal number end after' m.m.tok
    return 1
endProcedure ScanNat

/*--- scan an integer (optional sign, no decpoint ...) ---------------*/
scanInt: procedure expose m.
parse arg m, chEn
    call scanLit m, '+', '-'
    si = m.m.tok
    if \ scanNat(m, chEn) then do
        m.m.pos = m.m.pos - si
        return 0
        end
    m.m.tok = si || m.m.tok
    return 1
endProcedure scanInt

/*--- scan a number (optional sign, decpoint, exponent) ------------*/
scanNum: procedure expose m.
parse arg m, chEn
    sx = m.m.pos
    call scanLit m, '+', '-'
    po = scanLit(m, '.')
    if \ scanNat(m, 0) then do
        m.m.pos = sx
        return 0
        end
    if  \ po then
        if scanLit(m, '.') then
            call scanNat m, 0
       if scanLit(m, 'e', 'E') then
           if \ scanInt(m, 0) then
               call scanErr 'exponent expected after' ,
                   substr(m.m.src, sx, m.m.pos-sx)
    m.m.tok = substr(m.m.src, sx, m.m.pos-sx)
    m.m.val = translate(m.m.tok)
    if chEn \== 0 then
        if pos(scanLook(m , 1), m.m.scanNameR) > 0 then
            call scanErr m, 'illegal number end after' m.m.tok
    return 1
endProcedure scanNum

/*--- scan a word and put value into *.val
           a word is either delimited by space or stopper
                     or a string (with single or double quotes -------*/
scanWord: procedure expose m.
parse arg m, stopper
    if scanString(m)                   then return 1
    if \scanVerify(m, ' 'stopper, 'm') then return 0
    m.m.val = m.m.tok
    return 1
endProcedure scanWord

scanType: procedure expose m.
parse arg m, opt
    m.m.tok = ''
    if scanName(m) then
        m.m.type = 'n'
    else if scanNum(m) then
        m.m.type = 0
    else if scanString(m) then
        m.m.type = left(m.m.tok, 1)
    else if scanSpaceNl(m) then
        m.m.type = 's'
    else do
        call scanChar m, 1
        m.m.type = m.m.tok
        end
    return m.m.type
endProcedure scanType

scanBack: procedure expose m.
parse arg m, tok
    if m.m.pos <= length(tok) then
        call scanErr sc, 'cannot back "'tok'" length'
    cx = m.m.pos - length(tok)
    if substr(m.m.src, cx, length(tok)) \== tok then
        call scanErr sc, 'cannot back "'tok'" value'
    m.m.pos = cx
    return
endProcedure scanBack

/*--- scan a key = word phrase
          put key into m.key and word into m.m.val -------*/
scanKeyValue: procedure expose m.
parse arg m, def
    if \ scanName(m) then
        return 0
    m.m.key = m.m.tok
    if \ scanLit(scanSkip(m), '=') then do
        m.m.val = def
        m.m.tok = ' no='
        end
    else if \scanWord(scanSkip(m)) then
        return scanErr(m, 'word expected after' m.m.key '=')
    return 1
endProcedure scanKeyValue

/*--- return true/false whether we are at the end of input ----------*/
scanAtEnd: procedure expose m.
parse arg m
    return m.m.atEnd & m.m.pos > length(m.m.src)
endProcedure scanAtEnd

/*--- skip over spaces, nl and comments (if option set) --------------*/
scanSpaceNL: procedure expose m.
parse arg m
    lastTok = m.m.tok
    if m.m.rdr \== '' then
        interpret 'res = ' objMet(m, 'scanSpaceNl')
    else
        res = scanSpaceCom(m)
    m.m.tok = lastTok
    return res
endProcedure scanSpaceNL

scanSpaceCom: procedure expose m.
parse arg m
    res = scanVerify(m, ' ')
    if m.m.scanComment \== '' then
        if abbrev(substr(m.m.src, m.m.pos), m.m.scanComment) then do
            m.m.pos = 1 + length(m.m.src)
            return 1
            end
    return res
endProcedure scanSpaceCom

/*--- skip over space, nl and comments and return m -----------------*/
scanSkip: procedure expose m.
parse arg m
    call scanSpaceNl m
    return m
endProcedure scanSkip

/*--- emit an error with current scan pos ----------------------------*/
scanErr: procedure expose m.
parse arg m, txt
    m.m.err.0 = 0
    call err 'scanErr' txt'\n'scanInfo(m, m'.ERR')
    return 0
endProcedure scanErr

scanPos: procedure expose m.
parse arg m
    if m.m.rdr \== '' then
        interpret 'return' objMet(m, 'scanPos')
    else if scanAtEnd(m) then
        return E
    else
        return 1 m.m.pos
endProcedure scanPos

scanInfo: procedure expose m.
parse arg m
    msg = 'last token' m.m.tok 'scanPosition' ,
          strip(left(substr(m.m.src, m.m.pos), 40), 't')
    if m.m.rdr == '' then
        return msg'\npos' m.m.Pos 'in string' strip(m.m.src, 't')
    else
        interpret 'return msg ||' objMet(m, 'scanInfo')
endProcedure scanInfo
/* copy scan end   ****************************************************/
/* copy scanRead begin ************************************************/
scanReadIni: procedure expose m.
    if m.scanRead.ini = 1 then
        return
    m.scanRead.ini = 1
    call scanIni
    call jIni
    ts = classNew('n Scan u f TOK v, f VAL v, f KEY v, f TYPE v')
    call classNew 'n ScanRead u JRW', 'm',
        , 'jReset call scanReadReset m, arg, arg2, arg3',
        , 'jOpen call scanReadOpen m',
        , 'jClose if m.m.closeRdr then call jClose m.m.rdr',
        , 'jRead call scanType m; call oClaCopy "'ts'", m, var;' ,
            'return m.m.type \== ""',
        , 'scanReadNl return scanReadNlImpl(m, unCond)',
        , 'scanSpaceNl scanReadSpaceNl(m)',
        , 'scanInfo scanReadInfo(m)',
        , 'scanPos scanReadPos(m)'
    return
endProcedure scanReadIni

/*--- begin scanning the lines of a reader ---------------------------*/
scanRead: procedure expose m.
parse arg rdr, n1, np, co
    return scanOpts(oNew('ScanRead', rdr), n1, np, co)

scanReadReset: procedure expose m.
parse arg m, r, n1, np, co
    call scanReset m, n1, np, co
    m.m.rdr = r
    return m
endProcedure scanReadReset

scanReadOpen: procedure expose m.
parse arg m, r, n1, np, co
    call scanOpen m
    m.m.atEnd = 0
    m.m.lineX = 0
    m.m.closeRdr = jOpenIfNotYet(m.m.rdr, m.j.cRead)
    call scanReadNl m, 1
    return m
endProcedure scanReadOpen

/*--- scan over next newLine
        if unCond \== 1 only if we are already at endOfLine
         return true if we scanned a NL ------------------------------*/
scanReadNl: procedure expose m.
parse arg m, unCond
    interpret objMet(m, 'scanReadNl')
endProcedure scanReadNl

/*--- implementation of scanReadNl ----------------------------------*/
scanReadNLimpl: procedure expose m.
parse arg m, unCond
    if unCond \== 1 then
        if m.m.pos <= length(m.m.src) then
            return 0
    if m.m.atEnd then
        return 0
    m.m.atEnd = \ jRead(m.m.rdr, m'.SRC')
    if m.m.atEnd then do
        m.m.pos = 1 + length(m.m.src)
        end
    else do
        m.m.pos = 1
        m.m.lineX = m.m.lineX + 1
        end
    return \ m.m.atEnd
endProcedure scanReadNLimpl

scanReadSpaceNl: procedure expose m.
parse arg m
    fnd = 0
    do forever
        if scanSpaceCom(m) then
            fnd = 1
        if \ scanReadNl(m) then
             return fnd
        fnd = 1
        end
endProcedure scanReadSpaceNl

scanReadPos: procedure expose m.
parse arg m, msg
    if scanAtEnd(m) then
        return E
    else
        return m.m.lineX m.m.pos
endProcedure scanReadPos

scanReadInfo: procedure expose m.
parse arg m, msg
    if scanAtEnd(m) then
        msg = msg'\natEnd after'
    else
        msg = msg'\npos' m.m.pos 'in'
    return msg 'line' m.m.lineX':' strip(m.m.src, 't')
endProcedure scanReadInfo
/* copy scanRead end **************************************************/
/* copy scanWin begin *************************************************
     scan the the concatenation of the lines of a reader
         any token my be split over several line
         except the end-of-line-comment-token
***********************************************************************/
scanWinIni: procedure expose m.
    if m.scanWin.ini = 1 then
        return
    m.scanWin.ini = 1
    call scanIni
    call jIni
    call classNew 'n ScanWin u JRW', 'm',
        , 'jReset call scanWinReset m, arg, arg2, arg3',
        , 'jOpen call scanWinOpen m ',
        , 'jClose call scanWinClose m ',
        , 'scanReadNl return scanWinNl(m, unCond)',
        , 'scanSpaceNl scanWinSpaceNl(m)',
        , 'scanInfo scanWinInfo(m)',
        , 'scanPos  scanWinPos(m)'
    return
endProcedure scanReadIni

/*--- instanciate a new window scanner, open rdr ---------------------*/
scanWin: procedure expose m.
parse arg rdr, wiSz, wiBa, cuPo, cuLe
    return scanWinOpts(oNew('ScanWin', rdr), wiSz, wiBa, cuPo, cuLe)

/*--- set the reader and attributes of window scanner m -------------*/
scanWinReset: procedure expose m.
parse arg m, r, wiSz, wiGa, cuPo, cuLe
    call scanReset m
    m.m.rdr = r
    m.m.atEnd = 'still closed'
    return scanWinOpts(m, wiSz, wiGa, cuPo, cuLe)
endProcedure scanWinReset

/*--- set the attributes of window scanner m ------------------------*/
scanWinOpts: procedure expose m.
parse arg m, wiSz, wiGa, cuPo, cuLe
    wiSz = word(wiSz 5, 1)
    wiGa = word(wiGa 1, 1)
    m.m.cutPos = word(cuPo 1, 1)
    m.m.cutLen = word(cuLe 72, 1)
    m.m.winTot = (wiSz * 2 + wiGa) * m.m.cutLen
    m.m.posLim = (wiSz     + wiGa) * m.m.cutLen
    m.m.posOff =  wiGa * m.m.cutLen
    return m
endProcedure scanWinReset

/*--- open reader and start scanning --------------------------------*/
scanWinOpen: procedure expose m.
parse arg m, lx
    call scanOpen m
    m.m.atEnd = 0
    if lx = '' then
        m.m.lineX = 1
    else
        m.m.lineX = lx
    m.m.pos = 1
    m.m.src = ''
    call jOpen m.m.rdr, m.j.cRead
    call scanWinRead m
    return m
endProcedure scanWinOpen

scanWinClose: procedure expose m.
    m.m.atEnd = 'still closed'
    call jClose m.m.rdr
    return
endProcedure scanWinClose
/*--- move the source window: cut left side and append at right side
      return number of characters cut at left ------------------------*/
scanWinRead: procedure expose m.
parse arg m
    dlt = 0
    if m.m.atEnd then
        return 0
    if m.m.pos >= m.m.posLim then do     /*  cut left side */
        dlt = m.m.pos - (m.m.pos // m.m.cutLen + m.m.posOff)
        m.m.src = substr(m.m.src, dlt+1)
        m.m.pos = m.m.pos - dlt
        m.m.lineX = m.m.lineX + dlt % m.m.cutLen
        end
    do while length(m.m.src) < m.m.winTot /* read and fill to len */
        if \ jRead(m.m.rdr, m'.'one) then do
            m.m.atEnd = 1
            return dlt
            end
        m.m.src = m.m.src || substr(m.m.one, m.m.cutPos, m.m.cutLen)
        end
    call assert 'length(m.m.src) = m.m.winTot'
    return dlt
endProcedure scanWinRead

/*--- return position of next line start -----------------------------*/
scanWinNLPos: procedure expose m.
parse arg m
    return m.m.pos + m.m.cutLen - ((m.m.pos - 1) // m.m.cutLen)

/*--- scan over spaces and comments ----------------------------------*/
scanWinSpaceNL: procedure expose m.
parse arg m
    res = 0
    do forever
        r1 = 0
        if scanVerify(m, ' ') then do
            r1 = 1
            end
        else if m.m.scanComment \== '' ,
             & abbrev(substr(m.m.src, m.m.pos), m.m.scanComment) then do
            np = scanWinNlPos(m)
            r1 = length(m.m.scanComment) <= np - m.m.pos
            if r1 then
                m.m.pos = np
            end
        if r1 then
            res = 1
        else if scanWinRead(m) = 0 then
            return res
        end
endProcedure scanWinSpaceNl

/*--- return current position in input ------------------------------*/
scanWinPos: procedure expose m.
parse arg m
    if scanAtEnd(m) then
        return 'E'
    else
        ps = m.m.pos - 1
    return (m.m.lineX + (ps % m.m.cutLen)) (ps // m.m.cutLen + 1)
endProcedure scanWinPos

/*--- return a description of the current scan position --------------*/
scanWinInfo: procedure expose m.
parse arg m
    p = scanWinPos(m)
    if p == 'E' then do
        res = 'atEnd after'
        p = m.m.lineX - 1 + length(m.m.src) % m.m.cutLen
        end
    else do
        res = 'pos' word(p, 2) 'in'
        p = word(p, 1)
        end
    return '\n'res 'line' p':' strip(substr(m.m.src,
          , 1 + (p - m.m.lineX) * m.m.cutLen, m.m.cutLen), 't')
endProcedure scanWinInfo
/* copy scanWin end   *************************************************/
/* copy scanSql begin *************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanSql: procedure expose m.
parse arg inRdr
    return scanSqlReset(scanWin(inRdr), inRdr)

scanSqlReset: procedure expose m.
parse arg m, r, scanWin
    if scanWin \== 0 then
        call scanWinOpts m, 5, 2, 1, 72
    else
        m.m.rdr = r
    return scanOpts(m, , '0123456789_' , '--')

scanSqlSeekId: procedure expose m.
parse arg m, lx, cmd, opts
    if opts = '' then
        opts = word
    if adrEdit("cursor =" max(trunc(lx), 1), 12) = 12 then
        return -1
    do while adrEdit("seek" cmd opts, 4) = 0 /* find each command*/
        call adrEdit "(fx) = cursor"
        if m.debug then do
            call adrEdit "(LI) = LINE" fx
            call debug 'scanSqlSeekId after' lx 'found' cmd 'at' fx li
            end
        call editReadReset m.m.rdr, fx
        call scanWinOpen es, fx
        do while word(scanPos(m), 1) <= fx & scanSqlClass(m)
            if m.m.sqlClass = 'i' & m.m.val == cmd then
                return fx
            end
        end
    return -1
endProcedure scanSqlSeekId
/*--- scan a sql token put class in m.sqlclass:
      'i': ordinary identifier   e.g. Name
      'd': delimited identifier  e.g. "Delimited"
      'q': qualified identifier  e.g. abc."efg"
      'u': integer units         e.g. 8G
      'n': number                e.g. -234 or .2e3
      's': string                e.g. 'abc''ef'
      '' : at end
         : any other character   e.g. ;
      ----------------------------------------------------------------*/
scanSqlClass: procedure expose m.
parse arg m, retSpace
    m.m.val = ''
    if scanSpaceNl(m) & retSpace = 1 then do
        m.m.sqlClass = 'b'
        return 1
        end
    c2 = scanLook(m ,2)
    if scanString(m, "' x' X'") then do
        m.m.sqlClass = 's'
        if \abbrev(m.m.tok, "'") then
            m.m.val = x2c(m.m.val)
        end
    else if scanSqlQuId(m) then do
        if m.m.val.0 > 1 then
            m.m.sqlClass = 'q'
        else if abbrev(m.m.tok, '"') then
            m.m.sqlClass = 'd'
        else
            m.m.sqlClass = 'i'
        end
    else if scanSqlNum(m, 0, 1)  then
        m.m.sqlClass = 'n'
    else if scanChar(m, 1) then
        m.m.sqlClass = m.m.tok
    else if scanAtEnd(m) then do
        m.m.sqlClass = ''
        return 0
        end
    else
        call scanErr m, 'cannot scan sql'
    return 1
endProcedure scanSqlClass

scanSqlSkipBrackets: procedure expose m.
parse arg m, br
    if br \== '' then
        nop
    else if scanLit(m, '(') then
        br = 1
    else
        return 0
    do while scanSqlClass(m) & m.m.sqlClass \== ';'
        if m.m.sqlClass = '('        then br = br + 1
        else if m.m.sqlClass \== ')' then iterate
        else if br > 1              then br = br - 1
        else                             return 1
        end
    call scanErr m, '; or eof, but' br 'closing ) expected'
endProcedure skipBrackets
/*--- scan an ordinary sql identifier e.g. abc, ef_12 ----------------*/
scanSqlId: procedure expose m.
parse arg m
    if \ scanName(m) then
        return 0
    m.m.val = translate(m.m.tok)
    return 1
endProcedure scanSqlId

/*--- scan a delimited or ordinay sql identifier ---------------------*/
scanSqlDeId: procedure expose m.
parse arg m
    if scanSqlId(m) then
        return 1
    if \ scanString(m, '"') then
        return 0
    m.m.val = strip(m.m.val, 't')
    return 1
endProcedure scanSqlDeId

/*--- scan a qualified sql identifier --------------------------------*/
scanSqlQuId: procedure expose m.
parse arg m
    res = ''
    rto = ''
    do qx=1
        if \ scanSqlDeId(m) then do
            if qx <> 1 then
                call scanErr m, 'id expected after .'
            return 0
            end
        m.m.val.qx = m.m.val
        res = res'.'m.m.val
        rto = rto'.'m.m.tok
        if \ scanLit(scanSkip(m), '.') then
            leave
        call scanSpaceNl m
        end
    m.m.val.0 = qx
    m.m.val = substr(res, 2)
    m.m.tok = substr(rto, 2)
    return 1
endProcedure scanSqlQuId

/*--- scan a sql number ----------------------------------------------*/
scanSqlNum: procedure expose m.
parse arg m, checkEnd, noSp
    si = ''
    if noSp == 1 then
        call err 'deimplement ???? wk'
    if noSp \== 1 then do
        if scanLit(m, '+', '-') then do
            si = m.m.tok
            call scanSpaceNl m
            ch = scanLook(m, 2)
            if left(ch, 1) == '.' then
                ch = substr(ch, 2)
            if pos(left(ch, 1), '0123456789') < 1 then do
                call scanBack m, si
                m.m.val = ''
                return 0
                end
            end
        end
    res = scanNum(m, checkEnd)
    m.m.val = si || m.m.val
    return res

endProcedure scanSqlNum

/*--- scan a sql number with a unit which may follow without space ---*/
scanSqlNumUnit: procedure expose m.
parse arg m, both, units
    if \ scanSqlNum(m, 0) then
        return 0
    nu = m.m.val
    sp = scanSpaceNl(m)
    if scanSqlId(m) then do
        if units == '' | wordpos(m.m.val, units) > 0 then
            nu = nu m.m.val
        else if both | \ sp then
            call scanErr m, 'scanSqlNumUnit after' nu 'bad unit' m.m.val
        else
            call scanBack m, m.m.tok
        end
    else if both then
        call scanErr m, 'scanSqlNumUnit no unit after' nu
    else if \sp & pos(scanLook(m, 1), m.m.scanNameR) > 0 then
        call scanErr m, 'scanSqlNumUnit bad number end after' nu
    m.m.val = nu
    return 1
endProcedure scanSqlNumUnit

/* copy scanSql end   *************************************************/
/* copy scanUtil begin *************************************************
    scan db2 utility input statements using scan and a reader
**********************************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanUtilReader: procedure expose m.
parse arg m, inRdr
    call scanReader m, inRdr
    call scanOptions sc, , , '--'
    call scanUtilReset m
    return m
endProcedure scanUtilReader

scanUtilSql: procedure expose m.
parse arg inRdr
    m = scanSql(inRdr)
    call scanUtilReset m
    return m
endProcedure scanUtilReader

scanUtilReset: procedure expose m.
parse arg m
    m.m.utilBrackets = 0
    m.scanUtil =  'BACKUP CATENFM CATMAINT CHECK' ,
                  'COPY COPYTOCOPY DIAGNOSE EXEC LISTDEF LOAD' ,
                  'MERGECOPY MODIFY OPTIONS QUIESCE REBUILD' ,
                  'RECOVER REORG REPAIR REPORT RESTORE' ,
                  'RUNSTATS STOSPACE TEMPLATE UNLOAD'
    return
endProcedure scanUtilReset
/*--- scan next token and put its class in m.sc.utilClass:
      'u' a utility name
      'n' a name
      '"' a quoted name
      "'" an apostroph'd string
      '.' a .
      ',' a ,
      'v' a value
      ''  at end
      ---------------------------------------------------------------*/
scanUtil: procedure expose m.
parse arg sc
    m.sc.utilSpace = scanSpaceNl(sc)
    ty = '?'
    if scanLit(sc, '(') then do
        m.sc.utilBrackets = m.sc.utilBrackets + 1
        end
    else if scanLIT(sc, ')') then do
        m.sc.utilBrackets = m.sc.utilBrackets - 1
        if m.sc.utilBrackets < 0 then
           call scanErr sc, 'unmatched closing bracket )'
        end
    else if scanLit(sc, ',') then do
        end
    else if scanLit(sc, '.') then do
        end
    else if scanString(sc, "'") then do
        end
    else if scanString(sc, '"') then do
        end
    else if scanName(sc) then do
        m.sc.val = translate(m.sc.tok)
        if m.sc.utilBrackets > 0 then
            ty = 'n'
        else if 0 < wordPos(m.sc.val, m.scanUtil) then
            ty = 'u'
        else
            ty = 'n'
        end
    else if scanVerify(sc, ' (),''"', 'm') then do
        ty = 'v'
        m.sc.val = translate(m.sc.tok)
        end
    else if \scanAtEnd(sc) then do
            call scanErr sc, 'scanUtil stopped before end'
        end
    else do
        ty = ''
        m.sc.val = ''
        end
    if ty == '?' then
        m.sc.utilClass = left(m.sc.tok, 1)
    else
        m.sc.utilClass = ty
    return m.sc.utilClass
endProcedure scanUtil

/*--- scan a value or a bracketed list of values ---------------------*/
scanUtilValue: procedure expose m.
parse arg sc, remApo, nl
    if remApo = '' | rempApo = 0 then
        remApo = "nv"
    else if rempApo = 1 then
        remApo = "nv'"
    if '(' \== scanUtil(sc) then
         return scanUtilValueOne(sc, remApo)
    v = ''
    brx = m.sc.utilBrackets
    oLine = word(scanPos(sc), 1)
    do forever
        call scanUtil sc
        one = scanUtilValueOne(sc, remApo)
        if one == '' then
           call scanErr sc, 'eof in brackets'
        else if brx > m.sc.utilBrackets then
           return v
        nLine = word(scanPos(sc), 1)
        if \ m.sc.utilSpace then
            v = v || one
        else if nl \== '' & oLine <> nLine then
            v = v || nl || one
        else
            v = v' 'one
        oLine = nLine
        end
endProcedure scanUtilValue

scanUtilValueOne: procedure expose m.
parse arg sc, valTy
    if m.sc.utilClass == '' then
        return ''
    else if m.sc.utilClass == 'u' then
        call scanErr sc, 'util in scanUtilValueOne'
    if pos(m.sc.utilClass, valTy) > 0 then
        return m.sc.val
    else
        return m.sc.tok
endProcedure scanUtilValueOne
/* copy scanUtil end **************************************************/
/* copy pipe begin *****************************************************
***********************************************************************/
pipeIni: procedure expose m.
    if m.pipe.ini == 1 then
        return
    m.pipe.ini = 1
    call catIni
    call classNew "n PipeFrame u"
    call classNew "n PipeFramedRdr u JRWO", "m",
        , "jOpen call jOpen never-call-PipeFramedRdr-Open",
        , "jReadO call pipePushFrame m;" ,
            "res = jReadO(m.m.framedRdr, var);",
            "call pipeEnd; return res",
        , "jReset never-call-PipeFramedRdr-jReset",
        , "jClose call pipeFramedClose m"
    call mapReset env.vars
    call jReset oMutate("PIPE.framedNoOut", "JRWErr")
    m.pipe.0 = 0
    call pipeBeLa /* by default pushes in and out */
    return
endProcedure pipeIni

pipeOpen: procedure expose m.
parse arg e
    if m.e.inCat then
        call jClose m.e.in
    m.e.inCat = 0
    if m.e.in == '' then
        m.e.in = m.j.in
    else if jOpenIfNotYet(m.e.in, m.j.cRead) then
        m.e.toClose = m.e.toClose m.e.in
    if m.e.out == '' then
        m.e.out = m.j.out
    else if jOpenIfNotYet(m.e.out, m.e.outOp) then
        m.e.toClose = m.e.toClose m.e.out
    return e
endProcedure pipeOpen

pipePushFrame: procedure expose m.
parse arg e
    call mAdd pipe, e
    m.j.in = m.e.in
    m.j.out = m.e.out
    return e
endProcedure pipePushFrame

pipeBegin: procedure expose m.
    e = pipeFrame()
    do ax=1 to arg()
        call pipeAddIO e, arg(ax)
        end
    if m.e.out \== '' then
        call err 'pipeBegin output redirection' m.e.in
    call pipeAddIO e, '>' Cat()
    m.e.allInFrame = 1
    return pipePushFrame(pipeOpen(e))
endProcedure pipeBegin

pipe: procedure expose m.
    px = m.pipe.0
    f = m.pipe.px
    call pipeClose f
    m.f.in = jOpen(m.f.out, '<')
    m.f.out = jOpen(Cat(), '>')
    m.f.toClose = m.f.in m.f.out
    m.j.in = m.f.in
    m.j.out = m.f.out
    m.e.allInFrame = 1
    return
endProcedure pipe

pipeLast: procedure expose m.
    px = m.pipe.0
    f = m.pipe.px
    m.f.in = pipeClose(f)
    m.f.out = ''
    do ax=1 to arg()
        if word(arg(ax), 1) = m.j.cRead then
            call err 'pipeLast input redirection' arg(ax)
        else
            call pipeAddIO f, arg(ax)
        end
    m.f.allInFrame = 1
    if m.f.out == '' then do
        preX = px-1
        preF = m.pipe.preX
        m.f.out = m.preF.out
        m.f.allInFrame = m.preF.allInFrame
        end
    call pipeOpen f
    m.j.in = m.f.in
    m.j.out = m.f.out
    return
endProcedure pipeLast

pipeBeLa: procedure expose m.
    e = pipeFrame()
    do ax=1 to arg()
        call pipeAddIO e, arg(ax)
        end
    return pipePushFrame(pipeOpen(e))
endProcedure pipeBeLa

/*--- activate the last pipeFrame from stack
        and return outputbuffer from current pipeFrame --------------*/
pipeEnd: procedure expose m.
    ox = m.pipe.0  /* wkTst??? streamLine|| */
    if ox <= 1 then
        call err 'pipeEnd on empty stack' ex
    ex = ox - 1
    m.pipe.0 = ex
    e = m.pipe.ex
    m.j.in = m.e.in
    m.j.out = m.e.out
    return pipeClose(m.pipe.ox)
endProcedure pipeEnd

pipeFramedRdr: procedure expose m.
parse arg e
    m = pipeFrame()
    m.m.jReading = 1
    m.m.jWriting = 0
    m.m.framedRdr = jOpen(jClose(m.e.out), m.j.cRead)
    say 'framedRdr <' m.m.framedRdr
    m.m.in = m.e.in
    m.m.framedToClose = m.e.toClose
    m.e.toClose = ''
    m.m.out = "PIPE.framedNoOut"
    call oMutate m, 'PipeFramedRdr'
    return m
endProcedure pipeFramedRdr

pipeFramedClose: procedure expose m.
parse arg m
    m.m.allInFrame = 0
    call pipeClose m
    call oMutate m, 'PipeFrame'
    return
endProcedure pipeFramedClose

pipeFrame: procedure expose m.
     m = oBasicNew("PipeFrame")
     m.m.toClose = ''
     m.m.in = ''
     m.m.inCat = 0
     m.m.out = ''
     m.m.outOp = ''
     m.m.allInFrame = 0
     return m
endProcedure pipeFrame

pipeClose: procedure expose m.
parse arg m, finishLazy
    if m.m.allInFrame == 2 then
        return pipeFramedRdr(m)
    do wx=1 to words(m.m.toClose)
         call jClose word(m.m.toClose, wx)
         end
    m.m.toClose = ''
    return m.m.out
endProcedure pipeClose

pipeAddIO: procedure expose m.
parse arg m, opt file
    if opt == m.j.cRead then do
        if m.m.in == '' then
              m.m.in = o2file(file)
        else if m.m.inCat then
            call catWriteAll m.m.in, o2file(file)
        else do
            m.m.in = jOpen(cat(m.m.in, o2file(file)), m.j.cApp)
            m.m.inCat = 1
            end
        return m
        end
    if \ (opt = m.j.cWri | opt == m.j.cApp) then
        call err 'pipeAddIO('opt',' file') bad opt'
    else if m.m.out \== '' then
        call err 'pipeAddIO('opt',' file') duplicate output'
    m.m.out = o2file(file)
    m.m.outOp = opt
    return m
endProcedure pipeAddIO

/*--- write all from rdr (rsp in) to out, not lazy ----------------*/
pipeWriteNow: procedure expose m.
    parse arg rdr
    call jWriteNow m.j.out, if(rdr == '', m.j.in, rdr)
    return
endProcedure pipeWriteNow

/*--- write all from rdr (rsp in) to out, possibly lazy -----------*/
pipeWriteAll: procedure expose m.
parse arg rdr
    call jWriteAll m.j.out, if(rdr == '', m.j.in, rdr)
    return
endProcedure pipeWriteAll

/*--- write all from rdr (rsp in) to out, possibly lazy
           do lazy reads within current frame              -----------*/
pipeWriteAllFramed: procedure expose m.
parse arg rdr
    if rdr == '' then
        rdr = m.j.in
    px = m.pipe.0
    f = m.pipe.px
    if m.f.allInFrame = 0 then do
        call jWriteNow m.j.out, rdr
        return
        end
    m.f.allInFrame = 2
    call jWriteall m.j.out, rdr
    return
endProcedure pipeWriteFramed

pipePreSuf: procedure expose m.
parse arg le, ri
    do while in(v)
        call out le || m.v || ri
        end
    return
endProcedure pipePreSuf

/*--- out interface of pipe -----------------------------------------*/
outIni: procedure expose m.
    call pipeIni
    return
endProcedure outIni

outPush: procedure expose m.
parse arg st
    call pipeBeLa '>' oNew('JRWOut', st)
    return
endProcedure outPush

outPop: procedure expose m.
    call pipeEnd
    return
endProcedure outPop
/*--- write all from rdr (rsp in) to a new jBuf --------------------*/
env2Buf: procedure expose m.  /*wkTst remove |||| */
    parse arg rdr
    b = jOpen(jBuf(), m.j.cWri)
    call jWriteNow b, if(rdr=='', m.j.in, rdr)
    return jClose(b)
endProcedure env2Buf

envIsDefined: procedure expose m.
parse arg na
    return symbol('m.env.vars.na') == 'VAR'
endProcedure envIsDefined

envGetO: procedure expose m.
parse arg na
    return mapGet(env.vars, na)

envGet: procedure expose m.
parse arg na
    return o2String(mapGet(env.vars, na))
endProcedure envGet

envRead: procedure expose m.
parse arg na
    return in("ENV.VARS."na)

envReadO: procedure expose m.
parse arg na
    if \ inO("ENV.VARS.OBJ."na) then
        return 0
    call envPutO na, "ENV.VARS.OBJ."na
    return 1
    if \ inO('ENV.XX') then
        return 0
    call envPut na, m.env.xx
    return 1

envHasKey: procedure expose m.
parse arg na
    return mapHasKey(env.vars, na)

envVia: procedure expose m.
parse arg na
    return mapVia(env.vars, na)  /*wkTst??? remove?*/

envPutO: procedure expose m.
parse arg na, ref
    return mapPut(env.vars, na, ref)

envPut: procedure expose m.
parse arg na, va
    call mapPut env.vars, na, s2o(va)
    return va

envRemove: procedure expose m.
parse arg na
    return mapRemove(env.vars, na)
/* copy pipe end ******************************************************/
/* copy cat  begin ****************************************************
***********************************************************************/
/*--- create a new cat -----------------------------------------------*/
cat: procedure expose m.
    m = oNew('Cat') /* calls catReset */
    do ax=1 to arg()
        call catWriteAll m, arg(ax)
        end
    return m
endProcedure cat

catReset: procedure expose m.
parse arg m
    m.m.RWs.0 = 0
    m.m.catWr = ''
    m.m.catRd = ''
    m.m.catRdClose = 0
    m.m.catIx = -9e9
    return m
endProcedure catReset

catClose: procedure expose m.
parse arg m
    if m.m.catWr \== '' then do
        call jClose m.m.catWr
        call mAdd m'.RWS', m.m.catWr
        m.m.catWr = ''
        end
    if m.m.catRd \== '' then do
        if m.m.catRdClose then
            call jClose m.m.catRd
        m.m.catRd = ''
        end
    m.m.catIx = -9e9
    return m
endProcedure catClose

catOpen: procedure expose m.
parse arg m, oo
    if oo == m.j.cRead then do
        m.m.catIx = 0
        call catNextRdr m
        m.m.jReading = 1
        end
    else if oo == m.j.cWri | oo == m.j.cApp then do
        if oo == m.j.cWri then
            m.m.RWs.0 = 0
        m.m.catIx = -9e9
        m.m.jWriting = 1
        end
    else do
        call err 'catOpen('m',' oo') bad opt'
        end
    return m
endProcedure catOpen

/*--- return and open next reader ------------------------------------*/
catNextRdr: procedure expose m.
parse arg m
    if m.m.catRd \== '' & m.m.catRdClose then
        call jClose m.m.catRd
    cx = m.m.catIx + 1
    m.m.catIx = cx
    if cx > m.m.RWs.0 then do
        m.m.catRd = ''
        return 0
        end
    m.m.catRd = m.m.RWs.cx
    m.m.catRdClose = jOpenIfNotYet(m.m.catRd , m.j.cRead)
    return 1
endProcedure catNextRdr

catReadO: procedure expose m.
parse arg m, var
    do while m.m.catRd \== ''
        if jReadO(m.m.catRd, var) then
            return 1
        call catNextRdr m
        end
    return 0
endProcedure catReadO

catWrite: procedure expose m.
parse arg m, line
    if m.m.catWr == '' then
        m.m.catWr = jOpen(jBuf(), m.j.cWri)
    call jWrite m.m.catWr, line
    return
endProcedure catWrite

catWriteO: procedure expose m.
parse arg m, var
    if m.m.catWr == '' then
        m.m.catWr = jOpen(jBuf(), m.j.cWri)
    call jWriteO m.m.catWr, var
    return
endProcedure catWriteO

/*--- write contents of a reader to cat
          or keep it for later reading -------------------------------*/
catWriteAll: procedure expose m.
parse arg m
    if m.m.catWr \== '' then do
        call mAdd m'.RWS', jClose(m.m.catWr)
        m.m.catWr = ''
        end
    do ax=2 by 1 to arg()
        call mAdd m'.RWS', o2File(arg(ax))
        end
    return
endProcedure catWriteAll

/*--- create a reader/WriteO for an external file --------------------*/
file: procedure expose m.
parse arg str
    if abbrev(str, m.j.cVar) then do
        var = substr(str, 2)
        if envHasKey(var) then
            return envGetO(var)
        else
            return envPutO(var, jBuf())
        end
    return oNew('File', str)
endProcedure file

fileChild: procedure expose m.
parse arg m, name, opt
    interpret objMet(m, 'fileChild')
endProcedure fileChild

fileRm: procedure expose m.
parse arg m
    interpret objMet(m, 'fileRm')
    return
endProcedure fileRm

filePath: procedure expose m.
parse arg m
    interpret objMet(m, 'filePath')
endProcedure filePath

fileIsFile: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsFile')
endProcedure fileIsFile

fileIsDir: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsDir')
endProcedure fileIsDir

fileMkDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileMkDir')
    return
endProcedure fileRm

fileRmDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileRmDir')
    return
endProcedure fileRm

/*--- create a reader/WriteO for the filelist of a directory----------*/
fileList: procedure expose m.
parse arg m, opt
    return oNew('FileList', filePath(m),  opt)
endProcedure fileList

catIni: procedure expose m.
    if m.cat.ini == 1 then
        return
    m.cat.ini = 1
    call jIni
    call classNew "n Cat u JRWO", "m",
        , "jOpen  return catOpen(m, opt)",
        , "jReset return catReset(m, arg)",
        , "jClose call catClose m",
        , "jReadO return catReadO(m, var)",
        , "jWrite call catWrite m, line; return",
        , "jWriteO call catWriteO m, var; return",
        , "jWriteAll call catWriteAll m, rdr; return"
    os = errOS()
    if os == 'TSO' then
        call fileTsoIni
    else if os == 'LINUX' then
        call fileLinuxIni
    else
        call err 'file not implemented for os' os
    return
endProcedure catIni
/* copy cat  end   ****************************************************/
/* copy fiLinux begin *************************************************/
/*--- send ggShCmd to shell bash,
        fail if rc <> 0 or not listed in ggRet -----------------------*/
adrSh: procedure expose m.  /* really no need for variables???? */
    parse arg ggShCmd, ggRet
    address 'bash' ggShCmd
    if rc == 0                     then return 0
    else if ggRet == '*'           then return rc
    else if wordPos(rc, ggRet) > 0 then return rc
    else
        call err 'adrSh rc' rc 'for' ggShCmd
endProcedure adrSh

fileLinuxReset: procedure expose m.
parse arg m, nm
    m.m.spec = nm
    if abbrev(nm, '&') then do
        if nm == '&in' then do
            m.m.stream = .input
            m.m.jReading = 1
            end
        else if nm == '&out' then do
            m.m.stream = .output
            m.m.jWriting = 1
            end
        else do
            call err 'bad spec' nm
            end
        end
    else do
        m.m.stream = .Stream%%new(nm)
        m.m.stream%%init(m.m.stream%%qualify)
        end
    return m
endProcedure fileLinuxReset

fileLinuxOpen: procedure expose m.
parse arg m, opt
    if opt == m.j.cRead then do
        res = m.m.stream%%open(read shareread)
        m.m.jReading = 1
        end
    else do
        if opt == m.j.cApp then
            res = m.m.stream%%open(write append)
        else if opt == m.j.cWri then
            res = m.m.stream%%open(write replace)
        else
            call err 'fileLinuxOpen('m',' opt') with bad opt'
        m.m.jWriting = 1
        end
    if res \== 'READY:' then
        call err 'fileLinuxOpen fails' res':' opt m.m.stream%%qualify
    return m
endProcedure fileLinuxOpen

fileLinuxClose:
parse arg m
    res = m.m.stream%%close
    if res \== 'READY:' then
        call err 'fileLinuxClose' res':' m.m.stream%%qualify
    return m
endProcedure fileLinuxClose

fileLinuxRead: procedure expose m.
parse arg m, var
    res = m.m.stream%%lineIn
    if res == '' then
        if m.m.stream%%state \== 'READY' then
            return 0
    m.var = res
       m.class.o2c.var = m.class.classV
    return 1
endProcedure fileLinuxRead

fileLinuxWrite: procedure expose m.
parse arg m, line
    if m.m.stream%%lineOut(line) then
        call err 'fileLinuxWrite'
    return
endProcedure fileLinuxWrite

fileLinuxRmDir: procedure expose m.
parse arg m, opt
    if opt == '' then
        return adrSh('rmdir' m.m.spec)
    else if opt == '-r' then
        return adrSh('rm -r' m.m.spec)
    else
        call err 'bad opt' opt 'in fileLinuxRmDir'
endProcedure fileLInuxRmDir

fileLinuxListReset: procedure expose m.
parse arg m, m.m.spec, o
    if o == 'r' then
        m.m.opt = 'S'
    else if o == '' then
        m.m.opt = ''
    else
        call err 'bad opt' o 'in fileLinuxListReset'
    m.m.rx = 'closed'
    return m
endProcedure fileLinuxListReset

fileLinuxListOpen: procedure expose m.
parse arg m
    if m.m \== value('m.'m) then
        call err 'bad m for fileLinuxList:' m
    if m.m.opt == '' then
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST')
    else
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST', m.m.opt)
    if rc \== 0 then
        call err 'rc' rc 'in sysFileTree('m.m.spec', m.'m'.LIST)'
    m.m.rx = 0
    m.m.jReading = 1
    return m
endProcedure fileLinuxListOpen

fileLinuxListRead: procedure expose m.
parse arg m, var
    x = m.m.rx + 1
    if x > m.m.list.0 then
        return 0
    m.var = substr(m.m.list.x, 43)
    m.m.rx = x
    return 1
endProcedure fileLinuxListRead

fileLinuxIni: procedure expose m.
    if m.fileLinux.ini == 1 then
        return
    m.fileLinux.ini = 1
    m.file.sep = '/'
    call jIni
    call classNew "n File u JRW", "m",
        , "jReset return fileLinuxReset(m, arg)",
        , "jOpen  return fileLinuxOpen(m, opt)",
        , "jClose call fileLinuxClose m",
        , "jRead return fileLinuxRead(m, var)",
        , "jWrite call fileLinuxWrite m, line",
        , "jWriteO call jWrite m, o2String(var)",
        , "filePath return m.m.stream%%qualify",
        , "fileIsFile return sysIsFile(m.m.stream%%qualify)" ,
        , "fileIsDir return sysIsFileDirectory(m.m.stream%%qualify)" ,
        , "fileChild return file(m.m.stream%%qualify'/'name)",
        , "fileRm return adrSh(m.m.spec)",
        , "fileMkDir return adrSh('mkdir' m.m.stream%%qualify)" ,
        , "fileRmDir return fileLinuxRmDir(m, opt)"
    call classNew "n FileList u JRW", "m",
        , "jReset return fileLinuxListReset(m, arg, arg2)",
        , "jOpen  return fileLinuxListOpen(m, opt)",
        , "jClose m.m.rx = 'closed'",
        , "jRead return fileLinuxListRead(m, var)"
    return
endProcedure fileLinuxIni
/* copy fiLinux end   *************************************************/
/* copy fileTso begin *************************************************/
fileTsoReset: procedure expose m.
parse arg m, sp
    m.m.readIx = 'c'
    if symbol('m.m.defDD') \== 'VAR' then do
        ix = mInc('FILETSO.BUF')
        m.m.defDD = 'CAT'ix
        m.m.buf = 'FILETSO.BUF'ix
        m.m.spec = sp
        end
    if sp \== '' then do
        m.m.spec = dsnSpec(sp)
        rr = translate(subword(m.m.spec, 4))
        m.m.stripT = \ (pos(':V', rr) > 0 | pos('RECFM(V', rr) > 0)
        end
    return m
endProcedure fileTsoReset

fileTsoOpen: procedure expose m.
parse arg m, opt
    buf = m.m.buf
    if opt == m.j.cRead then do
        aa = dsnAlloc(m.m.spec, 'SHR', m.m.defDD)
        if m.dsnAlloc.dsn <> '' then
            if sysDsn("'"m.dsnAlloc.dsn"'") <> 'OK' then
                call err 'cannot read' m.dsnAlloc.dsn':',
                               sysDsn("'"m.dsnAlloc.dsn"'")
        call readDDBegin word(aa, 1)
        m.m.jReading = 1
        m.buf.0 = -1
        m.m.readIx = 0
        end
    else do
        if opt == m.j.cApp then
            aa = dsnAlloc(m.m.spec, 'MOD', m.m.defDD)
        else if opt == m.j.cWri then
            aa = dsnAlloc(m.m.spec, 'OLD', m.m.defDD)
        else
            call err 'fileTsoOpen('m',' opt') with bad opt'
        call writeDDbegin word(aa, 1)
        m.m.jWriting = 1
        m.buf.0 = 0
        m.m.readIx = 'w'
        end
    m.m.dd = word(aa, 1)
    m.m.free = subword(aa, 2)
    return m
endProcedure fileTsoOpen

fileTsoClose:
parse arg m
    buf = m.m.buf
    if m.m.readIx \== 'c' then do
        if m.m.readIx == 'w' then do
            if m.buf.0 > 0 then
                call writeDD m.m.dd, 'M.'BUF'.'
            call writeDDend m.m.dd
            end
        else do
            call readDDend m.m.dd
            end
        interpret m.m.free
        end
    m.buf.0 = 'closed'
    m.m.readIx = 'c'
    m.m.free  = ''
    m.m.dd    = ''
    return m
endProcedure fileTsoClose

fileTsoRead: procedure expose m.
parse arg m, var
    ix = m.m.readIx + 1
    buf = m.m.buf
    if ix > m.buf.0 then do
        res = readDD(m.m.dd, 'M.'buf'.')
        if \ res then
            return 0
        ix = 1
        end
    m.m.readIx = ix
    m.var = m.buf.ix
    call oMutate var, m.class.classV
    return 1
endProcedure fileTsoRead

fileTsoWrite: procedure expose m.
parse arg m, var
    buf = m.m.buf
    ix = m.buf.0 + 1
    m.buf.0 = ix
    if m.m.stripT then
        m.buf.ix = strip(var, 't')
    else
        m.buf.ix = var
    if ix > 99 then do
        call writeDD m.m.dd, 'M.'buf'.'
        m.buf.0 = 0
        end
    return
endProcedure fileTsoWrite

fileTsoWriteO: procedure expose m.
parse arg m, var
    if objClass(var, m.class.classV) == m.class.classV then do
        call fileTsoWrite m, m.var
        return
        end
    call err 'fileTsoWriteO('m',' var') cannot write objects of class',
                              objClass(var)
endProcedure fileTsoWriteO

jclSub: procedure expose m.
    return file('.sysout(T) writer(intRdr)')
endProcedure jclSub

fileTsoIni: procedure expose m.
    if m.fileTso.ini == 1 then
        return
    m.fileTso.ini = 1
    m.file.sep = '.'
    m.fileTso.buf = 0
    call jIni
    um = "call err 'for tso undefined method'"
    call classNew "n File u JRW", "m",
        , "jOpen  return fileTsoOpen(m, opt)",
        , "jReset return fileTsoReset(m, arg)",
        , "jClose call fileTsoClose m",
        , "jRead return fileTsoRead(m, var)",
        , "jWrite call fileTsoWrite m, line",
        , "jWriteO call fileTsoWriteO m, var",
        , "filePath return word(m.m.spec, 1)"           ,
        , "fileIsFile" um "'fileIsFile'"      ,
        , "fileIsDir   return 1"              ,
        , "fileChild   return file(word(m.m.spec, 1)'.'name opt)",
        , "fileRm"     um "'fileRm'"          ,
        , "fileMkDir"  ,
        , "fileRmDir"  um "'fileRmDir'"
 /*     , "filePath return m.m.stream%%qualify",
        , "fileIsFile return sysIsFile(m.m.stream%%qualify)" ,
        , "fileIsDir return sysIsFileDirectory(m.m.stream%%qualify)" ,
        , "fileChild return file(m.m.stream%%qualify'/'name)",
        , "fileRm return adrSh(m.m.spec)",
        , "fileMkDir return adrSh('mkdir' m.m.stream%%qualify)" ,
        , "fileRmDir return fileLinuxRmDir(m, opt)" */
    call classNew "n FileList u JRW", "m",
        , "jReset if arg2 == 'r' then m.m.dsnMask=arg'.**';",
                                "else m.m.dsnMask=arg'.*';",
        , "jOpen  call csiOpen m, m.m.dsnMask; m.m.jReading=1; return",
        , "jClose" ,
        , "jRead return csiNext(m, var)"
    return
endProcedure fileTsoIni
/* copy fileTso end   *************************************************/
/* copy sqlO   begin ***************************************************
    sql interface  mit  o und j Anbindung
***********************************************************************/
sqlOini: procedure expose m.
    if m.sqlO.ini == 1 then
        return
    m.sqlO.ini = 1
    m.sqlO.cursors = left('', 10, 'r')left('', 30, ' ')
    call sqlIni
    call pipeIni
    call classNew 'n SqlSel u JRWO', 'm',
        , "jReset m.m.src = arg; m.m.type = arg2;",
               "m.m.fetch = ''; m.m.type=''; m.m.cursor=''",
        , "jOpen  call sqlSelOpen m, opt",
        , "jClose call sqlSelClose m",
        , "jReadO return sqlSelRead(m, var)"
 /* call oDecMethods oNewClass("SqlLn", "SqlType"),
        , "jRead  return sqlFetchLn(substr(m, 8), var)"
 */ return
endProcedure sqlOini

sqlSel: procedure expose m.
parse arg src, type
     return oNew('SqlSel', src, type)
endProcedure sqlSel

sqlSel1: procedure expose m.
parse arg src, type, var
     r = jOpen(oNew('SqlSel', src, type), '<')
     if \ jReadO(r, var) then
         call err 'eof on 1. Read in sqlSel1'
     if jReadO(r, sqlSql.ver) then
         call err 'not eof on 2. Read in sqlSel1'
     call jClose r
     return
endProcedure sqlSel1

/*--- prepare and open cursor
      generate type and fetchList ------------------------------------*/
sqlSelOpen: procedure expose m.
parse arg m, opt
    if opt\== m.j.cRead then
        call err 'opt not' m.j.cRead 'sqlSelOpen('m',' opt')'
    m.m.cursor = sqlGetCursor(m.m.cursor)
    call sqlPreOpen m.m.cursor, m.m.src, m.m.type == ''
    if m.m.type == '' then do
        m.m.type = sqlDA2type('SQL.'m.m.cursor'.D')
        m.m.fetch = ''
        end
    if m.m.fetch == '' then
        m.m.fetch = sqlFetchVars(m.m.type, 'M.V')
    m.m.jReading = 1
    return m
endProcedure sqlOpen

/*--- return a free cursor -------------------------------------------*/
sqlGetCursor: procedure expose m.
parse arg last
    cx = 0
    if datatype(last, 'n') & last>0 & last<=length(m.sqlO.cursors) then
        if pos(substr(m.sqlo.cursors, last, 1), 'c ') > 0 then
            cx = last
    if cx == 0 then
        cx = pos(' ', m.sqlo.cursors)
    if cx == 0 then
        cx = pos('c', m.sqlo.cursors)
    if cx = 0 then
        call err 'no more cursors' m.sqlo.cursors
    m.sqlo.cursors = overlay('o', m.sqlo.cursors, cx)
    return cx
endProcedure sqlGetCursor

/*--- mark a cursor as closed ----------------------------------------*/
sqlFreeCursor: procedure expose m.
parse arg cx
     if cx < 1 | cx > length(m.sqlo.cursors) then
         call err 'bad cursor sqlFreeCursor('cx')'
    m.sqlo.cursors = overlay('c', m.sqlo.cursors, cx)
    return cx
endProcedure sqlFreeCursor
/*--- create a type for a sqlDA --------------------------------------*/
sqlDA2type: procedure expose m.
parse arg da , ind
    ff = ''
    do ix=1 to m.da.sqlD
        f1 = word(m.da.ix.sqlName, 1)
        if f1 == '' then
            f1 = 'COL'ix
        if (ind == 1 & m.da.ix.sqlType // 2 = 1) | ind == 2 then
            ff = ff', f' f1' v, f' f1'.IND v'
        else
            ff = ff', f' f1 'v'
        end
    return classNew('n SQL* u' substr(ff, 3))
endProcedure sqlGenType

/*--- create the fetch vars sql syntx -------------------------------*/
sqlFetchVars: procedure expose m.
parse arg cla, pre
    vv = ''
    f = class4name(cla)'.FLDS'
    la = '?'
    do fx=1 to m.f.0
        if la'.IND' \== m.f.fx then
            vv = vv','
        vv = vv ':'pre || m.f.fx
        end
    return substr(vv, 3)
endProcedure sqlFetchVars

/*--- fetch cursor for this sqlSel -----------------------------------*/
sqlSelRead: procedure expose m.
parse arg m, v
    call oMutate v, m.m.type
    return sqlFetchInto(m.m.cursor, m.m.fetch)
endProcedure sqlSelRead

/*--- close sql Cursor -----------------------------------------------*/
sqlSelClose: procedure expose m.
parse arg m, v
    call sqlClose m.m.cursor
    call sqlFreeCursor m.m.cursor
    return m
endProcedure sqlSelClose
/*--- fetch cursor 'c'cx into destination dst
          each column is formatted and assigned to m.dst.<colName> ---*/
sqlFetch: procedure expose m.
parse arg cx, dst
    if ^ sqlFetchInto(cx, 'descriptor :M.SQL.'cx'.D') then
        return 0
    if dst == '' then
        return 1
    fi = oFlds(sqlType(cx))
    fo = m.sql.cx.fmt
    do ix=1 to m.sql.cx.d.SQLD
        f = m.fi.ix
        if m.sql.cx.d.ix.sqlInd = 0 then
            m.dst.f = fmt(m.sql.cx.d.ix.sqlData, m.fo.ix)
        else
            m.dst.f = fmtS(m.sqlNull, m.fo.ix)
        end
    return 1
endProcedure sqlFetch

/*--- fetch all rows into stem st
           from sql src using type ty and format fmt -----------------*/
sql2St: procedure expose m.
parse arg st, src, ty, fmt
    cx = 49
    call sql2Cursor cx, src, ty, fmt
    call sqlOpen cx
    do ix=1 by 1 while sqlFetch(cx, st'.'ix)
        end
    m.st.0 = ix-1
    call sqlClose cx
    return ix-1
endProcedure sql2St

/*--- prepare statement 's'cx and declare cursor 'c'cx from sql src
           use or generate type ty and format fo ---------------------*/
sql2Cursor: procedure expose m.
parse arg cx, src, ty, fo
     call sqlPreDeclare cx, src, 1  /* with describe output */
     call sqlGenType cx, ty
     m.Sql.cx.FMT.0 = 0
     m.Sql.cx.FMT = sqlGenFmt('SQL.'cx'.FMT', cx, fo)
     call assert 'm.'m.sql.cx.fmt'.0 >= m.sql.cx.d.sqlD', 'too few fmt'
     return
endProcedure sql2Cursor

/*--- return the type for sql cx -------------------------------------*/
sqlType: procedure expose m.
parse arg cx
    return oGetTypePara('SQL.TY.'cx)

/*--- fetch cursor 'c'cx
          put the formatted and concatenated columns into m.var
          return 1 if a row fetched, 0 if not ------------------------*/
sqlFetchLn: procedure expose m.
parse arg cx, var
    st = 'SQL.'cx'.FET'
    if ^ sqlFetch(cx, st) then
        return 0
    m.var = oFldCat(sqlType(cx), st, m.sql.cx.fmt)
    return 1
endProcedure sqlFetchLn

/*--- generate the type sql cx as specified in ty
          use the information from the sqlDa -------------------------*/
sqlGenType: procedure expose m.
parse arg cx, ty
     if ty == '*' | ty = '' then do
         ff = ''
         do ix=1 to m.sql.cx.d.sqlD
             f1 = word(m.sql.cx.d.ix.sqlName, 1)
             if f1 == '' then
                 f1 = 'COL'ix
             ff = ff f1
             end
         ty = oFldOnly(ff, 'e')
         end
     call oSetTypePara 'SQL.TY.'cx, ty, 'noCall'
     return ty
endProcedure sqlGenType

/*--- generate the format ff for a sql cx as specified in sp
          use the information from the sqlDa -------------------------*/
sqlGenFmt: 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
/*--- sql o interface ------------------------------------------------*/
/*--- return a reader for the givenn sql or std input ----------------*/
sql2Obj: procedure expose m.
parse arg cx, src, ty, fo
    if ty = '' then
        ty = '*'
    if src == '' then
        src = envCatStr(' ', 'sb')
    call sql2Cursor cx, src, ty, substr(fo, 1+abbrev(fo, '~'))
    call oMutate 'SQL.TY.'cx, 'SqlType'
    return 'SQL.TY.'cx
endProcedure sql2obj

/*--- write to std output the result columns of
          the sql given in src or std input --------------------------*/
sql: procedure expose m.
parse arg cx, src, ty, fo
    call jWriteAll m.j.out, "r£", sql2Obj(cx, src, ty, fo)
    return
endProcedure sql

/*--- write to std output the result lines   of
          the sql given in src or std input --------------------------*/
sqlLn: procedure expose m.
parse arg cx, src, ty, fo
    if fo = '' then
        fo = '~'
    squash = abbrev(fo, '~')
    if ^ abbrev(fo, '=') then
        fo = left(fo, squash) 'sl=' substr(fo, squash+1)
    t = sql2Obj(cx, src, ty, fo)
    if squash then do
        call fmtFldSquashRw t, 'opCl'
        return
        end
    m = 'SQL.LN.'cx
    call oMutate m, 'SqlLn'
    call jWrite m.j.out, fmtFldTitle(m.sql.cx.fmt)
    call jWriteAll m.j.out, "r£", m
    return
endProcedure sqlLn
/* copy sqlO   end   **************************************************/
/* copy sql    begin ***************************************************
    sql interface
***********************************************************************/
sqlIni: procedure expose m.
    m.sqlNull = '---'
    return
endProcedure sqlIni

/*--- prepare statement 's'cx from sql src into descriptor desc ------*/
sqlPrepare: procedure expose m.
parse arg cx, src, descOut, descInp
     s = ''
     if descOut == 1 then
         s = 'into :M.SQL.'cx'.D'
     call sqlExec 'prepare s'cx s 'from :src'
     if descInp == 1 | (descInp == '' & pos('?', src) > 0) then
         call sqlExec 'describe input s'cx 'into :M.SQL.'cx'.I'
     else
         m.sql.cx.i.sqlD = 0
     return
endProcedure

/*--- prepare and declare 'c'cx from sql src -------------------------*/
sqlPreDeclare: procedure expose m.
parse arg cx, src, descOut, descInp
     call sqlPrepare cx, src, descOut, descInp
     call sqlExec 'declare c'cx 'cursor for s'cx
     return
endProcedure sqlPreDeclare

/*--- prepare, declare and open 'c'cx from sql src -------------------*/
sqlPreOpen: procedure expose m.
parse arg cx, src, descOut, descInp
     call sqlPreDeclare cx, src, descOut, descInp
     call sqlOpen cx
     return
endProcedure sqlPreOpen

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

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

/*--- fetch cursor 'c'cx into variables ggVars -----------------------*/
sqlFetchInto:
parse arg ggCx, ggVars
    if ggVars == '' then
        ggVars = 'descriptor :M.SQL.'ggCX'.D'
                        /* accept sqlCodes > 0 except 100 */
    return sqlExec('fetch c'ggCx 'into' ggVars, 0 100) <> 100
endProcedure sqlFetchInto

/*--- return sql variable list for stem st and fields the word in vars
          if withInd == 1 then with sqlIndicator variables
        sqlVars('S', 'A B') --> ':S.A, :S.B'
        sqlVars('S', 'A B', 1) --> ':S.A :S.A.SQLIND, :S.B :S.B.SQLIND'
----------------------------------------------------------------------*/
sqlVars: procedure expose m.
parse arg st, vars, withInd
    res = ''
    if st ^== '' then
        st = st'.'
    do ix=1 to words(vars)
        res = res', :'st || word(vars, ix)
        if withInd == 1 then
             res = res ':'st || word(vars, ix)'.SQLIND'
        end
    return substr(res, 3)
endProcedure sqlVars

sqlVarsNull: procedure expose m.
parse arg st, vars
    hasNulls = 0
    do ix = 1 to words(vars)
        fld = word(vars, ix)
        if m.st.fld.sqlInd < 0 then do
            m.st.fld = m.sqlNull
            hasNulls = 1
            end
        end
    return hasNulls
endProcedure sqlVarsNull

sqlDescNull: procedure expose m.
parse arg cx
    desc = 'SQL.'ggCX'.D',
    hasNulls = 0
    do ix=1 to m.desc.SQLD
        if m.desc.ix.sqlInd < 0 then do
            m.desc.ix.sqlData = m.sqlNull
            hasNulls = 1
            end
        end
    return hasNulls
endProcedure sqlDescNull

/*--- open cursor 'c'cx fetch all into variables vars and close
      st = passed stem, sx = row number
      return number of rows fetched ----------------------------------*/
sqlOpAllCl:
parse arg ggCx, st, ggVars
    do ggAx=4 to arg()
        call sqlDataSet 'SQL.'ggCx'.I', ggAx-3, arg(ggAx)
        end
    call sqlOpen ggCx
    do sx = 1 while sqlFetchInto(ggCx, ggVars)
        end
    m.st.0 = sx - 1
    call sqlClose ggCx
    return m.st.0
endProcedure sqlOpAllCl

sqlDataSet: procedure expose m.
parse arg da, ix, val
    m.da.ix.sqlData = val
    m.da.ix.sqlInd = - (arg(ix+2) == m.sqlNull)
    return
endProcedure sqlDataSet
/*--- prepare, declare open cursor 'c'cx, fetch all and close
      return number of rows fetched ----------------------------------*/
sqlPreAllCl:
parse arg ggCx, ggSrc, st, ggVars
    call sqlPreDeclare ggCx, ggSrc
    return sqlOpAllCl(ggCx, st, ggVars)
endProcedure sqlPreAllCl

/*--- execute statement 's'cx using arguments arg(2), arg(3)... ------*/
sqlExecute:
parse arg ggCx retOk
    do ggAx=2 to arg()
        call sqlDataSet 'SQL.'ggCx'.I', ggAx-1, arg(ggAx)
        end
     return sqlExec('execute s'ggCx 'using descriptor :M.SQL.'ggCx'.I',
                   , retOk)
endProcedure

/*--- execute immediate the sql src ----------------------------------*/
sqlExImm:
parse arg ggSrc, ggRet
     return sqlExec('execute immediate :ggSrc', ggRet)
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, ggRet, ggNo
    if ggNo <> '1' then
        ggSqlStmt = 'execSql' ggSqlStmt
    address dsnRexx ggSqlStmt
    /* say 'sqlCode' sqlCode 'rc' rc 'for' ggSqlStmt ggNo */
    if rc = 0 then
        return 0
    else if ggRet = '*' | wordPos(sqlCode, ggRet) > 0 then
        return sqlCode
    else if rc < 0 then
        call err sqlmsg()
    else if sqlWarn.0 ^== ' ' | sqlCode <> 0 then
        call errSay sqlMsg(), ,'w'
    return sqlCode
endSubroutine sqlExec

/*--- connect to the db2 subsystem ggSys -----------------------------*/
sqlConnect: procedure expose m.
parse arg ggSys, ggRetCon
    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 ggSys = '-' then
        return 0
    return sqlExec("connect" ggSys, ggRetCon ,1)
endProcedure sqlConnect

/*--- diconnect from db2 ---------------------------------------------*/
sqlDisconnect: procedure expose m.
parse arg ggRet
    return sqlExec("disconnect ", ggRet, 1)
endProcedure sqlDisconnect

/*--- issue an sql error message -------------------------------------*/
sqlMsg: /* no procedure, to keep variables sql... */
    signal on syntax name sqlMsgOnSyntax
    ggRes = 'sqlCode' sqlCodeT(sqlCode, sqlErrMc, sqlwarn.0':' ,
       || sqlwarn.1||sqlwarn.2||sqlwarn.3||sqlwarn.4||sqlwarn.5',',
       || sqlwarn.6||sqlwarn.7||sqlwarn.8||sqlwarn.9||sqlwarn.10)
    if 0 then
      sqlMsgOnSyntax: do
        ggRes = 'sqlCode' sqlCode translate(sqlErrMc, ',', 'ff'x),
                '<<rexx sqlCodeT not found or syntax>>\nwarnings'
        do ggX=0 to 10
            if sqlWarn.ggx <> '' then
                ggRes = ggRes ggx'='sqlWarn.ggx
            end
        end
    signal off syntax
    ggRes = ggRes'\nstate' sqlState'\nstmt = ' ggSqlStmt
    ggPref = '\nwith'
    ggXX = pos(':', ggSqlStmt)+1
    do 12 while ggXX > 1
        ggYY = verify(ggSqlStmt, ' ,:+-*/&%?|()¢!', 'm', ggXX)
        if ggYY < 1 then
            ggYY = length(ggSqlStmt) + 1
        ggVar = substr(ggSqlStmt, ggXX, ggYY - ggXX)
        if ggVar <> '' then do
            ggRes = ggRes || ggPref ggVar '=' value(ggVar)
            ggPref = '\n    '
            end
        ggXX = pos(':', ggSqlStmt, ggYY+1) + 1
        end
    return  ggRes
endSubroutine sqlMsg

/*--- 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
/* copy sql    end   **************************************************/
/* copy csi begin    ***************************************************
     csi interface: see dfs managing catalogs appendix c
         returncode/reason see message IDC3009I
**********************************************************************/
/*--- specify dsn mask and fields to start a csi catalog search --------
      arguments:
          m       objectPointer
          dsnMask specifies the dsns with wildcards:
              %  1 character
              *  0 - n character in one level
              ** 0 - n levels
          fields a (space separated) list of field names -------------*/
csiOpen: procedure expose m.
parse arg m, dsnMask, fields
    m.m.fld.0 = words(fields)
    ffix = d2c(m.m.fld.0, 2)
    do x=1 to m.m.fld.0
        m.m.fld.x = translate(word(fields, x))
        ffix = ffix || left(m.m.fld.x, 8)
        end
    if dsnMask \== '' & pos('*', dsnMask) < 1 then
        dsnMask = dsnMask'.**'
    m.m.filt = left(dsnMask, 149) ,
             || left('Y', 3) ,        /* resume offset 149      */
             || ffix                  /* csiNumEn offset 152    */

    WORKLEN = 1024 * 64
    m.m.work = D2C(WORKLEN,4) || COPIES('00'X,WORKLEN-4)
    m.m.pos = workLen + 1
    return
endProcedure csiOpen

/*--- put the next dsn into m.o and m.o.* (for other fields)
      return 1 if next dsn exists 0 otherwise ------------------------*/
csiNext: procedure expose m.
parse arg m, o
    usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET AMOUNT OF WORK AREA USED */
    px = m.m.pos
    do forever
        if px > usedL then do
            if substr(m.m.filt, 150, 1) \== 'Y' then do
                m.m.pos = px
                m.o = ''
                return 0
                end
            reason = left('', 4)
            ADDRESS LINKPGM 'IGGCSI00  reason m.'m'.filt  m.'m'.work'
            if rc == 0 & substr(reason, 3, 2) == '0000'x then
                nop
            else if rc == 4 & substr(reason, 3, 2) == '0464'x then
                say 'data set entry with error'
            else
                call err 'call csi returns' rc,
                             'rc' c2d(substr(reason, 4,1)),
                             'reason' c2d(substr(reason, 3,1)),
                             'module' substr(reason, 1,2)
            usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET WORK AREA USED */
            numFD = C2D(SUBSTR(m.m.work,13,2)) /* no flds + 1 */
            if numFd <> m.m.fld.0 + 1 then
                call err 'csiNumFd' numFd 'not' m.m.fld.0 '+' 1
            px = 15
            iterate
            end
        eType =  substr(m.m.work, px+1, 1)
        m.o = strip(substr(m.m.work, px+2, 44), 't')
        flag = substr(m.m.work, px, 1)
        /* say 'eType' eType m.o 'flag' c2x(flag) */
        if eType == '0' then do
            if flag \== '00'x & flag \== '40'x then
                call err 'flag' c2x(flag) 'for catalog' m.o
            px = px + 50    /* length of catalog entry  */
            iterate
            end
        else do
            if \ abbrev(x2b(c2x(flag)), '101') then
                call err 'call csi entry flag' x2b(c2x(flag)),
                             'rc' c2d(substr(m.m.work, px+49,1)),
                             'reason' c2d(substr(m.m.work, px+48,1)),
                             'module' substr(m.m.work, px+46, 2),
                             'for entry' m.o,
                             'see qw IDC3009I'
            py = px + 46
            tl = c2d(substr(m.m.work, py, 2))
            pl = py + 4
            pf = py + m.m.fld.0 * 2 + 4
            do fx = 1 to m.m.fld.0
                fi = m.m.fld.fx
                fl = c2d(substr(m.m.work, pl, 2))
                m.o.fi = substr(m.m.work, pf, fl)
                if fi = 'MGMTCLAS' then
                    m.o.fi = substr(m.o.fi, 3, c2d(left(m.o.fi ,2)))
                else if wordPos(fi, 'COMUDSIZ NOBYTTRK') > 0 then
                    m.o.fi = c2d(m.o.fi)
                pf = pf + fl
                pl = pl + 2
                end
            if py + tl <> pf then
                call err 'length mismatch for entry' m.o
            m.m.pos = pf
            return 1
            end
        end
endProcedure csiNext
/*--- if dsn is arcived return 'arcive'
      if dsn is tape return 'tape'
      otherwise return managment class ------------------------------*/
csiArcTape: procedure expose m.
parse arg vo, cl, dt, dsn
        if vo = '' then
            say err '||| no volume for dsn' dsn
        else if vo = 'ARCIVE' then
            res = 'arcive'
        else if cl <> '' then
            res = cl
        else if abbrev(vo, 'SHR') then
            res = 'SHR'
        else
            res = 'tape'
        if   res = 'arcive' then
            return res
      /*if   abbrev(res, 'ar') \= abbrev(dt, '00'x) , */
        if   abbrev(res, 'ta') \= abbrev(c2x(left(dt, 1)), '7') ,
           | (left(res, 1) >= 'A') \= abbrev(dt, '30'x) then
           say '||| mismatch cl' cl 'vo' vo 'dt' c2x(dt) 'dsn' dsn
        return res
endProcedure csiArcTape
/* copy csi 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 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

/*--- 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"')"
    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

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
    bl = 32760
    if abbrev(atts, ':') then do
        rl = substr(atts, 3)
        if abbrev(atts, ':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(atts, 2, 1) 'B'
            end
        end
    if pos('(', dsn) > 0 then
        po = 'dsntype(library) dsorg(po)'
    else
        po = ''
    dsn = dsnSetMbr(dsn)
    if forCsm == 1 then
        return "dataset('"dsn"')" po,
                "recfm("space(recfm, 0)") lrecl("rl") blkSize("bl")" ,
                "mgmtclas(COM#A091) space(10, 1000) cylinder"
    else
        return "dsn('"dsn"')" po,
                "recfm("recfm") lrecl("rl") block("bl")" ,
                "mgmtclas(COM#A091) space(10, 1000) cyl"
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 sleep begin ***************************************************/
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
/* copy sleep 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, var
    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
    doClose = jOpenIfNotYet(m, opt)
    do ax=2 to arg()
        call jWriteAll m, arg(ax)
        end
    if doClose then
        call jClose m
    return m
endProcedure jCat

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

jWriteNowImplO: procedure expose m.
parse arg m, rdr
    doClose = jOpenIfNotYet(rdr, m.j.cRead)
    do while jReadO(rdr, line)
        call jWriteO m, line
        end
    if doClose then
        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
    interpret objMet(m, 'jReset')
    return m
endProcedure jReset

jOpenIfNotYet: procedure expose m.
parse arg m, opt
    if opt == m.j.cRead & m.m.jReading then
        return 0
    if (opt == m.j.cWri | opt == m.j.cApp) & m.m.jWriting then
        return 0
    call jOpen m, opt
    return 1
endProcedure jOpenIfNotYet

jOpen: procedure expose m.
parse arg m, opt
    call objMetClaM m, 'jOpen'
    if m.m.jReading | m.m.jWriting then
        return err('already opened jOpen('m',' opt')')
    interpret ggCode
    return m
endProcedure jOpen

jClose: procedure expose m.
parse arg m
    call objMetClaM m, 'jClose'
    if m.m.jReading | m.m.jWriting then
        interpret ggCode
    else
        call err 'jClose' m 'but already closed'
    m.m.jReading = 0
    m.m.jWriting = 0
    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, mid
    call jOpen m, '<'
    if \ jRead(m, line) then
        return ''
    res = m.line
    do while jRead(m, line)
        res = res m.line
        end
    call jClose m
    return res
endProcedure jCatLines

jIni: procedure expose m.
    if m.j.ini == 1 then
        return
    m.j.ini = 1
    m.j.cRead = '<'
    m.j.cWri = '>'
    m.j.cApp = '>>'
    m.j.cVar = '}'
    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, var) then return 0;" ,
                 "call oMutate arg, m.class.classV; return 1" ,
        , "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, ' ')",
        , "o2File return m"
    call classNew 'n JRWO u JRW', 'm',
        , "jRead if \ jReadO(m, 'J.GGVAR.'m) then return 0;" ,
                "m.var = o2string('J.GGVAR.'m); return 1" ,
        , "jReadO"   am "jReadO('m',' var')'" ,
        , "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 JRWOut u JRW', 'm',
        , "jReset m.m.stem = arg;",
               "if arg \== '' & \ dataType(m.arg.0, 'n') then",
                   "m.arg.0 = 0" ,
        , "jWrite if m.m.stem == '' then say line;" ,
                 "else call mAdd m.m.stem, line" ,
        , "jWriteO call classOut , var, 'outO: '",
        , "jOpen if \ abbrev(opt, m.j.cWri) then",
            "call err 'can only write JRWOut.jOpen('m',' opt')';" ,
            "else m.m.jWriting = 1"
    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.out = jOpen(oNew('JRWOut'), m.j.cWri)
    call classNew "n JBuf u JRWO, f BUF s r", "m",
        , "jOpen return jBufOpen(m, opt)",
        , "jReset return jBufReset(m, arg)",
        , "jRead return jBufRead(m, var)",
        , "jReadO return jBufReadO(m, var)",
        , "jWrite call jBufWrite m, line",
        , "jWriteO call jBufWriteO m, var"
    call classNew "n JBufRun u JBuf, f RUNNER r", "m",
        , "jOpen return jBufRunOpen(m, opt)",
        , "jReset return jBufRunReset(m, arg)"
    return
endProcedure jIni

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

inO: procedure expose m.
parse arg arg
    return jReadO(m.j.in, arg)
endProcedur 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

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
    nx = mAdd(m'.BUF', line)
    if \ m.m.allV then
           m.class.o2c.nx = m.class.classV
    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)
               m.class.o2c.m.buf.ax = m.class.classV
               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
            adr = m'.BUF.'ax
            m.class.o2c.adr = m.class.classV
            end
        end
    call oCopy ref, m'.BUF.'mInc(m'.BUF.0')
    return
endProcedure jBufWriteO

jBufReadO: 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 do
        m.var = m.m.buf.nx
        m.class.o2c.var = m.class.classV
        end
    else
        call oCopy m'.BUF.'nx, var
    return 1
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 do
        m.var = m.m.buf.nx
        end
    else
        m.var = o2String(m'.BUF.'nx)
    return 1
endProcedure jBufRead

jBufRun: procedure expose m.
parse arg oRun
    return oNew('JBufRun', oRun) /* calls jBufRunReset */
endProcedure jBufRun

jBufRunReset: procedure expose m.
parse arg m, m.m.runner
    return m
endProcedure jBufRunReset

jBufRunOpen: procedure expose m.
parse arg m, opt
    call jBufOpen m, m.j.cWri /* to avoid recursive loop in push| */
    call pipeBeLa m.j.cWri m
    call oRun m.m.runner
    li = m.m.buf.0
    call pipeEnd
    call jBufOpen jClose(m), opt
    m.m.buf.0 = li
    return m
endProcedure jBufRunOpen

/* 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 oClassAdded m.class.classV
     call mRegister 'Class', 'call oClassAdded arg'
     call classNew 'n ORun u',
         , 'm oRun call err "call of abstract method oRun"',
         , 'm o2File return JBufRun(m)',
         , 'm o2String return jCatLines(JBufRun(m), " ")'
     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
    m.cl.oAdr = 'O.'substr(cl, 7)            /* object adresses */
    m.cl.oCnt = 0
       new = 'new'
       m.cl.oMet.new = ''
       call oAddMethod cl'.OMET', cl
       call oAddFields mCut(cl'.FLDS', 0), cl
       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
    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
     if m.cl.0 \== '' then
         do x=1 to m.cl.0
             call oAddMethod mt, m.cl.x
             end
     return
endProcedure oAddMethod

/*--- add the the fields of class cl to stem f ----------------------*/
oAddFields: procedure expose m.
parse arg f, cl, nm
    if pos(m.cl, 'rv') > 0 then do
     do fx=1 to m.f.0
             if m.f.fx == nm then
                return 0
            end
        if nm == '' then do
             call mMove f, 1, 2
             m.f.1 = ''
             end
        else do
            call mAdd f, nm
            end
           return 0
        end
    if m.cl = 'f' then
        return oAddFields(f, m.cl.class, nm'.'m.cl.name)
    if m.cl.class \== '' then
        return oAddFields(f, m.cl.class, nm)
    if m.cl.0 = '' then
        return 0
    do tx=1 to m.cl.0
        call oAddFields f, m.cl.tx, nm
        end
    return 0
endProcedure oAddFields

/*--- create an an object of the class className --------------------*/
oBasicNew: procedure expose m.
parse arg className
    cl = class4Name(className)
    m.cl.oCnt = m.cl.oCnt + 1
    m = m.cl.oAdr'.'m.cl.oCnt
    m.class.o2c.m = cl
    return m
endProcedure oBasicNew

/*--- create an an object of the class className
        and call it's new method ------------------------------------*/
oNew: procedure expose m.
parse arg className, arg, arg2, arg3
    m = oBasicNew(className)
    interpret classMet(className, 'new')
    return m
endProcedure oNew

/*--- 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 until 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 == 'n' 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
         call err 'no method in classMet('na',' me')'
     return m.cl.oMet.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

/*--- 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 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.o.o2c.m') == 'VAR' then
         return oCopy(m, oBasicNew(m.o.o2c.m))
     return oCopy(m, oBasicNew(m.class.classV))
endProcedure oCopyNew

/*--- return a new instance of a subclass of Run
        with code code in method oRun -------------------------------*/
oRunner: procedure expose m.
parse arg code
    r = oNew(classNew('n ORun* u', '\', 'ORun' ,
           , 'm oRun call err "undefined method oRun in oRun"'))
    if arg() > 0 then
        call oRunnerCode r, arg(1)
    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

/*--- 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, arg, arg2, arg3
    interpret objMet(m, 'o2String')
    call err 'o2String did not return'
endProcedure o2String

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

/*--- cast a String to an object or Null ---------------------------*/
s2oNull: procedure expose m.
parse arg str
    if str == '' then
        return ''
    return m.class.escW || str
endProcedure s2oNull
/* 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 done in O, which, hower, extends the class definitions

      meta
      c     choice       name class
      f     field        name class
      m        method         name            met
      n     name         name class
      r     reference         class
      s     stem              class
      u     union                  stem
      v     string (value)

      class expression (ce) allow the following syntax

      ce = name | 'v'        # value contains a string
                  | 'w'         # string reference =m.class.escW||string
                  | 'o'        # object: dynamic class lookup
                  | 'r' ce?     # reference instance of ce default 'o'
                  |     ('n'     # names ce
                      | 'f'     # field
                      | 'c') name ce        # choice if value=name
                | 's' ce     # stem
                | 'm' name code         # method
                | 'u' (ce (',' ce)*)?    # union
      # 'm' and 'u' extend to the end of whole ce
***********************************************************************/
classIni: procedure expose m.
    if m.class.ini == 1 then
        return
    m.class.ini = 1
    call mapIni
        /* to notify other modules (e.g. O) on every new named class */
    call mRegisterSubject 'Class',
        , 'call classAddedListener subject, listener'
    m.class.0 = 0
    m.class.tmp.0 = 0
    call mapReset 'CLASS.N2C'  /* name to class */
        /* meta meta data: description of the class datatypes */
    m.class.classV = classNew('n v u v', 'm o2String return m.m',
        , 'm o2File return file(m.m)')
    m.class.escW = '!'
    m.class.classW = classNew('n w u v',
        , 'm o2String return substr(m, 2)',
        , 'm o2File return file(substr(m, 2))')
    m.class.classO = classNew('o')
    m.class.classR = classNew('r')
    m.class.class = classNew('n class u', '\')
    call classNew 'class',
            , 'c v v' ,
            , 'c w w' ,
            , 'c o o' ,
            , 'c r f CLASS r class' ,
            , 'c s f CLASS r class' ,
            , 'c u s r class',
            , 'c f' classNew('u f NAME v, f CLASS r class'),
            , 'c n' classNew('u f NAME v, f CLASS r class'),
            , 'c c' classNew('u f NAME v, f CLASS r class'),
            , 'c m' classNew('u f NAME v, f MET  v')
    return
endProcedure classIni

/*--- to notify a new listener about already defined classes --------*/
classAddedListener: procedure expose m.
parse arg subject, listener
    do y = 1 to m.class.0
        if m.class.y == 'n' then
            call mNotify1 'Class', listener, 'CLASS.'y
        end
    return
endProcedure classAddedListener

/*--- return the name of a class if it exists otherwise the class ---*/
className: procedure expose m.
parse arg cl
    if m.cl = 'n' 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

/*--- get or create a class from the given class expression
        arg(2) may contain options
            '\' do not search for existing class
            '+' do not finish class
            type (1 char) type of following args
        the remaining args are type expressions and will
            be added to the first union -----------------------------*/
classNew: procedure expose m.
parse arg clEx
    if arg() <= 1 then
        if mapHasKey(class.n2c, clEx) then
            return mapGet(class.n2c, clEx)
    oldTmp = m.class.tmp.0
    ox = verify(arg(2), '\+')
    if ox < 1 then
        ox = length(arg(2)) + 1
    opts = left(arg(2), ox-1)
    pr = substr(arg(2), ox, (length(arg(2)) = ox) * 2)
    t = classNewTmp(clEx)
    if arg() > 1 then do
        u = t
        do while m.u \== 'u'
            if m.u.class == '' then
                call err 'no union found' clEx
            u = m.u.class
            end
        do ax = 2 + (opts \== '' | pr \== '') to arg()
            call mAdd u, classNew(pr || arg(ax))
            end
        end
    srch = pos('\', opts) < 1
    p = classPermanent(t, srch)
    if arg() <= 1 then
        call mapAdd class.n2c, clEx, p
    if \srch & p \== t & pos('+', opts) < 1 then
        call mNotify 'Class', p
    m.class.tmp.0 = oldTmp
    return p
endProcedure classNew

/*--- create a temporary class
        with type ty, name nm and class expression ce ---------------*/
classNewTmp: procedure expose m.
parse arg ty nm ce
    if length(ty) > 1 then do
        if nm \== '' then
            call err 'class' ty 'should stand alone:' ty nm ce
        return class4Name(ty)
        end
    t = mAdd(class.tmp, ty)
    m.t.name = ''
    m.t.class = ''
    m.t.met  = ''
    m.t.0 = ''
    if pos(ty, 'vwo') > 0 then do
        if nm \== '' then
            call err 'basicClass' ty 'end of Exp expected:' ty nm ce
        end
    else if ty = 'u' then do
        fx = 0
        m.t.0 = 0
        ce = nm ce
        ux = 0
        do until fx = 0
            tx = pos(',', ce, fx+1)
            if tx > fx then
                sub = strip(substr(ce, fx+1, tx-fx-1))
            else
                sub = strip(substr(ce, fx+1))
            if sub \== '' then do
                ux = ux + 1
                m.t.ux = classNewTmp(sub)
                end
            fx = tx
            end
        m.t.0 = ux
        end
    else if nm == '' & ty \== 'r' then do
        call err 'basicClass' ty 'name or class Exp expected:' ty nm ce
        end
    else do
        if pos(ty, 'sr') > 0 then do
            if nm == '' then
                nm = 'o'
            m.t.class = classNewTmp(nm ce)
            end
        else do
            if pos(ty, 'cfmn') < 1 then
                call err 'unsupported basicClass' ty 'in' ty nm ce
            m.t.name = nm
            if ty = 'm' then
                m.t.met = ce
            else if ce = '' then
                call err 'basicClass' ty 'class Exp expected:' ty nm ce
            else
                m.t.class = classNewTmp(ce)
            end
        end
    return t
endProcedure classNewTmp

/*--- return the permanent class for the given temporary class
        an existing one if possible otherwise a newly created -------*/
classPermanent: procedure expose m.
parse arg t, srch
    if \ abbrev(t, 'CLASS.TMP.') then
        return t
    if m.t.class \== '' then
        m.t.class = classPermanent(m.t.class, srch)
    if m.t.0 \== '' then do
        do tx=1 to m.t.0
            m.t.tx = classPermanent(m.t.tx, srch)
            end
        end
                      /* search equal permanent class */
    do vx=1 to m.class.0 * srch
        p = class'.'vx
        if m.p.search then
               if classEqual(t, p, 1) then
                   return p
           end
    p = mAdd(class, m.t)
    m.p.name = m.t.name
    m.p.class = m.t.class
    m.p.met = m.t.met
    m.p.search = srch
    if m.t.0 > 0 then
        call mAddSt mCut(p, 0), t
    else
        m.p.0 = m.t.0
    if mapHasKey(class.n2c, p) then
        call err 'class' p 'already defined as className'
    else
        call mapAdd class.n2c, p, p
    if m.p = 'n' then do
        if right(m.p.name, 1) == '*' then
            m.p.name = left(m.p.name, length(m.p.name)-1) ,
                || substr(p, length('class.x'))
        if mapHasKey(class.n2c, m.p.name) then
            call err 'class' m.p.name 'already defined'
        else
            call mapAdd class.n2c, m.p.name, p
        if srch then
            call mNotify 'Class', p
        end
    return p
endProcedure classPermanent

/*--- return true iff the two classes are equal
        (up to the name pattern if lPat == 1) -----------------------*/
classEqual: procedure expose m.
parse arg l, r, lPat
        if m.l \== m.r | m.l.class \== m.r.class | m.l.0 \= m.r.0,
                 | m.l.met \== m.r.met then
            return 0
        if m.l.name \== m.r.name then
            if lPat \== 1 | right(m.l.name, 1) \== '*' ,
                    | \ abbrev(m.r.name,
                    , left(m.l.name, length(m.l.name)-1)) then
                return 0
        if m.l.0 == '' then
            return 1
        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 ouput (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 m.t = 'o' then do
        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 m.t == 'v' then
        return out(p1'=' m.a)
    if m.t == 'w' then
        return out(p1'}' substr(a, 2))
    if m.t == 'n' then
        return classOutDone(m.t.class, a, pr, p1':'m.t.name)
    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
            call 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.t1 == 'v'
        call out p1'union' || 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 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 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
    return
endProcedure mapIni

mapInline: procedure expose m.
parse arg pName
    if mapHasKey(map.inlineName, pName) then
        return mapGet(map.inlineName, pName)
    if m.map.inlineSearch == 1 then
        call mapReset map.inlineName, map.inline
    inData = 0
    name = ''
    do lx=m.map.inlineSearch to sourceline()
        if inData then do
            if abbrev(sourceline(lx), stop) then do
                inData = 0
                if pName = name then
                    leave
                end
            else do
                call mAdd act, strip(sourceline(lx), 't')
                end
            end
        else if abbrev(sourceline(lx), '/*<<') then do
            parse value sourceline(lx) with '/*<<' name '<<' stop
            name = strip(name)
            stop = strip(stop)
            if stop == '' then
                stop = name
            if words(stop) <> 1 | words(name) <> 1 then
                call err 'bad inline data' strip(sourceline(lx))
            if mapHasKey(map.inline, name) then
                call err 'duplicate inline data name' name ,
                    'line' lx strip(sourceline(lx), 't')
            act = mapAdd(map.inlineName, name,
                    , mCut('MAP.INLINE.' || (m.map.inline.0 + 1), 0))
            inData = 1
            end
        end
    if inData then
        call err 'inline Data' name 'at' m.map.inlineSearch,
            'has no end before eof'
    m.map.inlineSearch = lx + 1
    if name = pName then
        return act
    if arg() > 1 then
        return arg(2)
    call err 'no inline data named' pName
endProcedure mapInline

/*--- create a new map ----------------------------------------------*/
mapNew: procedure expose m.
parse arg opt
    return mapReset('MAP.'mInc('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
        call 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
    opt = left('K', m.map.keys.a \== '')
    if opt == 'K' then
        call mAdd m.map.Keys.a, ky
    return res
endProcedure mapValAdr

/* copy map 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
***********************************************************************/
/*--- increase m.a and return it (fail if undefined) -----------------*/
mInc: procedure expose m.
    parse arg a
    m.a = m.a + 1
    return m.a
endProcedure mInc

/*--- 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

/*--- 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

/*--- 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 all elements of a stem together ----------------------------*/
mCat: procedure expose m.
parse arg st, mid
    if m.st.0 < 1 then
        return ''
    res = m.st.1
    do x=2 to m.st.0
        res = res || mid || m.st.x
        end
    return res
endProcedure mCat

/*--- notify all listeners of subject subj with argument arg --------*/
mNotify: procedure expose m.
parse arg subj, arg
    if symbol('m.m.subLis.subj') \== 'VAR' then
        call err 'subject' subj 'not registered'
    do lx=1 to m.m.subLis.subj.0
        call mNotify1 subj, lx, arg
        end
    return
endProcedure mNotify

/*--- notify the listener listener of subject subj with arg ---------*/
mNotify1: procedure expose m.
parse arg subject, listener, arg
    interpret m.m.subLis.subject.listener
    return
endProcedure mNotify1

/*--- notify subject subject about a newly registered listener
        or a new subject about previously registered listener -------*/
mNotifySubject: procedure expose m.
parse arg subject, listener
    interpret m.m.subLis.subject
    return
endProcedure mNotifySubject

/*--- register a new subject with the code for mNotifySubject -------*/
mRegisterSubject: procedure expose m.
parse arg subj, addListener
    if symbol('m.m.subLis.subj') == 'VAR' then
        call err 'subject' subj 'already registered'
    m.m.subLis.subj = addListener
    if symbol('m.m.subLis.subj.0') \== 'VAR' then do
         m.m.subLis.subj.0 = 0
         end
    else do lx=1 to m.m.subLis.subj.0
        call mNotifySubject subj, lx
        end
    return
endProcedure registerSubject

/*--- register a listener to subject subj with the code for mNotify -*/
mRegister: procedure expose m.
parse arg subj, notify
    if symbol('m.m.subLis.subj.0') \== 'VAR' then
         m.m.subLis.subj.0 = 0
    call mAdd 'M.SUBLIS.'subj, notify
    if symbol('m.m.subLis.subj') == 'VAR' then
         call mNotifySubject subj, m.m.subLis.subj.0
    return
endProcedure mRegister

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 || '.'
    return
endProcedure mIni
/* copy m end *********************************************************/
/* copy stringUt begin  ***********************************************/
/*--- return the count of occurrences of needle in heyStack ----------*/
posCount: procedure
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 stringUt 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 sysVar('sysISPF') = 'ACTIVE' then
            call adrIsp 'control errors return'
    return
endSubroutine errReset

/*--- error routine: abend with message ------------------------------*/
err:
trace ?r
    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 outPush
    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

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 = ';'
    call out 'err cleanup begin' cl
    call errInterpret cl
    call out '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

/*--- 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

/*--- 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 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

/*--- 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

/*--- if function ----------------------------------------------------*/
if: procedure expose m.
parse arg co, ifTrue, ifFalse
    if co then
        return ifTrue
    else
        return ifFalse
endProcedure if
/* copy err end   *****************************************************/
}¢--- A540769.WK.REXX(WAR) cre=2016-09-26 mod=2016-11-02-14.17.08 A540769 ------
/* rexx ****************************************************************
synopsis: WAR c <warFile> <ds>+
          WAR x <warFile> ( <frPr> <toPr> )*
     creates a warFile from a list of datasets and or members of a PDS
     or extracts datasets and or members from a warFile,

arguments:
    cf        create warfile (lowercase)
    xf        extract members/datasets from warfile (lowercase)
    <warfile> DSN of the warfile
    <home>    the prefix of added or extracted datasets, default userid
    <ds>      if <ds> is a PDS all members are added,
              if a seqential dataset or a member of a PDS it is added
    <frPr>  <toPr> if no prefix pair is given, all dsn are extracted
                  to the same name
              otherwise the prefix (of a DSN in warfile) is changed
                  from <frPr> to <toPr>, if no <frPr> matches
                  the file is not extracted
***********************************************************************/
parse arg args
    call errReset 'hi'
    m.charSetAdd = 0
    if pos('?', args) > 0 then
        return help()
    else if args = '' then do
        if 0 then
            return errHelp('no args')
        oPr  = '~tst.war.'f('%t Y')
        call warExtract '~TST.WAR.QK31O1MX(REXX3)' ,
                       , 'A540769.WK. ~TST.WAREXT.'
        call warExtract '~TST.WAR.QK31O1MX(REXX4)' ,
                       , 'A540769.WK. ~TST.WAREXT.'
        call warExtract '~TST.WAR.QK31O1MX(REXX5)' ,
                       , 'A540769.WK. ~TST.WAREXT.'
        return
        call warCreate oPr'(rexx0)', '~wk.rexx'
        return
        call warCreate oPr'(sql0)', '~wk.sql'
        call warCreate oPr'(jcl0)', '~wk.jcl'
        call warCreate oPr'(plb0)', '~wk.plb'
        call warCreate oPr'(skels0)', '~wk.skels'
        call warCreate oPr'(wk0)', '~wk.wk'
        return
        end
    parse upper var args fun war list
    if fun == 'C' then
        call warCreate war, list
    else if fun == 'X' then
        call warExtract war, list
    else if fun == 'CWK' then do
        call warCreate war'(rexx0)', '~wk.rexx'
        call warCreate war'(sql0)', '~wk.sql'
        call warCreate war'(jcl0)', '~wk.jcl'
        call warCreate war'(plb0)', '~wk.plb'
        call warCreate war'(skels0)', '~wk.skels'
        call warCreate war'(wk0)', '~wk.wk'
        end
    else
        call errHelp 'war fun='fun 'not supported in args' args
exit

warCreate: procedure expose m.
parse arg m.warDsn, list
    m.warCntF = 0
    m.warCntL = 0
    m.warCntB = 0
    m.warDsX = 0
    if m.charsetAdd then do
        m.charSet = ''
        call readDsn '~wk.rexx(charSet)', cc.
        do cx=1 to cc.0
            call checkCharset(cc.cx)
            end
        end
    charSetL = length(m.charSet)
    call warDsnOC 2
    do lx=1 to words(list)
       i = dsn2jcl(word(list, lx))
       if pos('(', i) > 0 then
           call warCreAddLib i
       else if pos("<"sysDsn("'"i"(noMbr)'")">" ,
                  , "<OK> <MEMBER NOT FOUND>") > 0 then
           call warCreAddLib i"(*)"
       else
           call warCreAddSeq i
       end
    call warDsnOC 1
    call warSayAdded
    if m.charSetAdd & (length(m.charSet) <> charSetL) then do
         cy = cc.0
         do cZ = charSetL+1 by 20 to length(m.charSet)
             cy = cy + 1
             cc.cy = 'plus     ' substr(m.charset, cZ, 20)
             end
         call writeDsn '~wk.rexx(charset)', cc., cy, 1
         say 'charset' cc.cy
         end
    return
endProcedure warCreate

warCreAddLib: procedure expose m.
parse arg iDsn
         /* open lmId for ispf member statistics */
    lmid = lmOpen(iDsn)
    if verify(dsnGetMbr(iDsn), '*?', 'm') <= 0 then do
        call warCreAddMbr iDsn, lmId
        end
    else do mx=1 to mbrList(ml, iDsn)
        call warCreAddMbr dsnSetMbr(iDsn, m.ml.mx), lmId
        end
    call lmClose lmid
    return
endProcedure warCreAddLib

warCreAddMbr: procedure expose m.
parse arg iDsn, lmId
    call adrIsp 'lmmFind dataid('lmid') member('dsnGetMbr(iDsn)')' ,
                'stats(yes) noLla'
    h2 = 'cre='translate(ZLC4DATE, '-', '/') ,
        'mod='translate(ZLM4DATE'-'zlmTime':'zlmSec, '-.', '/:') zlUser
    call warCreAddFile iDsn, h2
    return
endProcedure warCreAddMbr

warCreAddFile: procedure expose m.
parse arg m.iDsn, hd
    if m.warFiB >  5e06 then
        call warDsnOC 3
    call dsnAlloc 'dd(iDD)' m.iDsn
    h.1 = '}¢---' strip(m.iDsn hd) '---'
    if length(h.1) < 80 then
        h.1 = left(h.1, 80, '-')
    if m.charsetAdd then
        call checkCharset h.1
    call warWriteDD h., 1
    lc = 1
    bc = length(h.1)
    do while readDD(iDD, i., 1000)
        lc = lc + i.0
        do ix=1 to i.0
            li = i.ix
            if abbrev(li, '}¢') then
                 li = '}¢\'substr(li, 3)
            li = strip(li, 't')
            if m.charsetAdd then
                call checkCharSet(li)
            if li == '' then
                i.ix = ' '
            else
                i.ix = li
            bc = bc + length(i.ix)
            end
        call warWriteDD i.
        end
    call tsoClose iDD
    call tsoFree  iDD
    m.warCntF = m.warCntF + 1
    m.warCntL = m.warCntL + lc
    m.warFiB = m.warFiB + bc
    if m.warCntF // 50 = 0 then
        call warSayAdded
    return
endProcedure warCreAddFile

warCreAddFile22: procedure expose m.
parse arg m.iDsn, hd
    call dsnAlloc 'dd(iDD)' m.iDsn
    h.1 = left('}¢---' strip(m.iDsn hd)' ', max(80, length(hd)+16),'-')
    call warWriteDD h., 1
    lc = 1
    bc = length(h.1)
    pat = 'SELECT GRANT DELETE INSERT DROP ALTER REPLACE TRUNCATE' ,
          'UPDATE CREATE RENAME DESCRIBE FROM INTO TABLE DATABASE' ,
          'INDEX VIEW UNION PHP ADMIN CATALOG CATEGORY SAVE STATS RX',
          'CSS 1=1 TILE MAIL KEY OP SETTING OPEN PAGE EXTRA SETTI SQL' ,
          'REQUEST URI'
    tF = ' 'm.ut_rxid
    tx = 35
    tT = substr(tF, tx+1)left(tF, tx)
    do while readDD(iDD, i., 1000)
        lc = lc + i.0
        do ix=1 to i.0
            li = i.ix
            if abbrev(li, '}¢') then
                 li = '}¢\'substr(li, 3)
            li = strip(li, 't')
            li = translate(li, tT, tF)
 /*         lu = translate(li)
            do px=1 to words(pat)
                do forever
                    py = pos(word(pat, px), lu)
                    if py < 1 then
                        leave
                    li = insert('+', li, py+0)
                    lu = insert('+', lu, py+0)
                    end
                end
*/          if li == '' then
                i.ix = ' '
            else
                i.ix = li
            bc = bc + length(i.ix)
            end
        call warWriteDD i.
        end
    call tsoClose iDD
    call tsoFree  iDD
    m.warCntF = m.warCntF + 1
    m.warCntL = m.warCntL + lc
    m.warFiB = m.warFiB + bc
    if m.warCntF // 10 = 0 then
        call warSayAdded
    return
endProcedure warCreAddFile22

warSayAdded: procedure expose m.
    if m.charSetAdd then
        cm = ' charset' length(m.charset)','
    else
        cm = ''
    say m.warCntF 'files' m.warCntL 'lines,' ,
                 (m.warCntB+ m.warFiB) 'bytes,'cm ,
                 m.iDsn 'added to' m.tso_dsn.warDD
    return

warDsnOC: procedure expose m.
parse arg oc
    if oc // 2 == 1 then do
         m.warCntB = m.warCntB + m.warFiB
         call tsoClose warDD
         call tsoFree warDD
         end
    if oc >= 2 then do
        m.warFiB = 0
        m.warDsX = m.warDsX + 1
        if m.warDsX > length(m.ut_numUc) then
            call err 'overflow' m.warDsX
        m.warDsn = overlay(substr(m.ut_numUc, m.warDsX, 1),
              , m.warDsn, length(m.warDsn) - 1)
        call dsnAlloc m.warDsn 'dd(warDD) ::v100'
        call tsoOpen warDD, 'w'
        end
    return
endProcedure warDsnOC

warWriteDD:
    call writeDD warDD, arg(1), arg(2)
    return
endSubroutine warWriteDD

warExtract: procedure expose m.
parse upper arg m.warDsn, frTo
    parse value dsnAlloc('dd(warDD)' m.warDsn) with frDD frFr
    to.1 = ''
    do fx=1
        fr.fx = word(frTo, 2*fx-1)
        if fr.fx = '' then
            leave
        to.fx = dsn2jcl(word(frTo, 2*fx))
        if to.fx = '' then
            call err 'incomplete pair' fx':' frTo
        end
    fr.0 = max(1, fx-1)
    cL = 0
    cF = 0
    toDD = ''
    m.warExt.lmDsn = ''
    do while readDD(frDD, i.)
        do ix=1 to i.0
            if \ abbrev(i.ix, '}¢') then
                iterate
            if abbrev(i.ix, '}¢\') then do
                i.ix = '}¢'substr(i.ix, 4)
                iterate
                end
            if toDD <> '' then
                toDD = warExtWriClo(toDD, toFx, ix-1,toN,cre,mod,usr)
            parse var i.ix sepB dsn cre mod usr sepE
            if (sepB == '}¢---' & abbrev(cre, 'cre=') ,
                     & abbrev(mod, 'mod=') & abbrev(sepE, '---')) then
                nop
            else if (sepB == '}¢---' & cre == 'cre=' ,
                     & (abbrev(usr, '---') | abbrev(sepE, '---'))) then
                cre = ''
            else
                call err 'bad header' i.ix '#' || (cL+ix) 'in' m.warDsn
            cF = cF + 1
            do fx=1
                if fx > fr.0 then do
                    say 'ignore' cF '#' || (cL + ix) dsn cre mod usr
                    leave
                    end
                if abbrev(dsn, fr.fx) then do
                    toN = to.fx || substr(dsn, length(fr.fx)+1)
                    say 'extract' cF '#' || (cL + ix) dsn cre mod usr,
                          '=>' toN
                    toDD = dsnAlloc('dd(extDD)' toN '::f')
                    m.warExt.oCnt = -1
                    toFx = ix+1
                    leave
                    end
                end
            end
        if toDD <> '' then
            call warExtWri toDD, toFx, i.0
        toFx = 1
        cL = cL + i.0
        end
    if toDD <> '' then
        toDD = warExtWriClo(toDD, toFx, i.0 ,toN,cre,mod,usr)
    if m.warExt.lmDsn <> '' then
        call lmClose m.warExt.lmId
    say cF 'file,' cL 'lines in' m.warDsn
    call tsoClose frDD
    call tsoFree frFr
    return
endProcedure warExtract

warExtWri: procedure expose m. i.
parse arg toDD toFr, fx, tx
    if m.warExt.oCnt < 0 then do
        call tsoOpen toDD, 'w'
        m.warExt.oCnt = 0
        end
    if fx = 1 then do
        call writeDD toDD, i., tx
        m.warExt.oCnt = m.warExt.oCnt + tx
        end
    else do
        iy = 0
        do ix=fx to tx
            iy = iy + 1
            y.iy = i.ix
            end
        call writeDD toDD, y., iy
        m.warExt.oCnt = m.warExt.oCnt + iy
        end
    return
endProcedure warExtWri

warExtWriClo: procedure expose m. i.
parse arg toDD toFr, fx, tx, dsn, cre, mod, usr
    call warExtWri toDD toFr, fx, tx
    call tsoClose toDD
    call tsoFree toFr
    if cre = '' then
        return ''
    if m.warExt.lmDsn <> dsnSetMbr(dsn) then do
        if m.warExt.lmDsn <> '' then
            call lmClose m.warExt.lmId
        m.warExt.lmDsn = dsnSetMbr(dsn)
        m.warExt.lmId = lmOpen(m.warExt.lmDsn)
        end
/*   adrIsp 'lmmStats dataid('lmid') member(wsh) createD4(1908/04/01)',
       'modDate4(2122/05/31) modTime(23:24:25) user(MyUser7)' */
    st = 'curSize('m.warExt.oCnt') user('usr')' ,
         'created4('translate(substr(cre, 5, 10), '/', '-')')' ,
         'modDate4('translate(substr(mod, 5, 10), '/', '-')')' ,
         'modTime('translate(substr(mod, 16, 8), ':', '.')')'
    call adrIsp 'lmmStats dataid('m.warExt.lmid')' ,
                'member('dsnGetMbr(dsn)')' st

    return ''
endProcedure warExtWriClo

checkCharSet: procedure expose m.
parse arg li
    do forever
        cx = verify(li, m.charset)
        if cx = 0 then
            return
        m.charSet = m.charSet || substr(li, cx, 1)
        end
endProcedure checkCharSet

lmOpen: procedure expose m.
parse arg dsn
    call adrIsp "lmInit dataid(lmid) dataset('"dsnSetMbr(dsn)"')"
    call adrIsp 'lmOpen dataid('lmid')'
    return lmid
endProcedure lmOpen

lmClose: procedure expose m.
parse arg lmid
    call adrIsp 'lmClose dataid('lmid')'
    call adrIsp 'lmFree dataid('lmid')'
    return
endProcedure lmClose

/* copy f begin ******************************************************/
/*--- format with the given format ggA1, ggA2, etc. -----------------*/
f: procedure expose m.
parse arg ggFmt, ggA1, ggA2
    if symbol('M.f_gen.ggFmt') \== 'VAR' then
        call fGen ggFmt, ggFmt
    interpret m.f_gen.ggFmt
endProcedure f

fImm: procedure expose m.
parse arg ggFmt, ggA1
    interpret m.f_gen.ggFmt
endProcedure fImm

fCache: procedure expose m.
parse arg a, fmt
    if a \== '%>' then do
        if symbol('M.f_gen.a') == 'VAR' then
            if m.f_gen.a \== fmt then
                call err 'fCache('a',' fmt') already' m.f_gen.a
        end
    else do
        if symbol('m.f_gen0') == 'VAR' then
            m.f_gen0 = m.f_gen0 + 1
        else
            m.f_gen0 = 1
        a =  '%>'m.f_gen0
        end
    m.f_gen.a = fmt
    return a
endProcedure fCache

/*--- compile format fmt put in the cache with address a
          this procedure handles precompile and calls fGenF ---------*/
fGen: procedure expose m.
parse arg a, fmt
    if a \== '%>' then
        if symbol('M.f_gen.a') == 'VAR' then
            return a
    r3 = right(fmt, 3)
    if abbrev(r3, '%#') then do
        if substr(r3, 3) = '' then
            call err 'fGen bad suffix' fmt
        if right(a, 3) \== r3 then
            call err 'adr fmt mismatch' a '<->' fmt
        fmt = left(fmt, length(fmt) - 3)
        a = left(a, length(a) - 3)
        if symbol('m.a') == 'VAR' then
            call err 'base already defined' arg(2)
        end
    if \ abbrev(fmt, '%##') then
        return fCache(a, fGenF(fmt))
    parse var fmt '%##' fun ' ' rest
    interpret 'return' fun'(a, rest)'
endProcedure fGen

/*--------------------------------------------------------------------
 Format generator    should be compatible with fPrint|
 <<<< + extension of fPrint, - in fPrint but not implemented

   %%  %@ the escaped char
   ('@' argN? '.'? field)?'%' flags? width? ('.' precision)? specifier

 specifier: is the most significant one and defines the type

 - c  Character rigPad or lefPad, prec ==> substr(..., prec)
 -  C Cut %-nC = left(v,n), %nC = right(v,n) %n.mC = substr(m, n)
 - hH Characters in hex
 - iI Signed decimal integer (padded or cut)
 - eE Scientific notation (mantissa/exponent) using e character 3.92e+2
 - S  Strip (both)
 - txy time date formatting from format x to format y see fTstGen
 - kx  units Formatting x = t=time d=decimal b=binary (k) B=binary (kB)
 Flags:
 - -  Left-justify within the given field width; Right is default
 - +  print '+' before non negative numbers
 -' ' print ' ' before non negative numbers
 - /  cut to length

 preprocessor implemented in fGen
   %##fun fmt  format by function fun
   %>          address only
---------------------------------------------------------------------*/
fGenF: procedure expose m.
parse arg fmt
    if symbol('m.f_s_0') \== 'VAR' then
        m.f_s_0 = 1
    else
        m.f_s_0 = m.f_s_0 + 1
    f_s = 'F_S_'m.f_s_0
    call scanSrc f_s, fmt
    ax = 0
    cd = ''
    cp = ''
    do forever
        txt = fText(f_s)
        if txt \== '' then
            cd = cd '||' quote(txt, "'")
        if scanEnd(f_s) then
            leave
        if \ scanLit(f_s, '@') then do
            ax = ax + 1
            af = ''
            hasDot = 0
            end
        else do
            if scanWhile(f_s, '0123456789') then
                ax = m.f_s.tok
            else if ax < 1 then
                ax = 1
            hasDot = scanLit(f_s, '.')
            af = fText(f_s)
            end
        if \ scanLit(f_s, '%') then
            call scanErr f_s, 'missing %'
        call scanWhile f_s, '-+ /'
        flags = m.f_s.tok
        call scanWhile f_s, '0123456789'
        len = m.f_s.tok
        if \ scanLit(f_s, '.') then
            prec  = ''
        else do
            call scanWhile f_s, '0123456789'
            prec = m.f_s.tok
            end
        call scanChar f_s, 1
        sp = m.f_s.tok
        if ax < 3 | ass.ax == 1 then
            aa = 'ggA'ax
        else do
            aa = 'arg(' || (ax+1) || ')'
            if af \== '' then do
                 cp = cp 'ggA'ax '=' aa';'
                 aa = 'ggA'ax
                 ass.ax = 1
                 end
            end
        if af \== '' | hasDot then
            aa = rxMGet(aa, af)
        if sp == 'c' then do
            if prec \== '' then
                aa = 'substr('aa',' prec')'
            if len == '' then
                cd = cd '||' aa
            else if pos('-', flags) > 0 then
                cd = cd '|| lefPad('aa',' len')'
            else
                cd = cd '|| rigPad('aa',' len')'
            end
        else if sp == 'C' then do
            if prec \== '' then do
                cd = cd '|| substr('aa',' prec
                if len == '' then
                    cd = cd')'
                else
                    cd = cd',' len')'
                end
            else if len == '' then
                cd = cd '||' aa
            else if pos('-', flags) > 0 then
                cd = cd '|| left('aa',' len')'
            else
                cd = cd '|| right('aa',' len')'
            end
        else if sp == 'H' then
            cd = cd "|| fH("aa"," len',' (pos('-', flags) > 0)')'
        else if sp == 'h' then
            cd = cd "|| translate(fH("aa", '"siL"'),'abcdef','ABCDEF')"
        else if sp == 'i' then
            cd = cd "|| fI("aa"," len", '"flags"'," firstNS(prec, 0)")"
        else if sp == 'I' then
            cd = cd "|| fI("aa"," len", '/"flags"'," firstNS(prec, 0)")"
        else if sp == 'E' | sp == 'e' then do
            if len == '' then
                len = 8
            if prec = '' then
                prec = len - 6
            cd = cd "|| fE("aa"," len"," prec", '"sp"', '"flags"')"
            end
        else if sp = 'S' then
            cd = cd '|| strip('aa')'
        else if sp = 't' then do
            call scanChar f_s, 2
            cd = cd '||' fTstGen(m.f_s.tok, aa)
            end
        else if sp = 'k' then do
            call scanChar f_s, 1
            if pos(m.f_s.tok, 'tdbBiI') < 1 then
                call scanErr f_s, "bad unit type" m.f_s.tok
            if pos('+', flags) > 0 then
                pl = ", '+'"
            else if pos(' ', flags) > 0 then
                pl = ", ' '"
            else
                pl = ''
            cd = cd "|| fUnit('"m.f_s.tok || len"."prec"¢"pl"'," aa")"
            end
        else if sp == '(' then do
            c1 = aa
            do until m.f_s.tok = '%)'
                sx = m.f_s.pos
                do until m.f_s.tok == '%,' | m.f_s.tok == '%)'
                    call scanUntil f_s, '%'
                    if \ scanLit(f_s, '%,', '%)', '%') then
                         call scanErr f_s, '%( not closed'
                    end
                c1 = "fImm('"fGen('%>', substr(m.f_s.src, sx,
                              , m.f_s.pos - sx - 2))"'," c1")"
                end
            cd = cd '||' c1
            end
        else do
            call scanErr f_s, 'bad % clause'
            call scanBack f_s, '%'sp
            leave
            end
        end
    if \ scanEnd(f_s) then
        call scanErr f_s, "bad specifier '"m.f_s.tok"'"
    m.f_s_0 = m.f_s_0 - 1
    if cd \== '' then
        return strip(cp 'return' substr(cd, 5))
    else
        return "return ''"
endProcedure fGenF

fText: procedure expose m.
parse arg f_s
    res = ''
    do forever
        if scanUntil(f_s, '@%') then
            res = res || m.f_s.tok
        if scanLit(f_s, '%%', '%@') then
            res = res || substr(m.f_s.tok, 2)
        else if scanLit(f_s, '%>', '%##') then
            res = res || m.f_s.tok
        else
            return res
        end
endProcedure fText

fAll: procedure expose m.
parse arg fmt, rdr
    i = jOpen(in2File(rdr), '<')
    do while jRead(i)
        call out f(fmt, m.i)
        end
    call jClose i
    return
endProcedure fAll

/*--- format character2hex (if not sql null) ------------------------*/
fH: procedure expose m.
parse arg v, l, leftJ
    if v \== m.sqlNull then
        v = c2x(v)
    if length(v) > l then
        return v
    else if leftJ \== 1 then
        return right(v, l)
    else
        return left(v, l)
endProcedure fH

/*--- format integer or fixPoint Decimal ----------------------------*/
fI: procedure expose m.
parse arg v, l, flags, d
    if \ datatype(v, 'n') then
        return fRigLeft(strip(v), l, flags)
    v = format(v, , d, 0)
    if pos('+', flags) > 0 then
        if \ abbrev(v, '-') then
            v = '+'v
    if length(v) > l then
        if pos('/', flags) > 0 then
            return left('', l, '*')
        else
            return v
    return fRigLefPad(v, l, flags)
endProcedure fI

/*--- format with exponent l=total output len
                           d=number of digits after . in mantissa
                           c=exponent character
                           flags: - to ouput text left justified
    differences: exponent is always printed but without +
                 overflow ==> expand exponent, *****, 0e-999 --------*/
fE: procedure expose m.
parse arg v, l, d, c, flags
    if \ datatype(v, 'n') then
        return fRigLeft(v, l, flags)
    if pos(' ', flags) < 1 then
        if v >=  0 then
            if pos('+', flags) > 0 then
                return '+'substr(fE(v, l, d, c, ' 'flags), 2)
            else
                return substr(fE(v, l+1, d+1, c, ' 'flags), 2)
    x = format(v, 2, d, 7, 0)
    m = 2 + d + (d>0)
    call assert "length(x) == m+9", 'm x length(x)'
    if substr(x, m+1) = '' then
        return left(x, m)c || left('', l-m-1, 0)
    call assert "substr(x, m+1, 1) == 'E'","exponenent in x not at m"
    y = verify(x, '0', 'n',  m+3)
    call assert 'y>0'
    if substr(x, m+1, 2) == 'E+' then do
        if m+10-y <= l-m-1 then
             return left(x,m)c || right(x, l-m-1)
        z = l - 4 - (m+10-y)
        end
    else if substr(x, m+1, 2) == 'E-' then do
        if m+10-y <= l-m-2 then
             return left(x,m)c'-'right(x, l-m-2)
        z = l - 5 - (m+10-y)
        end
    else
        call err 'bad x' x
    if z >= -1 & max(0, z) < d then
        return fE(v, l, max(0, z), c, flags)
    else if substr(x, m+1, 2) == 'E-' then
        return left(x,1)'0'c'-'left('', l-4, 9)
    else
        return left('', l, '*')
endProcedure fE

/*--- right or left with truncation ---------------------------------*/
fRigLeft: procedure expose m
parse arg s, len, flags
    if length(s) = len then
        return s
    else if pos('-', flags) > 0 | length(s) > len then
        return left(s, len)
    else
        return right(s, len)
endProcedure fRigLefPad

/*--- right or left pad without truncation --------------------------*/
fRigLefPad: procedure expose m
parse arg s, len, flags
    if pos('-', flags) > 0 then
        if length(strip(s, 't')) >= len then
            return strip(s, 't')
        else
            return left(s, len)
    else
        if length(strip(s, 'l')) >= len then
            return strip(s, 'l')
        else
            return right(s, len)
endProcedure fRigLefPad

/*-- return char i+1 from codes cc ----------------------------------*/
fI2C: procedure expose m.
parse arg i, cc
    if i >= length(cc) then
        call err 'no code for fI2C('i',' cc')'
    return substr(cc, i+1, 1)

/*-- return pos-1 for char c in codes -------------------------------*/
fC2I: procedure expose m.
parse arg c, codes
    res = pos(c, codes)
    if res > 0 then
        return res - 1
    call err 'not  a code fI2C('c',' codes')'

/*--- generate timestamp format, derive from %t.. ------------------*/
fTstGen: procedure expose m.
parse arg ft, s
    fmt = '%t'ft
    if symbol('M.f_gen.fmt') \== 'VAR' then
        m.f_gen.fmt = 'return' fTstGe2(ft, 'ggA1')
    code =  m.f_gen.fmt
    if \ abbrev(code, 'return ') then
        call err 'fTstGen' ft 'bad code' code
    if pos('ggA1', code) == lastPos('ggA1', code) ,
              | verify(s, '()', 'm') < 1 then
        return repAll(substr(code, 8), 'ggA1', s)
    else
        return "fImm('"fmt"'," s")"
endProcedure fTstGen

/*--- generate timestamp formats: from format c to format d ---------*/
fTstGe2: procedure expose m.
parse arg c 2 d, s
    if pos(c, ' jJLlu') > 0 then do /* special cases */
        if c == ' ' then do  /* get current timestamp */
            if pos(d, 'sMAnY ') > 0 then
                return fTstGen('n'd, "date('S') time()")
            else if pos(d, 'DdEeJj') > 0 then
                return fTstGen('D'd, "date('S')")
            else if pos(d, 'tH') > 0 then
                return ftstGen('t'd, "time()")
            else if pos(d, 'T') > 0 then
                return fTstGen('T'd, "time('L')")
            else
                return fTstGen('N'd, "date('S') time('L')")
            end
        if c == 'j' then           /* via date D */
            return fTstGen('D'd, "date('s'," s", 'J')")
        if c == 'J' then
            return fTstGen('D'd, "date('s'," s", 'B')")
        call timeIni               /* via db2 timestamp */
        if c == 'L' then
            return fTstGen('S'd, 'timeLRSN2LZT('s')')
        if c == 'l' then
            return fTstGen('S'd, 'timeLRSN2LZT(c2x('s'))')
        if c == 'u' then
            return fTstGen('S'd, 'timeLRSN2LZT(timeUniq2lrsn('s'))')
        end

    if symbol('m.f_tstFo.c')=='VAR' & symbol('m.f_tstFo.d')=='VAR' then
        return ftstGFF(m.f_tstFo.c, m.f_tstFo.d, s)
    if m.f_tstIni == 1 then
        call err "bad timestamp from or to format '"c || d"'"
        /*--- initialize f_tst --------------------------------------*/
    m.f_tstIni = 1
    call utIni
    m.f_tstScan = 0
    a = 'F_TSTFO.'
    m.f_tstN0   =   'yz345678 hi:mn:st'
    m.f_tstN    =   'yz345678 hi:mn:st.abcdef'
    m.f_tstS0   =   'yz34-56-78-hi.mn.st'
    m.f_tstS    =   'yz34-56-78-hi.mn.st.abcdef'
        /*---------- picture characters not in DB2 timestamp
                     Y: year//25 A = 2000 Y=2024
                     Z: year//20 A = 2010                to deimplement
                     M: month B=Januar ...,
                     A: first digit of day A=0, D=30
                     B: day 1=1 10=A 31=V                 deimplemented
                     H: hour first digit  A=0 B=10 C=20 D=30
                     I: hour 1=A, 10=K 23=X               deimplemented
                     jjjjj: Julian
                     JJJJJJ: base date (days since 1.1.0001)
                     llllllllll: 10 Byte LRSN
                     LL...: 10 Byte LRSN as 20 HexCharacters
                     uuuuuuuu: db2 Utility Unique
                     qr: minuten//10, sec ==> aa - xy  base 25 ------*/
    m.f_tstPics =   'yz345678himnstabcdefYZMAHIjJlLuqr'
    m.f_tstZero =   '00010101000000000000???AAA??00?AA'
    call mPut a'S',  m.f_tstS
    call mPut a's',  m.f_tstS0
    call mPut a' ',  m.f_tstS0
    call mPut a'D', 'yz345678'
    call mPut a'd',   '345678'
    call mPut a't',            'hi.mn.st'
    call mPut a'T',            'hi:mn:st.abcdef'
    call mPut a'E', '78.56.yz34'
    call mPut a'e', '78.56.34'
    call mPut a'Y',    'YM78Imqr'
    call mPut a'Z',      'ZM78'    /* deimplement */
    call mPut a'M',    'M78himns'
/*  call mPut a'I',    'M78Imnst'   */
    call mPut a'A',    'A8himnst'
/*  call mPut a'B',    'YMBImnst'   */
    call mPut a'H',           'Himnst'
    call mPut a'n',  m.f_tstN0
    call mPut a'N',  m.f_tstN
    call mPut a'j', 'jjjjj' /* julian date 34jjj        */
    call mPut a'J', 'JJJJJJ' /* day since 1.1.00: 6 digits     */
    call mPut a'l', copies('l', 10) /*LRSN out 10 Byte, input var*/
    call mPut a'L', copies('L', 20) /* LRSN in hex */
    call mPut a'u', 'uuuuuuuu' /* Unique */
    return fTstGe2(c || d, s) /* retry after initialisation */
endProcedure fTstGe2

/*--- nest source s into code (at $)
      if source is not simpe and used several times then
          use fImm to avoid muliple evaluations ---------------------*/
fTstNest: procedure expose m.
parse arg code, s
    if pos('$', code) == lastPos('$', code) ,
              | verify(s, '(). ', 'm') < 1 then
        return repAll(code, '$', s)
    a = fCache('%>', 'return' repAll(code, '$', 'ggA1'))
    return "fImm('"a"'," s")"
endProcedure fTstFi

/*--- return rexx code for timestamp conversion
      from pic f to pic aT for source s -----------------------------*/
fTstgFF: procedure expose m.
parse arg f, aT, s
    m.f_tstScan = m.f_tstScan + 1
    a = f_tstScan || m.f_tstScan
    call scanSrc a, aT
    cd = ''
    pc = '' /* permutations and constants */
    do until t == ''
        c1 = '' /* a rexx function / expression */
        p1 = '' /* permutations and constants */
        tPos = m.a.pos
        call scanChar a, 1
        t = m.a.tok
        if pos(t, f' .:-') > 0 then do
            call scanVerify a, f' .:-', 'n'
            p1 = t || m.a.tok         /* permutate pics or constants */
            end
        else if pos(t, m.f_tstPics) <= 0 then do
            p1 = m.a.tok                                /* constants */
            end
        else if t == 'y' then do                             /* year */
            if scanLit(a, 'z34') then do
                if pos('34', f) > 0 then
                    c1 = "timeYear24(substr("s "," pos('34', f)", 2))"
                else if pos('Y', f) > 0 then
                    c1 = "timeY2Year(substr("s "," pos('Y', f)", 1))"
                end
            end
        else if t == '3' then do
            if scanLit(a, '4') then
                if pos('Y', f) > 0 then
                    c1 = "substr(timeY2Year(substr("s,
                            "," pos('Y', f)", 1)), 3)"
            end
        else if t == 'Y' then do
            if pos('34', f) > 0 then
                c1 = "timeYear2Y(substr("s "," pos('34', f)", 2))"
            end
        else if t == 'Z' then do
            if pos('34', f) > 0 then
                c1 = "timeYear2Z(substr("s "," pos('34', f)", 2))"
            end
        else if t == '5' then do                            /* month */
            if scanLit(a, '6') then
                if pos('M', f) > 0 then
                    c1 = "timeM2Month(substr("s"," pos('M', f)", 1))"
            end
        else if t == 'M' then do
            if pos('56', f) > 0 then
                c1 = "timeMonth2M(substr("s"," pos('56', f)", 2))"
            end
        else if t == '7' then do                              /* day */
            if scanLit(a, '8') then
                c1 = fTstGetDay(f, s)
            end
        else if t == 'A' then do
            if scanLit(a, '8') then do
                c1 = fTstGetDay(f, s)
                if c1 \== '' then
                    c1 = fTstNest("fI2C(left($, 1), 'ABCD')" ,
                                 || "right($, 1)", c1)
                end
            end
        else if t == 'h' then do                             /* hour */
            if scanLit(a, 'i') then
                c1 = fTstGetHour(f, s)
            end
        else if t == 'n' then do                             /* hour */
            if scanLit(a, 'i') then
                c1 = fTstGetHour(f, s)
            else if pos('qr', f) > 0 then do
                call scanLit a, 'st', '.st', ':st', 's', '.s', ':s'
                c1 = "fqr2ms(substr("s"," pos('qr', f)", 2)" ,
                    || ", '"left(m.a.tok, abbrev(m.a.tok, '.') ,
                                        | abbrev(m.a.tok, ':'))"')"
                if right(m.a.tok, 1) \== 't' then
                    c1 = "left("c1"," 1 + length(m.a.tok)")"
                end
            end
        else if t == 'H' then do
            if scanLit(a, 'i') then do
                c1 = fTstGetHour(f, s)
                if c1 \== '' then
                    c1 = fTstNest("fI2C(left($, 1), 'ABC')" ,
                                 || "right($, 1)", c1)
                end
            end
        else if t == 'I' then do
            c1 = fTstGetHour(f, s)
            if c1 \== '' then
                c1 = "fI2C("c1", m.ut_uc25)"
            end
        else if t == 'j' then do                           /* julian */
            if scanLit(a, 'jjjj') then
                c1 = "time2jul(" fTstGFF(f, 'yz34-56-78', s)")"
            end
        else if t == 'J' then do                  /* day since 1.1.1 */
            if scanLit(a, 'JJJJJ') then
                c1 = "date('B'," fTstGFF(f, 'yz345678', s)", 's')"
            end
        else if t == 'l' then do                     /* 10 byte lrsn */
            if scanLit(a, copies('l', 9)) then
                c1 = "x2c(timeLZT2LRSN("fTstGFF(f, m.f_tstS, s)"))"
            end
        else if t == 'L' then do                   /* lrsn in 20 hex */
            if scanLit(a, copies('L', 19)) then
                c1 = "timeLZT2LRSN("fTstGFF(f, m.f_tstS, s)")"
            end
        else if t == 'u' then do            /* 8 byte utility unique */
            if scanLit(a, 'uuuuuuu') then
                c1 = "timeLrsn2Uniq(timeLZT2LRSN(",
                        || fTstGFF(f, m.f_tstS, s)"))"
            end
        else if t == 'q' then do            /* 8 byte utility unique */
            if scanLit(a, 'r') then
                if pos('n', f) > 0 then do
                    c1 = "fms2qr(substr("s"," pos('n', f)", 1),"
                    if pos('st', f) > 0 then
                        c1 = c1 "substr("s"," pos('st', f)", 2))"
                    else if pos('s', f) > 0 then
                        c1 = c1 "substr("s"," pos('s', f)", 1)'0')"
                    else
                        c1 = c1 "0)"
                    end
            end

        if pos(t, 'lLu') > 0 then
            call timeIni
        if c1 == '' & p1 == '' & t \== '' then /* nothing -> zero */
            p1 = translate(substr(m.a.src, tPos, m.a.pos-tPos),
                   , m.f_tstZero, m.f_tstPics)

        pc = pc || p1
        if (c1 \== '' | t == '') & pc \== '' then do/*append pc to cd*/
            if verify(pc, m.f_tstPics, 'm') == 0 then
                cd = cd '||' quote(pc, "'")
            else if pc == f then
                cd = cd '||' s
            else if pos(pc, f) > 0 then
                cd = cd "|| substr("s"," pos(pc, f)"," length(pc)")"
            else
                cd = cd "|| translate('"pc"'," s", '"f"')"
            pc = ''
            end
        if c1 \== '' then                         /* append pc to cd */
            cd = cd '||' c1
        end
    m.f_tstScan = m.f_tstScan - 1
    if cd == '' then
        return "''"
    else
        return substr(cd, 5)
endProcedure fTstGFF

/*--- return code for day, d1Only = first digit only ----------------*/
fTstGetDay: procedure expose m.
parse arg f, s
    if pos('78', f) > 0 then
        return  "substr("s"," pos(78, f)", 2)"
    if pos('A', f) > 0 then
        if pos('8', f) > 0 then
            return "fc2i(substr("s"," pos('A', f)", 1), 'ABCD')",
                || "substr("s"," pos('8', f)", 1)"
    return ''
endProcedure fTstGetDay

/*--- return code for hour in 2 digits ------------------------------*/
fTstGetHour: procedure expose m.
parse arg f, s
    if pos('hi', f) > 0 then
        return "substr("s"," pos('hi', f)", 2)"
    if pos('Hi', f) > 0 then
        return "fC2I(substr("s"," pos('Hi', f)", 1), 'ABC')" ,
                 || "substr("s"," pos('Hi', f) + 1", 1)"
    if pos('I', f) > 0 then
        return "right(fC2I(substr("s"," pos('I', f)", 1)," ,
                     "m.ut_uc25), 2, 0)"
    return ''
endProcedure fTstGetHour

fms2qr: procedure expose m.
parse arg m, s
    t =  (m // 10) * 60 + s
    return substr(m.ut_uc25, t %  25 + 1,1),
        || substr(m.ut_uc25, t // 25 + 1,1)


fqr2ms: procedure expose m.
parse arg q, sep
    v = pos(left(q, 1), m.ut_uc25) * 25 ,
      + pos(substr(q, 2, 1), m.ut_uc25) - 26
    return (v % 60) || sep || right(v // 60, 2, 0)

fWords: procedure expose m.
parse arg fmt, wrds
    f2 = '%##fCatFmt' fmt
    if wrds = '' then
        return f(f2'%#0')
    res = f(f2'%#1', word(wrds, 1))
    do wx=2 to words(wrds)
        res = res || f(f2, word(wrds, wx))
        end
    return res || f(f2'%#r')
endProcedure fWords

fCat: procedure expose m.
parse arg fmt, st
    return fCatFT(fmt, st, 1, m.st.0)

fCatFT: procedure expose m.
parse arg fmt, st, fx, tx
    f2 = '%##fCatFmt' fmt
    if tx < fx then
        return f(f2'%#0')
    res = f(f2'%#1', m.st.fx)
    do sx=fx+1 to tx
        res = res || f(f2, m.st.sx)
        end
    return res || f(f2'%#r')
endProcedure fCatFT

fCatFmt: procedure expose m.
parse arg adr, fmt
    v.m = ''    /* middle */
    v.l = ''    /* left */
    v.r = ''    /* right */
    v.a = '%c'  /* all rows */
    nm = M
    cx = 1
    do forever        /* split clauses */
        cy = pos('#', fmt, cx)
        if cy < 1 then do
            v.nm = substr(fmt, cx)
            leave
            end
        v.nm = substr(fmt, cx, cy-cx)
        nm = translate(substr(fmt, cy+1, 1))
        cx = cy+2
        end
    if symbol('v.2') \== 'VAR' then  /* second and following */
        v.2 = v.M || v.a
    adr = fGen(adr, v.2)
    if symbol('v.0') \== 'VAR' then  /* empty */
        v.0 = v.l || v.r
    call fGen adr'%#0', v.0
    if symbol('v.1') \== 'VAR' then /* first row */
        v.1 = v.l || v.a
    call fGen adr'%#1', v.1
    call fGen adr'%#r', v.R
    return adr
endProcedure fCatFmt

/*--- format with units seconds ==> 3d13 oder 5.43e6 ==> 5M43 -------*/
fUnit: procedure expose m.
parse arg uFmt, v /* scale, aLen, aPrec, plus */
    uF = 'F_UNIT.'uFmt                 /* address of (global) format */
    if symbol('m.uF.0') \== 'VAR' then
         call fUnitGen uFmt
    if \ dataType(v, 'n') then
        return right(v, m.uF.len)
    uS = uF'!' || (v >= 0)               /* address of signed format */
    v = abs(v)                /* always get rid also of sign of -0 | */


    do fx=11 to m.uF.0-1 while v >= m.uS.fx.lim1     /* search range */
        end
    if fx = 11 & v <> trunc(v) then do
        do fx=10 by -1 to m.uF.min while v < m.uS.fx.lim1
            end
        fx = fx + 1
        end

    do fx=fx to m.uF.0                              /* try to format */
        uU = uS'.'fx
        w = format(v * m.uU.fact, , m.uU.prec)    /* address of Unit */
        if pos('E-', w) > 0 then
            w = format(0, , m.uU.prec)
        if w < m.uU.lim2 then do
            if m.uU.kind == 'r' then
                x = m.uS.sign || w || m.uU.unit
            else if m.uU.kind == 'm' then
                x = m.uS.sign || (w % m.uU.mod) || m.uU.mUnit ,
                    || right(w // m.uU.mod, m.uF.len2, 0)
            else
                call err 'bad kind' m.uU.kind 'in uU' uU
            if length(x) <= m.uF.len then
                return right(x, m.uF.len)
            end
        end
    return left(m.uS.sign, m.uF.len, '+')
endProcedure fUnit

/*--- generate all format entries for given scale -------------------*/
     aLen = total len, pLen =len of +, sLen = len of current sign ---*/
fUnitGen: procedure expose m.
parse arg uFmt
parse arg scale 2 aMid '¢' plus
parse var aMid aLen '.' aPrec
    if pos('!', uFmt) > 0 then
        call err 'bad fUnit format' uFmt
    sc = 'F_SCALE.'scale
    uF = 'F_UNIT.'uFmt                 /* address of (global) format */
    if symbol('m.sc.0') \== 'VAR' then do
        call fUnitIni
        if symbol('m.sc.0') \== 'VAR' then
            call err 'bad scale' sc 'for fUnitGen('uFmt')'
        end

    hasM = scale = 't'
    if aPrec == '' then
        if scale = 't' then
            aPrec = 2
        else
            aPrec = 0
    if aLen = '' then
        if scale = 't' then
            aLen = length(plus) + 3 + aPrec
        else
            aLen = aPrec + (aPrec >= 0) + 4 + length(plus)
    m.uF.len2  = aPrec
    if hasM then
        aPrec = 0
    m.uF.len = aLen
    m.uF.0   = m.sc.0
    m.uF.min = m.sc.min
    do geq0=0 to 1
        uS = uF'!'geq0                   /* address of signed format */
        if geq0 then do
            m.uS.sign = plus
            end
        else do
            m.uS.sign = '-'
            end
        sLen = length(m.uS.sign)
        dLen = aLen - sLen - hasM
        limR = '1e' || (aLen -sLen - hasM - (aPrec > 0) - aPrec)
        limM = '1e' || (aLen - m.uF.len2 - 1 - sLen)
        do ix=m.sc.0 by -1 to m.sc.min
            uU = uS'.'ix                      /* address of one unit */
            m.uU.unit = m.sc.ix.unit
            m.uU.fact = m.sc.ix.fact
            m.uU.val  = m.sc.ix.val
            m.uU.kind = m.sc.ix.kind
            m.uU.Len  = aLen
            m.uU.prec = aPrec
            if m.uU.kind = 'r' then do
                m.uU.lim2 = limR
                m.uU.lim1 = limR * m.uU.val
                end
            else do
                iy = ix + 1
                iz = ix + 2
                m.uU.mUnit = m.sc.iy.unit
                m.uU.mod   = m.sc.iy.val % m.sc.ix.val
                m.uU.wid2  = aPrec
                if iz <= m.sc.0 & m.sc.iz.kind == 'm' then
                    m.uU.lim1  = m.sc.iz.val
                else
                    m.uU.lim1 = limM * m.sc.iy.val
                m.uU.lim2  = m.uU.lim1 % m.uU.val
                end
            end
        end
    return
endProcedure fUnitGen

fUnitIni: procedure expose m.
    if m.f_unit_ini == 1 then
        return
    m.f_unit_ini = 1
      /*  0    5   10    5   20 */
    iso = '    afpnum kMGTPE   '
    sB = f_Scale'.b'
    sD = f_Scale'.d'
    sT = f_Scale'.t'
    fB = 1
    fD = 1
    call fUnitIni2 sB, 11, ' ', 'r', fB
    m.sB.0   =  17
    m.sB.min =  11
    call fUnitIni2 sD, 11, ' ', 'r', fD
    m.sD.0   = 17
    m.sd.min =  5
    do x=1 to 6
        fB = fB * 1024
  /*    call fUnitIni2 sB, (11-x), substr(iso, 11-x, 1), 'r', fB*/
        call fUnitIni2 sB, (11+x), substr(iso, 11+x, 1), 'r', 1/fB
        fD = fD * 1000
        call fUnitIni2 sD, (11+x), substr(iso, 11+x, 1), 'r', 1/fD
        call fUnitIni2 sD, (11-x), substr(iso, 11-x, 1), 'r', fD
        end
    kilo = 'k'
    m.sB.u2v.k = m.sB.u2v.kilo
    m.sD.u2v.k = m.sD.u2v.kilo
    m.sT.0   =  16
    m.sT.min =  11
    call fUnitIni2 sT, 11, ' ', 'm', 100
    call fUnitIni2 sT, 12, 's', 'm',   1
    call fUnitIni2 sT, 13, 'm', 'm', 1/60
    call fUnitIni2 sT, 14, 'h', 'm', 1/3600
    call fUnitIni2 sT, 15, 'd', 'm', 1/3600/24
    call fUnitIni2 sT, 16, 'd', 'r', 1/3600/24
    return 0
endProcedure fUnitIni

fUnitIni2: procedure expose m.
parse arg sc, ix, u, ki, fa
    sb = sc'.'ix
    m.sb.kind = ki
    m.sb.fact = fa
    m.sb.unit = u
    m.sb.val     = 1 / fa
    if m.sb.fact > 1 then
        m.sb.fact = format(fa, , 0)
    else
        m.sb.val  = format(m.sb.val, , 0)
    m.sc.u2v.u = m.sb.val
    return
endProcedure fUnitIni2

fUnitsF1I0: procedure expose m.
parse arg sc, ix
    si = sc'.'ix
parse arg , , m.si.kind, aU, m.si.fact,
                , m.si.lim2, m.si.len,
                , m.si.mod, m.si.len2
    m.si.unit = aU
    m.sc.u2f.aU = ''
    if \ datatype(ix, 'n') then
        return si
    m.sc.u2f.aU = 1 / m.si.fact
    if symbol('m.sc.0') \== 'VAR' then do
        m.sc.0   = ix
        m.sc.min = ix
        end
    else do
        m.sc.0   = max(ix, m.sc.0)
        m.sc.min = min(ix, m.sc.min)
        end
    return si
endProcedure fUnitsF1I0

fUnit2I: procedure expose m.
parse arg b, v
    v = strip(v)
    if datatype(v, 'n') then
        return v
    u = right(v, 1)
    key = f_Scale'.' || b'.U2V.'u
    if symbol('m.key') == 'VAR' then
        return strip(left(v, length(v)-1)) * m.key
    if m.f_unit_ini \== 1 then
        return fUnit2I(b, v, fUnitIni())
    call err 'bad unit' u 'or base' b 'for' v
endProcedure fUnit2I
/* copy f end   ******************************************************/
/* copy time begin ****************************************************
 timestamp format yz34-56-78-hi.mn.st.abcdef
 11.12.14 wk: added lrsn2uniq
 11.05.13 wk: numeric digits transparent: in jeder Procedure drin
              time2jul, tst externalisiert
**********************************************************************/
/*--- timestamp to julian -------------------------------------------*/
time2jul: procedure expose m.
parse arg yyyy '-' mm '-' dd '-'
    if yyyy < 1100 then
        yyyy = 11 || right(yyyy, 2, 0)
        /* date function cannot convert to julian, only from julian
           use b (days since start of time epoch) instead     */
    return right(yyyy, 2) ,
         || right(date('b', yyyy || mm || dd, 's') ,
                - date('b', yyyy-1 || '1231', 's') , 3, 0)
endProcedure time2jul

/*--- current timestamp ---------------------------------------------*/
timestampNow: procedure expose m.
    parse value date('s') time('l') with y 5 m 7 d t
    return y'-'m'-'d'-'translate(t, '.', ':')

/*--- timestamp expand: expand to timestamp of 26 chars -------------*/
timestampExp: procedure expose m.
parse arg tst .
    if length(tst) < m.timeStamp_Len then
        return overlay(tst, m.timeStamp_01)
    else
        return left(tst, timeStamp_Len)
endProcedure tiemstampExp

/*--- timestamp check return '' if correct or error text ------------*/
timestampcheck: procedure expose m.
parse arg tst
    if length(tst) < m.timestamp_d0Len | \ abbrev(m.timestamp_11,
             , translate(tst, '111111111', '023456789')) then
        return 'bad timestamp' tst
    parse arg yyyy '-' mo '-' dd '-' hh '.' mm '.' ss '.' u
    if mo < 1 | mo > 12 then
        return 'bad month in timestamp' tst
    if dd < 1 | dd > 3 || substr('101010110101', mo, 1) then
        return 'bad day in timestamp' tst
    if mo = 2 then
        if dd > date('d', yyyy'0301', 's') - 32 then
            return 'bad day in timestamp' tst
    if hh > 23 & \ abbrev(24 00 00 000000, hh mm ss u) then
        return 'bad hour in timestamp' tst
    if mm > 59 then
        return 'bad minute in timestamp' tst
    if ss > 59 then
        return 'bad second in timestamp' tst
    return ''
endProcedure timestampCheck

/*--- timestamp to days since 1.1.0001 ------------------------------*/
timestamp2days: procedure expose m.
parse arg yyyy '-' mo '-' dd '-' hh '.' mm '.' ss
numeric digits 20
    return date('b', yyyy || mo || dd, 's') ,
                + (((hh * 60) + mm) * 60 + ss) / 86400

/*--- days since 1.1.0001 (with time+microsecs) to timestamp --------*/
timeDays2tst: procedure expose m.
parse arg d
numeric digits 20
    r = min(format(d // 1 * 86400, 5, 6), 86399.999999)
    s = trunc(r)
    t = date('s', trunc(d), 'b')
    return left(t, 4)'-'substr(t, 5, 2)'-'right(t, 2) ,
             || '-' || right((s % 3600), 2, 0)       ,
             || '.' || right((s // 3600 % 60), 2, 0) ,
             || '.' || right((s // 60), 2, 0)        ,
             || substr(r, 6)
endProcedure timeDays2tst

/*--- timestamp diff in days ----------------------------------------*/
timestampDiff: procedure expose m.
parse arg t1, t2
numeric digits 20
    return timestamp2days(t1) - timestamp2Days(t2)

/*--- convert 2 digit year to 4 digit -------------------------------*/
timeYear24: procedure expose m.
parse arg s
    y = left(date('S'), 4)
    s4 = left(y, 2)right(s, 2, 0)
    if s4 > y + 30 then
        return (left(y, 2) - 1)substr(s4, 3)
    else if s4 > y - 70 then
        return s4
    else
        return (left(y, 2) + 1)substr(s4, 3)
endProcedure timeYear24

/*--- convert 2 or 4 digit year Y (A=0...Y=24) ----------------------*/
timeYear2Y: procedure expose m.
parse arg y
    return substr(m.ut_uc25, (y // 25) + 1, 1)
/*--- convert 1 char year Y (A=0...y=24) to year --------------------*/
timeY2Year: procedure expose m.
parse arg i
    j = pos(i, m.ut_uc25) - 1
    if j < 0 then
        call err 'timeY2Year bad input' i
    y = left(date('S'), 4)
    r = y - y // 25 + j
    if r > y + 4 then
        return r - 25
    else if r > y - 21 then
        return r
    else
        return r + 25
endProcedure timeY2Year
/*--- convert 2 or 4 digit year Y (A=10...T=29) ----------------------*/
timeYear2Z: procedure expose m.
parse arg y
    return substr('ABCDEFGHIJKLMNOPQRST', ((y+10) // 20) + 1, 1)
/*--- convert 1 char year Z (A=10...T=29) to year --------------------*/
timeZ2Year: procedure expose m.
parse arg i
    j = pos(i, 'ABCDEFGHIJKLMNOPQRST') - 1
    if j < 0 then
        call err 'timeZ2Year bad input' i
    y = left(date('S'), 4)
    r = y - y // 20 + j
    if r > y + 4 then
        return r - 20
    else if r > y - 16 then
        return r
    else
        return r + 20
endProcedure timeZ2Year

/*--- convert numeric month to M (Jan=B=1, dec=12=M) ----------------*/
timeMonth2M: procedure expose m.
parse arg m
    return substr('BCDEFGHIJKLM', m, 1)

/*--- convert M to numeric Month (Jan=B=1, dec=12=M) ----------------*/
timeM2Month: procedure expose m.
parse arg m
    p = pos(m, 'BCDEFGHIJKLM')
    if p= 0 then
        call err 'bad M month' m
    return right(p, 2, 0)

/*--- read timeZoneOffset and leapSeconds registers
        and set variables for uniq ----------------------------------*/
timeIni: procedure expose m.
parse arg debug
    if m.time_ini == 1 then
        return
    m.time_ini = 1
    numeric digits 25
    /* offsets documented in z/OS Data Areas  Vol.1 */
    cvtOH      = '00000010'          /* cvt control block Address */
    cvtext2O   = x2d('00000560') /* offset  to extension 2    */
    cvtldtoO   = x2d('00000038') /* offset to timezone    */
    cvtlsoO    = x2d('00000050') /* offset to leapSeconds */

    /* CVT CB        address               + extention2   */
    cvtExt2A       = C2D(STORAGE(cvtOH,4)) + cvtext2O
    /* cvtLdto timeZone              address +offset      */
    m.time_Zone    = c2d(STORAGE(d2x(cvtext2A+cvtldtoO, 8), 8))
    m.time_StckUnit = 1e-6 / 256 / 16
    /* cvtLso LeapSecs               address +offset      */
    m.time_Leap    = c2d(STORAGE(d2x(cvtext2A+cvtlsoO, 8), 8))
    m.time_LZT_TAI10_us16 = (m.time_zone - m.time_leap) % 256
    m.time_UQDigits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345678'
                         /* find lrsn of day 0, 0 out last 6 bits  */
    m.time_UQZero = x2d(left(timeTAI102Lrsn( ,
                 '2004-12-31-00.00.22.000000'), 14)) % 64 * 64
    m.timeStamp_01 = '0001-01-01-00.00.00.000000'
    m.timeStamp_11 = '1111-11-11-11.11.11.111111'
    m.timeStamp_99 = '9999-12-31-23.59.59.999999'
    m.timeStamp_len = length(m.timestamp_11)
    m.timeStamp_d0Llen = m.timestamp_len - 7
    return
endSubroutine timeIni

/*--- TAI10 timestamp yyyy-mm.... -> stckE value char(16)
         BLSUETID is described in z/OS MVS IPCS Customization -------*/
timeTAI102stckE: procedure expose m.
                        /* timestamp must include microSeconds |||*/
    parse arg year'-'mo'-'da'-'hh'.'mm'.'secs
    tDate = mo'/'da'/'year hh':'mm'.'secs
    ACC=left('', 16, '00'x)
    ADDRESS LINKPGM "BLSUETID TDATE ACC"
    RETURN acc
endProcedure timeTAI102stckE

timeTAI102lrsn: procedure expose m.
parse arg tst
    return c2x(left(timeTAI102StckE(tst), 10))

timeLZT2stckE: procedure expose m.
parse arg tst
    numeric digits 23
    s =timeTAI102StckE(tst)
    return d2c(c2d(left(s, 8)) - m.time_lzt_TAI10_us16,8) ||substr(s,9)
endProcedure timeLZT2stckE

timeLZT2lrsn: procedure expose m.
parse arg tst
    return c2x(left(timeLZT2StckE(tst), 10))

/*--- expand lrsn to 20 hexDigits -------------------------------*/
timeLrsnExp: procedure expose m.
parse arg s
    return left(copies('00', length(s) <= 12 & \abbrev(s, 0))s, 20,0)

/*--- expand stcK(e) to 16 Byte ---------------------------------*/
timeStckExp: procedure expose m.
parse arg s
    return left(copies('00'x, length(s) <= 8 & s >> '09'x)s, 16,'00'x)

/*--- conversion from StckE Clock Value to TAI10 Timestamp
        BLSUETOD is described in z/OS MVS IPCS Customization --------*/
timeStckE2TAI10: PROCEDURE expose m.
parse arg stck      /* must be 16 characters ||||| */
  TDATE = left('' , 26)
  ADDRESS LINKPGM "BLSUETOD stck TDATE"
  /* return format   : mo/dd/yyyy hh:mm:ss.uuuuuu */
  /* Timestamp format: yyyy-mm-dd-hh.mm.ss.uuuuuu */
  parse var TDATE mo '/' dd '/' yyyy hh ':' mm ':' secs
RETURN yyyy'-'mo'-'dd'-'hh'.'mm'.'secs
endProcedure timeStckE2TAI10

/*--- conversion from Lrsn Clock Value to TAI10 Timestamp ----------*/
timeLrsn2TAI10:
    return timeStckE2TAI10(x2c(arg(1))'000000000000'x)

/*--- conversion from Lrsn Clock Value to LZT Local Zurich Time -----*/
timeStckE2LZT: procedure expose m.
parse arg s
    numeric digits 23
    return timeStckE2TAI10(d2c(c2d(left(s, 8)) ,
                + m.time_LZT_TAI10_us16, 8) || substr(s, 9))
endProcedure timeStckE2LZT

/*--- conversion from Lrsn Clock Value to LZT Local Zurich Time -----*/
timeLrsn2LZT: procedure expose m.
parse arg lrsn
    return timeStckE2LZT(x2c(lrsn) || '000000000000'x)

/* convert a lrsn to the uniq variable *******************************/
timeLrsn2uniq: procedure expose m.
parse arg lrsn
    /* unique are bits 0:41 of the TodClock value
              minus 31.12.2004 represented
              by base 35 by 'ABC...YZ01..8'
    */
    lrsn = left(timeLrsnExp(lrsn), 14)
    numeric digits 20
    diff = x2d(lrsn) - m.time_UQZero
    if diff < 0 then
        return'< 2005'
    return right(i2q(diff % 64, m.time_UQDigits), 8, 'A')
endProcedure timeLrsn2Uniq

/* convert a uniq variable to lrsn ***********************************/
timeUniq2lrsn: procedure expose m.
parse arg uniq
    numeric digits 20
    u1 = q2i(left(uniq, 8, 'A'), m.time_UQDigits) * 64
    lrsn = '00'right(d2x(u1 + m.time_UQZero), 12, 0)'000000'
    return lrsn
endProcedure uniq2lrsn

/*--- translate a number in q-system to decimal
       arg digits givs the digits corresponding to 012.. in the q sysem
       q = length(digits) -------------------------------------------*/
q2i: procedure expose m.
parse arg v, digits
    b = length(digits)
    i = 0
    do x = 1 to length(v)
        q = substr(v, x, 1)
        r = pos(q, digits)
        if r < 1 then
            call err 'bad digit' q 'in' v 'valid digits' digits
        i = i * b + r - 1
        end
    return i
endProcedure q2i
/*--- translate a decimal number to q-system - inverse of q2i -------*/
i2q: procedure expose m.
parse arg i, digits
    if i = 0 then
        return left(digits, 1)
    b = length(digits)
    v = ''
    do while i > 0
        v = substr(digits, 1 + (i // b), 1) || v
        i = i % b
        end
    return v
endProcedure i2q
/* copy time 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>.** and m.<mbr>_**: every rexx Module (copy) should only
               allocate these addresses to avoid address conficts
               with <mbr> the name of therexx 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
    ax = m.m_area.0 + 1
    m.m_area.0 = ax
    m.m_area.ax = nm
    if adr == '=' then
        adr = nm
    else if adr == '' then
        adr = 'M.'ax
    if symbol('m.m_2a.nm') == 'VAR' then
        call err 'area name' nm 'already used'
    if symbol('m.m_2a.adr') == 'VAR' then
        call err 'adr' adr 'for area' nm 'already used'
    m.m_2a.adr = adr
    m.m_2a.nm  = adr
    m.adr.0 = 0
    m.m_free.adr.0 = 0
    return nm
endProcedure mNewArea

mNew: procedure expose m. ggArea
parse arg name
    if symbol('m.m_2a.name') \== 'VAR' then
        call err 'area' name 'does not exists'
    adr = m.m_2a.name
    if m.m_free.adr.0 > 0 then do
        fx = m.m_free.adr.0
        m.m_free.adr.0 = fx-1
        return m.m_free.adr.fx
        end
    m.adr.0 = m.adr.0 + 1
    return adr'.'m.adr.0
endProcedure mNew

mFree: procedure expose m.
parse arg m
    adr = left(m, lastPos('.', m)-1)
    fx = m.m_free.adr.0 + 1
    m.m_free.adr.0  = fx
    m.m_free.adr.fx = m
    return ''
endProcedure mFree
/*--- iterate over all allocate elements of an area -----------------*/
mIterBegin: procedure expose m.
parse arg nm
    return m.m_2a.nm'.0'
endProcedure mIterBegin

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

/*--- get m.a -------------------------------------------------------*/
mGet: procedure expose m.
parse arg a
    return m.a
endProcedure mGet

/*--- put value v into m.a ------------------------------------------*/
mPut: procedure expose m.
parse arg a, v
    m.a = v
    return v
endProcedure mPut

/*--- 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

/*--- create the inverse map of a stem ------------------------------*/
mInverse: procedure expose m.
parse arg a, i
    do x=1 to m.a.0
        v = m.a.x
        m.i.v = x
        end
    return m.a.0
endProcedure inverse

/*--- add to m.dst.* a (sub)sequence of m.src.* ---------------------*/
mAddSt: procedure expose m.
    parse arg dst, src, fx , tx
    dx = m.dst.0
    if fx == '' then
        fx = 1
    if tx == '' then
        tx = m.src.0
    do sx = fx to tx
        dx = dx + 1
        m.dst.dx = m.src.sx
        end
    m.dst.0 = dx
    return dst
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 with separator ---------------------------*/
mCat: procedure expose m.
parse arg st, sep
    if m.st.0 < 1 then
        return ''
    res = m.st.1
    do sx=2 to m.st.0
        res = res || sep || m.st.sx
        end
    return res
endProcedure mCat

mIni: procedure expose m.
    if m.m_ini == 1 then
        return
    m.m_ini = 1
    call utIni
    m.m_area.0 = 0
    call mNewArea
    return
endProcedure mIni

/* copy m end ********************************************************/
/* copy scan     begin ************************************************
     Achtung: inc generiert SB aus scan, Aenderungen nur in scan|
SB = scanBasic: single line, no reader, no newLines, class not needed
    scanSrc(m, source) starts scanning a single line = scanBasic
    scanLook(m,len) : returns next len chars, pos is not moved
    scanChar(m,len) : scans next len chars
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanEnd(m)     : returns whether we reached end of input
    scanLit(m,lit) : scan Literal lit if present or return 0
    scanChar(m,n)  : scan next n characters
    scanName(m)    : scan a name
    ScanNat(m)     : scan a natural number (without sign)
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanKeyValue(m): scan a key = value clause (with spaces)
    scanWord(m,st,uc)  : scan a space delimited word or a string,
                          st=stopper, if u=1 then uppercase non-strings
    scanSpace(m)   : skips over spaces (and nl and comment if \ basic
    scanInfo(m)    : text of current scan location
    scanErr(m, txt): error with current scan location

    m is an address, to store our state
    returns: true if scanned, false otherwise
    if a scan function succeeds, the scan position is moved

         m.m.tok  ==> last token
         m.m.val  ==> last value for scanString/Word
         m.m.pos ==> scan position
         m.m.src ==> scan source
**********************************************************************/
/*--- start basic scannig: set new src, reset outFields -------------*/
scanSrc: procedure expose m.
parse arg m, m.m.src
    m.m.pos = 1
    m.m.tok = ''
    m.m.scanIsBasic = 1
    return m
endProcedure scanSrc

scanBasic: procedure expose m.
parse arg src
    if symbol('m.scan.0') == 'VAR' then
        m.scan.0 = m.scan.0 + 1
    else
        m.scan.0 = 1
    return scanSrc('SCAN.'m.scan.0, src)
endProcedure scanBasic

scanEr3: procedure expose m.
parse arg m, txt, info
    return err('s}'txt'\n'info)

scanErr: procedure expose m.
parse arg m, txt
    if arg() > 2 then
        return err(m,'old interface scanErr('m',' txt',' arg(3)')')
    return scanEr3(m, txt, scanInfo(m))

/*--- scanInfo: text of current scan position for scanErr -----------*/
scanInfo: procedure expose m.
parse arg m
    if m.m.scanIsBasic then
        return scanSBInfo(m)
    else
        interpret objMet(m, 'scanInfo')
endProcedure scanInfo

scanSBInfo: procedure expose m.
parse arg m
    return 'last token' m.m.tok 'scanPosition' ,
        strip(substr(m.m.src, m.m.pos, 40), 't') ,
        || '\npos' m.m.Pos 'in string' strip(m.m.src, 't')

/*--- return the next len characters until end of src ---------------*/
scanLook: procedure expose m.
parse arg m, len
    if len == '' then
        return substr(m.m.src, m.m.pos)
    else
        return substr(m.m.src, m.m.pos,
                     , min(len, 1 + length(m.m.src) - m.m.pos))
endProcedure scanLook

/*--- scan len chararcters, atmost to end of src --------------------*/
scanChar: procedure expose m.
parse arg m, len
    m.m.tok = scanLook(m, len)
    m.m.pos = m.m.pos + length(m.m.tok)
    return m.m.tok \== ''
endProcedure scanChar

/*--- scan the literal lit ------------------------------------------*/
scanLit: procedure expose m.
parse arg m
    do ax=2 to arg()
        if abbrev(substr(m.m.src, m.m.pos), arg(ax)) then do
            m.m.tok = arg(ax)
            m.m.pos = m.m.pos + length(arg(ax))
            return 1
            end
        end
    m.m.tok = ''
    return 0
endProcedure scanLit

/*--- scan with verify, vOpt is passed to verify --------------------*/
scanVerify: procedure expose m.
parse arg m, alpha, vOpt
    if vOpt == '' then   /* empty string does not take default */
        nx = verify(m.m.src, alpha, , m.m.pos)
    else
        nx = verify(m.m.src, alpha, vOpt, m.m.pos)
    if nx = 0 then
        nx = length(m.m.src) + 1
    m.m.tok = substr(m.m.src, m.m.pos, nx - m.m.pos)
    m.m.pos = nx
    return m.m.tok \== ''
endProcedure scanVerify

/*--- scan while in charset -----------------------------------------*/
scanWhile: procedure expose m.
parse arg m, chSet
    return scanVerify(m, chSet, 'n')

/*--- scan until in charset -----------------------------------------*/
scanUntil: procedure expose m.
parse arg m, chSet
    return scanVerify(m, chSet, 'm')

/*--- scan until (and over) string End ------------------------------*/
scanStrEnd: procedure expose m.
parse arg m, sep
    sx = m.m.pos
    bx = sx
    do forever
        ex = pos(sep, m.m.src, sx)
        if ex = 0 then do
            m.m.val = m.m.val || substr(m.m.src, bx)
            return 0
            end
        m.m.val = m.m.val || substr(m.m.src, bx, ex-bx)
        bx = ex + length(sep)
        if \ abbrev(substr(m.m.src, bx), sep) then do
            m.m.tok = m.m.tok || substr(m.m.src, m.m.pos, bx-m.m.pos)
            m.m.pos = bx
            return 1
            end
        sx = bx + length(sep)
        end
endProcedure scanStrEnd

/*--- scan a string with quote char qu ------------------------------*/
scanString: procedure expose m.
parse arg m, prefs
    if prefs = '' then do
        call scanLit m, "'", '"'
        end
    else do
        do px=1 to words(prefs) until scanLit(m, word(prefs, px))
            end
        end
    if m.m.tok == '' then
        return 0
    m.m.val = ''
    if \ scanStrEnd(m, m.m.tok) then
        return scanErr(m, 'ending Apostroph missing')
    return 1
endProcedure scanString

/*--- scan a word and put value into *.val
           a word is either delimited by space or stopper
                     or a string (with single or double quotes ------*/
scanWord: procedure expose m.
parse arg m, stopper, ucWord
    if scanString(m) then
        return 1
    if stopper == '' then
        stopper = m.ut_space
    if \scanUntil(m, stopper) then
        return 0
    if ucWord == 1 then
        m.m.val = translate(m.m.tok)
    else
        m.m.val = m.m.tok
    return 1
endProcedure scanWord

/*--- skip, scan and return word or scanErr -------------------------*/
scanRetWord: procedure expose m.
parse arg m, stopper, ucWord, eWhat
    if scanWord(scanSKip(m), stopper, ucWord) then
        return m.m.val
    else
        return scanErr(m, eWhat 'expected')
endProcedure scanRetWord

/*--- scan a key = word phrase
          put key into m.key and word into m.m.val -------*/
scanKeyValue: procedure expose m.
parse arg m, def, uc
    if \ scanWord(m, ' =''"') then
        return 0
    m.m.key = m.m.val
    if \ scanLit(scanSkip(m), '=') then
        m.m.val = def
    else if \ scanWord(scanSkip(m)) then
        return scanErr(m, 'word expected after' m.m.key '=')
    if uc == 1 then
        upper m.m.key m.m.val
    return 1
endProcedure scanKeyValue

/*--- scan over space, nl, comments ---------------------------------*/
scanSpace: procedure expose m.
parse arg m
   if m.m.scanIsBasic then
       return scanSpaceOnly(m)
   else
       return scanSpNlCo(m)
endProcedure scanSpace

scanSpaceOnly: procedure expose m.
parse arg m
    nx = verify(m.m.src, m.ut_space, , m.m.pos)
    if nx = 0 then
        nx = length(m.m.src) + 1
    m.m.tok = left(' ', nx <> m.m.pos)
    m.m.pos = nx
    return m.m.tok == ' '
endProcedure scanSpaceOnly

/*--- skip over space and return m ----------------------------------*/
scanSkip: procedure expose m.
parse arg m
    call scanSpace m
    return m
endProcedure scanSkip

/*--- return true if at end of src ----------------------------------*/
scanEnd: procedure expose m.
parse arg m
    if m.m.pos <= length(m.m.src) then
        return 0
    else if m.m.scanIsBasic then
        return 1
    else
        return m.m.atEnd
endProcedure scanEnd

/*--- scan a natural number (no sign, decpoint ...) Ignore After ----*/
scanNatIA: procedure expose m.
parse arg m
    return scanVerify(m, '0123456789')

/*--- scan an integer (optional sign, no decpoint ...) Ignore After -*/
scanIntIA: procedure expose m.
parse arg m
    poX = m.m.pos
    call scanLit m, '-', '+'
    if \ scanNatIA(m) then do
        m.m.pos = poX
        return 0
        end
    m.m.tok = substr(m.m.src, poX, m.m.pos - poX)
    return 1
endProcedure scanIntIA

/*--- scanOpt set the valid characters for names, and comments
          it must be called
          before any of the following functions ---------------------*/
scanOpt: procedure expose m.
parse arg m, m.m.scanName1, namePlus, m.m.scanComment, nest
    if m.m.scanName1 == '' then
        m.m.scanName1 = m.ut_alfa
    if namePlus == '' then
        m.m.scanNameR = m.m.scanName1 || '0123456789'
    else
        m.m.scanNameR = m.m.scanName1 || namePlus
    m.m.scanNestCom = nest == 1
    return m
endProcedure scanOpt

/*--- return true if at comment -------------------------------------*/
scanSBCom: procedure expose m.
parse arg m
    m.m.tok = ''
    if m.m.scanComment == '' then
        return 0
    if \ abbrev(substr(m.m.src, m.m.pos), m.m.scanComment) then
        return 0
    m.m.tok = substr(m.m.src, m.m.pos)
    m.m.pos = 1 + length(m.m.src)
    return 1
endProcedure scanSBCom

/*--- scan a Name, first char in *.scanName1, rest in *.scanNameR ---*/
scanName: procedure expose m.
parse arg m
    if pos(substr(m.m.src, m.m.pos, 1),
                 , m.m.scanName1) <= 0 then do
        m.m.tok = ''
        return 0
        end
    return scanVerify(m, m.m.scanNameR)
endProcedure scanName

/*--- check character after a number
          must not be Identifier or plus ----------------------------*/
scanCheckNumAfter: procedure expose m.
parse arg m, res, plus
    if \ res then
        return 0
    if pos(substr(m.m.src, m.m.pos, 1), m.m.scanNameR || plus) > 0 then
        call scanErr m, 'illegal char after number' m.m.tok
    return 1
endProcedure scanCheckNumAfter

/*--- scan a natural number check character after -------------------*/
scanNat: procedure expose m.
parse arg m
    if arg() \== 1 then
        return err('old interface scanNat') / 0
    return scanCheckNumAfter(m, scanNatIA(m), '.')
endProcedure ScanNat

/*--- scan an Integer check character after -------------------------*/
scanInt: procedure expose m.
parse arg m
    if arg() \== 1 then
        return err('old interface scanInt') / 0
    return scanCheckNumAfter(m, scanIntIA(m), '.')
endProcedure ScanInt

/*--- scan a Number check character after ---------------------------*/
scanNum: procedure expose m.
parse arg m
    if arg() \== 1 then
        return err('old interface scanNum') / 0
    return scanCheckNumAfter(m, scanNumIA(m))
endProcedure ScanInt

/*--- scan a number (optional sign, decpoint, exponent) Ignore After-*/
scanNumIA: procedure expose m.
parse arg m
    poX = m.m.pos
    call scanLit m, '-', '+'
    cx = scanNumUSPos(m)
    if cx == 0 then do
        m.m.pos = poX
        return 0
        end
    m.m.tok = substr(m.m.src, poX, cx-poX)
    m.m.val = translate(m.m.tok)
    m.m.pos = cx
    return 1
endProcedure scanNumIA

/*--- scan unsigned number (opt. decpoint, exponent) Ignore After ---*/
scanNumUSPos: procedure expose m.
parse arg m
    poX = m.m.pos
    cx = verify(m.m.src, '0123456789', , poX)
    if cx > 0 then
        if substr(m.m.src, cx, 1) == '.' then
            cx = verify(m.m.src, '0123456789', , cx+1)
    if cx < 1 then  do
        if abbrev('.', substr(m.m.src, poX)) then
            return 0
        end
    else if abbrev('.', substr(m.m.src, poX, cx-poX)) then do
        return 0
        end
    else if pos(substr(m.m.src, cx, 1), 'eE') > 0 then do
        cy = cx + 1 + (pos(substr(m.m.src, cx+1, 1), '-+') > 0)
        cx = verify(m.m.src, '0123456789', , cy)
        if cx==cy | (cx == 0 & cy > length(m.s.src)) then
            call scanErr m, 'exponent expected after E'
        end
    if cx >= poX then
        return cx
    else
        return length(m.s.src)+1
  /*
        m.m.tok = substr(m.m.src, poX, cx-poX)
        m.m.pos = cx
        end
    else do
        m.m.tok = substr(m.m.src, poX)
        m.m.pos = length(m.s.src)+1
        end
    m.m.val = translate(m.m.tok)
    return 1  */
endProcedure scanNumUSPos

scanType: procedure expose m.
parse arg m, opt
    m.m.tok = ''
    if scanName(m) then
        m.m.type = 'n'
    else if scanNum(m) then
        m.m.type = 0
    else if scanString(m) then
        m.m.type = left(m.m.tok, 1)
    else if scanSpace(m) then
        m.m.type = 's'
    else do
        call scanChar m, 1
        m.m.type = m.m.tok
        end
    return m.m.type
endProcedure scanType

/* copy scan     end *************************************************/
/* copy dsnList begin *************************************************
     csi interface: see dfs managing catalogs chapt. 11
         returncode/reason see message IDC3009I
**********************************************************************/
/*--- specify dsn mask and fields to start a csi catalog search -------
      arguments:
          m       objectPointer
          dsnMask specifies the dsns with wildcards:
              %  1 character
              *  0 - n character in one level
              ** 0 - n levels
          fields a (space separated) list of field names ------------*/
csiOpen: procedure expose m.
parse arg m, dsnMask, fields
    m.m.fld.0 = words(fields)
    ffix = d2c(m.m.fld.0, 2)
    do x=1 to m.m.fld.0
        m.m.fld.x = translate(word(fields, x))
        ffix = ffix || left(m.m.fld.x, 8)
        end
    if dsnMask \== '' & right(dsnMask, 1) \== ' ' ,
          & pos('*', dsnMask) < 1 & length(dsnMask) < 42 then
        dsnMask = dsnMask'.**'
    m.m.filt = left(dsnMask, 149) ,
             || left('Y', 3) ,        /* resume offset 149      */
             || ffix                  /* csiNumEn offset 152    */

    WORKLEN = 1024 * 64
    m.m.work = D2C(WORKLEN,4) || COPIES('00'X,WORKLEN-4)
    m.m.pos = workLen + 1
    return
endProcedure csiOpen

/*--- put the next dsn into m.o and m.o.* (for other fields)
      return 1 if next dsn exists 0 otherwise -----------------------*/
csiNext: procedure expose m.
parse arg m, o
    usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET AMOUNT OF WORK AREA USED*/
    px = m.m.pos
    do forever
        if px > usedL then do
            if substr(m.m.filt, 150, 1) \== 'Y' then do
                m.m.pos = px
                m.o = ''
                return 0
                end
            reason = left('', 4)
            ADDRESS LINKPGM 'IGGCSI00  reason m.'m'.filt  m.'m'.work'
            if rc == 0 & substr(reason, 3, 2) == '0000'x then
                nop
            else if rc == 4 & substr(reason, 3, 2) == '0464'x then
                say 'data set entry with error'
            else
                call err 'call csi returns' rc,
                             'rc' c2d(substr(reason, 4,1)),
                             'reason' c2d(substr(reason, 3,1)),
                             'module' substr(reason, 1,2)
            usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET WORK AREA USED */
            numFD = C2D(SUBSTR(m.m.work,13,2)) /* no flds + 1 */
            if numFd <> m.m.fld.0 + 1 then
                call err 'csiNumFd' numFd 'not' m.m.fld.0 '+' 1
            px = 15
            iterate
            end
        eType =  substr(m.m.work, px+1, 1)
        m.o = strip(substr(m.m.work, px+2, 44), 't')
        flag = substr(m.m.work, px, 1)
        /* say 'eType' eType m.o 'flag' c2x(flag) */
        if eType == '0' then do
            if flag \== '00'x & flag \== '40'x then
                call err 'flag' c2x(flag) 'for catalog' m.o
            px = px + 50    /* length of catalog entry  */
            iterate
            end
        else do
            if \ abbrev(x2b(c2x(flag)), '101') then
                call err 'call csi entry flag' x2b(c2x(flag)),
                             'rc' c2d(substr(m.m.work, px+49,1)),
                             'reason' c2d(substr(m.m.work, px+48,1)),
                             'module' substr(m.m.work, px+46, 2),
                             'for entry' m.o,
                             'see qw IDC3009I'
            py = px + 46
            tl = c2d(substr(m.m.work, py, 2))
            pl = py + 4
            pf = py + m.m.fld.0 * 2 + 4
            do fx = 1 to m.m.fld.0
                fi = m.m.fld.fx
                fl = c2d(substr(m.m.work, pl, 2))
                m.o.fi = substr(m.m.work, pf, fl)
                if fi = 'MGMTCLAS' then
                    m.o.fi = substr(m.o.fi, 3, c2d(left(m.o.fi ,2)))
                else if wordPos(fi, 'COMUDSIZ NOBYTTRK') > 0 then
                    m.o.fi = utc2d(m.o.fi)
                pf = pf + fl
                pl = pl + 2
                end
            if py + tl <> pf then
                call err 'length mismatch for entry' m.o
            m.m.pos = pf
            return 1
            end
        end
endProcedure csiNext

/*--- if dsn is arcived return 'arcive'
      if dsn is tape return 'tape'
      otherwise return managment class ------------------------------*/
csiArcTape: procedure expose m.
parse arg vo, cl, dt, dsn
        if vo = '' then
            say err '||| no volume for dsn' dsn
        else if vo = 'ARCIVE' | vo = 'MIGRAT' then
            res = 'arcive'
        else if cl <> '' then
            res = cl
        else if abbrev(vo, 'SHR') then
            res = 'SHR'
        else
            res = 'tape'
        if   res = 'arcive' then
            return res
      /*if   abbrev(res, 'ar') \= abbrev(dt, '00'x) , */
        if   abbrev(res, 'ta') \= abbrev(c2x(left(dt, 1)), '7') ,
           | (left(res, 1) >= 'A') \= abbrev(dt, '30'x) then
           say '||| mismatch cl' cl 'vo' vo 'dt' c2x(dt) 'dsn' dsn
        return res
endProcedure csiArcTape

/*--- check if a dataset is archive -----------------------------------
          returns 'ok'                    if dataset on disk
                  'not'                   if dataset is not catalogued
                  'arc'                   if dataset archived
                  listDsi errorMsg        otherwise -----------------*/
dsnArc: procedure expose m.
parse upper arg dsn
    lc = listDsi("'"strip(dsn)"' noRecall")
    if lc = 0 then
        return 'ok'
    else if lc=4 & sysReason = 19 then  /* multiple volumes */
        return 'ok'
    else if lc=16 & sysReason = 5 then
        return 'notCat'
    else if lc=16 & sysReason = 9 then
        return 'arc'
    else
        return 'listDsi cc='lc', sysReason='sysReason ,
                          'm2='sysMsgLvl2', m1='sysMsgLvl1
endProcedure dsnArc

/*--- dsnList: csi or csm to stem -----------------------------------*/
dsnList: procedure expose m.
parse arg oo, aMsk, rzPref
    parse value dsnCsmSys(aMsk) with rz '/' msk
    if msk \== '' & right(msk, 1) \== ' ' ,
          & pos('*', msk) < 1 & length(msk) < 42 then
        msk = msk'.**'
    if rz == '*' then do
        call csiOpen dsnList_csi, msk
        do ox=1 while csiNext(dsnList_csi, oo'.'ox)
            end
        end
    else do
        pre = copies(rz'/', rzPref \== 0)
        call adrCsm 'dslist system('rz') dsnMask('msk') short', 4
        do ox=1 to stemSize
            m.oo.ox = pre || dsName.ox
            end
        end
    m.oo.0 = ox-1
    return m.oo.0
endProcedure dsnList

/*--- mbrList with listDS or csm to stem ----------------------------*/
mbrList: procedure expose m.
parse arg m, pds
    parse value dsnCsmSys(translate(dsn2jcl(pds))) with sys '/' dsn
    msk = strip(dsnGetMbr(dsn))
    if msk == '*' then
        msk = ''
    dsn = dsnSetMbr(dsn)
    if sys \== '*' then
        return csmMbrList(m, sys, dsn, msk)
    if adrTso(listDS "'"dsn"'" members, 8) <> 0 then
        mx = -99
    else if m.tso_trap.1 <> dsn then
        call err 'mbrList dsn='dsn '<> trap.1='m.tso_trap.1
    else if m.tso_trap.2 <> '--RECFM-LRECL-BLKSIZE-DSORG' then
        call err 'mbrList dsn='dsn 'trap.2='m.tso_trap.2
    else do
        parse var m.tso_trap.3 ,
            m.m.RECFM m.m.LRECL m.m.BLKSIZE m.m.DSORG .
        oy = m.tso_trap.0 + 99
        mFound = 0
        mx = 0
        do ox=4 to m.tso_trap.0
            if mFound then do
                if msk \== '' then
                    if \ match(strip(m.tso_trap.ox), msk) then
                        iterate
                mx = mx + 1
                m.m.mx = strip(m.tso_trap.ox)
                end
            else
                mFound = m.tso_trap.ox == '--MEMBERS--'
            end
        if \ mFound then
            mx = -98
        end
    m.m.0 = mx
    return mx
endProcedure mbrList

/*--- return whether a dsn exists -----------------------------------*/
dsnExists: procedure expose m.
parse upper arg aDsn
    parse value dsnCsmSys(aDsn) with rz '/' dsn
    if rz == '*' then
        return sysDsn("'"dsn"'") == 'OK'
    else if dsnGetMbr(dsn) <> '' then
        return csmMbrList(tso_dsnExits, rz, dsnSetMbr(dsn) ,
                  , dsnGetMbr(dsn)) == 1
    else do
        lc = adrCsm('dslist system('rz') dsnMask('dsn') short', 4)
        if stemsize = 0 | stemSize = 1 then
            return stemSize
        call err 'csmExists stemSize='stemsize 'for dsn='aDsn
        end
endProcedure dsnExists

/*--- copy members / datasets ---------------------------------------
      fr, to from or to dsn with or without member
      mbrs: space separated list of mbr or old>new
      opts
      *  all members from lib to lib
      &  members as defined in mbrs argument
      -  sequentiel (or library WITH member)
      *- if * fails then do - from fr to to
      &- if & fails then do - from fr(mbr) to to
---------------------------------------------------------------------*/
dsnCopy: procedure expose m.
parse upper arg fr opt . , to toPl, mbrs
    op1 = '?'
    if opt \== '' then do
        parse upper arg opt fr .
        if pos(left(opt, 1), 'WTC?') > 0 then
            parse var opt op1 2 opt
        end
    if opt == '-' then do
        if mbrs \== '' then
            call err 'op1 -  but mbrs not empty' mbrs
        end
    else do
        fMb = dsnGetMbr(fr)
        fr = dsn2jcl(dsnSetMbr(fr))
        tMb = dsnGetMbr(to)
        to = dsn2jcl(dsnSetMbr(to))
        if mbrs = '' then
            if fMb = '' then
                to = dsnSetMbr(to, tMb)
            else if tMb = '' then
                mbrs = fMb
            else
                mbrs = fMb'>'tMb
        else if fMb \== '' | tMb \== '' then
            call err 'fr='fr 'to='to 'but with mbrs='mbrs
        if mbrs = '' then
            o2 = left('*', tMb = '')'-'
        else if words(mbrs) = 1 & pos('>', mbrs) < 1 then
            o2 = if(verify(mbrs, '*?', 'm') > 0, '*', '&')'-'
        else
            o2 = '&'
        if opt == '' then
            opt = o2
        else if pos(opt, o2) == 0 then
            call 'bad opt' opt 'not in' o2
        end

    if abbrev(opt, '*') then do
        mbrs = ''
        do mx=1 to mbrList(tso_dsnCopy, fr'('fMb')')
            mbrs = mbrs m.tso_dsnCopy.mx
            end
        if m.tso_dsnCopy.0 > 0 then
            opt = '&'
        else if m.tso_dsnCopy.0 = 0 then do
            say 'nothing copied, no members in' fr
            return
            end
        else if substr(opt, 2, 1) == '-' then
            opt = '-'
        else
            return err(fr 'is not a library')
        end
         /* currently we use csm, which calls IBM Utilities
               for us, which seems not to be easy do to directly */
    if op1 == 'C' | op1 == '?' then do
        r = csmCop2(op1 opt, fr, to toPl, mbrs)
        if datatype(r, 'n') then
            return r
        op1 = r
        end
    if op1 == 'W' | op1 == 'T' then           /* use read and write,
                                                 allows reformatting */
        return dsnCopW(op1 opt, fr, to toPl, mbrs)
    call err 'dsnCopy bad opt' op1 opt
endProcedure dsnCopy

dsnCopW: procedure expose m. i.
parse arg o1 o2, fr, to tPl, mbrs
    if words(mbrs) > 1 then do
        do mx=1 to words(mbrs)
            call dsnCopW o1 o2, fr, to tPl, word(mbrs, mx)
            end
        return words(mbrs)
        end
    parse var tPl tA1 ':' tA2
    if \ abbrev(o2, '&') then do
        parse value dsnAlloc(fr, , 'readDD') with fDD fFr
        tAt = strip(tA1 ':'firstNS(tA2, ':D'fDD))
        parse value dsnAlloc(to tAt, , 'writeDD') with tDD tFr
        end
    else do
        parse value strip(mbrs) with fMb '>' tMb
        fr = dsnSetMbr(fr, fMb)
        parse value dsnAlloc(fr, , 'readDD') with fDD fFr
        tAt = strip(tA1 ':'firstNS(tA2, ':D'fDD))
        to = dsnSetMbr(to, firstNS(tMb, fMb))
        parse value dsnCsmSys(to) with rz '/' .
        if o2 = '&-' & rz == '*' then do
            r2 = sysDsn("'"to"'")
            if r2 == 'OK' | r2 == 'MEMBER NOT FOUND' ,
                 | r2 == 'DATASET NOT FOUND' then
                nop
            else if r2 ,
            == 'MEMBER SPECIFIED, BUT DATASET IS NOT PARTITIONED' then
                to = dsnSetMbr(to)
            else
                call err 'sysDsn(to='to')' r2
            end
        parse value dsnAlloc(to tAt, , 'writeDD') with tDD tFr
        if o2 = '&-' & rz \== '*' then do
            if m.tso_dsorg.tDD <> 'PO' then do
                call tsoFree tFr
                to = dsnSetMbr(to)
                parse value dsnAlloc(to tAt, , 'writeDD') with tDD tFr
                end
            end
        end
    cnt = 0
    trunc = 0
    do while readDD(fDD, i., 500)
        cnt = cnt + i.0
        call writeDD tDD, i., , o1 == 'T'
        if m.tso_rc then
            trunc = 1
        end
    call tsoClose fDD
    if cnt = 0 then
        call tsoOpen tDD, 'W'
    call tsoClose tDD
    call tsoFree fFr tFr
    say 'copied' cnt 'recs from' fr 'to' to copies('truncation',trunc)
    return cnt
endProcedure dsnCopW

dsnDel: procedure expose m.
parse upper arg aDsn, aMbrs
    parse value dsnCsmSys(dsn2jcl(aDsn)) with sys '/' dsn
    mbrs = dsnGetMbr(dsn) aMbrs
    dsn = dsnSetMbr(dsn)
    if sys \== '*' then
        return csmDel(sys, dsn, mbrs)
    if mbrs = '' then do
        dRc = adrTso("delete '"dsn"'", 8)
        end
    else do
        call dsnAlloc 'dd(deldd)' dsn
        do mx=1 to words(mbrs)
            m1 = word(mbrs, mx)
            dRc = adrTso("delete '"dsn"("m1")' file(delDD)", 8)
            if dRc <> 0 then do
                if pos('IDC3330I **' m1' ', m.tso_trap) < 1 then
                    leave
                say 'member not found and not deleted:' dsn'('m1')'
                dRc = 0
                end
            end
        call tsoFree deldd
        end
    if dRc = 0 then
        return 0
    if pos('IDC3012I ENTRY' dsn 'NOT FO', m.tso_trap) >= 1 then do
        say 'dsn not found and not deleted:' dsn
        return 4
        end
    call err 'adrTso rc='m.tso_rc 'stmt='m.tso_stmt m.tso_trap
    return 8
endProcedure dsnDel
/* copy dsnList end   ************************************************/
/* copy match begin **************************************************/
/*--- 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
    if symbol('m.match_m.mask') == 'VAR' then
        interpret m.match_m.mask
    else
        interpret matchGen('MATCH_M.'mask, mask, 'm')
endProcedure match

matchGG: procedure expose m.
parse arg wert, cd, vars
    interpret cd
endProcedure matchGG

matchVars: procedure expose m.
parse arg wert, mask, vars
    if symbol('m.match_v.mask') == 'VAR' then
        interpret m.match_v.mask
    else
        interpret matchGen('MATCH_V.'mask, mask, 'v')
endProcedure match

matchRep: procedure expose m.
parse arg wert, mask, mOut
    vars = 'MATCH_VV'
    mm = mask'\>'mOut
    if symbol('m.match_r.mm') == 'VAR' then
        interpret m.match_r.mm
    else
        interpret matchGen('MATCH_R.'mm, mask, 'r', mOut)
endProcedure matchRep

matchGen: procedure expose m.
parse arg m, mask, opt, mOut
    a = matchScan(match_sM, mask)
    if symbol('m.match_g') \== 'VAR' then
        m.match_g = 0
    if opt \== 'r' then do
        r = matchgenMat(a, opt, 1, m.a.0, 0)
        end
    else do
        m.match_g = m.match_g + 1
        sub = 'MATCH_G'm.match_g
        m.sub = matchgenMat(a, 'v', 1, m.a.0, 0)
        o = matchScan(match_sO, mOut)
        r = matchGenRep(o, m.a.wildC)
        r = 'if matchGG(wert, m.'sub', vars) then return' r';' ,
            'else return "";'
        end
    m.m = r
    return r
endProcedure matchGen

matchScan: procedure expose m.
parse arg a, mask, opt
    s = match_scan
    call scanSrc s, mask
    ax = 0
    vx = 0
    m.a.wildC = ''
    do forever
        if scanUntil(s, '*?&\') then do
            if m.a.ax == 'c' then do
                m.a.ax.val = m.a.ax.val || m.s.tok
                end
            else do
                ax = ax + 1
                m.a.ax = 'c'
                m.a.ax.val = m.s.tok
                end
            end
        else if scanChar(s, 1) then do
            if pos(m.s.tok, '*?') > 0 then do
                ax = ax + 1
                vx = vx + 1
                m.a.ax = m.s.tok
                m.a.ax.ref = vx
                m.a.wildC = m.a.wildC || m.s.tok
                end
            else if m.s.tok == '\' then do
                call scanChar s, 1
                if pos(m.s.tok, '\*?&') < 1 then
                    return scanErr(s, 'bad char after \')
                if abbrev(m.a.ax, 'c') then
                    m.a.ax.val = m.a.ax.val || m.s.tok
                else do
                    ax = ax + 1
                    m.a.ax = 'c'
                    m.a.ax.val = m.s.tok
                    end
                end
            else if m.s.tok == '&' then do
                if opt \== 'r' then
                    call scanErr s, '& in input'
                if \ scanChar(s,1) | pos(m.s.tok, 'ms123456789')<1 then
                    call scanErr s, 'bad & name' m.s.tok
                ax = ax + 1
                m.a.ax = '&'
                m.a.ax.ref = m.s.tok
                end
            else
                call scanErr s, 'bad char 1 after until'
            end
        else
            leave
        end
    m.a.0 = ax
    if vx \== length(m.a.wildC) then
        call scanErr 'vars' m.a.wildC 'mismatches' vx
    return a
endProcedure matchScan

matchGenMat: procedure expose m.
parse arg a, opt, fx, tx, minLL
    ml = 0
    if fx == 1 then do
        do ax=1 to m.a.0
            if m.a.ax == '?' then
               ml = ml + 1
            else if m.a.ax == 'c' then
               ml = ml + length(m.a.ax.val)
            m.a.minLen.ax = ml
            end
        end
    r = ''
    ret1 = ''
    ret1After = ''
    lO = 0
    do fy=fx to tx
        if m.a.fy == 'c' then do
            r = r 'if substr(wert,' (1+lO)
            if fy < m.a.0 then
                r = r',' length(m.a.fy.val)
            r = r') \==' quote(m.a.fy.val, "'") 'then return 0;'
            lO = lO + length(m.a.fy.val)
            end
        else if m.a.fy == '?' then do
            lO = lO + 1
            if opt == 'v' then
                ret1 = ret1 'm.vars.'m.a.fy.ref ,
                        '= substr(wert,' lO', 1);'
            end
        else if m.a.fy == '*' then
            leave
        else
            call err 'bad match ast' a'.'fy m.a.fy
        end
    rO = 0
    do ty=tx by -1 to fy
        if m.a.ty == 'c' then do
            rO = rO + length(m.a.ty.val)
            r = r 'if substr(wert, length(wert) -' (rO - 1)',' ,
                  length(m.a.ty.val)')' ,
                  '\==' quote(m.a.ty.val, "'") 'then return 0;'
            end
        else if m.a.ty == '?' then do
            rO = rO + 1
            if opt == 'v' then
                ret1 = ret1 'm.vars.'m.a.fy.ref ,
                        '= substr(wert, length(wert) -' (rO-1)', 1);'
            end
        else if m.a.ty ==  '*' then
            leave
        else
            call err 'bad match ast' a'.'fy m.a.fy
        end
    if fy > ty then do /* every thing is handled with fix len */
        if fx = tx & abbrev(m.a.fx, 'c') then
            r = 'if wert \==' quote(m.a.fx.val, "'") ,
                               'then return 0;'
        else
            r = 'if length(wert) \==' (lO + rO) 'then return 0;' r
        end
    else do
        myMiLe = m.a.minLen.ty
        if fy > 1 then do
            fq = fy -1
            myMiLe = myMiLe - m.a.minLen.fq
            end
        if minLL < myMiLe then
            r = 'if length(wert) <' myMiLe 'then return 0;' r
        if fy = ty & m.a.fy == '*' then     /* single * */
            ret1  = ret1 'm.vars.'m.a.fy.ref ,
                 '= substr(wert,' (1+lO)', length(wert) -' (lO+rO)');'
        else if fy < ty & abbrev(m.a.fy, '*') ,
                        & abbrev(m.a.ty, '*') then do
                                /* several variable length parts */
            suMiLe = m.a.minLen.ty - m.a.minLen.fy
            m.match_g = m.match_g + 1
            sub = 'MATCH_G'm.match_g
            m.sub = matchGenMat(a, opt, fy+1, ty, suMiLe)
            if rO = 0 then
                subV = 'substr(wert, lx)'
            else do
                r = r 'wSub = left(wert, length(wert) -' rO');'
                subV = 'substr(wSub, lx)'
                end
            r = r 'do lx = length(wert) -' (suMiLe+rO-1) ,
                       'by -1 to' (lO+1)';' ,
                       'if \ matchGG('subV', m.'sub', vars) then' ,
                            'iterate;'
            ret1  = ret1 'm.vars.'m.a.fy.ref '= substr(wert,' (lO+1) ,
                     ||  ', lx -' (lO+1)');'
            ret1After = 'end; return 0;'
            end
        else
            call err 'matchGenMat bad case' a'.'fy m.a.fy a'.'ty m.a.ty
        end
    if opt == 'v' & fx == 1 then do
        if r <> '' then
           r = 'm.vars.0 = -9;' r
        ret1 = ret1 'm.vars.0 =' length(m.a.wildC)';'
        end
    r = r ret1 'return 1;' ret1After
    return r
endProcedure matchGenMat

matchGenRep: procedure expose m.
parse arg o, wildC
    xQ = 0
    xS = 0
    do ox=1 to m.o.0
        if m.o.ox == '?' then do
             xQ = pos('?', wildC, xQ+1)
             if xQ < 1 then
                 call err 'unmatchted ?' ox
             m.o.ox.re2 = xQ
             end
        else if m.o.ox == '*' then do
             xS = pos('*', wildC, xS+1)
             if xS < 1 then
                 call err 'unmatchted *' ox
             m.o.ox.re2 = xS
             end
        else if m.o.ox == '&' & m.o.ox.ref >> '0' then do
            if m.o.ox.ref > length(wildC) then
                 call err '&'m.o.ox.ref 'but wildcards' wildC
            xQ = m.o.ox.ref
            xS = xQ
            m.o.ox.re2 = xQ
            end
        end
    r = ''
    do ox=1 to m.o.0
        if abbrev(m.o.ox, 'c') then
            r = r '||' quote(m.o.ox.val, "'")
        else if m.o.ox == '&' & m.o.ox.re2 == 's' then
            r = r '|| wert'
        else if m.o.ox == '&' & m.o.ox.re2 == 'm' then
            r = r '||' quote(mask, "'")
        else if pos(m.o.ox, '*?&') > 0 then
            r = r '|| m.vars.'m.o.ox.re2
        end
    if r=='' then
        return "''"
    else
        return substr(r, 5)
endProcedure matchGenRep

/* copy match end ****************************************************/
/* copy adrIsp begin *************************************************/
/*--- 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 m.tso_stmt, ggRet
    call outtrap m.tso_trap.
    address tso m.tso_stmt
    m.tso_rc = rc
    call outtrap off
    if m.tso_rc == 0 then
        return 0
    m.tso_trap = ''
    do ggXx=1 to min(7, m.tso_trap.0)
        m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
        end
    if m.tso_trap.0 > 7 then do
        if m.tso_trap.0 > 14 then
            m.tso_trap = m.tso_trap'\n............'
        do ggXx=max(8, m.tso_trap.0-6) to m.tso_trap.0
            m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
            end
        end
    m.tso_errL1 = 'adrTso rc='m.tso_rc 'stmt='m.tso_stmt
    if ggRet <> '*' & wordPos(m.tso_rc, ggRet) <= 0 then
        call err m.tso_errL1 m.tso_trap
    return m.tso_rc
endSubroutine adrTso

/*--- format dsn from tso format to jcl format
      replace any ~ by syspref or userid and necessary dots ---------*/
dsn2jcl: procedure expose m.
parse upper arg dsn ., addPrefix
    if left(dsn,1) = "'" then /* only remove apostrophs */
        return strip(dsn, 'b', "'")
    cx = pos('~', dsn)
    if cx < 1 then
        if addPrefix \== 1 then
            return dsn
    sp = sysvar('SYSPREF')
    if sp == '' then
        sp = userid()
    if cx < 1 then
        return sp'.'dsn
    do until cx == 0
        le = left(dsn, cx-1)
        if le \== '' & right(le, 1) \== '.' & right(le, 1) \== '/' then
            le = le'.'
        if cx == length(dsn) then
            return le || sp
        else
            dsn = le || sp'.' ,
                || substr(dsn, cx + 1 + (substr(dsn, cx+1, 1) == '.'))
        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 lib '(' . , mbr .
     if mbr = '' then
         return strip(lib)
     else
         return strip(lib)'('mbr')'
endProcedure dsnSetMbr

dsnGetMbr: procedure expose m.
parse arg dsn '(' mbr ')'
     if mbr = '' then
         return arg(2)
     else
         return strip(mbr)
endProcedure dsnGetMbr

dsnCsmSys: procedure expose m.
parse upper arg dsn, withStar
    if pos('/', dsn) < 1 then
        return copies('*/', withStar \== 0)dsn
    parse var dsn sys '/' d2
    if sys = '' | sys = sysvar(sysnode) then
        return copies('*/', withStar \== 0)d2
    else
        return dsn
endProcedure dsnCsmSys

/**********************************************************************
    io: read or write a dataset with the following callsequences:
        read:  tsoOpen...'R', readDD*,  tsoClose
        write: tsoOpen...'W', writeDD*, tsoClose

        readDD returns true if data read, false at eof
        do not forget that open is mandatory to write empty file|
**********************************************************************/

/*--- open dd for read (rw='R') or write (rw='W') -------------------*/
tsoOpen: procedure expose m.
parse upper arg dd, rw
    return adrTso('execio' 0 'disk'RW dd '(open)')
return /* end tsoOpen */

/*--- close dd -----------------------------------------------------*/
tsoClose: procedure expose m.
    parse upper arg dd
    return adrTso('execio 0 diskR' dd '(finis)')
endProcedure tsoClose

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

/*--- write to gg ggDD from stem ggSt, ggCnt records ----------------*/
writeDD:
    parse arg ggDD, ggSt, ggCnt, ggRetDD
    if ggCnt == '' then
        ggCnt = value(ggst'0')
    if adrTso('execio' ggCnt 'diskW' ggDD '(stem' ggSt')',
                 , 1 ggRetDD) = 1 then
        if wordPos(1, ggRetDD) < 1 then
            call err 'truncation on write dd' ggDD
    return
endSubroutine writeDD

/*--- 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
    parse value dsnAlloc('dd('m.m.dd')' m.m.dsn) with m.m.dd m.m.free
    call tsoOpen m.m.dd, 'R'
    return m
endProcedure readNxBegin

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

/*--- return the stem of the curr line, '' at end -------------------*/
readNxCur: procedure expose m.
parse arg m
    if m.m.cx <= m.m.0 then
        return m'.'m.m.cx
    else
        return ''
endProcedure readNxCur

/*--- return the position (line number) of reader
           plus le characters of the current line (default 50) ------*/
readNxPos: procedure expose m.
parse arg m, le
    if m.m.cx > m.m.0 then
        return 'line' (m.m.buf0x + m.m.cx)':after EOF'
    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 tsoClose m.m.dd
    call tsoFree m.m.free
    return
endProcedure readNxEnd

/*--- 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 arg m, spec
    upper spec
    m.m.dsn = ''
    m.m.dd = ''
    m.m.disp = ''
    do wx=1 by 1
        w = word(spec, wx)
        if w = '' | abbrev(w, ':') then
            leave
        else if wordPos(w, 'OLD SHR MOD NEW')>0 | abbrev(w,'SYSO') then
            m.m.disp = w
        else if abbrev('CATALOG', w, 3) | abbrev(DELETE, w, 3) then
            m.m.disp = di left(w, 3)
        else if abbrev(w, 'DD(') then
            m.m.dd = substr(w, 4, length(w)-4)
        else if abbrev(w, 'DSN(') then
            m.m.dsn = dsn2Jcl(substr(w, 5, length(w)-5))
        else if m.m.dsn == '' & (w = 'INTRDR' ,
                                | verify(w, ".~'/", 'm') > 0) then
            m.m.dsn = dsn2jcl(w)
        else if pos('(', w) > 0 then
            leave
        else if m.m.dd == '' then
            m.m.dd = w
        else
            leave
        end
    if pos('/', m.m.dsn) < 1 then
        m.m.sys = ''
    else do
        parse var m.m.dsn m.m.sys '/' m.m.dsn
        if m.m.sys == '*' | m.m.sys = sysvar(sysnode) then
            m.m.sys = ''
        end
    parse value subword(spec, wx) with at ':' nw
    m.m.attr = strip(at)
    m.m.new  = strip(nw)
    return m
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, dDi, dDD, timeOut
    x = max(1, arg() - 1)
    do rt=0
        res = dsnAlloc(spec, dDi, dDD, '*')
        if \ datatype(res, 'n') then
            return res
        if rt > timeOut & timeOut \== '' then
            return err('timeout allocating' spec time() '\n'm.tso_trap)
        if pos('DATA SET IS ALLOCATED TO ANOTHER', m.tso_trap) < 1 then
            return err('allocating' spec'\n'm.tso_trap)
        say time() 'sleep and retry alloc' spec
        call sleep 1, 0
        end
endProcedure dsnAllocWait

/*--- alloc a dsn or a dd
          spec dsnSpec
          dDi  default disposition
          dDD  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, dDi, dDD, retRc
   return dsnAllo2(dsnSpec(dsn_alloc, spec), dDi, dDD, retRc)

/*--- alloc a dsn or a dd, dsnSpec analysed in m --------------------*/
dsnAllo2: procedure expose m.
parse upper arg m, dDi, dDD, retRc
    m.tso_dsn.dd = ''
    if m.m.dd \== '' then
        dd = m.m.dd
    else if dDD \== '' then
        dd = dDD
    else
        dd = 'DD*'
    if m.m.dsn == '' & m.m.disp == '' & m.m.attr = '' then
        return dd          /* already allocated only use dd */
    dd = tsoDD(dd, 'a')    /* ensure it is free'd by errCleanup */
    if m.m.disp \== '' then
        di = m.m.disp
    else if dDi \== '' then
        di = dDi
    else
        di = 'SHR'
    if pos('(', m.m.dsn) < 1 then
        nop
    else if di = 'MOD' then
        call err 'disp mod for' na
    else
        di = 'SHR'
    if m.m.sys == '' then
        rx = tsoAlloc(m.m.dsn, dd, di, m.m.attr, m.m.new, retRc)
    else
        rx = csmAlloc(m.m.sys'/'m.m.dsn,dd, di, m.m.attr,m.m.new,retRc)
    if rx = 0 then
        return dd dd
    call tsoFree dd, 1, 1  /* over careful? would tsoDD , - suffice? */
    return rx
endProcedure dsnAlloc

/*--- find a free dd with prefix dd ---------------------------------*/
tsoDD: procedure expose m.
parse upper arg dd, f, noErr
    if m.err_ini \== 1 then
        call errIni  /* initialises tso_ddAll */
    if f == '-' then do
        ax = wordPos(dd, m.tso_ddAll)
        if ax > 0 then
            m.tso_ddAll = delWord(m.tso_ddAll, ax, 1)
        else if noErr \== 1 then
            call err 'tsoDD dd' dd 'not used' m.tso_ddAll
        end
    else if f <> 'A' then
        call err 'tsoDD bad fun' f
    else do
        if right(dd, 1) = '*' then do
            d0 = left(dd, length(dd)-1) || m.err_screen
            dd = d0
            do dx=1 while wordPos(dd, m.tso_ddAll) > 0
                dd = d0 || dx
                end
            end
        else if pos('?', dd) > 0 then
            dd = repAll(dd, '?', m.err_screen)
        if wordPos(dd, m.tso_ddAll) < 1 then
            m.tso_ddAll = strip(m.tso_ddAll dd)
        m.tso_dsn.dd = ''
        m.tso_dsOrg.dd = ''
        end
    return dd
endProcedure tsoDD

tsoAlloc: procedure expose m.
parse arg na, dd, disp, rest, nn, retRc
    dd = translate(dd)
    c = 'alloc dd('dd')' disp
    if na == '' then
        m.tso_dsn.dd = ''
    else if na \== 'INTRDR' then do
        c = c "DSN('"na"')"
        m.tso_dsn.dd = na
        end
    else do
        c = c "sysout(*) writer(intRdr)"
        m.tso_dsn.dd = '*intRdr'
        end
    if wordPos(disp, 'NEW MOD') > 0 & nn \== '' then
        c = c dsnCreateAtts(,nn)
    if adrTso(c rest, '*') = 0 then
        return 0
    if pos('IKJ56246I', m.tso_trap) > 0 then
        if pos('TED, FILE IN USE', m.tso_trap) > 0 then do
     /* IKJ56246I DATA SET A540769.WK.REXX NOT ALLOCATED, FILE IN USE*/
        say 'tsoAlloc dd' dd 'already in use:' substr(m.tso_trap, 3)
        say '.... trying to free'
        call tsoFree dd, 1
        say '.... retrying to allocate' c rest
        if adrTso(c rest, '*') = 0 then
            return 0
        end
    if nn \= '' & wordPos(disp, 'OLD SHR') > 0 ,
          & pos('IKJ56228I', m.tso_trap) > 0 ,
          & pos(' NOT IN CATALOG ', m.tso_trap) > 0 then do
       /* IKJ56228I DATA SET A540769.XY.REXX NOT IN CATALOG OR CATAL*/
        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
    if pos('*', retRc) < 1 & wordPos(m.tso_rc, retRc) < 1 then
        call err 'tsoAlloc rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
    call sayNl 'tsoAlloc rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
    return m.tso_rc
endProcedure tsoAlloc

dsnLikeAtts: procedure expose m.
parse upper arg dsn, isFile
     if isFile then do
         ddDsn = m.tso_dsn.dsn
         if lastPos('/', m.tso_dsn.dsn, 4) < 1 then
              return tsoLikeAtts(dsn, 1)
         dsn = m.tso_dsn.dsn
         end
     sx = lastPos('/', dsn, 4)
     if sx < 1 then
         return tsoLikeAtts(dsn, 0)
     else if abbrev(dsn, '*/') | abbrev(dsn, sysVar('SYSNODE')'/') then
         return tsoLikeAtts(substr(dsn, sx+1), 0)
     else
         return csmLikeAtts(dsn)
endProcedure dsnLikeAtts

tsoLikeAtts: procedure expose m.
parse arg dsn, isFile
    rc = listDsi("'"dsn"'" copies('FILE', isFile) "SMSINFO")
    if rc = 0 then
        r = ''
    else if rc = 4 & sysReason = 19 then do
        r = 'UCOUNT(30)' /* 7.8.13 corrected attribute */
        say 'creating' dsn 'with multi volume' mv
        end
    else if rc ^= 0 then
        call err 'listDsi rc' rc 'reason' sysReason,
                             sysMsgLvl1 sysMsgLvl2
    if right(sysDsSms, 7) == 'LIBRARY' | abbrev(sysDsSms, 'PDS') ,
                      | sysDsOrg = 'PO' then
         r = 'dsOrg(PO) DSNTYPE(LIBRARY)' r
    else
         r = "dsOrg("sysDSorg")" r
    if sysUnits = 'TRACK' then
        sysUnits = 'TRACKS'
    return r "MGMTCLAS("sysMgmtClass")",
        "DATACLAS("sysDataClass")" ,
        "RECFM("strip(translate('1 2 3', ' 'sysREcFM, '123'))")",
        "LRECL("SYSLRECL")",
        "SPACE("sysPrimary"," sysSeconds")" ,
            sysUnits || left('S', sysUnits == 'TRACK')
    /*  "blksize("sysBLkSIZE")" removed 3.4.13: let sms do the magic */
endProcedure tsoLikeAtts

tsoFree: procedure expose m.
parse arg ddList, tryClose, silent
    do dx=1 to words(ddList)
        dd = word(ddList, dx)
        if adrTso('free dd('dd')', '*') <> 0 then do
            if pos('IKJ56861I', m.tso_trap) > 0 & tryClose == 1 then
              if pos('NOT FREED, DATA SET IS OPEN', m.tso_trap),
                    > 0 then do
                  /* IKJ56861I  FILE A1 NOT FREED, DATA SET IS OPEN */
                say 'dataset open:' substr(m.tso_trap, 3)
                say '.... trying to close'
                if adrTso('execio 0 diskR' dd '(finis)', '*') = 0 then
                   call adrTso 'free dd('dd')', '*'
                end
            if m.tso_rc \== 0 then
                if silent \== 1 ,
                    | \ (pos('IKJ56247I FILE',m.tso_trap) > 0 ,
                        & pos('NOT FREED, IS NOT ALLOCATED' ,
                             , m.tso_trap) > 0) then
                   call sayNl m.tso_errL1 m.tso_trap
            end
        call tsoDD dd, '-', 1
        end
    return 0
endProcedure tsoFree

dsnCreateAtts: procedure expose m.
parse arg dsn, atts
    res = ''
    if dsn \== '' & \ abbrev(dsn, '-') then
        res = "dataset('"dsnSetMbr(dsn)"')"
    if abbrev(atts, ':') then do
        parse var atts a1 atts
        rl = substr(a1, 3)
        if abbrev(a1, ':F') then do
            if rl = '' then
                rl = 80
            res =  res "recfm("space(f b)") lrecl("rl")"
            end
        else if abbrev(a1, ':V') then do
            if rl = '' then
                rl = 32755 /* 32756 gives bad values in ListDSI | */
            res =  res "recfm("space(v b)") lrecl("rl")"
            end
        else if abbrev(a1, ':L') then
            res = res dsnLikeAtts(rl, 0)
        else if abbrev(a1, ':D') then
            res = res dsnLikeAtts(rl, 1)
        else
            call err 'dsnCreateAtt bad :' a1
        end
    aU = ' 'translate(atts)
    hasOrg = pos(' DSORG(', aU) > 0 | pos(' DSNTYPE(', aU) > 0
    hasMbr = pos('(', dsn) > 0
    if hasMbr & \ hasOrg then
        atts = atts 'dsorg(po) dsntype(library)'
    if hasOrg | hasMbr then do
        ww = DSORG DSNTYPE
        do wx=1 to words(ww)
            do forever
                cx = pos(' 'word(ww, wx)'(', ' 'translate(res))
                if cx == 0 then
                    leave
                cy = pos(')', res, cx)
                res = delstr(res, cx, cy+1-cx)
                end
            end
        end
    res = res atts
    aU = ' 'translate(res)
    if pos(' MGMTCLAS(', aU) < 1 then
        res = res 'mgmtclas(COM#A091)'
    if pos(' SPACE(', aU) < 1 then
        res = res 'space(1, 50) cylinders'
    return res
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)'
    call tsoFree word(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)'
    call tsoFree word(ggAlloc, 2)
    if ggSay == 1 | m.debug == 1 then
       say ggCnt 'records written to' ggDsnSpec
    return
endSubroutine writeDsn

tsoDsiMaxl:
    rc = listDsi(arg(1) 'FILE')
    if rc ^= 0 then
        call err 'listDsi rc' rc 'reason' sysReason,
                             sysMsgLvl1 sysMsgLvl2
    return SYSLRECL  - 4 * abbrev(sysRecFm, 'V')
endSubroutine tsoDsiMaxL
/* copy adrTso end ***************************************************/
/* copy err begin *** errorhandling, messages, help    ***************/
errIni: procedure expose m.
    if m.err_ini == 1 then
        return
    m.err_ini     = 1
    call utIni
    m.err_saySay  = 1
    m.err_sayOut  = 0
    m.err_handler  = ''
    m.err_handler.0 = 0
    m.err_cleanup = '\?'
    m.err_opt     = ''
    m.err_nest    = 0
    parse source m.err_os .
    m.tso_ddAll   = ''
    m.err_ispf    = 0
    m.err_screen  = 0
    if m.err_os \== 'LINUX' then do
        address tso 'profile MsgId'   /* brauchen wir in tsoAlloc| */
        if sysVar('sysISPF') = 'ACTIVE' then do
            m.err_ispf = 1
            address ispExec 'vget (zScreen zScreenD zScreenW) shared'
            m.err_screen = zScreen
            m.err_screenD = zScreenD
            m.err_screenW = zScreenW
            end
        end
    return
endProcedure errIni

/* configure err ----------------------------------------------------*/
errReset: procedure expose m.
    call errIni
parse arg m.err_opt, m.err_handler
    upper m.err_opt
    call errSetSayOut '-'
    m.err_handler.0 = 0
    if pos('I', m.err_opt) > 0 & m.err_ispf then
        address ispExec 'control errors return'
    return
endSubroutine errReset

/*--- set sayOut and sysSay -----------------------------------------*/
errSetSayOut: procedure expose m.
parse upper arg flags
    if flags \== '-' then
        m.err_opt = space(translate(m.err_opt, '  ' ,'OS')flags, 0)
    m.err_sayOut = pos('O', m.err_opt) > 0
    m.err_saySay = pos('S', m.err_opt) > 0 | \ m.err_sayOut
    return
endProcedure errSetSayOut

/*--- set rc for ispf: ------------------------------------------------
    if a cmd is run by ispStart, its RC is ignored,
         but ISPF passes the value of the shared variable zIspfRc
         back as return code
---------------------------------------------------------------------*/
errSetRc: procedure expose m.
parse arg zIspfRc
    if m.err_ispf then
        address ispExec vput 'zIspfRc' shared
    return zIspfRc
endProcedure errSetRc

/* push error handler -----------------------------------------------*/
errHandlerPush: procedure expose m.
parse arg aH
    ex = m.err_handler.0 + 1
    m.err_handler.0 = ex
    m.err_handler.ex = m.err_handler
    m.err_handler = aH
    return
endProcedure errHandlerPush
/* push error handler return Constant value -------------------------*/
errHandlerPushRet: procedure expose m.
parse arg rv
    call errHandlerPush "return '"rv"'"
    return
/* pop  error handler -----------------------------------------------*/
errHandlerPop: procedure expose m.
    if m.err_handler.0 < 1 then
        call err 'errHandlerPop but err_handler.0='m.err_handler.0
    ex = m.err_handler.0
    m.err_handler = m.err_handler.ex
    m.err_handler.0 = ex - 1
    return
endProcedure errHandlerPop
/* pop  error handler -----------------------------------------------*/
errHandlerCall:
    interpret m.err_handler
    m.err_handlerReturned = 0
    return ''
endProcedure errHandlerCall
/*--- error routine: abend with message -----------------------------*/
err:
    parse arg ggTxt, ggOpt
    if abbrev(ggOpt, '^') then
        return substr(ggOpt, 2)
    call errIni
    ggNx = m.err_nest + 1
    m.err_nest = ggNx
    m.err_nest.ggNx = ggTxt
    if ggNx \== 1 & ggNx \== 2 then do ggNx=ggNx by -1 to 1
        say '  error nesting.'ggNx '==>' m.err_nest.ggNx
        end
    drop err handler cleanup opt call return stackHistory
    if ggOpt == '' & m.err_handler <> '' then do
        m.err_handlerReturned = 1
        ggRet = errHandlerCall()
        ggDoR = m.err_handlerReturned
        m.err_handlerReturned = 1
        if ggDoR then do
            m.err_nest = m.err_nest - 1
            return ggRet
            end
        end
    call errSay ggTxt
    call errCleanup
    if ggOpt == '' then
        ggOpt = m.err_opt
    upper ggOpt
    if pos('T', ggOpt) > 0  then do
        trace ?r
        say 'trace ?r in err'
        end
    if pos('H', ggOpt) > 0 & m.err_cat == 'f' then
        x = show + stackHistory + by + bad + arithmetic + conversion
    call errSay ' }errorhandler exiting with exit(12)'
    m.err_nest = m.err_nest - 1
    exit errSetRc(12)
endSubroutine err

/*--- error routine: user message cleanup exit ----------------------*/
errAddCleanup: procedure expose m.
parse arg code
    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
    call errIni
    cx = pos('\?'code'\?', m.err_cleanup)
    if cx > 0 then
        m.err_cleanup = delStr(m.err_cleanup, cx, length(code)+2)
    return
endProcedure errRmCleanup

errCleanup: procedure expose m.
    call errIni
    m.err_saySay  = 1
    m.err_sayOut  = 0

    if m.err_cleanup <> '\?' then do
        do while m.err_cleanup <> '\?'
            cx = pos('\?', m.err_cleanup, 3)
            c1 = substr(m.err_cleanup, 3, cx-3)
            m.err_cleanup = substr(m.err_cleanup, cx)
            say 'errCleanup doing' c1
            interpret c1
            end
        say 'errCleanup end doing err_cleanup'
        end
    if m.tso_ddAll <> '' then
        call tsoFree m.tso_ddAll, 1
    return
endProcedure errCleanup

/*--- say an errorMessage msg with pref and linesplits --------------*/
errSay: procedure expose m.
parse arg msg
    return errSaySt(splitNl(err_l, 0, errMsg(msg)))

errSaySt: procedure expose m.
parse arg st
    if m.err_saysay | \ m.err_sayOut then
        call saySt st
    if m.err_sayOut & \ ( m.err_saySay & m.j.out == m.j.say) then
        call outSt st
    return st
endProcedure errSaySt

/*--- prefix an errormessage with pref,
          split it into lines at \n to stem m.err -------------------*/
errMsg: procedure expose m.
parse arg msg
    m.err_cat = 'f'
    do while substr(msg, 2, 1) == '}'
        parse var msg m.err_cat '}' msg
        end
   if m.err_cat == ' ' | m.err_cat == 'o' then
        return msg
   pTxt = ',fatal error,error,input error,scanErr,warning,onlyMsg,'
   px = pos(','m.err_cat, pTxt)
   if px < 1 then do
       px = 1
       m.err_cat = 'f'
       end
   pre = substr(pTxt, px+1, pos(',', pTxt, px+2)-px-1)
   if m.err_cat == 's' then
       return pre msg
   parse source . . s3 .              /* current rexx */
   return pre 'in' s3':' msg
endProcedure errMsg

/*--- fill stem st with lines of msg separated by \n ----------------*/
splitNl: procedure expose m.
parse arg st, sx, msg
    bx = 1
    sx = firstNS(sx, 1)
    do lx=sx+1 to sx+999
        ex = pos('\n', msg, bx)
        if ex < bx then
            leave
        m.st.lx = substr(msg, bx, ex-bx)
        bx = ex+2
        end
    m.st.lx = substr(msg, bx)
    m.st.0 = lx
    return st
endProcedure splitNl

/*--- out msg lines separated by \n ---------------------------------*/
outNL: procedure expose m.
parse arg msg
    return outSt(splitNl(err_outNl, 0, msg))

/*--- say msg lines separated by \n ---------------------------------*/
sayNl: procedure expose m.
parse arg msg
    return saySt(splitNl(err_outNl, 0, msg))

/*--- say (part of) the lines of a stem -----------------------------*/
saySt: procedure expose m.
parse arg st, fx, tx
    do lx=firstNS(fx, 1) to firstNS(tx, m.st.0)
        say strip(m.st.lx, 't')
        end
    return st
endProcedure saySt

/*--- say a trace message if m.trace is set -------------------------*/
debug: procedure expose m.
parse arg msg
    if m.debug == 1 then
        say 'debug:' msg
    return
endProcedure debug

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

/*--- assert that the passed rexx expression evaluates to true ------*/
assert:
    interpret 'if ('arg(1)') == 1 then return 1'
    interpret 'call err' quote('assert failed' arg(1)':' arg(2) '==>'),
                        arg(2)
endProcedure assert

/*--- abend with Message after displaying help ----------------------*/
errHelp: procedure expose m.
parse arg msg
    call errSay 'i}'msg
    call help 0
    call err 'i}'msg
endProcedure errHelp

/*--- display the first comment block of the source as help ---------*/
help: procedure expose m.
parse arg doClear
    if doClear \== 0 then
        address tso 'clear'
    parse source . . s3 .
    say right(' help for rexx' s3, 72, '*')
    do lx=1 while pos('/*', sourceLine(lx)) < 1
        if lx > 10 then
            return err('initial commentblock not found for help')
        end
    doInc = 1
    ho = m.err_helpOpt
    do lx=lx+1 to sourceline() while pos('*/', sourceline(lx)) = 0
        li = strip(sourceLine(lx), 't')
        cx = lastPos('{', li)
        if cx > 0 then do
            if length(ho) = 1 then
                doInc = cx = length(li) | pos(ho, li, cx+1) > 0
            li = left(li, cx-1)
            end
        if doInc then
            say li
        end
    say right(' end help for rexx' s3, 72, '*')
    return 4
endProcedure help

/* copy err end   ****************************************************/
/* copy ut begin  ****************************************************/
utIni: procedure expose m.
    if m.ut_ini == 1 then
        return
    m.ut_ini = 1
    m.ut_Num    = '0123456789'
                /* 012345678901234567890123456789 */
    m.ut_lc     = 'abcdefghijklmnopqrstuvwxyz'
    m.ut_uc     = translate(m.ut_lc)
    m.ut_uc25   = left(m.ut_uc, 25)
    m.ut_alfa   = m.ut_lc || m.ut_uc
    m.ut_alfNum = m.ut_alfa || m.ut_Num
    m.ut_alfDot = m.ut_alfNum || '.'
    m.ut_alfId  = m.ut_alfNum'_' /* avoid rexx allowed @ # $ ¬ . | ? */
    m.ut_alfIdN1 = m.ut_Num       /* not as first character */
    m.ut_rxId   = m.ut_AlfNum'@#$?' /* charset puff mit ¬*/
    m.ut_rxDot  = '.'m.ut_rxId
    m.ut_rxN1   = '.0123456789'
    m.ut_space  = '05'x' '         /* with space: space and Tab char */
    m.ut_alfPrint = m.ut_alfNum'+-*/=()¢!{}<> .:,;?|''"%&#@$£\_'
    m.ut_numUc = m.ut_num || m.ut_uc
    m.ut_base64 = m.ut_uc || m.ut_lc || m.ut_Num'+-'
    m.ut_alfLC  = m.ut_lc   /* backward compatibility */
    m.ut_alfUC  = m.ut_uc   /* backward compatibility */
    return
endProcedure utIni
/*--- 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

/*--- strip and returnn first argument not space --------------------*/
firstNS: procedure expose m.
    do ax=1 to arg()
        if arg(ax) <> '' then
            return strip(arg(ax))
        end
    return ''
endProcedure firstNS

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

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

/*--- left without truncation ---------------------------------------*/
lefPad: procedure expose m
parse arg s, len
    if length(strip(s, 't')) >= len then
        return strip(s, 't')
    return left(s, len)
endProcedure lefPad

/*--- right without truncation --------------------------------------*/
rigPad: procedure expose m
parse arg s, len
    if length(strip(s, 'l')) >= len then
        return strip(s, 'l')
    return right(s, len)
endProcedure rigPad

/*--- 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

/*--- translate to lowercase ----------------------------------------*/
ut2lc: procedure expose m.
parse arg s
    return translate(s, m.ut_lc, m.ut_uc)

/*--- verify an id --------------------------------------------------*/
verifId: procedure expose m.
    parse arg src, extra
    if pos(left(src, 1), m.ut_alfIdN1) > 0 then
        return 1
    else
        return verify(src, m.ut_alfId || extra, 'n')

/*--- 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
    do ax = 2 by 2 to arg()
        src = repAl2(src, src, arg(ax), arg(ax+1))
        end
    return src
endProcedure repAll

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

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
/*--- character to decimal '0140'x --> 256+64=320 -------------------*/
utc2d: procedure expose m.
parse arg ch
    cx = length(ch) // 3
    if cx = 0 then
        cx = 3
    res = c2d(left(ch, cx))
    do cx=cx+1 by 3 to length(ch)
        res = res * 16777216 + c2d(substr(ch, cx, 3))
        end
    return res
endProcedure utc2d

utInter: procedure expose m.
    interpret arg(1)
    return
endProcedure utInter
/* copy ut end *******************************************************/
}¢--- A540769.WK.REXX(WK) cre=2016-10-25 mod=2016-10-25-09.23.04 A540769 -------
/* rexx        allocate A540769.wk libraries
               copy to wk.clist, then tso ex wk works everywhere ----*/
parse arg a
if a = '' then
    a = wk
address tso "exec 'A540769.wk.rexx(alib)' '"a"'"
exit
}¢--- A540769.WK.REXX(WKREC) cre=2016-03-15 mod=2016-03-15-14.42.20 A540769 ----
$#@  $*(    remove recovery tables for rcQuery to fix recovery error

recovery error ==> f1 shows which tables

Initialization of backup data set failed, recovery suspended. This error
occur when the edit recovery table (RCEDRT or RCEIRT) can not be initial
or when the backup data set can not be created or written. If this error
happens again, delete the recovery tables and backup data sets.
$*)
call iiIni
rz = iiRz2dsn(sysvar(sysnode))
tbLib = 'TSU.'rz'.P0.'userid()'.PROF'
mbrs = 'RCEDRT RCEIRT'
say 'removing' mbrs 'from' tbLib
call dsnDel tbLib, mbrs
}¢--- A540769.WK.REXX(WSH) cre=2016-07-11 mod=2016-10-28-13.23.59 A540769 ------
/* rexx ***************************************************************
  wsh: walter's rexx shell                                  version 6.2
  interfaces:                                                  27.10.16
      edit macro: for adhoc evaluation or programming
              either block selection: q or qq and b or a
              oder mit Directives ($#...) im Text
      wsh i:  tso interpreter
      wsh s:  sql processor
      batch:  input in dd wsh
      docu:   http://chw20025641/host/db2wiki/pmwiki.php?n=Main.Wsh
      syntax: http://chw20025641/host/db2wiki/pmwiki.php?n=Main.WshSyn
      ==> previous version under wsh4 <==
--- history -----------------------------------------------------------
27.10.16 walter: nur 1 system in rq2, jTalkRdr, mark sqlClosed
*********/ /*** end of help *******************************************
21.10.16 walter: set current packageSet / path ... ohne immediate
 7.10.16 walter: fix redirection Hook mit only < >, fTst
30. 9.16 walter: blkSize fix fuer csmAlloc, csmAppc mit timeout
 8. 9.16 walter: redirection hook
 6. 9.16 walter: dsnCopy supports different recFM and lRecL
         avoid csm errors: mbrList dsn on Sequential,
           lrecl < 272 without blksize on rmtOut
12. 8.16 walter: f recursive %( %, %), fTst B,I,Y,Z / comp table deimp
           if, else, proc etc. erlaub nl,  * Kommentare für % und ^
29. 7.16 log for s_users to dsn.wshLog else to tss.ska.db2.wshlog
         allow dd out sysout, assume reclen 32755 / spell out truncat.
13. 7.16 sqlFTabAdd und fTabAddRCT ersetzt druch ftabAdd
    6.16 neues sql, sqlWsh, wshMain etc., test to end
23.12.15 dsnList, dsnCopy und dsnDel
16. 1.15 f: get/put read/write in/out Object/Strings transparent weiter
17.11.14 f: iirz2p ==> plex Buchstaben
17.06.14 f: %tS %tT und %tN mit MicroSekunden
16.06.14 csmCopy auch für LoadModule usw.
30.05.14 fix sql4obj fuer rcm profex
14.04.14 class vor obj, mit lazy
19.03.14 ii = installation Info
 9.01.14 walter: redesign formatting (fmt eliminiert), csm.div.p0.exec
 3.12.13 walter: db2 interface radikal geputzt
 3.10.13 walter: uCount fuer TSO <-> unitCount fuer Csm
23. 9.13 walter: ws2 syntax
 6. 2.13 w.keller csmCopy aendert mgmtClas für neue lokale Files
23. 1.13 w.keller sqlErrHandler sowie sqlRx und sql
11. 6.12 w.keller sqlUpdComLoop
23. 5.12 w.keller fix sqlStmt: drop accepts -204
31. 3.12 w.keller sql Query interface incl. sql über CSM
10. 2.12 w.keller div catTb* und eLong
 2. 6.11 w.keller sql error with current location and dsnTiar
 2. 5.11 w.keller sqlStmt etc..
16. 3.11 w.keller basic new r '' ==> r m.class_O
28. 2.11 w.keller compFile by exprBlock, fix $@do forever, |
 7. 2.11 w.keller cleanup block / with sqlPush....
 2. 2.11 w.keller simplified syntax, ast for blocks, ? for help
19. 6.10 w.keller open/close stacked mit jUsers, framed eliminiert
19. 5.10 w.keller class wird direkt erzeugt ohne temporary
18. 4.10 w.keller scanUtilInto ==> testCase noch einbauen
17. 4.10 w.keller oMutate in fileTsoList eingebaut
14. 1.10 w.keller batch interface no longer dies on openIfNotYet
                  CSM.RZ1.P0.EXEC korrigiert
**********************************************************************/
/*--- main code wsh -------------------------------------------------*/
    call errReset 'hI'
    numeric digits 12  /* full int precision, but not bigInt | */
    m.myLib  = 'A540769.WK.REXX'
    m.myWsh  = 'WSH'
    m.myVers = 'v62d 27.10.16'
    call wshLog
    parse arg spec
    isEdit = 0
    editDsn = ''
    m.wsh.outLen = 157
    if spec = '' & m.err_ispf then do /* z/OS edit macro */
        isEdit = adrEdit('macro (spec) NOPROCESS', '*') == 0
        if isEdit then do
            call adrEdit '(d) = dataset'
            call adrEdit '(m) = member'
            editDsn = dsnSetMbr(d, m)
            if abbrev(editDsn, 'A540769.WK.REXX(WS') ,
                    & length(dsnGetMbr(editDsn)) <= 4 then do
                isEdit = 0
                if spec = '' then
                    spec = 't'
                end
            end
        end
    spec = strip(spec)
    if spec = '?' then
        return help()
    inp = ''
    out = ''
    call utIni
    if m.err_os == 'TSO' then do
        if isEdit then do
            call pipeIni
            parse value wshEditBegin(wsh) with inp out
            end
        else if sysvar('sysEnv') = 'FORE' then do
            end
        else do
            call pipeIni
            inp = file('dd(wsh)')
            useOut = listDsi('OUT FILE')
            if useOut = 0 then do
                out = file('dd(out)')
                m.wsh.outLen = sysLrecL - 4 * abbrev(sysRecFM, 'V')
                end
            else if (useOut = 16 & sysReason = 2) then do
                end     /* dd out not allocated, use say to sysTsPrt */
            else if (useOut = 16 & sysReason = 3) then do
                out = file('dd(out)')             /* hope for sysout */
                m.wsh.outLen = 32755         /* assume large maxRecL */
                end
            else if \ (useOut = 16 & sysReason = 2) then do
                call err 'listDsi dd out cc='useOut ,
                    || ', sysReason='sysReason 'm2='sysMsgLvl2 ,
                    || ', m1='sysMsgLvl1
                end
            end
        end
    else if m.err_os == 'LINUX' then do
        inp = file('&in')
        out = file('&out')
        end
    else
        call err 'implement wsh for os' m.err_os
    m.wsh.pipeCnt = (out \== '') * 2
    if m.wsh.pipeCnt == 2 then do
        call pipe '+F', out
        call pipe '+F', jText(out, m.wsh.outLen)
        end
    m.wsh.exitCC = 0
    call wshRun wsh, spec, inp
    if isEdit then
        call wshEditEnd wsh
    do m.wsh.pipeCnt
        call pipe '-'
        end
    if m.pipe_ini == 1 & m.pipe.0 \== 2  then
        call err 'wsh end: pipe.0='m.pipe.0
    else if m.err_cleanup <> '\?' | m.tso_ddAll <> '' then
        call err 'wsh end: still err cleanups'
    exit m.wsh.exitCC
/* end of main of wsh */

/*--- log user of wsh, to public ds to allow public usage -----------*/
wshLog: procedure expose m.
parse arg msg, st
    if sysVar(sysNode) = 'RZ0' then
        return
    if abbrev(userid(), 'S') then
        lNm = 'dsn.wshlog'          /* da duerfen S-Pids */
    else
        lNm = 'tss.ska.db2.wshlog'  /* da duerfen alle User */
    f1 = dsnAlloc('dd(log) mod' lNm '::f', , , '*')
    if datatype(f1, 'n') then do
        lN2 = lNm'.R' || ( random() // 19)
        f1 = dsnAlloc('dd(log) old' lN2 '::f', , , '*')
        if datatype(f1, 'n') then do
            say 'could not allocate log' lNm lN2
            return
            end
        end
    parse source . . s3 .
    o.1 = m.myLib'('s3')' word(m.myVers, 1) sysvar(sysnode) ,
            'j='mvsvar('symdef', 'jobname') ,
             'u='userid() date('s') time()
    if msg <> '' then
        o.2 = left(msg, 80)
    ox = 1 + (msg <> '')
    if st <> '' then do sx=1 to m.st.0
        ox = ox+1
        o.ox = left(m.st.sx, 80)
        end
    call writedd log, o., ox
    call tsoClose log
    call tsoFree log
    return
endProcedure wshLog

/*--- hook for out format -------------------------------------------*/
wshHook_outFmt: procedure expose m.
parse arg m, rest
    if m.pipe.0 \== 4 then
        call err 'wshHook_outFmt but pipe.0='m.pipe.0

    call pipe '-'
    if rest = 'e' then
        call pipe '+F', csvV2Frdr(csvExtRdr(m.j.out), m.m.outLen-4)
    else
        call err 'wshHook_outFmt unsupported fmt='rest
    return ''
endProcedure wshHook_outFmt

/*--- i hook: interpret user input: rexx, expr, data or shell -------*/
wshHook_I: procedure expose m.
parse arg m, inp
    mode = '*'
    call wshIni
    do forever
        if pos(left(inp, 1), '/;:*@.-=') > 0 then
            parse var inp mode 2 inp
        if mode == '/' then
            exit 0
        mode = translate(mode, ';', ':')
        if inp <> '' then do
            say 'as' mode 'interpreting' inp
            if mode = ';' then
                interpret inp
            else if mode = '*' then
                interpret 'say' inp
            else do
                call wshIni
                call errReset 'h', 'say "******* intercepting error";',
                    'call errSay ggTxt; return "";'
                call oRun wshHookComp( ,mode, jBuf(inp))
                call errReset 'h'
                end
            end
        say 'enter' mode 'expression, / for end, ; or * for Rexx' ,
                                                 '@ . - =  for wsh'
        parse pull inp
        end
endProcedure wshInter

/*--- find input ramge, destination and set errHandler
       and return input and output files ----------------------------*/
wshEditBegin: procedure expose m.
parse arg m
    pc = adrEdit("process dest range Q", 0 4 8 12 16)
    call adrEdit "(zLa) = lineNum .zl"
    if pc = 16 then
        call err 'bad range must be q'
    rFi = 1
    rLa = zLa
    if pc = 0 | pc = 8 then do
        call adrEdit "(rFi) = lineNum .zFrange"
        call adrEdit "(rLa) = lineNum .zLrange"
    /*  say 'range' rFi '-' rLa */
        end
    dst = ''
    if pc = 0 | pc = 4 then do
        call adrEdit "(dst) = lineNum .zDest"
     /* say 'dest' dst */
        end
    call jReset oMutate(m'.EDITIN', m.class_JBuf)
    b = m'.EDITIN.BUF'
    bx = 0
    do lx=rFi to rLa
        call adrEdit "(li) = line" lx
        if abbrev(li, '$#end') then do lx=lx+1 to rLa ,
                  until abbrev(li, '$#out')
            end
        if abbrev(li, '$#out') then do
            if dst = '' then
                dst = lx - 1
            leave
            end
        bx = bx + 1
        m.b.bx = li
        end
    m.b.0 = bx
    m.m.editRFirst = rFi
    m.m.editREnd   = rFi + bx
    m.m.editDst    = dst
    if dst == '' then do
        m.m.editOut = ''
        end
    else do
        call adrEdit '(recl) = LRECL'
        m.m.outLen = recL
        m.m.editOut = jOpen(jReset(oMutate(m'.EDITOUTF',
                          , m.class_JBuf)), '>')
        call jWrite m.m.editOut, left('$#out', 50) date('s') time()
        end
    call errReset 'hso', "return wshEditErrH('"m"', ggTxt)"
    return m'.EDITIN'  m.m.editOut
endProcedure wshEditBegin

/*--- copy output to editArea ---------------------------------------*/
wshEditEnd: procedure expose m.
parse arg m
    call errReset 'h'
    if m.m.editOut == '' then
        return 0
    call jClose m.m.editOut
    call wshEditInsertSt wshEditInsertCmd(m.m.editDst, 'wshDs'),
                         , , m.m.editOut'.BUF'
    call wshEditLocate m.m.editDst, 1
    return 1
endProcedure wshEditEnd

/*--- scroll such that given line is nicely visible -----------------*/
wshEditLocate: procedure
parse arg ln, top
    call adrEdit 'down max'
    call adrEdit '(fi, la) = display_lines'
    if top then
        lx = ln - 7
    else
        lx = ln - la + fi + 7
    if fi <> 1 & lx < fi then
        call adrEdit 'locate' max(1, lx)
    return
endProcedure wshEditLocate

/*--- error handle for wsh in edit mode
      mark location of wsh syntax error -----------------------------*/
wshEditErrH: procedure expose m.
parse arg m, ggTxt
    call errReset 'hso'
    ee = errSay(ggTxt'\nin wsh phase' m.m.info)
    isScan = 0
    if wordPos("pos", m.ee.3) > 0 ,
        & pos(" in line ", m.ee.3) > 0 then do
        parse var m.ee.3 "pos " pos .      " in line " lin":"
        if pos = '' then do
            parse var m.ee.3 " line " lin":"
            pos = 0
            end
        isScan = lin \== ''
        end
    call wshEditEnd m
    if m.m.Info=='compile' & isScan then do
        lx = m.m.editRFirst + lin - 1
        cmd = wshEditInsertCmd(lx, 'wshEr')
        if pos \= '' then
            call wshEditInsert cmd, 'msgline', right('*',pos)
        call wshEditInsertSt cmd, 'msgline', ee
        call wshEditLocate lx, 0
        end
    call errCleanup
    exit 8
    exit
endSubroutine wshEditErrH

/*--- return editor insert cmd for after line afX -------------------*/
wshEditInsertCmd: procedure
parse arg afX, lb
    call adrEdit "(zLa) = lineNum .zl"
    if afX >= 1 & afX < zLa then do
        call adrEdit 'label' (afX+1) '= .'lb
        return 'line_before .'lb '='
        end
    else if afX = zLa then
        return 'line_after .zl ='
    else
        call err 'dst='afX 'but .zl='zLa
endProcedure wshEditInsertCmd

/*--- insert lines, format msgLines ---------------------------------*/
wshEditInsert: procedure
parse arg cmd, type
    do ax=3 to arg()
        li = strip(arg(ax), 't')
        if translate(type) = 'MSGLINE' then do while length(li) > 72
            sx = lastPos(' ', li, 72)
            if sx < 10 then
                sx = 72
            one = left(li, sx)
            li = '    'strip(substr(li, sx))
            call adrEdit cmd type "(one)"
            end
        call adrEdit cmd type "(li)", 0 4
        end
    return
endProcedure wshEditInsert

/*--- insert all lines of stem st -----------------------------------*/
wshEditInsertSt: procedure expose m.
parse arg cmd, type, st
    if cmd == '' then do
        do ox=1 to m.st.0
            say m.st.ox
            end
        return ''
        end
    do ax=1 to m.st.0
        call wshEditInsert cmd, type, m.st.ax
        end
    return
endProcedure wshEditInsertSt


/*** end wsh, begin all copies ***************************************/
/* copy wshCopy begin ************************************************/
wshIni: procedure expose m.
    call compIni
    call sqlIni
    call fTabIni
    call csmIni
    return
endProcedure wshIni

/*--- call hooks and/or compile wsh
      finally execute any generated code ----------------------------*/
wshRun: procedure expose m.
parse arg m, spec, inp
    m.m.info = 'compile'
    r = wshHookComp(m, spec, inp)
    m.m.info = 'run'
    if r \== '' then
        call oRun r
    return
endProcedure wshRun

/*--- call hooks, handle $# clauses, compile wsh
      return generated code as ORunner or ''-------------------------*/
wshHookComp: procedure expose m.
parse arg m, spec, inp
    if m == '' then do
        if symbol('m.wsh_new') \== 'VAR' then
            m.wsh_new = 1
        else
            m.wsh_new = m.wsh_new + 1
        m = 'wsh_new'm.wsh_new
        end
    m.m.in   = inp
    m.m.comp = ''
    m.m.kind = '@'
    m.m.out  = ''
    m.m.wshEnd = 0
    run = ''
    rest = strip(spec)
    if abbrev(rest, '$#') then
        rest = strip(substr(rest, 3))
    workDone = 0
    do until m.m.comp \== '' | (workDone & rest = '')
        if pos(left(rest, 1), '<>') > 0 then do
            parse var rest s2 r2
            end
        else do
            workDone = 1
            parse var rest s2 '$#' r2
            end
        run = run wshHook(m, strip(s2), rest)
        rest = strip(r2)
        end
    if m.m.comp \== '' then do
        c = m.m.comp
        s = m.c.scan
        do while \ m.m.wshEnd
             if \ scanLit(s, '$#') then
                     leave
             call scanChar s
             sp2 = m.s.tok
             run = run wshHook(m, sp2, sp2)
             end
        call compEnd c, left(m.m.kind, \ m.m.wshEnd)
        end
    run = space(run, 1)
    if words(run) <= 1 then
        return run
    else
        return oRunner('call oRun' repAll(run, ' ', '; call oRun '))
endProcedure wshHookComp

/*--- compile wsh until eof or unknown syntax -----------------------*/
wshHook: procedure expose m.
parse arg m, spec, specAll
    parse var spec sp1 spR
    if pos(left(sp1, 1), '<>') > 0 then
        return wshHookRedir(m, sp1 spR)
    if verifId(sp1) > 0 | sp1 == '' then
        return wshCompile(m, specAll)
    if wordPos(sp1, 'out end version') <= 0 then do
        cd = "return wshHook_"sp1"(m, '"strip(spR)"')"
     /* say 'interpreting hook' cd */
        interpret cd
        end
    c = m.m.comp
    s = m.c.scan
    if c == '' then
        call err 'wshHook before compiler created:' spec
    else if sp1 == 'out' then do
        m.m.out = scanPos(s)
        m.m.wshEnd = 1
        end
    else if sp1 == 'end' then
        call scanNlUntil s, '$#out'
    else if m.s.tok == 'version' then
        call scanErr s, 'implement version'
    return ''
endProcedure wshHook

/*--- initialize compiler if necessary and compile one unit ---------*/
wshCompile: procedure expose m.
parse arg m, spec
    spec = strip(spec, 'l')
    if m.m.comp == '' then
        call wshIni
    if pos(left(spec, 1), m.comp_chKind'*') > 0 then
        parse var spec m.m.kind 2 spec
    if m.m.comp == '' then do
        c = comp(m.m.in)
        m.m.comp = c
        call compBegin c, spec
        end
    else do
        c = m.m.comp
        call scanBack m.c.scan, spec
        end
    return compile(c, m.m.kind)
endProcedure wshCompile

/*--- redirection hook ----------------------------------------------*/
wshHookRedir: procedure expose m.
parse upper arg m,  op 2 dsn
    call pipeIni
    f = ''
    if op == '<' then
        call pipe '+f', , file(dsn)
    else if op \== '>' then
        call err 'bad op' op 'in wshHookRedir' op || dsn
    else do
        if pos('>', dsn) > 0 then
            parse var dsn f '>' dsn
        else if verify(dsn, '.~/', 'm') > 0 then
            nop
        else if abbrev(dsn, 'E') | abbrev(dsn, 'VV') ,
                                  | abbrev(dsn, 'VF') then
            parse var dsn f 2 dsn
        else
            f = 'E'
        dsn = strip(dsn)
        if dsn \== '' & verify(dsn, '.~/:', 'm') == 0 then
            dsn = '::'dsn
        if f <> '' then
             call pipe '+F', fEdit(dsn, f)
        else
             call pipe '+F', file(dsn)
        end
    m.m.pipeCnt = m.m.pipeCnt + 1
    return ''
endProcedure wshHookRedir
/* copy wshCopy end   ************************************************/
/* copy time begin ****************************************************
 timestamp format yz34-56-78-hi.mn.st.abcdef
 11.12.14 wk: added lrsn2uniq
 11.05.13 wk: numeric digits transparent: in jeder Procedure drin
              time2jul, tst externalisiert
**********************************************************************/
/*--- timestamp to julian -------------------------------------------*/
time2jul: procedure expose m.
parse arg yyyy '-' mm '-' dd '-'
    if yyyy < 1100 then
        yyyy = 11 || right(yyyy, 2, 0)
        /* date function cannot convert to julian, only from julian
           use b (days since start of time epoch) instead     */
    return right(yyyy, 2) ,
         || right(date('b', yyyy || mm || dd, 's') ,
                - date('b', yyyy-1 || '1231', 's') , 3, 0)
endProcedure time2jul

/*--- current timestamp ---------------------------------------------*/
timestampNow: procedure expose m.
    parse value date('s') time('l') with y 5 m 7 d t
    return y'-'m'-'d'-'translate(t, '.', ':')

/*--- timestamp expand: expand to timestamp of 26 chars -------------*/
timestampExp: procedure expose m.
parse arg tst .
    if length(tst) < m.timeStamp_Len then
        return overlay(tst, m.timeStamp_01)
    else
        return left(tst, timeStamp_Len)
endProcedure tiemstampExp

/*--- timestamp check return '' if correct or error text ------------*/
timestampcheck: procedure expose m.
parse arg tst
    if length(tst) < m.timestamp_d0Len | \ abbrev(m.timestamp_11,
             , translate(tst, '111111111', '023456789')) then
        return 'bad timestamp' tst
    parse arg yyyy '-' mo '-' dd '-' hh '.' mm '.' ss '.' u
    if mo < 1 | mo > 12 then
        return 'bad month in timestamp' tst
    if dd < 1 | dd > 3 || substr('101010110101', mo, 1) then
        return 'bad day in timestamp' tst
    if mo = 2 then
        if dd > date('d', yyyy'0301', 's') - 32 then
            return 'bad day in timestamp' tst
    if hh > 23 & \ abbrev(24 00 00 000000, hh mm ss u) then
        return 'bad hour in timestamp' tst
    if mm > 59 then
        return 'bad minute in timestamp' tst
    if ss > 59 then
        return 'bad second in timestamp' tst
    return ''
endProcedure timestampCheck

/*--- timestamp to days since 1.1.0001 ------------------------------*/
timestamp2days: procedure expose m.
parse arg yyyy '-' mo '-' dd '-' hh '.' mm '.' ss
numeric digits 20
    return date('b', yyyy || mo || dd, 's') ,
                + (((hh * 60) + mm) * 60 + ss) / 86400

/*--- days since 1.1.0001 (with time+microsecs) to timestamp --------*/
timeDays2tst: procedure expose m.
parse arg d
numeric digits 20
    r = min(format(d // 1 * 86400, 5, 6), 86399.999999)
    s = trunc(r)
    t = date('s', trunc(d), 'b')
    return left(t, 4)'-'substr(t, 5, 2)'-'right(t, 2) ,
             || '-' || right((s % 3600), 2, 0)       ,
             || '.' || right((s // 3600 % 60), 2, 0) ,
             || '.' || right((s // 60), 2, 0)        ,
             || substr(r, 6)
endProcedure timeDays2tst

/*--- timestamp diff in days ----------------------------------------*/
timestampDiff: procedure expose m.
parse arg t1, t2
numeric digits 20
    return timestamp2days(t1) - timestamp2Days(t2)

/*--- convert 2 digit year to 4 digit -------------------------------*/
timeYear24: procedure expose m.
parse arg s
    y = left(date('S'), 4)
    s4 = left(y, 2)right(s, 2, 0)
    if s4 > y + 30 then
        return (left(y, 2) - 1)substr(s4, 3)
    else if s4 > y - 70 then
        return s4
    else
        return (left(y, 2) + 1)substr(s4, 3)
endProcedure timeYear24

/*--- convert 2 or 4 digit year Y (A=0...Y=24) ----------------------*/
timeYear2Y: procedure expose m.
parse arg y
    return substr(m.ut_uc25, (y // 25) + 1, 1)
/*--- convert 1 char year Y (A=0...y=24) to year --------------------*/
timeY2Year: procedure expose m.
parse arg i
    j = pos(i, m.ut_uc25) - 1
    if j < 0 then
        call err 'timeY2Year bad input' i
    y = left(date('S'), 4)
    r = y - y // 25 + j
    if r > y + 4 then
        return r - 25
    else if r > y - 21 then
        return r
    else
        return r + 25
endProcedure timeY2Year
/*--- convert 2 or 4 digit year Y (A=10...T=29) ----------------------*/
timeYear2Z: procedure expose m.
parse arg y
    return substr('ABCDEFGHIJKLMNOPQRST', ((y+10) // 20) + 1, 1)
/*--- convert 1 char year Z (A=10...T=29) to year --------------------*/
timeZ2Year: procedure expose m.
parse arg i
    j = pos(i, 'ABCDEFGHIJKLMNOPQRST') - 1
    if j < 0 then
        call err 'timeZ2Year bad input' i
    y = left(date('S'), 4)
    r = y - y // 20 + j
    if r > y + 4 then
        return r - 20
    else if r > y - 16 then
        return r
    else
        return r + 20
endProcedure timeZ2Year

/*--- convert numeric month to M (Jan=B=1, dec=12=M) ----------------*/
timeMonth2M: procedure expose m.
parse arg m
    return substr('BCDEFGHIJKLM', m, 1)

/*--- convert M to numeric Month (Jan=B=1, dec=12=M) ----------------*/
timeM2Month: procedure expose m.
parse arg m
    p = pos(m, 'BCDEFGHIJKLM')
    if p= 0 then
        call err 'bad M month' m
    return right(p, 2, 0)

/*--- read timeZoneOffset and leapSeconds registers
        and set variables for uniq ----------------------------------*/
timeIni: procedure expose m.
parse arg debug
    if m.time_ini == 1 then
        return
    m.time_ini = 1
    numeric digits 25
    /* offsets documented in z/OS Data Areas  Vol.1 */
    cvtOH      = '00000010'          /* cvt control block Address */
    cvtext2O   = x2d('00000560') /* offset  to extension 2    */
    cvtldtoO   = x2d('00000038') /* offset to timezone    */
    cvtlsoO    = x2d('00000050') /* offset to leapSeconds */

    /* CVT CB        address               + extention2   */
    cvtExt2A       = C2D(STORAGE(cvtOH,4)) + cvtext2O
    /* cvtLdto timeZone              address +offset      */
    m.time_Zone    = c2d(STORAGE(d2x(cvtext2A+cvtldtoO, 8), 8))
    m.time_StckUnit = 1e-6 / 256 / 16
    /* cvtLso LeapSecs               address +offset      */
    m.time_Leap    = c2d(STORAGE(d2x(cvtext2A+cvtlsoO, 8), 8))
    m.time_LZT_TAI10_us16 = (m.time_zone - m.time_leap) % 256
    m.time_UQDigits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345678'
                         /* find lrsn of day 0, 0 out last 6 bits  */
    m.time_UQZero = x2d(left(timeTAI102Lrsn( ,
                 '2004-12-31-00.00.22.000000'), 14)) % 64 * 64
    m.timeStamp_01 = '0001-01-01-00.00.00.000000'
    m.timeStamp_11 = '1111-11-11-11.11.11.111111'
    m.timeStamp_99 = '9999-12-31-23.59.59.999999'
    m.timeStamp_len = length(m.timestamp_11)
    m.timeStamp_d0Llen = m.timestamp_len - 7
    return
endSubroutine timeIni

/*--- TAI10 timestamp yyyy-mm.... -> stckE value char(16)
         BLSUETID is described in z/OS MVS IPCS Customization -------*/
timeTAI102stckE: procedure expose m.
                        /* timestamp must include microSeconds |||*/
    parse arg year'-'mo'-'da'-'hh'.'mm'.'secs
    tDate = mo'/'da'/'year hh':'mm'.'secs
    ACC=left('', 16, '00'x)
    ADDRESS LINKPGM "BLSUETID TDATE ACC"
    RETURN acc
endProcedure timeTAI102stckE

timeTAI102lrsn: procedure expose m.
parse arg tst
    return c2x(left(timeTAI102StckE(tst), 10))

timeLZT2stckE: procedure expose m.
parse arg tst
    numeric digits 23
    s =timeTAI102StckE(tst)
    return d2c(c2d(left(s, 8)) - m.time_lzt_TAI10_us16,8) ||substr(s,9)
endProcedure timeLZT2stckE

timeLZT2lrsn: procedure expose m.
parse arg tst
    return c2x(left(timeLZT2StckE(tst), 10))

/*--- expand lrsn to 20 hexDigits -------------------------------*/
timeLrsnExp: procedure expose m.
parse arg s
    return left(copies('00', length(s) <= 12 & \abbrev(s, 0))s, 20,0)

/*--- expand stcK(e) to 16 Byte ---------------------------------*/
timeStckExp: procedure expose m.
parse arg s
    return left(copies('00'x, length(s) <= 8 & s >> '09'x)s, 16,'00'x)

/*--- conversion from StckE Clock Value to TAI10 Timestamp
        BLSUETOD is described in z/OS MVS IPCS Customization --------*/
timeStckE2TAI10: PROCEDURE expose m.
parse arg stck      /* must be 16 characters ||||| */
  TDATE = left('' , 26)
  ADDRESS LINKPGM "BLSUETOD stck TDATE"
  /* return format   : mo/dd/yyyy hh:mm:ss.uuuuuu */
  /* Timestamp format: yyyy-mm-dd-hh.mm.ss.uuuuuu */
  parse var TDATE mo '/' dd '/' yyyy hh ':' mm ':' secs
RETURN yyyy'-'mo'-'dd'-'hh'.'mm'.'secs
endProcedure timeStckE2TAI10

/*--- conversion from Lrsn Clock Value to TAI10 Timestamp ----------*/
timeLrsn2TAI10:
    return timeStckE2TAI10(x2c(arg(1))'000000000000'x)

/*--- conversion from Lrsn Clock Value to LZT Local Zurich Time -----*/
timeStckE2LZT: procedure expose m.
parse arg s
    numeric digits 23
    return timeStckE2TAI10(d2c(c2d(left(s, 8)) ,
                + m.time_LZT_TAI10_us16, 8) || substr(s, 9))
endProcedure timeStckE2LZT

/*--- conversion from Lrsn Clock Value to LZT Local Zurich Time -----*/
timeLrsn2LZT: procedure expose m.
parse arg lrsn
    return timeStckE2LZT(x2c(lrsn) || '000000000000'x)

/* convert a lrsn to the uniq variable *******************************/
timeLrsn2uniq: procedure expose m.
parse arg lrsn
    /* unique are bits 0:41 of the TodClock value
              minus 31.12.2004 represented
              by base 35 by 'ABC...YZ01..8'
    */
    lrsn = left(timeLrsnExp(lrsn), 14)
    numeric digits 20
    diff = x2d(lrsn) - m.time_UQZero
    if diff < 0 then
        return'< 2005'
    return right(i2q(diff % 64, m.time_UQDigits), 8, 'A')
endProcedure timeLrsn2Uniq

/* convert a uniq variable to lrsn ***********************************/
timeUniq2lrsn: procedure expose m.
parse arg uniq
    numeric digits 20
    u1 = q2i(left(uniq, 8, 'A'), m.time_UQDigits) * 64
    lrsn = '00'right(d2x(u1 + m.time_UQZero), 12, 0)'000000'
    return lrsn
endProcedure uniq2lrsn

/*--- translate a number in q-system to decimal
       arg digits givs the digits corresponding to 012.. in the q sysem
       q = length(digits) -------------------------------------------*/
q2i: procedure expose m.
parse arg v, digits
    b = length(digits)
    i = 0
    do x = 1 to length(v)
        q = substr(v, x, 1)
        r = pos(q, digits)
        if r < 1 then
            call err 'bad digit' q 'in' v 'valid digits' digits
        i = i * b + r - 1
        end
    return i
endProcedure q2i
/*--- translate a decimal number to q-system - inverse of q2i -------*/
i2q: procedure expose m.
parse arg i, digits
    if i = 0 then
        return left(digits, 1)
    b = length(digits)
    v = ''
    do while i > 0
        v = substr(digits, 1 + (i // b), 1) || v
        i = i % b
        end
    return v
endProcedure i2q
/* copy time end ----------------------------------------------------*/
/* copy sort begin ***************************************************/
sortComparator: procedure expose m.
parse arg cmp, le, ri
    if cmp == '' then
        cmp = '<<='
    if length(cmp) < 6 then
        m.sort_comparator = 'cmp =' le cmp ri
    else if pos(';', cmp) < 1 then
        m.sort_comparator = "aLe =" le"; aRi =" ri"; cmp =" cmp
    else
        m.sort_comparator = "aLe =" le "; aRi =" ri";" cmp
    return
endProcedure sort

sortWords: procedure expose m.
parse arg wrds, cmp
    if words(wrds) <= 1 then
        return strip(wrds)
    m.sort_ii.0 = words(wrds)
    do sx=1 to m.sort_ii.0
        m.sort_ii.sx = word(wrds, sx)
        end
    call sort sort_ii, sort_oo, cmp
    r = m.sort_oo.1
    do sx=2 to m.sort_oo.0
        r = r m.sort_oo.sx
        end
    return r
endProcedure sortWords

sortWordsQ: procedure expose m.
parse arg wrds, cmp
    call sortComparator cmp, 'word(le, lx)', 'word(ri, rx)'
    return strip(sortWord1(wrds))
endProcedure sortWordsQ

sortWord1: procedure expose m.
parse arg wrds
    if words(wrds) <= 1 then
        return wrds
    h = words(wrds) % 2
    le = sortWord1(subWord(wrds, 1, h))
    ri = sortWord1(subWord(wrds, h+1))
    lx = 1
    rx = 1
    res = ''
    do forever
        interpret m.sort_comparator
        if cmp then do
            res = res word(le, lx)
            if lx >= words(le) then
                return res subword(ri, rx)
            lx = lx + 1
            end
        else do
            res = res word(ri, rx)
            if rx >= words(ri) then
                return res subword(le, lx)
            rx = rx + 1
            end
        end
endProcedure sortWord1

sort: procedure expose m.
parse arg i, o, cmp
    call sortComparator cmp, 'm.l.l0', 'm.r.r0'
    call sort1 i, 1, m.i.0, o, 1, sort_work, 1
    m.o.0 = m.i.0
    return
endProcedure sort

sort1: procedure expose m.
parse arg i, i0, le, o, o0, w, w0
    if le <= 3 then do
        if le = 3 then do
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, w, w0
            call sortMerge i, i0+2, i0+3, w, w0, w0+2, o, o0
            end
        else if le = 2 then
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, o, o0
        else if le = 1 then
            m.o.o0 = m.i.i0
        return
        end
    h = (le + 1) % 2
    call sort1 i, i0,   h,    o, o0+le-h, w, w0
    call sort1 i, i0+h, le-h, w, w0,      o, o0
    call sortMerge o, o0+le-h, o0+le, w, w0, w0+le-h, o, o0
    return
endProcedure sort1

sortMerge: procedure expose m.
parse arg l, l0, le, r, r0, re, o, o0
    do while l0 < le & r0 < re
        interpret m.sort_comparator
        if cmp then do
            m.o.o0 = m.l.l0
            l0 = l0 + 1
            end
        else do
            m.o.o0 = m.r.r0
            r0 = r0 + 1
            end
        o0 = o0 + 1
        end
    do while l0 < le
        m.o.o0 = m.l.l0
        l0 = l0 + 1
        o0 = o0 + 1
        end
    do while r0 < re
        m.o.o0 = m.r.r0
        r0 = r0 + 1
        o0 = o0 + 1
        end
    return
endProcedure sortMerge
/* copy sort end   ***************************************************/
/* copy match begin **************************************************/
/*--- 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
    if symbol('m.match_m.mask') == 'VAR' then
        interpret m.match_m.mask
    else
        interpret matchGen('MATCH_M.'mask, mask, 'm')
endProcedure match

matchGG: procedure expose m.
parse arg wert, cd, vars
    interpret cd
endProcedure matchGG

matchVars: procedure expose m.
parse arg wert, mask, vars
    if symbol('m.match_v.mask') == 'VAR' then
        interpret m.match_v.mask
    else
        interpret matchGen('MATCH_V.'mask, mask, 'v')
endProcedure match

matchRep: procedure expose m.
parse arg wert, mask, mOut
    vars = 'MATCH_VV'
    mm = mask'\>'mOut
    if symbol('m.match_r.mm') == 'VAR' then
        interpret m.match_r.mm
    else
        interpret matchGen('MATCH_R.'mm, mask, 'r', mOut)
endProcedure matchRep

matchGen: procedure expose m.
parse arg m, mask, opt, mOut
    a = matchScan(match_sM, mask)
    if symbol('m.match_g') \== 'VAR' then
        m.match_g = 0
    if opt \== 'r' then do
        r = matchgenMat(a, opt, 1, m.a.0, 0)
        end
    else do
        m.match_g = m.match_g + 1
        sub = 'MATCH_G'm.match_g
        m.sub = matchgenMat(a, 'v', 1, m.a.0, 0)
        o = matchScan(match_sO, mOut)
        r = matchGenRep(o, m.a.wildC)
        r = 'if matchGG(wert, m.'sub', vars) then return' r';' ,
            'else return "";'
        end
    m.m = r
    return r
endProcedure matchGen

matchScan: procedure expose m.
parse arg a, mask, opt
    s = match_scan
    call scanSrc s, mask
    ax = 0
    vx = 0
    m.a.wildC = ''
    do forever
        if scanUntil(s, '*?&\') then do
            if m.a.ax == 'c' then do
                m.a.ax.val = m.a.ax.val || m.s.tok
                end
            else do
                ax = ax + 1
                m.a.ax = 'c'
                m.a.ax.val = m.s.tok
                end
            end
        else if scanChar(s, 1) then do
            if pos(m.s.tok, '*?') > 0 then do
                ax = ax + 1
                vx = vx + 1
                m.a.ax = m.s.tok
                m.a.ax.ref = vx
                m.a.wildC = m.a.wildC || m.s.tok
                end
            else if m.s.tok == '\' then do
                call scanChar s, 1
                if pos(m.s.tok, '\*?&') < 1 then
                    return scanErr(s, 'bad char after \')
                if abbrev(m.a.ax, 'c') then
                    m.a.ax.val = m.a.ax.val || m.s.tok
                else do
                    ax = ax + 1
                    m.a.ax = 'c'
                    m.a.ax.val = m.s.tok
                    end
                end
            else if m.s.tok == '&' then do
                if opt \== 'r' then
                    call scanErr s, '& in input'
                if \ scanChar(s,1) | pos(m.s.tok, 'ms123456789')<1 then
                    call scanErr s, 'bad & name' m.s.tok
                ax = ax + 1
                m.a.ax = '&'
                m.a.ax.ref = m.s.tok
                end
            else
                call scanErr s, 'bad char 1 after until'
            end
        else
            leave
        end
    m.a.0 = ax
    if vx \== length(m.a.wildC) then
        call scanErr 'vars' m.a.wildC 'mismatches' vx
    return a
endProcedure matchScan

matchGenMat: procedure expose m.
parse arg a, opt, fx, tx, minLL
    ml = 0
    if fx == 1 then do
        do ax=1 to m.a.0
            if m.a.ax == '?' then
               ml = ml + 1
            else if m.a.ax == 'c' then
               ml = ml + length(m.a.ax.val)
            m.a.minLen.ax = ml
            end
        end
    r = ''
    ret1 = ''
    ret1After = ''
    lO = 0
    do fy=fx to tx
        if m.a.fy == 'c' then do
            r = r 'if substr(wert,' (1+lO)
            if fy < m.a.0 then
                r = r',' length(m.a.fy.val)
            r = r') \==' quote(m.a.fy.val, "'") 'then return 0;'
            lO = lO + length(m.a.fy.val)
            end
        else if m.a.fy == '?' then do
            lO = lO + 1
            if opt == 'v' then
                ret1 = ret1 'm.vars.'m.a.fy.ref ,
                        '= substr(wert,' lO', 1);'
            end
        else if m.a.fy == '*' then
            leave
        else
            call err 'bad match ast' a'.'fy m.a.fy
        end
    rO = 0
    do ty=tx by -1 to fy
        if m.a.ty == 'c' then do
            rO = rO + length(m.a.ty.val)
            r = r 'if substr(wert, length(wert) -' (rO - 1)',' ,
                  length(m.a.ty.val)')' ,
                  '\==' quote(m.a.ty.val, "'") 'then return 0;'
            end
        else if m.a.ty == '?' then do
            rO = rO + 1
            if opt == 'v' then
                ret1 = ret1 'm.vars.'m.a.fy.ref ,
                        '= substr(wert, length(wert) -' (rO-1)', 1);'
            end
        else if m.a.ty ==  '*' then
            leave
        else
            call err 'bad match ast' a'.'fy m.a.fy
        end
    if fy > ty then do /* every thing is handled with fix len */
        if fx = tx & abbrev(m.a.fx, 'c') then
            r = 'if wert \==' quote(m.a.fx.val, "'") ,
                               'then return 0;'
        else
            r = 'if length(wert) \==' (lO + rO) 'then return 0;' r
        end
    else do
        myMiLe = m.a.minLen.ty
        if fy > 1 then do
            fq = fy -1
            myMiLe = myMiLe - m.a.minLen.fq
            end
        if minLL < myMiLe then
            r = 'if length(wert) <' myMiLe 'then return 0;' r
        if fy = ty & m.a.fy == '*' then     /* single * */
            ret1  = ret1 'm.vars.'m.a.fy.ref ,
                 '= substr(wert,' (1+lO)', length(wert) -' (lO+rO)');'
        else if fy < ty & abbrev(m.a.fy, '*') ,
                        & abbrev(m.a.ty, '*') then do
                                /* several variable length parts */
            suMiLe = m.a.minLen.ty - m.a.minLen.fy
            m.match_g = m.match_g + 1
            sub = 'MATCH_G'm.match_g
            m.sub = matchGenMat(a, opt, fy+1, ty, suMiLe)
            if rO = 0 then
                subV = 'substr(wert, lx)'
            else do
                r = r 'wSub = left(wert, length(wert) -' rO');'
                subV = 'substr(wSub, lx)'
                end
            r = r 'do lx = length(wert) -' (suMiLe+rO-1) ,
                       'by -1 to' (lO+1)';' ,
                       'if \ matchGG('subV', m.'sub', vars) then' ,
                            'iterate;'
            ret1  = ret1 'm.vars.'m.a.fy.ref '= substr(wert,' (lO+1) ,
                     ||  ', lx -' (lO+1)');'
            ret1After = 'end; return 0;'
            end
        else
            call err 'matchGenMat bad case' a'.'fy m.a.fy a'.'ty m.a.ty
        end
    if opt == 'v' & fx == 1 then do
        if r <> '' then
           r = 'm.vars.0 = -9;' r
        ret1 = ret1 'm.vars.0 =' length(m.a.wildC)';'
        end
    r = r ret1 'return 1;' ret1After
    return r
endProcedure matchGenMat

matchGenRep: procedure expose m.
parse arg o, wildC
    xQ = 0
    xS = 0
    do ox=1 to m.o.0
        if m.o.ox == '?' then do
             xQ = pos('?', wildC, xQ+1)
             if xQ < 1 then
                 call err 'unmatchted ?' ox
             m.o.ox.re2 = xQ
             end
        else if m.o.ox == '*' then do
             xS = pos('*', wildC, xS+1)
             if xS < 1 then
                 call err 'unmatchted *' ox
             m.o.ox.re2 = xS
             end
        else if m.o.ox == '&' & m.o.ox.ref >> '0' then do
            if m.o.ox.ref > length(wildC) then
                 call err '&'m.o.ox.ref 'but wildcards' wildC
            xQ = m.o.ox.ref
            xS = xQ
            m.o.ox.re2 = xQ
            end
        end
    r = ''
    do ox=1 to m.o.0
        if abbrev(m.o.ox, 'c') then
            r = r '||' quote(m.o.ox.val, "'")
        else if m.o.ox == '&' & m.o.ox.re2 == 's' then
            r = r '|| wert'
        else if m.o.ox == '&' & m.o.ox.re2 == 'm' then
            r = r '||' quote(mask, "'")
        else if pos(m.o.ox, '*?&') > 0 then
            r = r '|| m.vars.'m.o.ox.re2
        end
    if r=='' then
        return "''"
    else
        return substr(r, 5)
endProcedure matchGenRep

/* copy match end ****************************************************/
/* copy comp begin ****************************************************
    the shell compiler
         syntax and semantics of the shell language see javaDoc
**********************************************************************/
/***** initialisation ************************************************/
/*--- module initialisation -----------------------------------------*/
compIni: procedure expose m.
    if m.compIni = 1 then
        return
    m.compIni = 1
    call pipeIni
    call scanReadIni
    cc = classNew('n Compiler u')
    call mNewArea 'COMP.AST', '='

    m.comp_chOp   = '.-<@|?%^'
    m.comp_chKind = '.-=#@:%^'
    m.comp_chKindDesc = 'Obj Str Skel Text Exe Wsh Call Fun'
    m.comp_chKiNO = '=:#'
    m.comp_chKiNBOE = '=<#:' /* nonBLock only expression not Primary*/
    m.comp_chDol = '$'
    m.comp_chSpa = m.ut_space
    call mPut 'COMP_EXTYPE.b', m.comp_chDol'{}'       /* braces */
    call mPut 'COMP_EXTYPE.d', m.comp_chDol            /* data */
    call mPut 'COMP_EXTYPE.s', m.comp_chDol            /* strip */
    call mPut 'COMP_EXTYPE.w', m.comp_chDol||m.comp_chSpa /* word */

    m.comp_idChars  = m.ut_alfNum'@_'
    m.comp_wCatC    = 'compile'
    m.comp_wCatS    = 'do withNew with for forWith ct proc arg if else'
    m.comp_astOps   = m.comp_chOp'!)&'
    m.comp_astOut   = '.-@<^' /*ast kind for call out */
    m.comp_astStats = ''
    return
endProcedure compIni

compKindDesc: procedure expose m.
parse arg ki
    kx = pos(ki, m.comp_chKind)
    if length(ki) == 1 & kx > > 0 then
        return "kind"word(m.comp_chKindDesc, kx)"'"ki"'"
    else
        return "badKind'"ki"'"
endProcedure compKindDesc

/*--- constructor of Compiler ---------------------------------------*/
comp: procedure expose m.
parse arg src
    nn = oNew('Compiler')
    m.nn.cmpRdr = in2File(src)
    return nn
endProcedure comp

/*--- compile one unit of the source with kind ki
           and return oRunner with the code -------------------------*/
compile: procedure expose m.
parse arg m, ki, hook
    s = m.m.scan
    m.m.comp_assVars = 0
    call compSpComment m
    a = ''
    if m.m.end \== '' then
        call scanNlUntil s, '$#out'
    else if ki == '*' then
        call scanNlUntil s, '$#'
    else
        a = compUnit(m, ki, '$#')
    if compIsEmpty(m, a, 0) then
        return ''
    cd = compAst2Rx(m, '!', a)
    if 0 then
        say cd
    return oRunner(cd)
endProcedure compile

compBegin: procedure expose m.
parse arg m, spec
    m.m.scan = m'.scan'
    m.m.out = ''
    m.m.end = ''
    s = m.m.scan
    if m.m.cmpRdr == '' then
        call scanOpt scanSrc(s, spec), , '0123456789'
    else
        call scanReadOpen scanReadReset(scanOpt(s, , '0123456789'),
                          , m.m.cmpRdr), spec' '
    return m
endProcedure compBegin

compEnd: procedure expose m.
parse arg m, erKi
    s = m.m.scan
    if erKi \== '' then
        if \ scanEnd(s) then
            return scanErr(s, 'wsh' compKindDesc(erKi),
                   "expected: compile stopped before end of input")
    call scanClose s
    return m
endProcedure compEnd

/*--- parse the whole syntax of a unit ------------------------------*/
compUnit: procedure expose m.
parse arg m, ki, stopper
    s = m.m.scan
    if pos(ki, m.comp_chKind) < 1 then
        return scanErr(s, 'bad kind' ki 'in compUnit(...'stopper')')
    else if ki <> '#' then do
        a = compAst(m, '¢')
        do forever
            one = compPipe(m, ki)
            if one \== '' then
                call mAdd a, one
            if \ scanLit(m.m.scan, '$;', '<>', '$<>') then
                return compUnNest(a)
            end
        end
    else do
        res = compAST(m, '¢')
        call scanChar s
        if verify(m.s.tok, m.comp_chSpa) > 0 then
            call mAdd res, compAst(m, '=', strip(m.s.tok, 't'))
        do while scanNL(s, 1) & \ abbrev(m.s.src, stopper)
            call mAdd res, compAst(m, '=', strip(m.s.src, 't'))
            end
        return res
        end
endProcedure compUnit

compUnnest: procedure expose m.
parse arg a
    do while m.a.0 = 1 & pos(m.a.kind, '¢-.;') > 0
        n = m.a.1
        if m.a.kind \== m.n.kind then
            return a
        call mFree a
        a = n
        end
    return a
endProcedure compUnnest

/*--- compile an expression, type d=data, b=block w=word, s=strip ---*/
compExpr: procedure expose m.
parse arg m, type, ki, textEnd
    s = m.m.scan
    if symbol('m.comp_exType.type') \== 'VAR' then
        call err s, 'bad type' type 'in compExpr'
    if ki == '#' then do
        if textEnd == '' then
            call scanChar(s)
        else if textEnd <= m.s.pos then
            return ''
        else
            call scanChar s, textEnd - m.s.pos
        if type == 's' then
            res = compAst(m, '=', strip(m.s.tok))
        else
            res = compAst(m, '=', , m.s.tok)
        res = compAST(m, '-', , res)
        m.res.containsC = 1
        m.res.containsD = 1
        return res
        end
    else if ki == '%' | ki == '^' then do
        call compSpComment m
        vr = compVar(m, left('c', ki == '^'))
        if vr == '' then
            return ''
        if m.vr.var == 'c' then
            res = compAst(m, 'M')
        else
            res = compAst(m, ki, , compASTAddOp(m, vr, '&'))
        call compSpComment m
        if textEnd == '' | textEnd < m.s.pos then do
            ex = compOpBE(m, '=', 1, , textEnd)
            if ex \== '' then do
                call mAdd res, ex
                call compSpComment m
                end
            end
        m.res.containsC = 1
        m.res.containsD = 1
        return res
        end

    if length(ki) \== 1 | pos(ki, '.-=@') < 1 then
        return scanErr(s, 'bad kind' ki 'in compExpr')
    res = compAST(m, translate(ki, '-;', '=@'))
    m.res.containsC = 0
    txtKi = translate(ki, '++=+', '.-=@')
    laPrim = 0
    gotTxt = 0
    if pos(type, 'sb') > 0 then
        m.res.containsC = compSpComment(m) >= 2
    do forever
        if textEnd \== '' then
            if m.s.pos >= textEnd then
                leave
        if scanVerify(s, m.comp_exType.type, 'm') then do
            if textEnd \== '' then
                if m.s.pos > textEnd then do
                    m.s.tok = left(m.s.tok, length(m.s.tok) ,
                                    + textEnd - m.s.pos)
                    m.s.pos = textEnd
                    end
            one = compAST(m, txtKi, m.s.tok)
            if verify(m.s.tok, m.comp_chSpa) > 0 then
                gotTxt = 1
            end
        else do
            old = scanPos(s)
            if \ scanLit(s, m.comp_chDol) then
                leave

            if pos(scanLook(s, 1), '.-') > 0 then
                one = compCheckNN(m, compOpBE(m, , 1, 0),
                   , 'primary block or expression expected')
            else
                one = compPrimary(m)
            if one = '' then do
                call scanBackPos s, old
                leave
                end
            laPrim = m.res.0 + 1
            end
        call mAdd res, one
        if compComment(m) then
            m.res.containsC = 1
        end
    if pos(type, 'bs') > 0 then do
        do rx=m.res.0 by -1 to laPrim+1
            one = m.res.rx
            m.one.text = strip(m.one.text, 't')
            if length(m.one.text) <> 0 then
                leave
            call mFree one
            end
        m.res.0 = rx
        end
    m.res.containsD = laPrim > 0 | gotTxt
    return compAstFree0(res, '')
endProcedure compExpr

/*--- compile a primary and return code -----------------------------*/
compPrimary: procedure expose m.
parse arg m, ops
    s = m.m.scan
    if scanString(s) then
        return compASTAddOp(m, compAST(m, '=', m.s.val), ops)
    r = compVar(m, left('c', right(ops, 1) == '^'))
    if r == '' then
        return ''
    if m.r.var \== 'c' then
         return compASTAddOp(m, compAst(m, '&', m.r.var, r), ops)
    else
         return compASTAddOp(m, compAst(m, 'M'),
                              , left(ops, length(ops)-1))
endProcedure compPrimary

/*--- oPBE ops (primary or block or expression)
       oDef = default Kind, oPre = opPrefix,
       uniq=1 extract unique, uniq='<' prefix <
       withEx <> 0: expression allowed ------------------------------*/
compOpBE: procedure expose m.
parse arg m, oDef, uniq, withEx, textEnd
    s = m.m.scan
    old = scanPos(s)
    op = compOpKind(m, oDef)
    if uniq == '<' & left(op, 1) \== '<' then
        op = left('<', uniq == '<') || op
    if pos(scanLook(s, 1), '/¢') > 0 then do
        if uniq == 1 & length(op) == 1 then
            if op == '.' then
                op = '|.'
            else if op == '=' then
                op = '-='
            else if pos(op, '-@<') > 0 then
                op = op || op
        return compBlock(m, op)
        end
    if compSpComment(m) == 0 ,
        & pos(right(op, 1), m.comp_chKiNBOE) <= 0 then
        return compPrimary(m, op)
    if withEx \== 0 then do
        res = compExpr(m, 's', right(op, 1), textEnd)
        if res \== '' then
            return compASTAddOp(m, res, left(op, length(op)-1))
        end
    call scanBackPos s, old
    return ''
endProcedure compOPBE

/*--- compile var of ^or % clause -----------------------------------*/
compCallVar: procedure expose m.
parse arg m, ki
    call compSpComment m
    vr = compVar(m, left('c', ki == '^'))
    if vr == '' then
        call scanErr m.m.scan, 'var expected after' ki
    call compSpComment m
    if m.vr.var == 'c' then
        return compAst(m, 'M')
    else
        return compAst(m, ki, , compASTAddOp(m, vr, '&'))
endProcedure compCallVar

/*--- compile a pipe and return code --------------------------------*/
compPipe: procedure expose m.
parse arg m, ki
    s = m.m.scan
    res = compAST(m, 'P', ' ', '', '')
    do forever
        one = compExprStmts(m, ki)
        if one \== '' then do
            if m.res.0 > 2 then
                call scanErr s, '$| before statements needed'
            call mAdd res, one
            end
        pre = left(m.comp_chDol, scanLit(s, m.comp_chDol))
        if scanLook(s, 2) == '<>' then
            leave
        if scanLit(s, '<') then do
            if m.res.2 == '' then
                m.res.2 = compAst(m, '.')
            else
                call mAdd m.res.2, compAst(m, '+', ', ')
            call mAdd m.res.2, compOpBE(m, '<', '<')
            m.res.text = m.res.text'f'
            end
        else if scanLit(s, '>>', '>') then do
            if m.res.1 <> '' then
                call scanErr s, 'duplicate output'
            m.res.text = if(m.s.tok == '>', 'F', 'A') ,
                ||substr(m.res.text, 2)
            m.res.1 = compOpBE(m, '<', '<')
            end
        else if scanLit(s, '|') then do
            if m.res.0 < 3 then
                call scanErr s, 'stmts expected before |'
            call compSpNlComment m
            call mAdd res, compCheckNE(m, compExprStmts(m, ki),
                , 'stmts or expressions after | expected')
            end
        else
            leave
        end
    call scanBack s, pre
    if m.res.0 > 3 | m.res.1 \== '' | m.res.2 \== '' then
        return res
    one = if(m.res.0 = 3, m.res.3)
    call mFree res
    return one
endProcedure compPipe

/*--- compile expressions and stmts ---------------------------------*/
compExprStmts: procedure expose m.
parse arg m, ki
    s = m.m.scan
    res = compAst(m, '¢')
    nlLe = 0 /* sophisticated logic using left and right NLs*/
    do forever
        one = compExprStm1(m, ki, nlLe)
        if one == '' then
            return compAstFree0(res)
        call mAdd res, one
        nlLe = scanNl(s)
        end
endProcedure compExprStmts

/*--- scan over space comm nl until next
          expression or statement and compile it --------------------*/
compExprStm1: procedure expose m.
parse arg m, ki, nlLe
    s = m.m.scan
    if pos(ki, ':%^') > 0 then do /* statements with $ are ok */
        call compSpNlComment m, '*'
        if ki \== ':' then do
            one = compExpr(m, 's', ki)
            if one \== '' then
                return one
            end
        end
    else if ki == '@' then do /* rexx statements */
        call compSpNlComment m
        one = compExpr(m, 's', ki)
        if one\ == '' then do
            if m.one.0 < 1 then
                call scanErr s, 'assert not empty' m.one.0
            do forever /* scan all continued rexx lines */
                la = m.one.0
                la = m.one.la
                if m.la.kind \== '+' then
                    leave
                m.la.text = strip(m.la.text, 't')
                if right(m.la.text, 1) \== ',' then
                    leave
                m.la.text = strip(left(m.la.text,
                        , length(m.la.text)-1), 't')' '
                call compSpNlComment m
                cont = compExpr(m, 's', '@')
                if cont == '' | m.cont.kind \== m.one.kind then
                    call scanErr s, 'bad rexx continuation'
                call mAddSt one, cont
                call mFree cont
                end
            return compAstFree0(one)
            end
        end
    else do /* statemens need $, nl logic for expr */
        do forever /* tricky logic for empty lines */
            do forever
                sx = m.s.pos
                call scanSpaceOnly s
                if \ compComment(m) then
                    leave
                nlLe = 0
                end
            m.s.pos = sx
            one = compExpr(m, 'd', ki)
            nlRi = scanNL(s, '?')
            if one == '' then do
                if nlLe & nlRi then
                    return compAst(m, translate(ki, ';-', '@=') ,
                                  , ,compAst(m,'='))
                end
            else if m.one.containsD then
                return one
            if \ nlRi then
                leave
            nlLe = scanNL(s)
            end
        end
    return compStmt(m, ki)
endProcedure compExprStm1

/*--- compile a single statement ------------------------------------*/
compStmt: procedure expose m.
parse arg m, ki
    s = m.m.scan
    res = compAss(m)
    if res \== '' then
        return res
    pre = ''
    old = scanPos(s)
    if scanLit(s,m.comp_chDol'$',m.comp_chDol'@',m.comp_chDol,'@') then
        pre = m.s.tok
    if pre == m.comp_chDol'$' then
        return  compCheckNN(m, compOpBE(m,'=', 1),
                   , 'block or expression expected after $$')
    if right(pre, 1) == '@' then do
        one = compOpBE(m, '@')
        if one \== '' then
            return compAstAddOp(m, one, ')')
        end

    wCat = compName(m, 'sv')
    fu = m.s.tok

    if right(pre, 1) == '@' & wCat \== 's' then
        call scanErr s, 'primary, block or expression expected'

    if fu == 'arg' then do
        res = compAst(m, 'R')
        do forever
            call compSpComment m
            if scanLit(s, ',') then
                a1 = compAst(m, '+', ',')
            else do
                gotV = 1
                a1 = compVar(m, 'v')
                end
            if a1 \== '' then
                call mAdd res, a1
            else if gotV == 1 then
                return res
            else
                call scanErr s, 'empty arg'
            end
        end

    if fu == 'ct' then do
        call compSpComment m
        return compAst(m, 'C', , compCheckNN(m, compExprStm1(m, ki, 0),
            , 'ct statement'))
        end

    if fu == 'do' then do
        call compSpComment m
        pre = compExpr(m, 's', '@')
        res = compAst(m, 'D', , pre)
        p1 = m.pre.1
        if pre \== '' then do
            txt = ''
            do px=1 to m.pre.0
                pC = m.pre.px
                if m.pC.kind \== '+' then
                    leave
                txt = txt m.pC.text
                cx = pos('=', txt)
                if cx > 0 then do
                    m.res.text = strip(left(txt, cx-1))
                    leave
                    end
                end
            end
        call compSpComment m
        call mAdd res, compCheckNN(m, compExprStm1(m, ki, 0),
                , 'stmt after do')
        return res
        end

    if wordPos(fu, 'for forWith with') > 0 then do
        res = compAst(m, 'F', fu)
        call compSpComment m
        if fu \== 'with' then do
            b = compVar(m)
            end
        else do
            b = compAss(m)
            if b == '' then
                b = compCheckNE(m, compExpr(m, 's', '.'),
                , "assignment or expression after with")
            end
        call compSpComment m
        st = compCheckNN(m, compExprStm1(m, ki, 0),
                        , "var? statement after" fu)
        if b = '' then do
            b = compBlockName(m, st)
            if b \== '' then
                b = compAst(m, '=', b)
            else if \ abbrev(fu, 'for') then
                call scanErr s, "variable or named block after" fu
            end
        call mAdd res, b, st
        return res
        end

    if fu == 'withNew' then do
        oldVars = m.m.comp_assVars
        m.m.comp_assVars = ''
        one = compCheckNN(m, compExprStm1(m, ki, 0), 'after withNew')
        r = compAst(m, 'F', 'withNew', '', one,
                          , compAst(m, '*', '!.'))
        m.r.class = classNew('n* CompTable u' ,
                   substr(m.m.comp_assVars, 3))
        m.r.1 = compAst(m, '.', ,
                  , compAst(m, '+', "oNew('"m.r.class"')"))
        m.m.comp_assVars = oldVars
        return r
        end
    if fu == 'proc' then do
           call compSpComment m
        nm = ''
        if compName(m, 'v') == 'v' then do
            nm = m.s.tok
            call compSpComment m
            end
        st = compCheckNN(m, compExprStm1(m, ki, 0), 'proc statement')
        if nm == '' then do
            nm = compBlockName(m, st)
            if nm == '' then
                call scanErr s, 'var or namedBlock expected after proc'
            end
        return compAst(m, 'B', '', compAst(m, '=', nm), st)
        end
    if fu == 'if' | fu == 'else' then do /* unchanged rexx */
        call scanBack s, fu
        return compExpr(m, 's', '@')
        end
    call scanBack s, pre || fu
    return ''
endProcedure compStmt

compBlockName: procedure expose m.
parse arg m, a
    a1 = m.a.1
    if m.a.kind == '¢' then
         return m.a.text
    else if m.a.kind == '*' & m.a1.kind == '¢' then
        return m.a1.text
    return ''
endProcedure compBlockName

compVar: procedure expose m.
parse arg m, vk
    if pos('o', vk) > 0 then call err(sdf)/0
    s = m.m.scan
    ty = compName(m, 'v' || vk)
    if ty \== '' then do
        r = compAst(m, '=', m.s.tok)
        m.r.var = ty
        return r
        end
    if \ scanLit(s, '{') then
        return ''
    call scanLit s, '?', '>'
    f = m.s.tok
    r = compCheckNE(m, compExpr(m, 'b', '='), 'var name')
    if \scanLit(s, '}') then
        call scanErr s, 'closing } missing after {'
    m.r.var = f
    return r
endProcedure compVar

compAss: procedure expose m.
parse arg m, vk
    s = m.m.scan
    old = scanPos(s)
    call scanLit s, m.comp_chDol'=', '='
    pr = m.s.tok
    if pr \== '' then
        call compSpComment m
    v = compVar(m, vk)
    if v \== '' then do
        call compSpComment m
        if \ scanLit(s, '=') then do
            call scanBackPos s, old
            return ''
            end
        end
    else if pr == '' then
        return ''
    else
        oldInfo = scanInfo(s)
    eb = compCheckNE(m, compOpBE(m, '=', 1),
        , 'block or expression in assignment after' pr)
    if m.eb.kind == '¢' then
        eb = compAstAddOp(m, eb, '-')
    if v == '' then do
        v = compBlockName(m, eb)
        if v == '' then
            call scanEr3 s, 'var or namedBlock expected',
                    'in assignment after' pr, oldInfo
        v = compAst(m, '=', v)
        m.v.var = 'v'
        end
    if m.m.comp_assVars \== 0 then
        if m.v.kind == '=' & m.v.var == 'v' then do
            if words(m.v.text) \= 1 then
                call compAstErr v, 'bad var'
            if m.eb.kind == '*' then
                ki = left(m.eb.text, 1)
            else
                ki = m.eb.kind
            if pos(ki, '-=s') > 0 then
                f = ', f' m.v.text 'v'
            else if pos(ki, '.<@o') > 0 then
                f = ', f' m.v.text 'r'
            else
                call compAstErr eb, 'string or object'
            if pos(f, m.m.comp_assVars) < 1 then
                m.m.comp_assVars = m.m.comp_assVars || f
            end
    return compAst(m, 'A', , v, eb)
endProcedure compAss

/*--- block deals with the correct kind and operators
      the content is parsed by compUnit -----------------------------*/
compBlock: procedure expose m.
parse arg m, ops
    s = m.m.scan
    if \ scanLit(s, '¢', '/') then
        return ''
    start = m.s.tok
    if ops == '' | pos(right(ops, 1), m.comp_chKind) < 1 then
        return scanErr(s, 'bad kind' ops 'for block')
    ki = right(ops, 1)
    ops = left(ops, length(ops)-1)
    starter = start
    if start == '¢' then
        stopper = m.comp_chDol'!'
    else do
        call scanVerify s, '/', 'm'
        starter = '/'m.s.tok'/'
        stopper = m.comp_chDol || starter
        if \scanLit(s, '/') then
            call scanErr s, 'ending / after stopper' stopper 'expected'
        end
    res = compUnit(m, ki, stopper)
    if \ scanLit(s, stopper, substr(stopper, 2)) then
           call scanErr s, 'ending' stopper 'expected after' starter
    if abbrev(starter, '/') then
        m.res.text = substr(starter, 2, length(starter)-2)
    return compAstAddOp(m, res, ops)
endProcedure compBlock

/**** lexicals *******************************************************/
/*--- skip a comment. return 0 if there is none ---------------------*/
compComment: procedure expose m.
parse arg m
    s = m.m.scan
    got = 0
    do forever
        if scanLit(s, m.comp_chDol'**') then
            m.s.pos = 1 + length(m.s.src) /* before next nl */
        else if scanLit(s, m.comp_chDol'*+') then
            call scanNL s, 1
        else if scanLit(s, m.comp_chDol'*(') then do
            do forever
                if scanVerify(s, m.comp_chDol, 'm') then iterate
                if scanNL(s) then iterate
                if compComment(m) then iterate
                if \ scanLit(s, m.comp_chDol) then
                    call scanErr s, 'source end in comment'
                if scanLit(s, '*)') then
                    return 1
                if scanLit(s, m.comp_chDol) then iterate
                if scanString(s) then iterate
                end
            end
        else
            return got
        got = 1
        end
endProcedure compComment

/*--- skip spaces and comments --------------------------------------*/
compSpComment: procedure expose m.
parse arg m, xtra
    s = m.m.scan
    got = 0
    do forever
        if scanVerify(s, m.comp_chSpa) then
            got = bitOr(got, 1)
        else if compComment(m) then
            got = bitOr(got, 2)
        else if xtra == '' then
            return got
        else if \ scanLit(s, xtra) then
            return got
        else do
            got = bitOr(got, 4)
            m.s.pos = 1+length(m.s.src)
            end
        end
endProcedure compSpComment

/*--- skip spaces, NLs and comments ---------------------------------*/
compSpNlComment: procedure expose m.
parse arg m, xtra
    found = 0
    do forever
        if compSpComment(m, xtra) < 1 then
            if \ scanNL(m.m.scan) then
             return found
        found = 1
        end
endProcedure compSpNlComment
/*--- scan a name in one of the categories
        v=var, c=compile, s=stmt ----------------------------------*/
compName: procedure expose m.
parse arg m, cats
    s = m.m.scan
    if \ scanName(s) then
        return ''
    if wordPos(m.s.tok, m.comp_wCatS) > 0 then do
        if pos('s', cats) > 0 then
            return 's'
        end
    else if wordPos(m.s.tok, m.comp_wCatC) > 0 then do
        if pos('c', cats) > 0 then
            return 'c'
        end
    else if pos('v', cats) > 0 then do
        return 'v'
        end
    call scanBack s, m.s.tok
    return ''
endProcedure compName

compOpKind: procedure expose m.
parse arg m, op
    s = m.m.scan
    if scanVerify(s, m.comp_chOp || m.comp_chKiNO) then
        op = m.s.tok
    else if op == '' then
        return ''
    /* ??????? temporary until old syntax vanished ????? */
    x = verify(op, '%^', 'm')
    if x > 0 & x < length(op) then
        call scanErr s, 'old syntax? run not at end'
    if right(op, 1) == '<' then
        op = op'='
    kx = verify(op, m.comp_chKiNO, 'm')
    if kx \== 0 & kx \== length(op) then
        call scanErr s, 'kind' substr(op, kx, 1) 'not after ops'
    if pos(right(op, 1), m.comp_chKind) == 0 then
        call scanErr s, 'no kind after ops' op
    return op
endProcedure compOpKind

compSpNlComment: procedure expose m.
/**** small helper routines ******************************************/
/*--- if va == null then issue an error with msg --------------------*/
compCheckNN: procedure expose m.
parse arg m, va, msg
    if va == '' then
        call scanErr m.m.scan, msg 'expected'
    return va
endProcedure compCheckNN

/*--- return true iff expression is empty ---------------------------*/
compIsEmpty: procedure expose m.
parse arg m, a, block0
    do forever
        if a == '' then
            return 1
        else if m.a.kind == '*' then
            a = m.a.1
        else if m.a.kind \== '¢' then
            return 0
        else if block0 then
            return 0
        else if m.a.0 = 1 then
            a = m.a.1
        else
            return m.a.0 < 1
        end
endProcedure compIsEmpty

/*--- if va == null or empty then issue an error with msg -----------*/
compCheckNE: procedure expose m.
parse arg m, ex, msg
    e1 = left(ex, 1)
    if compIsEmpty(m, ex, 1) then
        call scanErr m.m.scan, msg 'expected'
    return ex
endProcedure compCheckNE

/**** AST = Astract Syntax Tree ***************************************

------- atoms, no children
  =  string constant
  +  rexx fragment

------- containers (any number of children)
  -  string expression
  .  object expression
  ;  rexx statements
  ¢  block

------- molecules
  *  operand chain  ==> 1 operands in text, as in syntax plus
                          ) run ($@ stmt), & variable access, ! execute
  &  variable access==> 1
  A  assignment     ==> 2
  B  proc           ==> 2
  C  ct             ==> 1
  D  do             ==> 2
  F  for + with     ==> 2
  P  Pipe           ==> * 1=input 2=output , 3..* piped stmtBlocks
  R  aRg                * list of arguments/separators
  T  Table
  M  compile
  %  RunOut         ==> 1,2 (Run, arguments)
  ^  RunRet         ==> 1,2 (Run, arguments)

**********************************************************************/
/*--- create a new AST ----------------------------------------------*/
compAST: procedure expose m.
parse arg m, ki, txt
    n = mNew('COMP.AST')
    if length(ki) <> 1 then
        return err('compAST bad kind' ki) / 0
    m.n.kind = ki
    m.n.text = txt
    if pos(ki, '¢;-.*&ABCDFPRTM%^') > 0 then do
        do cx=1 to arg()-3
            m.n.cx = arg(cx+3)
            end
        m.n.0 = cx-1
        if ki == '*' then do
            if verify(txt, m.comp_astOps) > 0 then
                return err('compAst ki=* bad ops:' txt) / 0
            end
        else if txt \== '' & pos(ki, '&*FPT') < 1 then
            return err('kind' ki 'text='txt'|')/0
        end
    else if pos(ki, '=+') > 0  then do
        m.n.0 = 'kind'ki
        end
    else do
        return err( "compAst kind '"ki"' not supported") / 0
        end
    return n
endProcedure compAST

/*--- free AST if empty ---------------------------------------------*/
compASTFree0: procedure expose m.
parse arg a, ret
    if m.a.0 > 0 then
        return a
    call mFree a
    return ret
endProcedure compAstFree0

/*--- add operandands to an AST -------------------------------------*/
compASTAddOp: procedure expose m.
parse arg m, a, ops
    if verify(ops, m.comp_astOps) > 0 then
        return err('addOp bad ops:' ops) / 0
    k = if(m.a.kind=='*', left(m.a.text, 1), m.a.kind)
    do while right(ops, 1) == k
        ops = left(ops, length(ops)-1)
        end
    if ops == '' then
        return a
    if ki \== '*' then
        return compAst(m, '*', ops, a)
    m.a.text = ops || m.a.text
    return a
endProcedure compAstAddOp

/*--- return the kind of an AST -------------------------------------*/
compASTKind: procedure expose m.
parse arg m, a
    m.m.astOps = ''
    if \ abbrev(a, 'COMP.AST.') then
        return left(a, 1)
    c = a
    do while m.c.kind == 'ops'
        if m.c.text \== '' then
            return left(m.c.text, 1)
        c = m.c.1
        end
    if a == c then
        return '?'
    return compAstKind(m, c)
endProcedure compASTKind

compAstSay: procedure expose m.
parse arg a, lv
    if \ abbrev(a, 'COMP.AST.') then do
        if a \== '' then
            return err('bad ast' a)
        say left('', 19)': * empty ast'
        return
        end
    say lefPad(left('', lv) m.a.kind, 10) ,
        || rigPad(if(dataType(m.a.0, 'n'), m.a.0), 3),
        '@'rigPad(substr(a, 10), 4)':' m.a.text'|'
    if dataType(m.a.0, 'n') then do cx=1 to m.a.0
        call compAstSay m.a.cx, lv+1
        end
    return
endProcedure compAstSay

compAstErr: procedure expose m.
parse arg a, txt
    call errSay txt
    call compAstSay a, 0
    return err(txt)
endProcedure compAstErr

/*--- return the code for an AST with operand chain trg -------------*/
compCode2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if pos(o1, ')!') > 0 then
        return compCode2rx(m, oR, strip(f))
    if pos(o1, '-.<|?@') > 0 then
        return compRun2rx(m, ops, quote(oRunner(f)))
    call err 'compCode2rx bad ops' ops 'code='f
endProcedure compCode2rx

compCon2rx: procedure expose m.
parse arg m, ops, f, a
    do ox=length(ops) by -1 to 1 while pos(substr(ops,ox,1), '.-')>0
        end
    if substr(ops, ox+1, 1) == '.' then
        f = s2o(f)
    if length(f) < 20 then
        v = quote(f, "'")
    else if a \== '' & m.a.text == f then
        v = 'm.'a'.text'
    else
        v = 'm.'compAst(m, '=', f)'.text'
    if substr(ops, ox+1, 1) == '.' then
        return compObj2rx(m, left(ops, ox), v)
    else
        return compString2rx(m, left(ops, ox), v)
endProcedure compCon2rx

compString2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if pos(o1, '!') then
        return compCode2rx(m, oR, 'call out' f)
    if o1 == '-' then
        return compString2rx(m, oR, f)
    if o1 == '.' then
        return compObj2rx(m, oR, 's2o('f')')
    if o1 == '&' then do
        o2 = substr('1'ops, length(ops), 1)
        if pos(o2,  '.<^%@)') < 1 then
            return compString2rx(m, oR, 'vGet('f')')
        else
            return compObj2rx(m, oR, 'vGet('f')')
        end
    if o1 == '<' then
        return compFile2rx(m, oR, 'file('f')')
    call err 'compString2rx bad ops' ops
endProcedure compString2rx

compObj2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '.' then
        return compObj2rx(m, oR, f)
    if o1 == '-' then
        return compString2rx(m, oR, 'o2string('f')')
    if o1 == '!' then
        return compCode2rx(m, oR, 'call out' f)
    if o1 == '<' then
        return compFile2rx(m, oR, 'o2file('f')')
    if o1 == '@' then
        return compRun2Rx(m, oR, f)
    if pos(o1, ')%^') > 0 then
        return compRun2rx(m, ops, f)
    call err 'compObj2rx bad ops' ops 'for' f
endProcedure compObj2rx

compRun2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '@' then
        return compRun2Rx(m, oR, f)
    if pos(o1, ')%') > 0 then
        return compCode2Rx(m, oR, 'call oRun' f)
    if o1 == '^' then
        if pos(right(oR, 1),  '.<^%') < 1 then
            return compString2Rx(m, oR, 'oRun('f')')
        else
            return compObj2Rx(m, oR, 'oRun('f')')
    return compObj2rx(m, ops, f)
endProcedure compRun2rx

compFile2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if pos(o1, '<.@') > 0 then
        return compFile2rx(m, oR, f)
    if o1 == '|' | o1 == '?' then
        return compObj2Rx(m, oR, 'jSingle('f ||if(o1=='?', ", ''")')')
    return compRun2rx(m, ops, f)
endProcedure compFile2rx

compAst2rx: procedure expose m.
parse arg m, ops, a
    ki = m.a.kind
    /* astStats ausgeschaltet
    if pos(ki, m.comp_astStats) < 1 then do
        m.comp_astStats = m.comp_astStats ki
        m.comp_astStats.ki = 0
        m.comp_astStatT.ki = 0
        end
    m.comp_astStats.ki = m.comp_astStats.ki + 1
    if m.a.text \== '' then
        m.comp_astStatT.ki = m.comp_astStatT.ki + 1
    if ki == '*' then do
        k2 = vGet(a'.1>>KIND')
        if symbol('m.comp_astStat1.k2') \== 'VAR' then
            m.comp_astStat1.k2 = 1
        else
            m.comp_astStat1.k2 = m.comp_astStat1.k2 + 1
        end         */
    if ki == '+' & ops == '' then
        return m.a.text
    if ki == '=' then
        return compCon2Rx(m, ops, m.a.text, a)
    if ki == '*' then
        return compAst2Rx(m, ops || m.a.text, m.a.1)
    o1 = right(ops, 1)
    oR = left(ops, max(0, length(ops)-1))
    if ki == '-' then
        return compString2rx(m, ops, compCatRexxAll(m, a,,, ' || '))
    if ki == '.' then
        return compObj2Rx(m, ops, compCatRexxAll(m, a,,, ' || '))
    if ki == ';' then
        return compCode2Rx(m, ops, compCatRexxAll(m, a,,,' || '))
    if ki == '¢' then do
        a1 = m.a.1
        if m.a.0 == 1 & m.a1.kind == '¢' then
            return compAst2Rx(m, ops, a1)
        if o1 == '-' then do
            res = compAst2CatStr(m, a)
            if res \== '' then /* () necessary if part of expression */
                return compString2rx(m, oR, '('strip(res)')')
            end
        if o1 == '.' then
            return compAst2Rx(m, ops'|', a)
        if pos(o1, '|?') > 0 then
            if m.a.0 = 1 & compAstOut(a1) then
                return compAst2Rx(m, oR, a1)
        res = ''
        do ax=1 to m.a.0
            res = res';' compAst2rx(m, '!', m.a.ax)
            end
        if verify(res, '; ') = 0 then
            res = 'nop'
        else
            res = 'do'res'; end'
        if pos(o1, '-@!)') > 0 then
            return compCode2Rx(m, ops, res)
        if pos(o1, '|?<') > 0 then
            return compCode2Rx(m, ops'<@', res)
        end
    if ki == '&' then do
        nm = compAst2Rx(m, '-', m.a.1)
        if m.a.text=='' | m.a.text=='v' then
            return compString2rx(m, ops'&', nm)
        else if m.a.text == '?' then
            return compString2rx(m, ops, 'vIsDefined('nm')')
        else if m.a.text == '>' then
            return compString2rx(m, ops, 'vIn('nm')')
        else
            call compAstErr a, 'bad text' m.a.text 'in ast &'
        end
    if ki == '%' | ki == '^' then do
        c1 = compAst2Rx(m, '.', m.a.1)
        if m.a.0 > 1 then
            c1 =  c1',' compAst2Rx(m, '', m.a.2)
        return compRun2Rx(m, ops || ki, c1)
        end
    if ki == 'A' then do /* assignment */
        nm = compAst2Rx(m, '-', m.a.1)
        vl = m.a.2
        if m.vl.kind == '=' | m.vl.kind == '-' ,
            | (m.vl.kind == '*' & right(m.vl.text, 1) == '-') then
            return compCode2Rx(m, ops,
                , 'call vPut' nm',' compAst2Rx(m, '-', vl))
        else
            return compCode2Rx(m, ops,
                , 'call vPut' nm',' compAst2Rx(m, '.', vl))
        end
    if ki == 'B' then do /* proc */
        call vPut utInter('return' compAst2Rx(m, '-', m.a.1)),
            , oRunner(compAst2Rx(m ,'!', m.a.2))
        return ''
        end
    if ki == 'C' then do /* ct */
     call utInter compAst2Rx(m, '!', m.a.1)
        return ''
        end
    if ki == 'D' then do /* do */
        res = 'do' compAst2rx(m, '', m.a.1)
        if m.a.text \== '' then
            res = res"; call vPut '"m.a.text"'," m.a.text
        return compCode2Rx(m, ops, res';' compAst2Rx(m, '!', m.a.2),
             || "; end")
        end
    if ki == 'F' then do /* for... */
        a1 = m.a.1
        st = compAst2Rx(m, '!', m.a.2)
        if abbrev(m.a.text, 'for') then do
            if m.a.1 == '' then
                v = "''"
            else
                v = compAst2Rx(m, '-', m.a.1)
            if m.a.text == 'for' then
                s1 = 'do while vIn('v')'
            else if m.a.text \== 'forWith' then
                call compAstErr a, 'bad for...'
            else
                s1 = 'call vWith "+"; do while vForWith('v')'
            return compCode2Rx(m, ops, s1';' st'; end')
            end
        else if \ abbrev(m.a.text, 'with') then
            call compAstErr a, 'bad with...'
        if m.a1.kind \== 'A' then do
            v = compAst2Rx(m, '.', a1)
            end
         else do
            v = compAst2Rx(m, ,a1)
            if \ abbrev(v, 'call vPut ') | pos(';', v) > 0 then
                call scanErr s, 'bad vPut' v
            v = 'vPut('substr(v, 11)')'
            end
        ret1 = 'call vWith "+",' v';' st
        if m.a.0 <= 2 then
            return ret1"; call vWith '-'"
        a3 = m.a.3
        if m.a3.kind \== '*' then
            call compAstErr a, 'for/with a.3 not *'
        return ret1';' compObj2Rx(m, m.a3.text, "vWith('-')")
        end
    if ki == 'P' then do /* pipe */
        if ((left(m.a.text, 1) == ' ') \== (m.a.1 == '')) ,
         | ((substr(m.a.text, 2) == '') \== (m.a.2 == '')) ,
         | (m.a.0 <= 3 & m.a.text == '') then
            call compAstErr a, 'bad/trivial astPipe'
        res = ''
        do ax=3 to m.a.0
            a1 = ''
            if ax < m.a.0 then /* handle output */
                t1 = 'N'
            else if m.a.1 == '' then
                t1 = 'P'
            else do
                t1 = left(m.a.text, 1)
                a1 = compAst2Rx(m, '.', m.a.1)
                end
            if ax == 3 then do /* handle input */
                t1 = '+'t1 || substr(m.a.text, 2)
                if m.a.2 \== '' then
                    a1 = a1',' compAst2Rx(m, '.', m.a.2)
                end
            else
                t1 = t1'|'
            res = res"; call pipe '"t1"'," a1 ,
                   ";" compAst2Rx(m, '!', m.a.ax)
            end
        return compCode2Rx(m, ops, substr(res, 3)"; call pipe '-'")
        end
    if ki == 'R' then do /* aRg statement */
        prs = 'parse arg ,'
        pts = ''
        do ax=1 to m.a.0
            a1 = m.a.ax
            if m.a1.kind = '+' & m.a1.text == ',' then
                prs = prs','
            else do
                prs = prs 'ggAA'ax
                pts = pts'; call vPut' compAst2Rx(m, '-', a1)', ggAA'ax
                end
            end
        return compCode2rx(m, ops, prs pts)
        end
    if ki == 'M' then do
        if m.a.0 = 0 then
            args = ''
        else
            args = compAst2Rx(m, , m.a.1)
        return compRun2rx(m, ops, 'wshHookComp( ,'args ',in2Buf())')
        end
    return compAstErr(a, 'compAst2rx bad ops='ops 'kind='ki 'ast='a)
endProcedure compAst2rx

compAstOut: procedure expose m.
parse arg a
    if m.a.kind \== '*' then
        return pos(m.a.kind, m.comp_astOut) > 0
    return pos(left(m.a.text, 1), m.comp_astOut) > 0
endProcedure compAstOut

compAst2CatStr: procedure expose m.
parse arg m, a
    res = ''
    if compAstOut(a) then
        res = compCatRexx(res, compAst2rx(m, , a), ' ')
    else if m.a.kind \== '¢' then
        return ''
    else do ax=1 to m.a.0
        b = compAst2CatStr(m, m.a.ax)
        if b == '' then
            return ''
        res = compCatRexx(res, b, ' ')
        end
    return res
endProcedure compAst2CatStr

compCatRexxAll: procedure expose m.
parse arg m, a, ops, mi, sep
    res = ''
    do ax=1 to m.a.0
        a1 = m.a.ax
        res = compCatRexx(res, compAst2rx(m, ops, m.a.ax), mi , sep)
        end
    return strip(res)
endProcedure compCatRexxAll

/*--- cat two rexx parts, avoid strange effects ---------------------*/
compCatRexx: procedure expose m.
parse arg le, ri, mi, sep
    if mi \== '' then
        return le || mi || ri
    lr = right(le, 1)
    rl = left(ri, 1)
    if (lr == "'" | lr == '"') then do
        if rl == lr then                          /* "a","b" -> "ab" */
            return left(le, length(le)-1) || substr(ri, 2)
        else if  rl == '(' then                 /* "a",( -> "a" || ( */
            return le||sep||ri             /* avoid function call    */
        end
    else if pos(lr, m.comp_idChars) > 0 then
        if pos(rl, m.comp_idChars'(') > 0 then
            return le || sep || ri        /* a,b -> a || b */
    return le || ri
endProcedure compCatRexx
/* copy comp end *****************************************************/
/* copy scan     begin ************************************************
     Achtung: inc generiert SB aus scan, Aenderungen nur in scan|
SB = scanBasic: single line, no reader, no newLines, class not needed
    scanSrc(m, source) starts scanning a single line = scanBasic
    scanLook(m,len) : returns next len chars, pos is not moved
    scanChar(m,len) : scans next len chars
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanEnd(m)     : returns whether we reached end of input
    scanLit(m,lit) : scan Literal lit if present or return 0
    scanChar(m,n)  : scan next n characters
    scanName(m)    : scan a name
    ScanNat(m)     : scan a natural number (without sign)
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanKeyValue(m): scan a key = value clause (with spaces)
    scanWord(m,st,uc)  : scan a space delimited word or a string,
                          st=stopper, if u=1 then uppercase non-strings
    scanSpace(m)   : skips over spaces (and nl and comment if \ basic
    scanInfo(m)    : text of current scan location
    scanErr(m, txt): error with current scan location

    m is an address, to store our state
    returns: true if scanned, false otherwise
    if a scan function succeeds, the scan position is moved

         m.m.tok  ==> last token
         m.m.val  ==> last value for scanString/Word
         m.m.pos ==> scan position
         m.m.src ==> scan source
**********************************************************************/
/*--- start basic scannig: set new src, reset outFields -------------*/
scanSrc: procedure expose m.
parse arg m, m.m.src
    m.m.pos = 1
    m.m.tok = ''
    m.m.scanIsBasic = 1
    return m
endProcedure scanSrc

scanBasic: procedure expose m.
parse arg src
    if symbol('m.scan.0') == 'VAR' then
        m.scan.0 = m.scan.0 + 1
    else
        m.scan.0 = 1
    return scanSrc('SCAN.'m.scan.0, src)
endProcedure scanBasic

scanEr3: procedure expose m.
parse arg m, txt, info
    return err('s}'txt'\n'info)

scanErr: procedure expose m.
parse arg m, txt
    if arg() > 2 then
        return err(m,'old interface scanErr('m',' txt',' arg(3)')')
    return scanEr3(m, txt, scanInfo(m))

/*--- scanInfo: text of current scan position for scanErr -----------*/
scanInfo: procedure expose m.
parse arg m
    if m.m.scanIsBasic then
        return scanSBInfo(m)
    else
        interpret objMet(m, 'scanInfo')
endProcedure scanInfo

scanSBInfo: procedure expose m.
parse arg m
    return 'last token' m.m.tok 'scanPosition' ,
        strip(substr(m.m.src, m.m.pos, 40), 't') ,
        || '\npos' m.m.Pos 'in string' strip(m.m.src, 't')

/*--- return the next len characters until end of src ---------------*/
scanLook: procedure expose m.
parse arg m, len
    if len == '' then
        return substr(m.m.src, m.m.pos)
    else
        return substr(m.m.src, m.m.pos,
                     , min(len, 1 + length(m.m.src) - m.m.pos))
endProcedure scanLook

/*--- scan len chararcters, atmost to end of src --------------------*/
scanChar: procedure expose m.
parse arg m, len
    m.m.tok = scanLook(m, len)
    m.m.pos = m.m.pos + length(m.m.tok)
    return m.m.tok \== ''
endProcedure scanChar

/*--- scan the literal lit ------------------------------------------*/
scanLit: procedure expose m.
parse arg m
    do ax=2 to arg()
        if abbrev(substr(m.m.src, m.m.pos), arg(ax)) then do
            m.m.tok = arg(ax)
            m.m.pos = m.m.pos + length(arg(ax))
            return 1
            end
        end
    m.m.tok = ''
    return 0
endProcedure scanLit

/*--- scan with verify, vOpt is passed to verify --------------------*/
scanVerify: procedure expose m.
parse arg m, alpha, vOpt
    if vOpt == '' then   /* empty string does not take default */
        nx = verify(m.m.src, alpha, , m.m.pos)
    else
        nx = verify(m.m.src, alpha, vOpt, m.m.pos)
    if nx = 0 then
        nx = length(m.m.src) + 1
    m.m.tok = substr(m.m.src, m.m.pos, nx - m.m.pos)
    m.m.pos = nx
    return m.m.tok \== ''
endProcedure scanVerify

/*--- scan while in charset -----------------------------------------*/
scanWhile: procedure expose m.
parse arg m, chSet
    return scanVerify(m, chSet, 'n')

/*--- scan until in charset -----------------------------------------*/
scanUntil: procedure expose m.
parse arg m, chSet
    return scanVerify(m, chSet, 'm')

/*--- scan until (and over) string End ------------------------------*/
scanStrEnd: procedure expose m.
parse arg m, sep
    sx = m.m.pos
    bx = sx
    do forever
        ex = pos(sep, m.m.src, sx)
        if ex = 0 then do
            m.m.val = m.m.val || substr(m.m.src, bx)
            return 0
            end
        m.m.val = m.m.val || substr(m.m.src, bx, ex-bx)
        bx = ex + length(sep)
        if \ abbrev(substr(m.m.src, bx), sep) then do
            m.m.tok = m.m.tok || substr(m.m.src, m.m.pos, bx-m.m.pos)
            m.m.pos = bx
            return 1
            end
        sx = bx + length(sep)
        end
endProcedure scanStrEnd

/*--- scan a string with quote char qu ------------------------------*/
scanString: procedure expose m.
parse arg m, prefs
    if prefs = '' then do
        call scanLit m, "'", '"'
        end
    else do
        do px=1 to words(prefs) until scanLit(m, word(prefs, px))
            end
        end
    if m.m.tok == '' then
        return 0
    m.m.val = ''
    if \ scanStrEnd(m, m.m.tok) then
        return scanErr(m, 'ending Apostroph missing')
    return 1
endProcedure scanString

/*--- scan a word and put value into *.val
           a word is either delimited by space or stopper
                     or a string (with single or double quotes ------*/
scanWord: procedure expose m.
parse arg m, stopper, ucWord
    if scanString(m) then
        return 1
    if stopper == '' then
        stopper = m.ut_space
    if \scanUntil(m, stopper) then
        return 0
    if ucWord == 1 then
        m.m.val = translate(m.m.tok)
    else
        m.m.val = m.m.tok
    return 1
endProcedure scanWord

/*--- skip, scan and return word or scanErr -------------------------*/
scanRetWord: procedure expose m.
parse arg m, stopper, ucWord, eWhat
    if scanWord(scanSKip(m), stopper, ucWord) then
        return m.m.val
    else
        return scanErr(m, eWhat 'expected')
endProcedure scanRetWord

/*--- scan a key = word phrase
          put key into m.key and word into m.m.val -------*/
scanKeyValue: procedure expose m.
parse arg m, def, uc
    if \ scanWord(m, ' =''"') then
        return 0
    m.m.key = m.m.val
    if \ scanLit(scanSkip(m), '=') then
        m.m.val = def
    else if \ scanWord(scanSkip(m)) then
        return scanErr(m, 'word expected after' m.m.key '=')
    if uc == 1 then
        upper m.m.key m.m.val
    return 1
endProcedure scanKeyValue

/*--- scan over space, nl, comments ---------------------------------*/
scanSpace: procedure expose m.
parse arg m
   if m.m.scanIsBasic then
       return scanSpaceOnly(m)
   else
       return scanSpNlCo(m)
endProcedure scanSpace

scanSpaceOnly: procedure expose m.
parse arg m
    nx = verify(m.m.src, m.ut_space, , m.m.pos)
    if nx = 0 then
        nx = length(m.m.src) + 1
    m.m.tok = left(' ', nx <> m.m.pos)
    m.m.pos = nx
    return m.m.tok == ' '
endProcedure scanSpaceOnly

/*--- skip over space and return m ----------------------------------*/
scanSkip: procedure expose m.
parse arg m
    call scanSpace m
    return m
endProcedure scanSkip

/*--- return true if at end of src ----------------------------------*/
scanEnd: procedure expose m.
parse arg m
    if m.m.pos <= length(m.m.src) then
        return 0
    else if m.m.scanIsBasic then
        return 1
    else
        return m.m.atEnd
endProcedure scanEnd

/*--- scan a natural number (no sign, decpoint ...) Ignore After ----*/
scanNatIA: procedure expose m.
parse arg m
    return scanVerify(m, '0123456789')

/*--- scan an integer (optional sign, no decpoint ...) Ignore After -*/
scanIntIA: procedure expose m.
parse arg m
    poX = m.m.pos
    call scanLit m, '-', '+'
    if \ scanNatIA(m) then do
        m.m.pos = poX
        return 0
        end
    m.m.tok = substr(m.m.src, poX, m.m.pos - poX)
    return 1
endProcedure scanIntIA

/*--- scanOpt set the valid characters for names, and comments
          it must be called
          before any of the following functions ---------------------*/
scanOpt: procedure expose m.
parse arg m, m.m.scanName1, namePlus, m.m.scanComment, nest
    if m.m.scanName1 == '' then
        m.m.scanName1 = m.ut_alfa
    if namePlus == '' then
        m.m.scanNameR = m.m.scanName1 || '0123456789'
    else
        m.m.scanNameR = m.m.scanName1 || namePlus
    m.m.scanNestCom = nest == 1
    return m
endProcedure scanOpt

/*--- return true if at comment -------------------------------------*/
scanSBCom: procedure expose m.
parse arg m
    m.m.tok = ''
    if m.m.scanComment == '' then
        return 0
    if \ abbrev(substr(m.m.src, m.m.pos), m.m.scanComment) then
        return 0
    m.m.tok = substr(m.m.src, m.m.pos)
    m.m.pos = 1 + length(m.m.src)
    return 1
endProcedure scanSBCom

/*--- scan a Name, first char in *.scanName1, rest in *.scanNameR ---*/
scanName: procedure expose m.
parse arg m
    if pos(substr(m.m.src, m.m.pos, 1),
                 , m.m.scanName1) <= 0 then do
        m.m.tok = ''
        return 0
        end
    return scanVerify(m, m.m.scanNameR)
endProcedure scanName

/*--- check character after a number
          must not be Identifier or plus ----------------------------*/
scanCheckNumAfter: procedure expose m.
parse arg m, res, plus
    if \ res then
        return 0
    if pos(substr(m.m.src, m.m.pos, 1), m.m.scanNameR || plus) > 0 then
        call scanErr m, 'illegal char after number' m.m.tok
    return 1
endProcedure scanCheckNumAfter

/*--- scan a natural number check character after -------------------*/
scanNat: procedure expose m.
parse arg m
    if arg() \== 1 then
        return err('old interface scanNat') / 0
    return scanCheckNumAfter(m, scanNatIA(m), '.')
endProcedure ScanNat

/*--- scan an Integer check character after -------------------------*/
scanInt: procedure expose m.
parse arg m
    if arg() \== 1 then
        return err('old interface scanInt') / 0
    return scanCheckNumAfter(m, scanIntIA(m), '.')
endProcedure ScanInt

/*--- scan a Number check character after ---------------------------*/
scanNum: procedure expose m.
parse arg m
    if arg() \== 1 then
        return err('old interface scanNum') / 0
    return scanCheckNumAfter(m, scanNumIA(m))
endProcedure ScanInt

/*--- scan a number (optional sign, decpoint, exponent) Ignore After-*/
scanNumIA: procedure expose m.
parse arg m
    poX = m.m.pos
    call scanLit m, '-', '+'
    cx = scanNumUSPos(m)
    if cx == 0 then do
        m.m.pos = poX
        return 0
        end
    m.m.tok = substr(m.m.src, poX, cx-poX)
    m.m.val = translate(m.m.tok)
    m.m.pos = cx
    return 1
endProcedure scanNumIA

/*--- scan unsigned number (opt. decpoint, exponent) Ignore After ---*/
scanNumUSPos: procedure expose m.
parse arg m
    poX = m.m.pos
    cx = verify(m.m.src, '0123456789', , poX)
    if cx > 0 then
        if substr(m.m.src, cx, 1) == '.' then
            cx = verify(m.m.src, '0123456789', , cx+1)
    if cx < 1 then  do
        if abbrev('.', substr(m.m.src, poX)) then
            return 0
        end
    else if abbrev('.', substr(m.m.src, poX, cx-poX)) then do
        return 0
        end
    else if pos(substr(m.m.src, cx, 1), 'eE') > 0 then do
        cy = cx + 1 + (pos(substr(m.m.src, cx+1, 1), '-+') > 0)
        cx = verify(m.m.src, '0123456789', , cy)
        if cx==cy | (cx == 0 & cy > length(m.s.src)) then
            call scanErr m, 'exponent expected after E'
        end
    if cx >= poX then
        return cx
    else
        return length(m.s.src)+1
  /*
        m.m.tok = substr(m.m.src, poX, cx-poX)
        m.m.pos = cx
        end
    else do
        m.m.tok = substr(m.m.src, poX)
        m.m.pos = length(m.s.src)+1
        end
    m.m.val = translate(m.m.tok)
    return 1  */
endProcedure scanNumUSPos

scanType: procedure expose m.
parse arg m, opt
    m.m.tok = ''
    if scanName(m) then
        m.m.type = 'n'
    else if scanNum(m) then
        m.m.type = 0
    else if scanString(m) then
        m.m.type = left(m.m.tok, 1)
    else if scanSpace(m) then
        m.m.type = 's'
    else do
        call scanChar m, 1
        m.m.type = m.m.tok
        end
    return m.m.type
endProcedure scanType

/* copy scan     end *************************************************/
/* copy scanRead begin ***********************************************
Scan: scan an input: with multiple lines
    ==> all of scan

returns: true if scanned, false otherwise
         m.m.tok  ==> last token
         m.m.val  ==> last value for scanString/Word/KeyValue
         m.key    ==> key for scanKeyValue
         m.m.pos ==> scan position
         m.m.src ==> scan source
**********************************************************************/
scanReadIni: procedure expose m.
    if m.scanRead_ini == 1 then
        return
    m.scanRead_ini = 1
    call jIni
    ts = classNew('n ScanRes u f TOK v, f VAL v, f KEY v, f TYPE v')
    call classNew 'n ScanRead u JRW', 'm',
        , 'oReset return scanReadReset(m, arg)',
        , 'scanNL return scanReadNL(m, unCond)',
        , 'scanCom  return scanSBCom(m)',
        , 'scanInfo return scanReadInfo(m)',
        , 'scanPos return scanReadPos(m)',
        , "jOpen   call scanReadOpen m, arg(3)" ,
        , "jClose  call scanReadClose m" ,
        , 'isWindow 0',
        , "jRead if scanType(m) == '' then return 0;" ,
                  "m.rStem.1 = oClaCopy('"ts"', m, ''); m.rStem.0 = 1"
    call classNew "n EditRead u JRW", "m" ,
        , "jRead  if \ editRead(m, rStem) then return 0",
        , "jOpen" ,
        , "jReset m.m.linex = arg - 1"
    call classNew 'n ScanSqlStmtRdr u JRW', 'm',
        , "jReset   call scanSqlStmtRdrReset m, arg, arg2",
        , "jOpen    call scanOpen m'.SCAN'" ,
        , "jClose   call scanClose m'.SCAN'" ,
        , "jRead    r = scanSqlStmt(m'.SCAN');if r=='' then return 0" ,
                         "; m.rStem.1 = r; m.rStem.0 = 1"
    return
endProcedure scanReadIni

scanOpen: procedure expose m.
parse arg m
    interpret objMet(m, 'jOpen')
    return m
endProcedure scanOpen

scanClose: procedure expose m.
parse arg m
    interpret objMet(m, 'jClose')
    return m
endProcedure scanClose

/*--- scan over white space, nl, comments ...------------------------*/
scanSpNlCo: procedure expose m.
parse arg m
    res = 0
    do while scanSpaceOnly(m) | scanCom(m) | scanNl(m)
        res = 1
        end
    m.m.tok = left(' ', res)
    return res
endProcedure scanSpNlCo

/*--- scan next line ------------------------------------------------*/
scanNL: procedure expose m.
parse arg m, unCond
    interpret objMet(m, 'scanNL')

/*--- scanNl until line starts with trg -----------------------------*/
scanNlUntil: procedure expose m.
parse arg s, trg
    do until scanLook(s, length(trg)) == trg
        if \ scanNl(s, 1) then
            return 0
        end
    return 1
endProcedure scanNlUntil
/*--- scan one comment ----------------------------------------------*/
scanCom: procedure expose m.
parse arg m
    interpret objMet(m, 'scanCom')
/*--- go back the current token -------------------------------------*/
scanBack: procedure expose m.
parse arg m, tok
    if m.m.pos <= length(tok) then
        call scanErr sc, 'cannot back "'tok'" length'
    cx = m.m.pos - length(tok)
    if substr(m.m.src, cx, length(tok)) \== tok then
        return scanErr(m, 'cannot back "'tok'" value') + sauerei
    m.m.pos = cx
    return
endProcedure scanBack

/*--- return position in simple format ------------------------------*/
scanPos: procedure expose m.
parse arg m
    interpret objMet(m, 'scanPos')
endProcedure scanPos

/*--- set position to position in arg to-----------------------------*/
scanBackPos: procedure expose m.
parse arg m, to
    cur = scanPos(m)
    wc = words(cur)
    if wc <> words(to) ,
        | subWord(cur, 1, wc-1) <> subWord(to, 1, wc-1) then
        call scanErr m 'cannot back from' cur 'to' to
    m.m.pos = word(to, wc)
    return
endProcedure scanBackPos

/*--- begin scanning the lines of a reader --------------------------*/
scanRead: procedure expose m.
parse arg rdr, n1, np, co
    return scanOpt(oNew(m.class_ScanRead, rdr), n1, np, co)

scanReadReset: procedure expose m.
parse arg m, m.m.rdr m.m.strip .
    return oMutate(m, m.class_ScanRead)
endProcedure scanReadReset

scanReadOpen: procedure expose m.
parse arg m, line0
    call jOpen m.m.rdr, '<'
    return scanSetPos0(m, (line0 == '') 1, line0)
endProcedure scanReadOpen

scanReadClose: procedure expose m.
parse arg m
    call jClose m.m.rdr
    m.m.atEnd = 'closed'
    return m
endProcedure scanReadClose

/*--- scan over next newLine
        if unCond \== 1 only if we are already at endOfLine
         return true if we scanned a NL -----------------------------*/
scanReadNL: procedure expose m.
parse arg m, unCond
    m.m.tok = ''
    if unCond \== 1 then
        if m.m.pos <= length(m.m.src) then
            return 0
    if m.m.atEnd then
        return 0
    if unCond == '?' then
        return 1
    m.m.tok = substr(m.m.src, m.m.pos)
    r = m.m.rdr
    if \ jRead(r) then do
        m.m.atEnd = 1
        m.m.pos = 1 + length(m.m.src)
        return 0
        end
    if m.m.strip ==  '-' then
        m.m.src = m.r
    else  /* strip trailing spaces for vl32755 inputs ,
                 use only if nl space* is equivalent to nl */
        m.m.src = strip(m.r, 't')
    m.m.pos = 1
    m.m.lineX = m.m.lineX + 1
    return 1
endProcedure scanReadNl

/*--- postition scanner to lx px (only with jBuf)
        after rdr is positioned to line before ----------------------*/
scanSetPos: procedure expose m.
parse arg m, lx px
    call jPosBefore m.m.rdr, lx
    return scanSetPos0(m, lx px)

/*--- postition scanner to lx px
     after rdr is positioned to line before -------------------------*/
scanSetPos0: procedure expose m.
parse arg m, lx px, line0
    call scanReset m, line0
    call scanNl m
    m.m.lineX = lx
    m.m.pos = px
    return m
endProcedure scanSetPos0

/*--- reset scanner fields ------------------------------------------*/
scanReset: procedure expose m.
parse arg m, m.m.src
    m.m.pos = 1
    m.m.tok = ''
    m.m.scanIsBasic = 0
    m.m.atEnd = 0
    m.m.lineX = 0
    m.m.val = ''
    m.m.key = ''
    return m
endProcedure

scanTextCom: procedure expose m.
parse arg m, untC, untWrds
    if \ m.m.scanNestCom then
        return scanText(m, untC, untWrds)
    else if wordPos('*/', untWrds) > 0 then
        return scanText(m, untC'*/', untWrds)
    res = scanText(m, untC'*/', untWrds '*/')
    if res then
        if scanLook(m, 2) == '*/' then
            call scanErr m, '*/ without preceeding comment start /*'
    return res
endProcedure scanTextCom

scanText: procedure expose m.
parse arg m, untC, untWrds
    res = ''
    do forever
        if scanUntil(m, untC) then do
            res = res || m.m.tok
            if m.m.pos > length(m.m.src) then do
                /* if windowing we need to move the window| */
                if scanNl(m, 0) then
                    if right(res, 1) \==' ' & scanLook(m, 1)\==' ' then
                        res = res' '
                iterate
                end
            end
        c9 = scanLook(m, 9)
        do sx=1 to words(untWrds)
            if abbrev(c9, word(untWrds, sx)) then do
                m.m.tok = res
                return 1
                end
            end
        if scanCom(m) | scanNl(m, 0) then do
            if right(res, 1) \== ' ' & scanLook(m, 1) \== ' ' then
                res = res' '
            end
        else if scanString(m) then
            res = res || m.m.tok
        else if scanChar(m, 1) then
            res = res || m.m.tok
        else if scanEnd(m) then do
            m.m.tok = res
            return res \== ''  /* erst hier NACH scanCom,  scanNl */
            end
        else
            call scanErr m, 'bad pos'
        end
endProcedure scanText

scanReadPos: procedure expose m.
parse arg m, msg
    return m.m.lineX m.m.pos
endProcedure scanReadPos

scanReadInfo: procedure expose m.
parse arg m
    msg = 'last token' m.m.tok 'scanPosition' ,
        strip(substr(m.m.src, m.m.pos, 40), 't')
    if scanEnd(m) then
        msg = msg'\natEnd after'
    else
        msg = msg'\npos' m.m.pos 'in'
    return msg 'line' m.m.lineX':' strip(m.m.src, 't')
endProcedure scanReadInfo

/*--- use scan sqlEdit macro --> temporarily here -------------------*/
/*--- read next line from edit data ---------------------------------*/
editRead: procedure expose m.
parse arg m, rStem
    m.m.lineX = m.m.lineX + 1
    if adrEdit('(ll) = line' m.m.lineX, 12) \= 0 then
        return 0
    m.rStem.1 = ll
    m.rStem.0 = 1
    return 1
endProcedure editRead
/*--- search loop in edit macro -------------------------------------*/
scanSqlSeekId: procedure expose m.
parse arg m, lx, cmd, opts
    if opts = '' then
        opts = word
                     /* line 1 col 0, otherwise first word is skipped*/
    if adrEdit("cursor =" max(trunc(lx), 1) 0, 12) = 12 then
        return -1
    do while adrEdit("seek" cmd opts, 4) = 0 /* find each command*/
        call adrEdit "(fx) = cursor"
        if m.debug then do
            call adrEdit "(LI) = LINE" fx
            call debug 'scanSqlSeekId after' lx 'found' cmd 'at' fx li
            end
        call jReset m.m.rdr, fx
        call jOpen m, '<'
        m.m.lineX = fx
        do while word(scanPos(m), 1) <= fx & scanSqlClass(m)
            if m.m.sqlClass = 'i' & m.m.val == cmd then
                return fx
            end
        call jClose m
        end
    return -1
endProcedure scanSqlSeekId
/* copy scanRead end *************************************************/
/* copy scanWin begin *************************************************
     scan the the concatenation of the lines of a reader
         any token my be split over several line
         except the end-of-line-comment-token
**********************************************************************/
scanWinIni: procedure expose m.
    if m.scanWin.ini = 1 then
        return
    m.scanWin.ini = 1
    call scanReadIni
    call classNew 'n ScanWin u ScanRead', 'm',
        , "oReset call scanWinReset m, arg, arg2",
        , "jOpen call scanWinOpen m, arg(3)",
        , "jClose call scanReadClose m",
        , 'scanNL return scanWinNl(m, unCond)',
        , 'scanCom return scanWinCom(m)',
        , 'scanInfo return scanWinInfo(m)',
        , 'scanPos  return scanWinPos(m)',
        , 'isWindow 1'
    return
endProcedure scanWinIni

/*--- instanciate a new window scanner ------------------------------*/
scanWin: procedure expose m.
parse arg rdr, wOpts
    return oNew(m.class_ScanWin, rdr, wOpts)

/*--- set the reader and window attributes of scanner m -------------*/
scanWinReset: procedure expose m.
parse arg m, m.m.rdr, winOpt
    return scanSqlOpt(scanWinOpt(oMutate(m, m.class_ScanWin), winOpt))

/*--- set the window scanner attributes -----------------------------*/
scanWinOpt: procedure expose m.
parse arg m, cuLe wiLi wiBa
    if pos('@', cuLe) > 0 then
        parse var cuLe cuLe '@' m.m.cutPos
    else
        m.m.cutPos = 1
    cuLe = word(cuLe 72, 1)
    m.m.cutLen = cuLe                      /* fix recLen */
    wiLe = cuLe * (1 + word(wiLi 5, 1))
    m.m.posMin = word(wiba 3, 1) * cuLe    /* room to go back */
    m.m.posLim = m.m.posMin + wiLe
    m.m.winTot = m.m.posLim + wiLe
    return m
endProcedure scanWinReset

/*--- open reader and start scanning --------------------------------*/
scanWinOpen: procedure expose m.
parse arg m, line0
    call jOpen m.m.rdr, '<'
    if line0 == '' then
        return scanSetPos0(m, 1 1)
    if length(line0) // m.m.cutLen \== 0 then
        line0 = line0||left('', m.m.cutLen - length(line0)//m.m.cutLen)
    return scanSetPos0(m, (1 - length(line0) % m.m.cutLen) 1, line0)
endProcedure scanWinOpen

/*--- move the source window: cut left side and append at right side
      return number of characters cut at left -----------------------*/
scanWinRead: procedure expose m.
parse arg m
    dlt = 0
    if m.m.atEnd then
        return 0
    if m.m.pos >= m.m.posLim then do     /*  cut left side */
        dlt = m.m.pos - ((m.m.pos-1) // m.m.cutLen + 1 + m.m.posMin)
        call assert 'dlt >= 0 & dlt // m.m.cutLen = 0','dlt m.m.cutLen'
        m.m.src = substr(m.m.src, dlt+1)
        m.m.pos = m.m.pos - dlt
        m.m.lineX = m.m.lineX + dlt % m.m.cutLen
        end
    r = m.m.rdr
    do while length(m.m.src) < m.m.winTot /* read and fill to len */
        if \ jRead(r) then do
            m.m.atEnd = 1
            return dlt
            end
        m.m.src = m.m.src || substr(m.r, m.m.cutPos, m.m.cutLen)
        end
    call assert 'length(m.m.src) = m.m.winTot',
              , 'm.m.winTot length(m.m.src) m.m.src'
    return dlt
endProcedure scanWinRead

/*--- return position of next line start ----------------------------*/
scanWinNLPos: procedure expose m.
parse arg m
    return m.m.pos + m.m.cutLen - ((m.m.pos - 1) // m.m.cutLen)

/*--- scan comment --------------------------------------------------*/
scanWinCom: procedure expose m.
parse arg m
    call scanWinRead m
    if m.m.scanComment \== '' then do
        cl = length(m.m.scanComment)
        if scanLook(m, cl) == m.m.scanComment then do
            np = scanWinNlPos(m)
            if np = m.m.pos then
                np = np +  m.m.cutLen
            if np >= m.m.pos + cl then do
                m.m.tok = substr(m.m.src, m.m.pos, np - m.m.pos)
                m.m.pos = np
                return 1
                end
            end
        end
    if m.m.scanNestCom then
        if scanLit(m, '/*') then do
            tk = substr(m.m.src, m.m.pos, m.m.cutLen + 2)
            call scanTextCom m, , '*/'
            if \ scanLit(m, '*/') then
                 call scanErr m, 'nested comment after /* not finished'
            if pos('*/', tk) < 1 then
                m.m.tok = left(tk, m.m.cutLen - 5)'...*/'
            else
                m.m.tok = left(tk, pos('*/', tk) + 1)
            return 1
            end
    m.m.tok = ''
    return 0
endProcedure scanWinCom


/*--- scan nl -------------------------------------------------------*/
scanWinNL: procedure expose m.
parse arg m, unCond
    call scanWinRead m
    m.m.tok = ''
    if unCond \== 1 then
       return 0
    np = scanWinNLPos(m)
    if np = m.m.pos then
        return 0
    if unCond == '?' then
        return 1
    m.m.tok = substr(m.m.pos, np-m.m.pos)
    m.m.pos = np
    return 1
endProcedure scanWinNl

/*--- return current position in input ------------------------------*/
scanWinPos: procedure expose m.
parse arg m
    ps = m.m.pos - 1
    return (m.m.lineX + (ps % m.m.cutLen)) (ps // m.m.cutLen + 1)
endProcedure scanWinPos

/*--- return a description of the current scan position -------------*/
scanWinInfo: procedure expose m.
parse arg m
    p = scanWinPos(m)
    if scanEnd(m) then do
        res = 'atEnd after'
        p = m.m.lineX - 1 + length(m.m.src) % m.m.cutLen
        p = word(p, 1)
        end
    else do
        res = 'pos' word(p, 2) 'in'
        p = word(p, 1)
        end
    return 'last token' m.m.tok 'scanPosition' ,
          strip(left(substr(m.m.src, m.m.pos), 40), 't') ,
        || '\n'res 'line' p':' strip(substr(m.m.src,
          , 1 + (p - m.m.lineX) * m.m.cutLen, m.m.cutLen), 't')
endProcedure scanWinInfo
/* copy scanWin end   ************************************************/
/* copy scanSql begin ************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanSqlReset: procedure expose m.
parse arg m, r, scanWin, stmtOpt
    call scanSqlStmtOpt scanSqlOpt(m), strip(stmtOpt)
    if scanWin \== 0 then
        return scanWinReset(m, r, scanWin)
    else if r \== '' then
        return scanReadReset(m, r)
    else
        return scanSrc(m, m.m.src)
endProcedure scanSqlReset

scanSqlOpt: procedure expose m.
parse arg m
    return scanOpt(m, m.ut_alfa'$#@', '0123456789_' , '--', 1)
endProcedure scanSqlOpt

/*--- scan a sql token put class in m.sqlclass:
      'i': ordinary identifier   e.g. Name
      'd': delimited identifier  e.g. "Delimited"
      'q': qualified identifier  e.g. abc."efg"
      'u': integer units         e.g. 8G
      'n': number                e.g. -234 or .2e3
      's': string                e.g. 'abc''ef'
      '' : at end
         : any other character   e.g. ;
      ---------------------------------------------------------------*/
scanSqlClass: procedure expose m.
parse arg m, retSpace
    m.m.val = ''
    if scanSpNlCo(m) & retSpace = 1 then do
        m.m.sqlClass = 'b'
        return 1
        end
    c2 = scanLook(m ,2)
    if scanLit(m, "'",  "x'", "X'") then do
        if \ scanStrEnd(m, "'") then
            call scanErr m, 'ending apostroph missing'
        m.m.sqlClass = 's'
        if \abbrev(m.m.tok, "'") then
            m.m.val = x2c(m.m.val)
        end
    else if scanSqlQuId(m, 1) then do
        if m.m.val.0 > 1 then
            m.m.sqlClass = 'q'
        else if abbrev(m.m.tok, '"') then
            m.m.sqlClass = 'd'
        else
            m.m.sqlClass = 'i'
        end
    else if scanSqlNumPM(m) then do
        if m.m.tok == '-' | m.m.tok == '+' then
            m.m.sqlClass = m.m.tok
        else
            m.m.sqlClass = 'n'
        end
    else if scanChar(m, 1) then
        m.m.sqlClass = m.m.tok
    else if scanEnd(m) then do
        m.m.sqlClass = ''
        return 0
        end
    else
        call scanErr m, 'cannot scan sql'
    return 1
endProcedure scanSqlClass

scanSqlSkipBrackets: procedure expose m.
parse arg m, br
    if br \== '' then
        nop
    else if scanLit(m, '(') then
        br = 1
    else
        return 0
    do while scanSqlClass(m) & m.m.sqlClass \== ';'
        if m.m.sqlClass = '('        then br = br + 1
        else if m.m.sqlClass \== ')' then iterate
        else if br > 1              then br = br - 1
        else                             return 1
        end
    call scanErr m, '; or eof, but' br 'closing ) expected'
endProcedure skipBrackets
/*--- scan an ordinary sql identifier e.g. abc, ef_12 ---------------*/
scanSqlId: procedure expose m.
parse arg m
    if \ scanName(m) then
        return 0
    m.m.val = translate(m.m.tok)
    return 1
endProcedure scanSqlId

/*--- scan a delimited or ordinay sql identifier --------------------*/
scanSqlDeId: procedure expose m.
parse arg m
    if scanSqlId(m) then
        return 1
    if \ scanString(m, '"') then
        return 0
    m.m.val = strip(m.m.val, 't')
    return 1
endProcedure scanSqlDeId

/*--- scan a qualified sql identifier -------------------------------*/
scanSqlQuId: procedure expose m.
parse arg m, starOk
    res = ''
    rto = ''
    do qx=1
        if \ scanSqlDeId(m) then do
            if qx == 1 then
                return 0     /* sometimes last qual may be '*' */
            if starOk \== 1 | \ scanLit(m, '*') then
                call scanErr m, 'id expected after .'
            else if scanLit(scanSkip(m), '.') then
                call scanErr m, 'dot after id...*'
            else
                leave
            end
        m.m.val.qx = m.m.val
        res = res'.'m.m.val
        rto = rto'.'m.m.tok
        if \ scanLit(scanSkip(m), '.') then
            leave
        call scanSpace m
        end
    m.m.val.0 = qx
    m.m.val = substr(res, 2)
    m.m.tok = substr(rto, 2)
    return 1
endProcedure scanSqlQuId

/*--- scan a sql number, Ignore After -------------------------------*/
scanSqlNumIA: procedure expose m.
parse arg m
    if \ scanSqlNumPM(m) then
        return 0
    else if m.m.tok == '+' | m.m.tok == '-' then
        call scanErr m, 'no sqlNum after +-'
    return 1
endProcedure scanSqlNumIA

/*--- scan a sql number, + or -, ignore after -----------------------*/
scanSqlNumPM: procedure expose m.
parse arg m

    if scanLit(m, '+', '-') then do
        si = m.m.tok
        call scanSkip m
        end
    else
        si = ''
    cx = scanNumUSPos(m)
    if cx == 0 then do
        m.m.val = si
        m.m.tok = si
        return si \== ''
        end
    m.m.tok = si || substr(m.m.src, m.m.pos, cx-m.m.pos)
    m.m.val = translate(m.m.tok)
    m.m.pos = cx
    return 1
endProcedure scanSqlNumIA

/*--- scan a sql number, check After --------------------------------*/
scanSqlNum: procedure expose m.
parse arg m
    if arg() \== 1 then
        return err('old interface scanSqlNum') / 0
    return scanCheckNumAfter(m, scanSqlNumIA(m))
endProcedure ScanSqlNum

/*--- scan a sql number with a unit which may follow without space --*/
scanSqlNumUnit: procedure expose m.
parse arg m, both, units
    if \ scanSqlNumIA(m) then
        return 0
    nu = m.m.val
    sp = scanSpace(m)
    if scanSqlId(m) then do
        if units == '' | wordpos(m.m.val, units) > 0 then
            nu = nu m.m.val
        else if both | \ sp then
            call scanErr m, 'bad unit' m.m.val 'after' nu
        else
            call scanBack m, m.m.tok
        end
    else if both then
        call scanErr m, 'no unit after' nu
    else if \sp & pos(scanLook(m, 1), m.m.scanNameR) > 0 then
        call scanErr m, 'bad unit after number' nu
    m.m.val = nu
    return 1
endProcedure scanSqlNumUnit

/*--- find next statement, after scanSqlStmtOpt -----------------------
       m.m.stop contains delimiter, will be changed by
          terminator?; or --#terminator               */
scanSqlStmtOpt: procedure expose m.
parse arg m, m.m.stop
    if m.m.stop == '' then
        m.m.stop = ';'
    return m
endProcedure scanSqlStmtOpt

scanSqlStop: procedure expose m.
parse arg m
    res = ''
    fuCo = copies(m.m.scanComment'#SET', m.m.scanComment \== '')
    u1 = '''"'left(m.m.scanComment, m.m.scanComment \== '')
    do lx=1
        if lx > 100 then
            say '????iterating' scanLook(m)
        if m.m.stop == '' then
            scTx = scanTextCom(m, u1 ,fuCo)
        else
            scTx = scanTextCom(m, u1||left(m.m.stop,1), m.m.stop fuCo)
        if scTx then
            res = res || m.m.tok
        if fuCo \== '' then
            if scanLook(m, length(fuCo)) == fuCo then do
                if scanCom(m) then do
                    tx = m.m.tok
                    if word(tx, 2) == 'TERMINATOR' ,
                           & length(word(tx, 3)) == 1 then do
                        m.m.stop = word(tx, 3)
                        if \ (right(res, 1) == ' ' ,
                             | scanLook(m, 1) == ' ') then
                            res = res' '
                        end
                    else
                        say 'ignoring --##SET at' scanInfo(m)
                    end
                iterate
                end
        if m.m.stop \== '' then
            call scanLit m, m.m.stop
        res = strip(res)
        if length(res)=11 ,
            & abbrev(translate(res), 'TERMINATOR') then do
            m.m.stop = substr(res, 11, 1)
            res = ''
            end
        return res
        end
endProcedure scanSqlStop

scanSqlStmt: procedure expose m.
parse arg m
    do forever
        res = scanSqlStop(m)
        if res <> '' then
            return res
        if scanEnd(m) then
            return ''
        end
endProcedure scanSqlStmt

/*-- return next sqlStmt from rdr ( or string or '' = j.in) ---------*/
scanSqlIn2Stmt: procedure expose m.
parse arg rdr, wOpt
    s = scanSqlIn2Scan(rdr, m'.SCAN_SqlIn2Stmt', wOpt)
    res = scanSqlStmt(scanOpen(s))
    call scanReadClose s
    return res
endProcedure scanSqlIn2Stmt

/*-- reset s as scanSql from rdr ( or string or '' = j.in) ----------*/
scanSqlIn2Scan: procedure expose m.
parse arg m, s, wOpt, sOpt
    if m \== '' & wOpt == '' then
        if oKindOfString(m) then
            wOpt = 0
    return scanSqlReset(s, in2File(m), wOpt, sOpt)
endProcedure scanSqlIn2Scan

/*-- create a new scanSqlStmtRdr ------------------------------------*/
scanSqlStmtRdr: procedure expose m.
parse arg rdr, wOpt, sOpt
    return oNew('ScanSqlStmtRdr', rdr, wOpt, sOpt)

/*-- reset a new scanSqlStmtRdr
         must be called from jReset to allow jRead ------------------*/
scanSqlStmtRdrReset: procedure expose m.
parse arg m, rdr, wOpt, sOpt
    call scanSqlIn2Scan rdr, m'.SCAN', wOpt, sOpt
    return oMutate(m, m.class_ScanSqlStmtRdr)
endProcedure scanSqlStmtRdrReset
/* copy scanSql end   ************************************************/
/* copy scanUtil begin ************************************************
    scan db2 utility input statements using scan and a reader
**********************************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanUtilOpt: procedure expose m.
parse arg m
    call scanSqlOpt m
    m.m.scanNestCom = 0
    m.m.utilBrackets = 0
    m.scanUtil =  'BACKUP CATENFM CATMAINT CHECK' ,
                  'COPY COPYTOCOPY DIAGNOSE EXEC LISTDEF LOAD' ,
                  'MERGECOPY MODIFY OPTIONS QUIESCE REBUILD' ,
                  'RECOVER REORG REPAIR REPORT RESTORE' ,
                  'RUNSTATS STOSPACE TEMPLATE UNLOAD'
    return m
endProcedure scanUtilOpt
/*--- scan next token and put its class in m.sc.utilClass:
      'u' a utility name
      'n' a name
      '"' a quoted name
      "'" an apostroph'd string
      '.' a .
      ',' a ,
      'v' a value
      ''  at end
      ---------------------------------------------------------------*/
scanUtil: procedure expose m.
parse arg sc
    m.sc.utilSpace = scanSpace(sc)
    ty = '?'
    if scanLit(sc, '(') then do
        m.sc.utilBrackets = m.sc.utilBrackets + 1
        end
    else if scanLIT(sc, ')') then do
        m.sc.utilBrackets = m.sc.utilBrackets - 1
        if m.sc.utilBrackets < 0 then
           call scanErr sc, 'unmatched closing bracket )'
        end
    else if scanLit(sc, ',') then do
        end
    else if scanLit(sc, '.') then do
        end
    else if scanString(sc, "'") then do
        end
    else if scanString(sc, '"') then do
        end
    else if scanName(sc) then do
        m.sc.val = translate(m.sc.tok)
        if m.sc.utilBrackets > 0 then
            ty = 'n'
        else if 0 < wordPos(m.sc.val, m.scanUtil) then
            ty = 'u'
        else
            ty = 'n'
        end
    else if scanVerify(sc, ' (),''"', 'm') then do
        ty = 'v'
        m.sc.val = translate(m.sc.tok)
        end
    else if \scanEnd(sc) then do
            call scanErr sc, 'scanUtil stopped before end'
        end
    else do
        ty = ''
        m.sc.val = ''
        end
    if ty == '?' then
        m.sc.utilClass = left(m.sc.tok, 1)
    else
        m.sc.utilClass = ty
    return m.sc.utilClass
endProcedure scanUtil

/*--- scan a value or a bracketed list of values --------------------*/
scanUtilValue: procedure expose m.
parse arg sc, remApo, nl
    if remApo = '' | rempApo = 0 then
        remApo = "nv"
    else if rempApo = 1 then
        remApo = "nv'"
    if '(' \== scanUtil(sc) then
         return scanUtilValueOne(sc, remApo)
    v = ''
    brx = m.sc.utilBrackets
    oLine = word(scanPos(sc), 1)
    do forever
        call scanUtil sc
        one = scanUtilValueOne(sc, remApo)
        if one == '' then
           call scanErr sc, 'eof in brackets'
        else if brx > m.sc.utilBrackets then
           return v
        nLine = word(scanPos(sc), 1)
        if \ m.sc.utilSpace then
            v = v || one
        else if nl \== '' & oLine <> nLine then
            v = v || nl || one
        else
            v = v' 'one
        oLine = nLine
        end
endProcedure scanUtilValue

scanUtilValueOne: procedure expose m.
parse arg sc, valTy
    if m.sc.utilClass == '' then
        return ''
    else if m.sc.utilClass == 'u' then
        call scanErr sc, 'util in scanUtilValueOne'
    if pos(m.sc.utilClass, valTy) > 0 then
        return m.sc.val
    else
        return m.sc.tok
endProcedure scanUtilValueOne

/*--- skip over nested brackets -------------------------------------*/
scanUtilSkipBrackets: procedure expose m.
parse arg m, br, doCat
    if br \== '' then
        lim = m.m.utilBrackets - br
    else if scanLit(m, '(') then do
        lim = m.m.utilBrackets
        m.m.utilBrackets = lim + 1
        end
    else
        return 0
    doCat = doCat == 1
    res = ''
    do while scanUtil(m) \== ''
        if m.m.utilBrackets <= lim then do
            if doCat then
                m.m.val = res
            return 1
            end
        if doCat then
            res = res m.m.tok
        end
    return scanErr(m, 'eof with' m.m.utilBrackets 'open (')
endProcedure skipBrackets

/*--- analyze a punch file write intoField to stdOut ----------------*/
scanUtilInto: procedure expose m.
parse arg m
    if m.m.utilBrackets \== 0 then
        call scanErr m, 'scanUtilInto with brackets' m.m.utilBrackets
  /*sc = scanUtilReader(m.j.in)
    call jOpen sc, 'r'
 */ do forever
        cl = scanUtil(m)
        if cl == '' then
            return 0
        if cl = 'n' & m.m.tok == 'INTO' then
            leave
        end
    if scanUtil(m) \== 'n' | m.m.tok \== 'TABLE' then
        call scanErr m, 'bad into table '
    if \ scanSqlQuId(scanSkip(m)) then
        call scanErr m, 'table name expected'
    if m.m.utilBrackets \== 0 then
        call scanErr m, 'into table in brackets' m.m.utilBrackets
    m.m.tb = m.m.val
    m.m.part = ''
    m.m.when = ''
    do forever
        cl = scanUtil(m)
        if cl == '' then
            call scanErr m, 'eof after into'
        if cl == 'n' & m.m.tok == 'PART' then do
            if scanUtil(m) == 'v' then
                m.m.part = m.m.val
            else
                call scanErr m, 'bad part'
            end
        else if cl=='n' & wordPos(m.m.val, 'WHEN WORKDDN') > 0 then do
            call scanUtilSkipBrackets m
            end
        else if cl == '(' then do
           leave
           end
        end
    oX =  m.m.lineX
    oL =  overlay('', m.m.src, 1, m.m.pos-2)
    do while m.m.utilBrackets > 0
        call scanUtil m
        if oX \== m.m.lineX then do
            call out strip(oL, 't')
            oX =  m.m.lineX
            oL =  m.m.src
            end
        end
    call out left(oL, m.m.pos)
 /* call jClose sc
 */ return 1
endProcedure scanUtilInto
/* copy scanUtil end *************************************************/
/* copy pipe begin ****************************************************
**********************************************************************/
pipeIni: procedure expose m.
    if m.pipe_ini == 1 then
        return
    m.pipe_ini = 1
    call catIni
    call mapReset v
    m.v_with.0 = 0
    m.v_withMap = ''
    m.v_with.0.map = ''
    m.pipe.0 = 1
    m.pipe.1.in  = m.j.in
    m.pipe.1.out = m.j.out
    call pipe '+'
    return
endProcedure pipeIni

/*-------------------------------
  +-       push pop frame
  PYNFA    ouput: Parent saY Newcat File, Appendtofile
  psf|     input: parent string file oldOut
  old          --> new
  pipeBegin    --> pipe '+N'
  pipeBeLa f   --> pipe '+F'
  pipeLast     --> pipe 'P|'
  pipeLast f   --> pipe 'F|', f
  pipeEnd      --> pipe '-'
--------------------------------*/
pipe: procedure expose m.
parse arg opts, aO
    ox = 1; oc = substr(opts, ox, 1)
    ax = m.pipe.0
    px = ax -1
    if oc == '-' then do
        if px < 2 then
            call err 'pipe pop empty'
        call jClose m.pipe.ax.out
        call jClose m.pipe.ax.in
        ax = px
        m.pipe.0 = ax
        px = ax-1
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if oc == '+' then do
        px = ax
        ax = ax+ 1
        m.pipe.0 = ax
        m.pipe.ax.in  = jOpen(m.pipe.px.in, '<')
        m.pipe.ax.out = jOpen(m.pipe.px.out, '>')
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    oOut = m.pipe.ax.out
    if pos(oc, 'NYPFA') > 0 then do
        call jClose oOut
        if oc == 'Y' then
            m.pipe.ax.out = jOpen(m.pipe.1.out, '>')
        else if oc == 'P' then
            m.pipe.ax.out = jOpen(m.pipe.px.out, '>')
        else if oc == 'N' then
            m.pipe.ax.out = jOpen(Cat(), '>')
        else if oc == 'F' then
            m.pipe.ax.out = jOpen(o2file(aO), '>')
        else if oc == 'A' then
            m.pipe.ax.out = jOpen(o2file(aO), '>>')
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    m.j.out = m.pipe.ax.out
    if oc \== ' ' then do
        call jClose m.pipe.ax.in
        if substr(opts, ox+1) = '' & oc \== 's' then
            ct = ''
        else
            ct = jOpen(Cat(), '>')
        lx = 3
        do forever
            if oc == 's' then do
                call jWrite ct, arg(lx)
                lx = lx + 1
                end
            else do
                if oc == 'p' then
                    i1 = m.pipe.px.in
                else if oc == '|' then
                    i1 = oOut
                else if oc == 'f' then do
                    i1 = arg(lx)
                    lx = lx + 1
                    end
                else
                    call err 'implement' oc 'in pipe' opts
                if ct \== '' then
                    call jWriteAll ct, o2File(i1)
                end
            ox = ox + 1
            if substr(opts, ox, 1) == ' ' then
                leave
            else if ct == '' then
                call err 'pipe loop but ct empty'
            else
                oc = substr(opts, ox, 1)
            end
        if ct == '' then
            m.pipe.ax.in = jOpen(o2file(i1), '<')
        else
            m.pipe.ax.in = jOpen(jClose(ct), '<')
        if lx > 3 & lx <> arg() + 1 then
            call err 'pipe opts' opts 'but' arg() 'args not' (lx-1)
        end
    m.j.in  = m.pipe.ax.in
    return
endProcedure pipe

/*--- write all from rdr (rsp in) to out, not lazy ----------------*/
pipeWriteNow: procedure expose m.
    parse arg rdr
    call jWriteNow m.j.out, if(rdr == '', m.j.in, rdr)
    return
endProcedure pipeWriteNow

/*--- write all from rdr (rsp in) to out, possibly lazy -----------*/
pipeWriteAll: procedure expose m.
parse arg rdr
    call jWriteAll m.j.out, if(rdr == '', m.j.in, rdr)
    return
endProcedure pipeWriteAll

pipePreSuf: procedure expose m.
parse arg le, ri
    do while in()
        call out le || m.in || ri
        end
    return
endProcedure pipePreSuf

vIsDefined: procedure expose m.
parse arg na
    return   '' \== vAdr(na, 'g')
endProcedure vIsDefined

vWith: procedure expose m.
parse arg fun, o
    if fun == '-' then do
        tBe = m.v_with.0
        tos = tBe - 1
        if tos < 0 then
            call err 'pop empty withStack'
        m.v_with.0 = tos
        m.v_withMap = m.v_with.tos.map
        return m.v_with.tBe.obj
        end
    else if fun \== '+' then
        call err 'bad fun vWith('fun',' o')'
    par = m.v_with.0
    tos = par + 1
    m.v_with.0 = tos
    if symbol('m.v_with.tos.obj') == 'VAR' then
      if objClass(o) == objClass(m.v_with.tos.obj) then do
          m.v_with.tos.obj = o
          m.v_withMap = m.v_with.tos.map
          return
          end
    m.v_with.tos.obj = o
    if par > 0 then
        key = m.v_with.par.classes
    else
        key = ''
    if o \== '' then
        key = strip(key objClass(o))
    m.v_with.tos.classes = key
    if symbol('m.v_withManager.key') == 'VAR' then do
        m.v_with.tos.map = m.v_withManager.key
        m.v_withMap = m.v_withManager.key
        return
        end
    m = mapNew()
    m.v_with.tos.map = m
    m.v_withMap = m
    m.v_withManager.key = m
    do kx=1 to words(key)
        c1 = word(key, kx)
        call vWithAdd m, kx, classMet(c1, 'oFlds')
        call vWithAdd m, kx, classMet(c1, 'stms')
        end
    return
endProcedure vWith

vWithAdd: procedure expose m.
parse arg m, kx, ff
    do fx=1 to m.ff.0
        n1 = m.ff.fx
        dx = pos('.', n1)
        if dx > 1 then
            n1 = left(n1, dx-1)
        else if dx = 1 | n1 = '' then
            iterate
        call mPut m'.'n1, kx
        end
    return
endProcedure vWithAdd

vForWith: procedure expose m.
parse arg var
    call vWith '-'
    if \ vIn(var) then
        return 0
    call vWith '+', m.in
    return 1
endProcedure vForWith

vGet: procedure expose m.
parse arg na
    a = vAdr(na, 'g')
    if a = '' then
        call err 'undefined var' na
    return m.a
endProcedure vGet


vPut: procedure expose m.
parse arg na, val
    a = vAdr(na, 'p')
    m.a = val
    return val
endProcedure vPut

/*--- find the final address
      return f || a with address a and
             f = m -> mapGet(a), o -> obect m.a, s -> string m.a  ---*/
vAdr: procedure expose m.
parse arg na, f
    cx = 0
    cx = verify(na, '&>', 'm')
    if cx > 0 then
        a = left(na, cx-1)
    else do
        a = na
        cx = length(na)+1
        end
    nxt = 0
    do forever
        cy = verify(na, '&>', 'm', cx+1)
        if cy > 0 then
            fld = substr(na, cx+1, cy-cx-1)
        else
            fld = substr(na, cx+1)
        if substr(na, cx, 1) == '>' then do
            if nxt then
                a = vAdrByM(a)
            if fld \== '' then
               a = a'.'fld
            end
        else do
            if nxt then
                a = vAdrByM(a)
            mp = m.v_withMap
            aL = a
            if pos('.', a) > 0 then
                aL = left(a, pos('.', a)-1)
            if mp \== '' & symbol('m.mp.aL') == 'VAR' then do
                wx = m.mp.aL
                a = m.v_with.wx.obj'.'a
                end
            else if cx >= length(na) then
                return mapAdr(v, a, f)
            else
                a = mapAdr(v, a, 'g')
            if fld \== '' then
                a = vAdrByM(a)'.'fld
            end
        if cy < 1 then do
            if f == 'g' then
                if symbol('m.a') \== 'VAR' then
                    return ''
            return a
            end
        cx = cy
        nxt = 1
        end
endProcedure vAdr

vAdrByM:
parse arg axx
    if axx = '' then
        return err('null address at' substr(na, cx) 'in' na)
    if symbol('m.axx') \== 'VAR' then
        return err('undef address' axx 'at' substr(na, cx) 'in' na)
    ayy = m.axx
    if ayy == '' then
          return err('null address at' substr(na, cx) 'in' na)
    return ayy
endProcedure vAdrByM

vIn: procedure expose m.
parse arg na
    if \ in() then
       return 0
    if na \== '' then
       call vPut na, m.in
    return 1
endProcedure vIn

vRead: procedure expose m.    /* old name ????????????? */
parse arg na
    say '||| please use vIn instead fo vIn'
    return vIn(na)

vHasKey: procedure expose m.
parse arg na
    return mapHasKey(v, na)

vRemove: procedure expose m.
parse arg na
    return mapRemove(v, na)
/* copy pipe end *****************************************************/
/* copy cat  begin ****************************************************
**********************************************************************/
/*--- create a new cat ----------------------------------------------*/
cat: procedure expose m.
    m = oNew('Cat') /* calls catReset */
    do ax=1 to arg()
        call catWriteAll m, arg(ax)
        end
    return m
endProcedure cat

catReset: procedure expose m.
parse arg m
    m.m.RWs.0 = 0
    m.m.catWr = ''
    m.m.catRd = ''
    m.m.catIx = -9e9
    m.m.catKeepOpen = ''
    return m
endProcedure catReset

catClose: procedure expose m.
parse arg m
    if m.m.catWr \== '' then do
        call jClose m.m.catWr
        call mAdd m'.RWS', m.m.catWr
        m.m.catWr = ''
        end
    if m.m.catRd \== '' then do
        call jClose m.m.catRd
        m.m.catRd = ''
        end
    m.m.catIx = -9e9
    return m
endProcedure catClose

catOpen: procedure expose m.
parse arg m, oo
    if oo == m.j.cRead then do
        m.m.catIx = 0
        call catNextRdr m
        end
    else if oo == m.j.cWri | oo == m.j.cApp then do
        if oo == m.j.cWri then
            m.m.RWs.0 = 0
        m.m.catIx = -55e55
        end
    else do
        call err 'catOpen('m',' oo') bad opt'
        end
    return m
endProcedure catOpen

/*--- return and open next reader -----------------------------------*/
catNextRdr: procedure expose m.
parse arg m
    if m.m.catRd \== '' then
        call jClose m.m.catRd
    cx = m.m.catIx + 1
    m.m.catIx = cx
    if cx > m.m.RWs.0 then do
        m.m.catRd = ''
        return 0
        end
    m.m.catRd = m.m.RWs.cx
    if cx = word(m.m.catKeepOpen, 1) then
        m.m.catKeepOpen = subWord(m.catKeepOpen, 2)
    else
        call jOpen m.m.catRd , m.j.cRead
    return 1
endProcedure catNextRdr

catRead: procedure expose m.
parse arg m, rStem
    do while m.m.catRd \== ''
        if jReadSt(m.m.catRd, rStem) then
            return 1
        call catNextRdr m
        end
    return 0
endProcedure catRead

catWrite: procedure expose m.
parse arg m, wStem
    if m.m.catWr == '' then
        m.m.catWr = jOpen(jBuf(), m.j.cWri)
    call jWriteSt m.m.catWr, wStem
    return
endProcedure catWrite

/*--- write contents of a reader to cat
          or keep it for later reading ------------------------------*/
catWriteAll: procedure expose m.
parse arg m
    if m.m.catWr \== '' then do
        call mAdd m'.RWS', jClose(m.m.catWr)
        m.m.catWr = ''
        end
    do ax=2 by 1 to arg()
        r = o2File(arg(ax))
        call mAdd m'.RWS', r
        if m.r.jReading then do
            m.m.catKeepOpen = m.m.rws.0 m.m.catKeepOpen
            call jOpen r, m.j.cRead
            end
        end
    return
endProcedure catWriteAll

/*--- create a reader/WriteO for an external file -------------------*/
file: procedure expose m.
parse arg str
    return oNew('File', str)
endProcedure file

fileChild: procedure expose m.
parse arg m, name, opt
    interpret objMet(m, 'fileChild')
endProcedure fileChild

fileRm: procedure expose m.
parse arg m
    interpret objMet(m, 'fileRm')
    return
endProcedure fileRm

filePath: procedure expose m.
parse arg m
    interpret objMet(m, 'filePath')
endProcedure filePath

fileIsFile: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsFile')
endProcedure fileIsFile

fileIsDir: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsDir')
endProcedure fileIsDir

fileMkDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileMkDir')
    return
endProcedure fileRm

fileRmDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileRmDir')
    return
endProcedure fileRm

/*--- create a reader/WriteO for the filelist of a directory---------*/
fileList: procedure expose m.
parse arg m, opt
    if oKindOfString(m) then
        return oNew('FileList', dsn2Jcl(oAsString(m)),  opt)
    else
        return oNew('FileList', filePath(m),  opt)
endProcedure fileList

catIni: procedure expose m.
    if m.cat.ini == 1 then
        return
    m.cat.ini = 1
    call errIni
    call jIni
    call classNew "n Cat u JRW", "m",
        , "jOpen  call catOpen m, opt",
        , "jReset call catReset m, arg",
        , "jClose call catClose m",
        , "jRead if \ catRead(m, rStem) then return 0",
        , "jWrite  call catWrite m, wStem",
        , "jWriteAll call catWriteAll m, rdr; return"

    if m.err_os == 'TSO' then
        call fileTsoIni
    else
        call err 'file not implemented for os' m.err_os
    return
endProcedure catIni
/* copy cat  end   ***************************************************/
/* copy mail begin ***************************************************/
mailHead: procedure expose m.
parse arg m, subj, rec, snd
    m.m.1  = 'sender='if(snd=='', userid(), snd)
    m.m.2  = 'type=TEXT/HTML'
    m.m.3  = 'to='rec
    m.m.4  = 'subject='subj
    m.m.5  = 'SEND=Y'
    m.m.6  = 'TEXT=<HTML>'
    m.m.7  = 'TEXT=<HEAD>'
    m.m.8  = 'TEXT=</HEAD>'
    m.m.9  = 'TEXT=<BODY>'
    m.m.10 = 'TESTINFO=Y'
    m.m.0 = 10
    return m
endProce4 re mailHead

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

mailSend: procedure expose m.
parse arg m, dsn
    call mAdd m,'INFO=Y' ,
               ,'TEXT=</BODY>' ,
               ,'TEXT=</HTML>'
    call dsnAlloc 'dd(mailIn)' if(dsn<> '', dsn, 'new') '::v4092'
    call writeDD mailIn, 'M.'m'.'
    call tsoClose mailIn
    if m.mail_libAdd \== 0 then do
        dsnOs3560 = 'PCL.U0000.P0.'iirz2dsn(sysVar(sysNode)) ,
              ||    'AKT.PERM.@008.LLB'
        call adrTSO "TLIB ADD DATASET('"dsnOs3560"') STEPLIB"
        end
    address LINKMVS 'OS3560'
    if rc <> 0 then
        call err 'call OS3560 failed Rc('rc')'
    if m.mail_libAdd \== 0 then
        call adrTSO "TLIB delete DATASET('"dsnOs3560"') STEPLIB"
    call tsoFree mailIn
    return 0
endProcedure mailSend
/* copy mail end *****************************************************/
/* copy fileTso begin ************************************************/
fileTsoReset: procedure expose m.
parse arg m, sp
    m.m.wriMax = 200
    if symbol('m.m.defDD') \== 'VAR' then
        m.m.defDD = 'CAT*'
    m.m.spec = sp
    return m
endProcedure fileTsoReset

fileTsoOpen: procedure expose m.
parse arg m, opt
    call dsnSpec m, m.m.spec
    if m.m.dsn ='INTRDR' | wordPos('WRITER(INTRDR)', m.m.attr) > 0 then
        m.m.stripT = 80
    else
        m.m.stripT = copies('t',
             , pos(':V', m.m.attr) < 1 | pos('RECFM(V', m.m.attr) > 0)
    if opt == m.j.cRead then do
        aa = dsnAllo2(m, 'SHR', m.m.defDD)
        if pos('(', m.m.dsn) > 0 & m.m.sys == '' then
            if sysDsn("'"m.m.dsn"'") <> 'OK' then
                call err 'cannot read' m.m.dsn':' sysDsn("'"m.m.dsn"'")
        call tsoOpen word(aa, 1), 'R'
        end
    else do
        if opt == m.j.cApp then
            aa = dsnAllo2(m, 'MOD', m.m.defDD)
        else if opt == m.j.cWri then
            aa = dsnAllo2(m, 'OLD', m.m.defDD)
        else
            call err 'fileTsoOpen('m',' opt') with bad opt'
        call tsoOpen word(aa, 1), 'W'
        end
    m.m.buf.0 = 0
    parse var aa m.m.dd m.m.free
    call errAddCleanup 'call jCloseClean' m
    return m
endProcedure fileTsoOpen

fileTsoClose: procedure expose m.
parse arg m
    call tsoClose m.m.dd
    call tsoFree  m.m.free
    m.m.free  = ''
    m.m.dd    = ''
    call errRmCleanup 'call jCloseClean' m
    return m
endProcedure fileTsoClose

fileTsoWrite: procedure expose m.
parse arg m, wStem
    if m.m.stripT \== '' then do
        m.j_b.0 = m.wStem.0
        if m.m.stripT == 't' then do bx=1 to m.j_b.0
            m.j_b.bx = strip(m.wStem.bx, m.m.stripT)
            end
        else do bx=1 to m.j_b.0
            m.j_b.bx = left(m.wStem.bx, m.m.stripT)
            end
        wStem = j_b
        end
    call writeDD m.m.dd, 'M.'wStem'.', , m.m.tso_truncOk == 1
    return
endProcedure fileTsoWrite

fSub: procedure expose m.
    return file('sysout(T) writer(intRdr)')
endProcedure fSub
/*--- open file with spec spec, edit it at close --------------------
         vw = if contains abbrev of VIEW then view
              if contains 0 then do NOT use o2text ------------------*/
fEdit: procedure expose m.
parse arg spec, vw
    if spec == '' then
        spec = 'new ::f'
    else if abbrev(spec, '::') then
        spec = 'new' spec
    else if abbrev(spec, ':') then
        spec = 'new' ':'spec
    if pos('0', vw) < 1 then
        f = oNew(m.class_FileEdit, spec)
    else do
        f = oNew(m.class_FileEdit0, spec)
        vw = strip(translate(vw, ' ', 0))
        end
    m.f.editArgs = vw
    return f
endProcedure fEdit

fileTsoEditClose: procedure expose m.
parse arg m
    dsn = m.m.dsn
    parse var m.m.editArgs eTy eAr
    upper eTy
    if abbrev('VIEW', eTy, 1) then
        eTy = 'view'
    else do
        if \ abbrev('EDIT', eTy) then
            eAr = m.m.editArgs
        eTy = 'edit'
        end
                    /* parm uses a variable not text ||||*/
    cx = pos('PARM(', translate(eAr))
    cy = pos(')', eAr, cx+5)
    if cx > 0 & cy > cx then do
        macrParm = substr(eAr, cx+5, cy-cx-5)
        eAr = left(eAr, cx+4)'macrParm'substr(eAr, cy)
        end
    if dsn \== '' then do
        call fileTsoClose m
        call adrIsp eTy "dataset('"dsn"')" eAr, 4
        return
        end
    fr = m.m.free
    dd = m.m.dd
    m.m.free = ''
    call fileTsoClose m
    call adrIsp "LMINIT DATAID(lmmId) ddName("dd") ENQ(SHRW)"
    eRc = adrIsp(eTy "dataid("lmmId")" eAr, '*')
    lRc = adrIsp("LMFree DATAID("lmmId")", '*')
    call tsoFree fr
    if (eRc \== 0 & eRc \== 4) | lRc \== 0 then
        call err eTy eAr 'rc' eRc', lmFree rc' lRc
    return
endProcedure fileTsoEditClose

fileTsoIni: procedure expose m.
    if m.fileTso.ini == 1 then
        return
    m.fileTso.ini = 1
    m.file.sep = '.'
    m.fileTso.buf = 0
    call jIni
    um = "call err 'for tso undefined method'"
    call classNew "n File u JRW", "m",
        , "jOpen  call fileTsoOpen m, opt",
        , "jReset call fileTsoReset m, arg",
        , "jClose call fileTsoClose m",
        , "jRead if \ readDD(m.m.dd, 'M.'rStem'.') then return 0",
        , "jWrite call fileTsoWrite m, wStem",
        , "filePath call dsnSpec m, m.m.spec; return m.m.dsn" ,
        , "fileIsFile" um "'fileIsFile'"      ,
        , "fileIsDir   return 1"              ,
        , "fileChild   return file(word(m.m.spec, 1)'.'name opt)",
        , "fileRm"     um "'fileRm'"          ,
        , "fileMkDir"  ,
        , "fileRmDir"  um "'fileRmDir'"
    call classNew "n FileList u JRW", "m",
        , "jReset if arg2 == 'r' then m.m.dsnMask=arg'.**';",
            "else m.m.dsnMask=arg || copies('.*', pos('*', arg) < 1)" ,
        , "jOpen  call csiOpen m, m.m.dsnMask",
        , "jClose" ,
        , "jRead do bx=1 to 10 while csiNext(m, rStem'.'bx); end;",
                "m.rStem.0=bx-1"
    call classNew "n FileEdit0 u File", "m",
        , "jClose call fileTsoEditClose m"
    call classNew "n FileEdit u FileEdit0, f MAXL v", "m",
        , "jOpen  call fileTsoOpen m,opt; m.m.maxL=tsoDSIMaxl(m.m.dd)",
        , "jWrite call fileTsoWrite m, o2TextStem(wStem, j_b,m.m.maxL)"
    return
endProcedure fileTsoIni
/* copy fileTso end   ************************************************/
/* copy mat begin ****************************************************/
sqrt: procedure expose m.
parse arg n
    if n < 2 then
        return n
     k = 1
     g = n
     do while k+1 < g
         m = (g + k) % 2
         if m * m <= n then
             k = m
         else
             g = m
         end
     return k
endProcedure sqrt

isPrime: procedure expose m.
parse arg n
    if n < 2 then
        return 0
    if n // 2 = 0 then
        return n = 2
    do q=3 by 2 to sqrt(n)
        if n // q = 0 then
            return 0
        end
    return 1
endProcedure isPrime

nxPrime: procedure expose m.
parse arg n
    do i = n + (\ (n // 2)) by 2
        if isPrime(i) then
            return i
        end
endProcedure nxPrime

permut: procedure expose m.
parse arg m, p
    m.m.1 = 1
    do i=2 while p > 0
        j = i - (p // i)
        m.m.i = m.m.j
        m.m.j = i
        p = p % i
        end
    m.m.0 = i-1
    return i-1
endProcedure permut
/* copy mat end   ****************************************************/
/* copy db2Util begin ************************************************/
/--- return (first) list of columns from punch file
     i.e. lines between first pair of ( and ) on a line
     used by abub gbGr ----------------------------------------------*/
loadCols: procedure expose m.
    if (\ in()) | word(m.in, 1) <> 'LOAD' then
       call err 'not load but' m.l1
    do while in() & strip(m.in) \== '('
        end
    if strip(m.in) \== '(' then
        call err '( not found in load:' m.in
    m.in = '-'
    do while in() & strip(m.in) \== ')'
        call out m.in
        end
    if strip(m.in) \== ')' then
        call err ') not found in load:' m.in
    return 1
endProcedure loadCols
/* ???????????? achtung nicht fertig |
          Idee: allgemein Punch Umformungs Utility
              aber man müsste wohl auf scan Util umstellen
                  und abstürzen wenn man etwas nicht versteht
          GrundGerüst von cadb2 umgebaut
????????????????? */

db2UtilPunch: procedure expose m.
parse upper arg args
    call scanSrc scanOpt(s), args
    a.rep = 1
    a.tb = ''
    a.trunc = 0
    a.iDD = ''
    a.iDSN = ''
    do while scanKeyValue(scanSkip(s), 1)
        ky = m.s.key
        say '????ky' ky m.s.val
        if wordPos(ky, 'REP TB TRUNC IDD IDSN') < 1 then
            call scanErr s, 'bad key' ky
        a.ky = m.s.val
        end
    if a.iDSN \== '' then do
        if a.iDD == '' then
            a.iDD = 'IDSN'
        call out '  TEMPLATE' a.iDD 'DSN('a.iDsn')'
        end
    do while in() & word(m.in, 1) <> 'LOAD'
        call out m.in
        end
    ll = space(m.in, 1)
    if \ abbrev(ll, 'LOAD DATA ') then
        call err 'bad load line:' m.in
    call out subword(m.in, 1, 2) 'LOG NO'
    if abbrev(ll, 'LOAD DATA INDDN ') then
        call db2UtilPunchInDDn word(ll, 4)
    else if \ abbrev(ll, 'LOAD DATA LOG ') then
        call err 'bad load line' m.in
    if a.rep then
        call out '    STATISTICS INDEX(ALL) UPDATE ALL'
    call out '    DISCARDS 1'
    call out '    ERRDDN   TERRD'
    call out '    MAPDDN   TMAPD '
    call out '    WORKDDN  (TSYUTD,TSOUTD) '
    call out '  SORTDEVT DISK '
    do in()
        li = m.in
        if pos('CHAR(', li) > 0 then
            call out strip(li, 't') 'TRUNCATE'
        else if word(li, 1) word(li, 3) == 'PART INDDN' then do
            call out li,
            call out '  RESUME NO REPLACE COPYDDN(TCOPYD)' ,
            call out '  DISCARDDN TDISC '
            end
        else
            call out li
        end
    return
endProcedure db2UtilPunch

db2UtilPunchInDDn:
parse arg inDDn
     if a.iDD == '' then
         ll =  '    INDDN' inDDn
     else
         ll =  '    INDDN' a.iDD
     if a.rep then
         call out ll 'RESUME NO REPLACE COPYDDN(TCOPYD)'
     else
         call out ll 'RESUME YES'
     call out  '    DISCARDDN TDISC'
     return
endSubroutine db2UtilPunchInDDn
/* copy db2Util end   ************************************************/
/* copy sqlDiv begin *************************************************/
/*--- generate the format m for a sql cx as specified in sp
          use the information from the sqlDa ------------------------*/
sqlFTabReset: procedure expose m.
parse arg ff, maxCh, maxBlo, maxDe
    return sqlFTabOpts(fTabReset(ff, , , '-'), maxCh, maxBlo, maxDe)

/*--- default formats per datatype ----------------------------------*/
sqlFTabOpts: procedure expose m.
parse arg ff, m.ff.maxChar, m.ff.blobMax, m.ff.maxDec
    if m.ff.maxChar == '' then
        m.ff.maxChar = 32
    if m.ff.blobMax == '' then
        m.ff.blobMax = 200
    bf = '%-'max(m.ff.blobMax, 4)'C'
    m.ff.sql2fmt.384 = '%-10C' /* date    */
    m.ff.sql2fmt.388 = '%-8C'  /* time    */
    m.ff.sql2fmt.392 = '%-26C' /* timestamp */
    m.ff.sql2fmt.400 = 'c'     /* graphic string */
    m.ff.sql2fmt.404 = bf      /* BLOB           */
    m.ff.sql2fmt.408 = bf      /* CLOB           */
    m.ff.sql2fmt.412 = bf      /* DBCLOB         */
    m.ff.sql2fmt.448 = 'c'     /* varchar        */
    m.ff.sql2fmt.452 = 'c'     /* char           */
    m.ff.sql2fmt.452 = 'c'     /* long varchar   */
    m.ff.sql2fmt.460 = 'c'     /* null term. string */
    m.ff.sql2fmt.464 = 'c'     /* graphic varchar   */
    m.ff.sql2fmt.468 = 'c'     /* graphic char      */
    m.ff.sql2fmt.472 = 'c'     /* long graphic varchar   */
    m.ff.sql2fmt.480 = '%-7e'  /* float                  */
    m.ff.sql2fmt.484 = 'd'     /* packed decimal         */
    m.ff.sql2fmt.492 = '%20i'  /* bigInt                 */
    m.ff.sql2fmt.496 = '%11i'  /* int                    */
    m.ff.sql2fmt.500 = '%6i'   /* smallInt               */
    m.ff.sql2fmt.904 = '%-34H' /* rowID 17 Byte Binary   */
    return ff
endProcedure sqlFTabOpts

/*--- set a defaultFormat for type tx in fTab ff --------------------*/
sqlFTabDef: procedure expose m.
parse arg ff, tx, m.ff.sql2fmt.tx
return ff

/*--- complete / override column info from sqlCa --------------------*/
sqlFTabComplete: procedure expose m.
parse arg m, cx, aOth, aFmt
    if aOth then
        call sqlFTabOthers m, cx
    f2x = classMet(sqlFetchClass(cx), 'f2x')
    do tx=1 to m.m.0
        c1 = m.m.tx.col
        if symbol('m.m.set.c1') == 'VAR' then do
            sx = m.m.set.c1
            parse var m.m.set.sx c1 aDone
            m.m.tx.done = aDone \== 0
            m.m.tx.fmt = m.m.set.sx.fmt
            m.m.tx.labelSh = m.m.set.sx.labelSh
            end
        if symbol('m.f2x.c1') \== 'VAR' then
            iterate
        kx = m.f2x.c1
        if m.m.tx.labelLo = '' then
            m.m.tx.labelLo = m.sql.cx.d.kx.sqlName
        if m.m.tx.labelSh = '' then
            m.m.tx.labelSh = m.sql.cx.d.kx.sqlName
        if m.m.tx.fmt <> '' | \ aFmt then
            iterate
        /* use format for datatype */
        ty = m.sql.cx.d.kx.sqlType
        ty = ty - ty // 2 /* odd = with null */
        le = m.sql.cx.d.kx.sqlLen
        if symbol('m.m.sql2fmt.ty') <> 'VAR' then
            call err 'sqlType' ty 'col' c1 'not supported'
        f1 = m.m.sql2fmt.ty
        if f1 == 'c' then
            f1 = '%-'min(le, m.m.maxChar)'C'
        else if f1 == 'd' then do
            pr = m.sql.cx.d.kx.sqlLen.sqlPrecision
            sc = m.sql.cx.d.kx.sqlLen.sqlScale
            if sc < 1 then
                f1 = '%' || (pr + 1) || 'i'
            else
                f1 = '%' || (pr + 2) || '.'sc'i'
            end
        if \ abbrev(f1, '%') then
            call err 'sqlType' ty 'col' c1 'bad format' f1
        m.m.tx.fmt = f1
        end
    return m
endProcedure sqlFTabComplete

/*--- add all cols of sqlCA to fTab,
              that are not yet (witho aDone=0) ----------------------*/
sqlFTabOthers: procedure expose m.
parse arg m, cx
    do cy=1 to m.m.0
        if m.m.cy.done then do
            nm = m.m.cy.col
            done.nm = 1
            end
        end
    ff = m.sql.cx.fetchFlds
    do kx=1 to m.sql.cx.d.sqlD
        c1 = word(ff, kx)
        if done.c1 \== 1 then
            call ftabAdd m, c1
        end
    return m
endProcedure sqlFTabOthers

/*--- fetch all rows from cursor cx, tabulate and close crs
           opt = a autoformat from data
                 c column format (each column on separate line)
                 s silent
                 o ouput objects
                 q format by sqlCA ----------------------------------*/
sqlFTab: procedure expose m.
parse arg m, cx
    if pos('o', m.m.opt) < 1 then
        call sqlFTabComplete m, cx, pos('|', m.m.opt) < 1,
                                  , pos('a', m.m.opt) < 1
    if verify(m.m.opt, 'ao', 'm') > 0 then
        return fTab(m, sqlQuery2Rdr(cx))
    /* fTab would also work in other cases,
           however, we do it without sqlQuery2Rdr */
    dst = 'SQL_fTab_dst'
    if pos('c', m.m.opt) > 0 then do
        if pos('c', m.m.generated) < 1 then
            call fTabGenCol m
        do rx=1 while sqlFetch(cx, dst)
            call out left('--- row' rx '',  80, '-')
            call fTabCol m, dst
            end
        call out left('--- end of' (rx-1) 'rows ', 80, '-')
        end
    else do
        call fTabBegin m
        do rx=1 while sqlFetch(cx, dst)
            call out f(m.m.fmt, dst)
            end
        call fTabEnd m
        end
    call sqlClose cx
    return m
endProcedure sqlFTab

/*--- create insert statment into table tb
         for object m in spufi (72chars) format ---------------------*/
sql4obj: procedure expose m.
parse arg m, tb
    call out 'insert into' tb '--' className(objClass(m))
    line = ''
    ff = oFldD(m)
    pr = '   ('
    do fx=1 to m.ff.0
        call sql4ObjOut substr(m.ff.fx, 2)
        end
    call sql4ObjOut , 1
    call out '   ) values '
    pr = '   ('
    do fx=1 to m.ff.0
        f1 = m || m.ff.fx
        v = m.f1   /* no strip T, gives errors in RCM profile | */
        if dataType(v, n) then
            call sql4ObjOut v
        else do qx=1 until v == ''
            vx = verify(v, m.ut_alfPrint)
            if vx = 0 then do
                l1 = min(60, length(v))
                w = quote(left(v, l1), "'")
                end
            else if vx > 29 then do
                l1 = min(60, vx-1)
                w = quote(left(v, l1), "'")
                end
            else do
                l1 = min(29, length(v))
                w = 'x'quote(c2x(left(v, l1)), "'")
                end
            if qx == 1 then
                call sql4ObjOut w
            else do
                if qx = 2 then
                    call sql4ObjOut , 1
                call out '   ||' w
                end
            v = substr(v, l1+1)
            end
        end
    call sql4ObjOut , 1
    call out '   ) ; '
    return
endProcedure

sql4objOut:
parse arg t1, force
    if (force == 1 & line \== '') | length(line t1) > 65 then do
        call out pr  substr(line, 3)
        pr = '   ,'
        line = ''
        end
    if force \== 1 then
        line = line',' t1
    return
endProcedure sql4objOut
/*--- -dis db interface ---------------------------------------------*/
/*--- do one -dis db... and insert it into stem  --------------------*/
sqlDisDb: procedure expose m.
parse upper arg o, cc
    do cx=1
        mid = strip(left(m.cc.cx, 10))
        if words(mid) > 1 then
            call err 'bad msgId' mid 'line:' m.cc.cx
        if mid == '' | wordPos(mid, 'DSNT360I DSNT361I DSNT365I') ,
                > 0 then
            iterate
        if mid == 'DSN9022I' then
            if cx = m.cc.0 then
                return m.o.0
            else
                call err 'not at end' cx':' m.cc.cx
        if mid \== 'DSNT362I' then
            call err 'DSNT362I expected not line:' m.cc.cx
        dx = pos('DATABASE =', m.cc.cx)
        sx = pos('STATUS ='  , m.cc.cx)
        if dx < 1 | sx <= dx then
            call err 'bad DSNT362I line' cx':' m.cc.cx
        db = word(substr(m.cc.cx, dx+10), 1)
        sta = strip(substr(m.cc.cx, sx+8))
        call sqlDisDbAdd o, db, ,0, 0, 'DB', sta
        do cx=cx+1 while abbrev(m.cc.cx, '   ')
            end
        if abbrev(m.cc.cx, 'DSNT397I ') then do
            cx = cx + 1
            if \ abbrev(space(m.cc.cx, 1),
                 , 'NAME TYPE PART STATUS ') then
                call err 'NAME TYPE PART STATUS mismatch' cx m.cc.cx
            txNa = pos('NAME', m.cc.cx)
            txTy = pos('TYPE', m.cc.cx)
            txPa = pos('PART', m.cc.cx)
            txSt = pos('STAT', m.cc.cx)
            txEn = verify(m.cc.cx, ' ', 'n', txSt+6)
            if 0 then say 'title' txNa txTy txPa txSt txEn cx m.cc.cx
            cx=cx+1
            do forever
                do while abbrev(m.cc.cx, '----')
                    cx = cx + 1
                    end
                if abbrev(m.cc.cx, '*') then
                    leave
                parse var m.cc.cx sp =(txTy)  ty . =(txPa)  paFr . ,
                                       =(txSt) sta   =(txEn)
                sp = strip(sp)
                if words(sp) \= 1 | wordPos(ty, 'TS IX') < 0 then
                    call err 'bad name or type' cx':'m.cc.cx
                if paFr == '' | paFr == 'L*' then
                    paFr = 0
                else if abbrev(paFr, 'D') | abbrev(paFr, 'L') then
                    paFr = substr(paFr, 2)
                if \ datatype(paFr, 'n') then
                    call err 'part not numeric' cx':'m.cc.cx
                paTo = paFr
                cw = cx
                cx = cx + 1
                if abbrev(m.cc.cx, '    -THRU ') then do
                    parse var m.cc.cx =(txPa)  paTo . =(txSt)
                    if \ datatype(paTo, 'n') then
                        call err '-thru part not numeric' cx':'m.cc.cx
                    cx = cx + 1
                    end
                call sqlDisDbAdd o, db, sp, paFr, paTo, ty, sta
                end
            end
        if m.cc.cx = '******** NO SPACES FOUND' then
            cx = cx + 1
        if abbrev(m.cc.cx, '******* DISPLAY OF DATABASE ') ,
          & word(m.cc.cx,5) == db then
            if word(m.cc.cx,6) == 'ENDED' then
                iterate
            else if word(m.cc.cx,6) == 'TERMINATED' then
                call err 'db display overflow' cx':' m.cc.cx
        call err 'database' db 'ENDED mismatch' cx':' m.cc.cx
        end
endProcedure sqlDbDis

/*--- insert one tuple into tDbState --------------------------------*/
sqlDisDbAdd: procedure expose m.
    if arg(7) == '' | arg(7) == 'RW' then
         return
parse arg o
    m.o.0 = m.o.0 + 1
    q = o'.'m.o.0
parse arg , m.q.db, m.q.sp, m.q.paFr, m.q.paTo, m.q.ty, m.q.sta
 /*say added q m.q.db'.'m.q.sp':'m.q.paFr'-'m.q.paTo m.q.ty':'m.q.sta*/
    ky = m.q.db'.'m.q.sp
    if symbol('m.o.ky') \== 'VAR' then
        m.o.ky = m.o.0
    return
endProceedure sqlDisDbAdd

/*--- get index in o for db sp part ---------------------------------*/
sqlDisDbIndex: procedure expose m.
parse arg st, d, s, pa
     if symbol('m.st.d.s') \== 'VAR' then
         return 0
     ix = m.st.d.s
     if ix > m.st.0 | d \== m.st.ix.db | s \== m.st.ix.sp then
         return 0
     if pa == '' then
         return ix
     do ix=ix to m.st.0 while d == m.st.ix.db & s == m.st.ix.sp
         if pa < m.st.ix.paFr then
             return 0
         else if pa <= m.st.ix.paTo then
             return ix
         end
     return 0
endProcedure sqlDisDbIndex

/*--- dsn Command, return true if continuation needed ---------------*/
sqlDsnCont: procedure expose m.
parse arg cc, ssid, cmd
    say '???dsnCont' cmd
    cont = sqlDsn(cc, ssid, cmd, 12) <> 0
    if cont then do
        cz = m.cc.0
        cy = cz - 1
        if \ abbrev(m.cc.cy, DSNT311I) ,
                | \ abbrev(m.cc.cz, 'DSN9023I') then
            call err 'sqlDsn rc=12 for' cmd 'out='cz ,
                     '\n'cy'='m.cc.cy'\n'cz'='m.cc.cz
        m.cc.0 = cz-2
        end
    return cont
endProcedure sqlDsnCont
/* copy sqlDiv end   *************************************************/
/* copy db2Cat begin *************************************************/
catTbLastCol: procedure expose m.
parse upper arg cr, tb
    return sql2one( ,
          "select strip(char(colcount)) || ' ' || strip(c.name) one"  ,
              "from sysibm.sysTables t left join sysibm.sysColumns c" ,
                  "on c.tbCreator = t.creator and c.tbName = t.name"  ,
                       "and c.colNo = t.colCount"                     ,
               "where t.creator = '"cr"' and t.name = '"tb"'",,,, 'r')
endProcedure catTbLastCol

catTbCols: procedure expose m.
parse upper arg cr, tb
    if sql2St("select strip(name) name "     ,
          "from sysibm.sysColumns " ,
          "where tbcreator = '"cr"' and tbname='"tb"'",
          "order by colNo", ggSt) < 1 then
        return ''
    res = m.ggst.1.name
    do cx=2 to m.ggst.0
        res = res m.ggst.cx.name
        end
    return res
endProcedure catTbCols

catTbColsTrunc: procedure expose m.
parse upper arg cr, tb, maxL
    if sql2St("select strip(name) name, colType, length, length2"     ,
          "from sysibm.sysColumns " ,
          "where tbcreator = '"cr"' and tbname='"tb"'",
          "order by colNo", ggSt) < 1 then
        return ''
    res = ''
    do cx=1 to m.ggst.0
        ty = m.ggSt.cx.colType
        if pos('LOB', ty) > 0 then
            res = res', substr('m.ggSt.cx.name', 1,' ,
                 min(maxL, m.ggSt.cx.length2)') 'm.ggSt.cx.name
        else if pos('CHAR', ty) > 0 & m.ggSt.cx.length > maxL then
            res = res', substr('m.ggSt.cx.name', 1,' maxL')',
                 m.ggSt.cx.name
        else
            res = res',' m.ggSt.cx.name
        end
    return substr(res, 3)
endProcedure catTbColsTrunc

catIxKeys: procedure expose m.
parse upper arg cr, ix
    sql = "select colSeq sq, colName col, ordering ord"       ,
              "from sysibm.sysKeys"                                   ,
               "where ixCreator = '"cr"' and ixName = '"ix"'" ,
               "order by colSeq"
    call sqlQuery 1, sql
    res = ''
    drop d
    do kx=1 while sqlFetch(1, d)
        if m.d.sq \= kx then
            call err 'expected' kx 'but got colSeq' m.d.sq ,
                     'in index' cr'.'ix'.'m.d.col
        res = res || strip(m.d.col) || translate(m.d.ord, '<>?', 'ADR')
        end
    call sqlClose 1
    return res
endProcedure catIxKeys

catColCom: procedure expose m.
parse upper arg fCr, fTb, tCr, tTb
    sql = "select t.name, t.colType, t.nulls, t.""DEFAULT"""        ,
                    ", coalesce(f.nulls, 'new')"                    ,
              "from sysibm.sysColumns t"                            ,
                "left join sysibm.sysColumns f"                     ,
                  "on f.tbCreator = '"fCr"' and f.tbName = '"fTb"'" ,
                    "and f.name = t.name"                           ,
              "where t.tbCreator = '"tCr"' and t.tbName = '"tTb"'"  ,
              "order by t.colNo"
    call sqlQuery 1, sql, 'na ty nu de nn'
    pr = ' '
    do kx=1 while sqlFetch(1)
        /* say kx m..na m..ty m..nu m..de 'nn' m..nn */
        if pos('CHAR', m..ty) > 0 then
            dv = "''"
        else if pos('INT' ,m..ty) > 0 ,
                | wordPos(m..ty, 'REAL FLOAT') > 0 then
            dv = 0
        else if m..ty == 'TIMESTMP' then
            dv = '0001-01-01-00.00.00'
        else if pos('LOB', m..ty) > 0 then
            dv = m..ty"('')"
        else
            dv = '???'
        if m..nu = 'Y' then
            dv = 'case when 1=0 then' dv 'else null end'
        r = '???'
        if m..ty = 'ROWID' then do
            r = '--'
            end
        else if m..nn == 'new' then do
            if m..de = 'Y' then
                r = '--'
            else if m..nu == 'N' then
                r = dv
            else
                r = 'case when 1=0 then' dv 'else null end'
            end
        else do
            if m..nu = 'Y' | (m..nu = m..nn) then
                r = ''
            else
                r = 'coalesce('m..na',' dv')'
            end
        if abbrev(r, '--') then do
            r = ' ' r
            end
        else do
            r = pr r
            pr = ','
            end
        if pos('???', r) > 0 then
            call err 'no default for type' m..ty 'in' tCr'.'tTb'.'m..na
        call out r m..na
        end
    call sqlClose 1
    return
endProcedure catColCom
/* copy db2Cat end   *************************************************/
/* copy sqlWsh begin **************************************************
        remote SQL using csmExWsh ************************************/
sqlConClass_w: procedure expose m.
    if m.sqlWsh_ini == 1 then
        return m.class_SqlWshConn
    m.sqlWsh_ini = 1
    call sqlConClass_S
    call csmIni
    call classNew 'n SqlWshRdr u CsmExWsh', 'm',
        , "jReset call jReset0 m; m.m.rdr = jBuf()" ,
                 "; m.m.rzDb=arg; m.m.sql = arg2;m.m.type= arg(3)" ,
        , "jOpen  call sqlWshRdrOpen m, opt"
    return classNew('n SqlWshConn u', 'm',
        , "sqlRdr return oNew(m.class_sqlWshRdr, m.sql_conRzDb" ,
            ", src, type)" ,
        , "sqlsOut return sqlWshOut(rdr,m.sql_conRzDB,retOk,m.ft.opt)")
endProcedure sqlConClass_w

/*--- send an sql to csm and handle sqlCode -------------------------*/
sqlWshRdrOpen: procedure expose m.
parse arg m, oOpt
    r = m.m.rdr
    m.r.buf.0 = 1
    m.r.buf.1 = m.m.sql
    parse var m.m.RzDb m.m.rz '/' dbSys
    m.m.wOpt = 'e sqlRdr' dbSys
    call csmExWshOpen m, oOpt
    d = m.m.deleg
    em = ''
    do while jRead(d)
        if objClass(m.d) \== m.class_S then do
            m.d.readIx = m.d.readIx - 1
            leave
            end
        em = em'\n'm.d
        end
    if em == '' then
        return m
    call jClose m.m.deleg
    return err('sqlWshRdr got' substr(em, 3))
endProcedure sqlWshRdrOpen

/*--- send an sql to csm and handle sqlCode -------------------------*/
sqlWshOut: procedure expose m.
parse arg rdr, rzDb, retOk, oo
    parse value dsnCsmSys(rzDb) with rz '/' dbSys
    if pos('o', oo) > 0 then
        spec = 'e sqlsOut'
    else
        spec = 'v' || (m.wsh.outLen+4) 'sqlsOut'
    call csmExWsh rz, rdr, spec dbSys oo retOk
    return 1
endProcedure sqlWshOut
/* copy sqlWsh end   *************************************************/
/* copy sqlS   begin **************************************************
               sqlStmts **********************************************/
sqlConClass_S: procedure expose m.
    if m.sqlS_ini == 1 then
        return m.class_SqlConnS
    m.sqlS_ini = 1
    call sqlConClass_R
    call scanWinIni
    return classNew('n SqlConnS u SqlConn', 'm',
        , "sqlsOut return sqlsOutSql(rdr, retOk, ft)")
endProcedure sqlConClass_S

/*** execute sql's in a stream (separated by ;) and output as tab    */
sqlStmts: procedure expose m.
parse arg sqlSrc, retOk, wOpt, sOpt, fOpt
   return sqlsOut(scanSqlStmtRdr(sqlSrc, wOpt, sOpt), retOk, fOpt)
endProcedure sqlStmts

/*--- output sql as table -------------------------------------------*/
sql2tab: procedure expose m.
parse arg src, retOk, ft
    cx = m.sql_defCurs
    if ft == '' then
        ft = sqlFTabOpts(fTabReset('SQL.'cx'.2Tab', 'c 1','1 c' , '-'))
    else if objClass(ft) == m.class_s then
        ft = sqlFTabOpts(fTabReset('SQL.'cx'.2Tab', 'c 1','1 c','-'ft))
    call sqlQuery cx, in2str(src, ' '), retOk
    call sqlFTab ft, cx
    return
endProcedure sql2tab

/*--- result of each sql read from rdr to out
           oo='a' autoTab, 't'=sqlTab, 'o'=object, 'c'=csv ----------*/
sqlsOut: procedure expose m.
parse arg rdr, retOk, ft
    if ft = '' then
        ft = sqlFTabOpts(fTabReset(sql_outFTab, 'c 1', , 'a'))
    else if objClass(ft) == m.class_s then
        ft = sqlFTabOpts(fTabReset(sql_outFTab, 'c 1', , ft))
    interpret classMet(m.sql_ConCla, 'sqlsOut')

sqlsOutSql: procedure expose m.
parse arg rdr, retOk, ft
    m.sql_errRet = 0
    cx = m.sql_defCurs
    r = jOpen(in2file(rdr), '<')

    do while jRead(r)
        sqlC = sqlExecute(cx, m.r, retOk)
        if m.sql_errRet then
            leave
        if m.sql.cx.resultSet == '' | m.sql.cx.fun == 'CALL' then
            if m.ft.verbose then
                call outNl(m.sql_HaHi ,
                    || sqlMsgLine(sqlC, m.sql.cx.updateCount, m.r))
        if m.sql.cx.resultSet == '' then
             iterate
        do until \ sqlNextResultSet(cx) | m.sql_errRet
            m.sql.cx.sqlClosed = 0
            call sqlFTab fTabResetCols(ft), cx
            if m.ft.verbose & m.sql.cx.sqlClosed then/* sql finished*/
                call out sqlMsgLine(m.sql.cx.fetchCount ,
                        'rows fetched', , m.r)
            end
        end
    call jClose r
    if m.sql_errRet then do
        call sqlClose cx, '*'
        say 'sqlsOut terminating because of sql error'
        end
    return \ m.sql_errRet
endProcedure sqlsOutSql

/*--- sql hook ------------------------------------------------------
      hook paramter db | windowSpec |  db? , windowSpec? , fTabOpt?
          db: dbSys, rz/dbSysAbkürzung, 1 oder 2 chars
          windowSpec: 0 = variable len, 123 = window123
                      default spufi = window72 ---------------------*/
wshHook_S: procedure expose m.
parse arg m, spec
    parse var spec ki 2 rest
    call errSetSayOut 'so'
    if ki == '/' then do
        inp = m.m.in
        end
    else do
        call compIni
        if pos(ki, m.comp_chKind) <= 0 then do
            ki = '='
            rest = spec
            end
        inp = wshCompile(m, ki)
        end
    if pos('@',rest)>0  then call err 'was ist das??????' spec
    if words(rest)==1 & (datatype(rest, 'n') | pos('@',rest)>0) then
         rest = ','rest
    parse var rest dbSy ',' wOpt ',' fOpt
    d2 = ii2rzDb(dbSy, 1)
    call sqlConnect d2
    m.m.info = 'runSQL'
    if \ sqlStmts(inp, 'rb ret', strip(wOpt), , strip(fOpt)) then do
        m.m.end = 1
        m.m.exitCC = 8
        end
    call sqlDisConnect
    return ''
endProcedure wshHook_s

/*--- wshHook for sqlRdr read from dd wsh --------------------------*/
wshHook_sqlRdr: procedure expose m.
parse arg m, dbSys
    call errSetSayOut 'so'
    call sqlIni
    m.sql_retOk = m.sql_retOk 'rb ret'
    m.sql_errRet = 0
    call sqlConnect dbSys
    if \ m.sql_errRet then
        r = sqlRdr(m.m.in)
    if \ m.sql_errRet then
        call jOpen r, '<'
    if \ m.sql_errRet then do
        call pipeWriteAll r
        call jClose r
        end
    if m.sql_errRet then do
        call out 'from' sysvar(sysnode) mvsVar(sysName) m.sql_ConRzdb
        m.m.end = 1
        m.m.exitCC = 4
        end
 /* else
        call out sqlMsgLine(m.r.bufI0 'rows fetched', , m.r.srcTxt) */
    call sqlDisConnect
    return ''
endProcedure wshHook_sqlRdr

/*--- wshHook for sqlsOut read from dd wsh --------------------------*/
wshHook_sqlsOut: procedure expose m.
parse arg m, dbSys oo retOk
    call errSetSayOut 'so'
    call sqlIni
    m.sql_retOk = m.sql_retOk 'rb ret'
    m.sql_errRet = 0
    call sqlConnect dbSys
    if oo == 'a' | oo == 't' then do
        myOut = m.j.out
        m.myOut.truncOk = 1
        end
    if \ m.sql_errRet then
        call sqlsOut m.m.in, retOk, oo
    if m.sql_errRet then do
        call out 'from' sysvar(sysnode) mvsVar(sysName) m.sql_ConRzdb
        m.m.end = 1
        m.m.exitCC = 4
        end
    call sqlDisConnect
    return ''
endProcedure wshHook_sqlsOut
/* copy sqlS   end   *************************************************/
/* copy sqlCsm begin *************************************************/
sqlConClass_C: procedure expose m.
    if m.sqlCsm_ini == 1 then
        return m.class_sqlCsmConn
    m.sqlCsm_ini = 1
    call sqlConClass_R
    call csmIni
    call classNew 'n SqlCsmRdr u JRW', 'm',
        , "jReset m.m.rzDb=arg; m.m.src=arg2; m.m.type=arg(4)" ,
        , "jOpen  call sqlCsmRdrOpen m, opt",
        , "jClose" ,
        , "jRead return 0"
    return classNew('n SqlCsmConn u', 'm',
        , "sqlRdr return oNew(m.class_SqlCsmRdr" ,
               ", m.sql_conRzDB, src, type)" ,
        , "stmts return err('no stmts in sqlCsm')")
endProcedure sqlConClass_C

/*--- send an sql to csm and handle sqlCode -------------------------*/
sqlCsmExe:
parse arg ggRzDb, sql_query, ggRetOk
    parse value dsnCsmSys(ggRzDb) with sql_host '/' sql_db2SSID
    sql_query = strip(sql_query) /* csmASqls fails on leading spaces */
    call csmAppc 'csmASql', , , 4
    if sqlCode = 0 then
        return 0
    ggSqlStmt = sql_query /* for sqlMsg etc. */
    if pos('*', ggRetOk) > 0 | wordPos(sqlCode, ggRetOk) > 0 ,
            then do
        if sqlCode < 0 & pos('say', ggRetOk) > 0 then
            call errSay ' }'sqlmsg(sqlCA2Rx(sqlCa))'\nsqlCsmExe' ggRzDb
        return sqlCode
        end
    else if sqlCode < 0 then
        call err sqlmsg(sqlCA2rx(sqlCa))'\nsqlCsmExe' ggRzDb
    else if pos('w', ggRetOk) < 1 then
        if sqlCode = 100 then
            call errSay ' }sqlCode +100 row not found\nsql =' ggSqlStmt
        else
            call errSay 'w}'sqlMsg(sqlCA2rx(sqlCa))'\nsqlCsmExe' ggRzDb
    return sqlCode
endProcedure sqlCsmExe

/*--- execute a query from sql, with one resultset ------------------*/
sqlCsmRdrOpen: procedure expose m.
parse arg m, opt
    src = sqlRdrOpenSrc(m, opt)
    res = sqlCsmExe(m.m.rzDb, src, 100 retOk)
    if res < 0 then
        return res
    if words(m.m.type) = 1 & \ abbrev(m.m.type, ' ') then
        cl = class4name(m.m.type)
    else if m.m.type <> '' then
        cl = classNew('n* SqlCsm u f%v' m.m.type)
    else do
        vv = ''
        do kx=1 to sqlD
            vv = sqlNiceVarsApp(vv, SQLDA_REXXNAME.kx)
            end
        cl = classNew('n* SqlCsm u f%v' vv)
        end
    ff = classFldD(cl)
    if sqlD <> m.ff.0 then
        return err('sqlCsmQuery sqlD' sqlD '<>' m.ff.0 'for' ,
                className(cl))
    do rx=1 to sqlRow#
        m.m.buf.rx = m'.BUFD.'rx
        call oMutate m'.BUFD.'rx, cl
        end
    m.m.buf.0 = sqlRow#
    do kx=1 to sqlD
        rxNa = SQLDA_REXXNAME.kx
        do rx=1 to sqlRow#
            dst = m'.BUFD.'rx || m.ff.kx
            if substr(sqlIndicator.rx, kx ,1) == 'ff'x then
                m.dst = m.sqlNull
            else
                m.dst = value(rxNa'.'rx)
            end
        end
    return 0
endProcedure sqlCsmRdrOpen
/* copy sqlCsm end   *************************************************/
/* copy sqlO   begin **************************************************
    sql interface  mit  o und j Anbindung
**********************************************************************/
sqlConClass_R: procedure expose m.
    if m.sqlO_ini == 1 then
        return m.class_sqlConn
    m.sqlO_ini = 1
    call sqlIni
    call jIni
/*  call scanReadIni */
    call classNew 'n SqlRdr u JRW', 'm',
        , "jReset m.m.sql = arg; m.m.type = arg2;",
        , "jOpen  call sqlRdrOpen m, opt",
        , "jClose call sqlRdrClose m",
        , "jRead if \ sqlRdrRead(m, rStem) then return 0"
    call classNew 'n SqlResRdr u JRW', 'm',
        , "jReset m.m.cursor = arg; m.m.type = arg2;",
        , "jOpen  call sqlRdrO2 m" ,
        , "jClose call sqlClose m.m.cursor" ,
        , "jRead if \ sqlRdrRead(m, rStem) then return 0"
    return classNew('n SqlConn u', 'm',
        , "sqlRdr return oNew(m.class_SqlRdr, src, type)" ,
        , "sqlsOut return err('no stmts/sqlsOut in conClass_R')")
endProcedure sqlConClass_R

/*--- return a new sqlRdr with sqlSrc from src
      type is the class for result, if empty generated --------------*/
sqlRdr: procedure expose m.
parse arg srcRdr, type
    src = in2str(srcRdr, ' ')
    interpret classMet(m.sql_ConCla, 'sqlRdr')
endProcedure sqlRdr

/*--- execute sql query, generate type and fetchList ----------------*/
sqlRdrOpen: procedure expose m.
parse arg m, opt
    src = m.m.sql
    cx = sqlGetCursor()
    m.m.cursor = cx
    if words(m.m.type) <> 1 | abbrev(m.m.type, ' ') then do
        m.sql.cx.fetchClass = ''
        res = sqlQuery(cx, src, m.m.type)
        m.m.type = sqlFetchClass(cx)
        end
    else do
        m.m.type = class4name(m.m.type)
        res = sqlQuery(cx, src, mCat(classFlds(m.m.type),' '))
        m.sql.cx.fetchClass = m.m.type
        end
    if res >=  0 then
        return sqlRdrO2(m)
    call sqlFreeCursor cx
    return res
endProcedure sqlRdrOpen

sqlRdrOpenSrc: procedure expose m.
parse arg m, opt
    if opt\== m.j.cRead then
        call err 'opt not' m.j.cRead 'sqlRdrOpenSrc('m',' opt')'
    m.m.srcTxt = in2str(m.m.src, ' ')
    return m.m.srcTxt

sqlRdrO2: procedure expose m.
parse arg m
    cx  = m.m.cursor
    if m.m.type \== m.sql.cx.fetchClass | m.m.Type == '' then
        call err 'type' m.m.type '\==' m.sql.cx.fetchClass 'fetchClass'
    m.m.fetchCount = ''
    return m
endProcedure sqlRdrO2

/*--- generate class for fetched objects, if necessary --------------*/
sqlFetchClass: procedure expose m.
parse arg cx, force
     if m.sql.cx.fetchClass == '' | force == 1 then
          m.sql.cx.fetchClass = classNew('n* Sql u f%v' ,
              m.sql.cx.fetchFlds)
     return m.sql.cx.fetchClass
endProcedure sqlFetchClass

/*--- read next from cursor, return as object -----------------------*/
sqlRdrRead: procedure expose m.
parse arg m, rStem
    cx = m.m.cursor
    if m.sql.cx.fetchcount \== m.m.bufI0 then
        call err cx 'fetchCount='m.sql.cx.fetchcount ,
             '<> m'.m'.bufI0='m.m.bufI0
    do bx=1 to 10
        v = oNew(m.m.type)
        if \ sqlFetch(m.m.cursor, v) then do
            call mFree v
            leave
            end
        m.rStem.bx = v
        end
    m.rStem.0 = bx-1
    return bx > 1
endProcedure sqlRdrRead

/*--- close sql Cursor ----------------------------------------------*/
sqlRdrClose: procedure expose m.
parse arg m
    cx = m.m.cursor
    call sqlClose cx
    call sqlFreeCursor cx
    m.m.cursor = ''
    m.m.fetchCount = m.sql.cx.fetchCount
    return m
endProcedure sqlRdrClose

sqlQuery2Rdr: procedure expose m.
parse arg cx
    r = jReset(oMutate('SQL_RDR.'cx, m.class_SqlResRdr), cx)
    m.r.type = sqlFetchClass(cx)
    return r
endProcedure sqlQuery2Rdr

/*--- select and write all to stdOut --------------------------------*/
sqlSel: procedure expose m.
parse arg src, type
     s = sqlRdr(src, type)
     call pipeWriteAll s
     return /* do not return fetchCount, writeAll may be delayed| */
endProcedure sqlSel

/* copy sqlO   end   *************************************************/
/* copy sql begin ****************************************************/
/*** sql.1: basic interface to dsnRexx *******************************/
/*--- initialize sql ------------------------------------------------*/
sqlIni: procedure expose m.
    if m.sql_ini == 1 then
        return
    m.sql_ini = 1
    call utIni
    m.sqlNull = '---'
    m.sqlInd = 'sqlInd'
    m.sql_defCurs= 49
    m.sqlCAMsg = 0
    m.sqlSuMsg = 2
    m.sql_dbSys = ''
    m.sql_rzDb = ''
    isInProd = wordPos(sysvar(sysNode), 'RZ2') > 0
    m.sql_retOk   = 'dne' copies('rod', \ isInProd)
    m.sql_retOkDef = m.sql_RetOk
    m.sql_cursors   = left('', 100)
    return 0
endProcedure sqlIni

sqlRetDef: procedure expose m.
    m.sql_retOk = m.sql_retOkDef
    return
/*--- execute sql thru the dsnRexx interface fail if sqlcode <> 0 ---*/
sqlExec0: procedure expose m.
    parse arg ggSqlStmt, ePlus
    if abbrev(ggSqlStmt, 'disCon') | abbrev(ggSqlStmt, 'connec') then
        address dsnRexx ggSqlStmt
    else
        address dsnRexx 'execSql' ggSqlStmt
    if rc == 0  & sqlCode == 0 & sqlWarn.0 == ' ' then
        return 0
    m.sql_errRet = 1
    if \ dataType(sqlCode, 'n') | abs(rc) \== 1 then
        ePlus = strip(ePlus 'dsnRexx rc='rc'\n')
    if wordPos('ret', m.Sql_retOK) < 1 then
        call err ePlus || sqlMsg()
    else
        call errSay ePlus || sqlMsg()
    return sqlCode
endProcedure sqlExec0

/*--- connect to the db2 subsystem sys
    cCla = connectionClass
        e = rexx local only
        r = rexx local only, rdr&objects
        s = rexx local only, rdr&objects, stmts (default local)
        c = with csmASql   , rdr&objects
        w = with sqlWsh    , rdr&objects, stmts (default remote) ----*/
sqlConnect: procedure expose m.
parse arg sys, cCla
    upper sys
    if abbrev(sys, '*/') then
        sys = substr(sys, 3)
    if pos('/', sys) <= 0 then
        cCla = firstNS(translate(cCla, 'rs', 'cw'), 's')
    else if cCla = '' then
        cCla = 'w'
    if cCla == 'e' then
        m.sql_conCla = 'sql E no connection class'
    else
        interpret 'm.sql_conCla = sqlConClass_'cCla'(sys, cCla)'
    if pos(cCla, 'ers') == 0 then do
        m.sql_conRzDB = sys
        return
        end

    call sqlIni     /* initialize dsnRexx */
    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
    if sys == '' then
        if sysvar(sysnode) == 'RZ4' then
            sys = 'DP4G'
        else if sysvar(sysnode) == 'RZX' then
            sys = 'DX0G'
        else
            call err 'no default dbSys for' sysvar(sysnode)
    m.sql_conRzDB = sys
    m.sql_dbSys = sys
    return sqlExec0('connect' sys)
endProcedure sqlConnect

/*--- diconnect from db2 --------------------------------------------*/
sqlDisconnect: procedure expose m.
parse arg retOk
    m.sql_conCla = ''
    m.sql_conRzDb = ''
    if m.sql_dbSys == '' then
        return 0
    m.sql_dbSys = ''
    m.sql_csmHost = ''
    return sqlExec0('disConnect')
endProcedure sqlDisconnect

/*--- execute sql thru the dsnRexx interface
           check for handled errors, recover from easy errors -------*/
sqlExec: /* no procedure, to keep variables sql... */
    parse arg ggSqlStmt, ggSqlRet0
    m.sql_HaHi = ''  /* empty error Handler History */
    do forever /* for retries */
        address dsnRexx 'EXECSQL' ggSqlStmt
        if rc == 0  & sqlCode == 0 & sqlWarn.0 == ' ' then
            return 0
        if \ dataType(sqlCode, 'n') | abs(rc) \== 1 then
             return err('dsnRexx rc='rc sqlMsg())
        ggSqlRet = ggSqlRet0 m.sql_retOk
        if pos('*', ggSqlRet) >0 | wordPos(sqlCode,ggSqlRet) >0 then do
            if sqlCode < 0 & pos('say', ggSqlRet) > 0 then
                call outNl errMsg(' }'sqlMsg())
            return sqlCode
            end
        if sqlCode >= 0 then do
            if (sqlCode > 0 | sqlWarn.0 \== ' ') ,
                    & pos('w', ggSqlRet) < 1 then
                call outNl errMsg(' }'sqlMsg())
            return sqlCode
            end
        if translate(word(ggSqlStmt, 1)) == 'DROP' then do
            if (sqlCode == -204 | sqlCode == -458) ,
                           & wordPos('dne', ggSqlRet) > 0 then
                return sqlCode
            if sqlCode = -672 & wordPos('rod', ggSqlRet) > 0 ,
                      & length(m.sql_hahi) < 1000 then do
                m.sql_hahi = m.sql_HaHi || sqlMsgLine(sqlCode,
                        , 'tb='sqlErrMc ,ggSqlStmt)'\n'
                m.sql_hahi = m.sql_HaHi || sqlExecHaHi('alter table' ,
                           SqlErrMc 'drop restrict on drop')'\n'
                iterate
                end
            end
        ggSqlEE = ''
        if wordPos('rb', ggSqlRet) > 0 then
            ggSqlEE = '\n'sqlExecHaHi('rollback')
        if wordPos('ret', ggSqlRet) < 1 then do
            call err m.sql_hahi || sqlMsg() || ggSqlEE
            return sqlCode
            end
        m.sql_errRet = 1
        call errSay errMsg(' }'m.sql_haHi || sqlMsg() || ggSqlEE)
        return sqlCode
        end
endProcedure sqlExec

/*--- execute sql fail showing haHi or return msgLine ---------------*/
sqlExecHaHi: procedure expose m.
parse arg ggSqlStmt
    return sqlMsgLine(sqlExec0(ggSqlStmt, m.sql_hahi) ,
                     , , ggSqlStmt)
endProcedure sqlExechaHi

/*--- short message for executed sql including count ----------------*/
sqlMsgLine: procedure expose m. sqlErrD.
parse arg res, cnt, src, plus
    verb = translate(word(src, 1))
    if datatype(res, 'n') then
        res = 'sqlCode' res
    if cnt \== '' then do
        res = res',' cnt
        vx = wordPos(translate(word(src,1)), 'DELETE INSERT UPDATE')
        if datatype(cnt, 'n') then
            if vx > 0 then
               res = res 'rows' word('deleted inserted updated', vx)
            else if cnt <> 0 then
                res = res 'rows updated'
        end
    if plus \== '' then
        res = res',' plus
    if abbrev(res, ', ') then
        res = substr(res, 3)
    if src \== '' then do
        ll = 75 - length(res)
        aa = strip(src)
        if length(aa) > ll then
            aa = space(aa, 1)
        if length(aa) > ll then
           aa = left(aa,  ll-3)'...'
        res = res':' aa
        end
    return res
endProcedure sqlMsgLine

/*--- 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

/*** sql.2: error Handler and error Reporting ************************/
/*--- return an sql error message (multiline \n) --------------------*/
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()
        end
    ggSt = 'SQL_HOST'
    ggVa = 'SQL_HOST.VAR'
    ggBe = 'SQL_HOST.BEF'
    call sqlHostVars ggSqlStmt, 12, ggSt
    ggFrom = 'ggSqlStmt'
    ggW1 = translate(word(ggSqlStmt, 1))
    ggW2 = translate(word(ggSqlStmt, 2))
    if ggW1 == 'PREPARE' then
        ggFrom = sqlHostVarFind(ggSt, 'FROM')
    else if ggW1 ggW2 == 'EXECUTE IMMEDIATE' then
        ggFrom = sqlHostVarFind(ggSt, 1)
    ggPos = 0
    if datatype(sqlErrd.5, 'n') & sqlErrd.5 > 0 then do
        ggPos = sqlErrd.5
        ggRes = ggRes || sqlMsgSrcPos(value(ggFrom), sqlErrd.5)
        end
    if ggFrom == 'ggSqlStmt' then do
        ggRes = ggRes'\nsql =' sqlShorten(ggSqlStmt, 2000, ggPos)
        end
    else do
        ggRes = ggRes'\nsql =' sqlShorten(value(ggFrom), 2000, ggPos)
        ggRes = ggRes'\nstmt =' sqlShorten(ggSqlStmt, 2000)
        end
    ggPref = '\nwith'
    do ggXX=1 to m.ggSt.0
        if ggFrom = m.ggVa.ggXX then
            iterate
        ggRes = ggRes || ggPref m.ggBe.ggXX ':'m.ggVa.ggXX ,
                      '=' sqlShorten(value(m.ggVa.ggXX), 210)
        ggPref = '\n    '
        end
/*  if m.sqlSuMsg == 1 | (m.sqlSuMsg == 2 & m.sql_csmhost \== '') then
        ggRes = ggRes'\nsubsys =' m.sql_dbSys ,
             || ', host =' m.sql_csmhost
*/  return  ggRes
endSubroutine sqlMsg

sqlShorten: procedure expose m.
parse arg txt, maxL, pos
    if length(txt) <= maxL then
        return txt
    if \ datatype(pos, 'n') | pos < 1 then
        pos = 1
    ex = pos + min(60, maxL-7)
    if ex <= maxL - 4 then
        return left(txt, maxL-4) '...'
    if ex >= length(txt) then
        return left(txt, 67) '...\n'substr(txt, length(txt)-maxL+72)
    else
        return left(txt, 67) '...\n'substr(txt, ex-maxL+76, maxL-75) ,
                       '...'
endProcedure sqlShorten
/*--- 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 nop
    else if rc = 4 then say 'warn linkPgm dsnTiar rc' rc 'sqlCa' ca
    else                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

/*--- 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

/*--- format the sqlCA into the dsnTiar SQLCA -----------------------*/
sqlRx2Ca: procedure expose m. ,
                   sqlCode sqlErrMc sqlErrP sqlErrD. sqlWarn. sqlState
    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

/*--- 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 = 1
    do cnt
        cx = pos(':', src, cx) + 1
        if cx < 2 then
           leave
        if pos(substr(src, cx, 1), m.ut_rxN1) > 0 then
            iterate
        ex = verify(src, m.ut_rxDot, 'n', cx)
        if ex < 1 then
            m.st.var.sx = substr(src, cx)
        else
            m.st.var.sx = substr(src, cx, ex - cx)
        if m.st.var.sx == '' | length(m.st.var.sx) > 100 then
            iterate
                       /* 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.ut_alfa) > 0
            end
        if bB < bE & bB >= 0 then
            m.st.bef.sx = substr(src, bB+1, bE-bB)
        else
            m.st.bef.sx = ''
        sx = sx + 1
        end
    m.st.0 = sx-1
    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

/*** sql.3: query/update/call interface ******************************/
/*--- reset sql cursor 'c'cx fields ---------------------------------*/
sqlReset: procedure expose m.
parse arg cx
     m.sql.cx.updateCount = ''
     m.sql.cx.fetchCount = 0
     m.sql.cx.resultSet   = ''
     m.sql.cx.resultSet.0 = 0
     m.sql.cx.i.sqlD = 'noDescInp'
     m.sql.cx.var.0 = 0
     return sqlResetCrs(cx)
endProcedue sqlReset

sqlResetCrs: procedure expose m.
parse arg cx
     m.sql.cx.d.sqlD = 'noSqlDA'
     m.sql.cx.fetchVars = ''
     m.sql.cx.fetchFlds = ''
     m.sql.cx.fetchClass = ''
     m.sql.cx.type  = ''
     return 0
endProcedue sqlResetCrs

/*--- execute a query from sql, with one resultset ------------------*/
sqlQuery: procedure expose m.
parse arg cx, src, feVa, retOk
     res = sqlPreDec(cx, src, feVa, retOk)
     if res < 0 then
         return res
     res = sqlExec('open c'cx, retOk)
     if res < 0 then
         return res
     m.sql.cx.updateCount = sqlErrd.3
     m.sql.cx.resultSet = cx
     return res
endProcedure sqlQuery

/*--- prepare statement and declare cursor --------------------------*/
sqlPreDec: procedure expose m.
parse arg cx, src, feVa, retOk
     call sqlReset cx
     s1 = ''
     if pos(left(feVa, 1), '?:') < 1 then
         s1 = 'into :M.SQL.'cx'.D'
     res = sqlExec('prepare s'cx s1 'from :src', retOk)
     if res < 0 then
         return res
     call sqlFetchVars cx, feVa
     return sqlExec0('declare c'cx 'cursor for s'cx)
endProcedure sqlPreDec

/*--- prepare a query from sql, with one resultset ------------------*/
sqlQueryPrepare: procedure expose m.
parse arg cx, src, feVa, retOk
     res = sqlPreDec(cx, src, feVa, retOk)
     if res < 0 then
         return res
     return sqlExec0('describe input s'cx 'into :M.SQL.'cx'.I')
endProcedure sqlQueryPrepare

/*--- open a prepared query -----------------------------------------*/
sqlQueryExecute: procedure expose m.
parse arg cx retOk
    do ix=1 to arg()-1
        call sqlDASet cx , 'I', ix, arg(ix+1)
        end
     res = sqlExec('open c'cx 'using descriptor :M.SQL.'cx'.I', retOk)
     if res < 0 then
         return res
     m.sql.cx.updateCount = sqlErrd.3
     m.sql.cx.resultSet = cx
     return res
endProcedure sqlQueryExecute

/*--- fetch next row to m.dst.* at end return false -----------------*/
sqlFetch: procedure expose m.
parse arg cx, dst, retOk
    fetCode = sqlExec('fetch c'm.sql.cx.resultSet ,
                      'into' m.sql.cx.fetchVars, 100 retOk)
    if fetCode == 100 then
        return 0
    if fetCode < 0 then
        return fetCode
    m.sql.cx.fetchCount = m.sql.cx.fetchCount + 1
    interpret m.sql.cx.fetchCode
    return 1
endProcedure sqlFetch

/*--- close cursor 'c'cx --------------------------------------------*/
sqlClose: procedure expose m.
parse arg cx, retOk
     m.sql.cx.sqlClosed = 1
     return sqlExec('close c'm.sql.cx.resultSet, retOk)
endProcedure sqlRxClose

/*-- execute an sql with no resultset, but possibly outParms --------*/
sqlUpdate: procedure expose m.
parse arg cx, src, retOk
    m.sql.cx.updateCount = ''
    m.sql.cx.resultSet   = ''
    bx = verify(src, '( ')
    if bx > 0 then do
        parse upper value substr(src, bx) with fun fu2 fu3 .
    if  fun == 'SET' & \ (fu2=='CURRENT' & left(fu3, 7)=='PACKAGE') ,
            then do
        w2 = translate(word(substr(src, bx), 2))
        if \ abbrev(w2, ':') then
            return sqlExec('execute immediate :src', retOk)
        ex = pos('=', w2)
        if ex = 0 then
            ex = length(w2)+1
        vn = strip(substr(w2, 2, ex-2))
        if vn = '' then
            call err 'bad hostVar in' src
        m.sql.cx.Var.0 = 1
        m.sql.cx.VarName.1 = vn
        abc = 'und so weiter'
        trace ?r
        src2 = 'set :M.sql.'cx'.var.1' substr(w, ex) subword(src, 3)
        src2 = 'set :abc' substr(w, ex) subword(src, 3)
        return sqlExec('execute immediate :src2', retOk)
        end
    if fun == 'DECLARE'  then do
        if 'GLOBAL' == translate(word(substr(src, bx), 2)) then
            return sqlExec('execute immediate :src', retOk)
        end
    res = sqlExec(src, retOk)
    if wordPos(fun, 'DELETE INSERT UPDATE') > 0 then
        m.sql.cx.updateCount = sqlErrd.3
    return res
endProcedure sqlUpdate

/*-- prepare an update ----------------------------------------------*/
sqlUpdatePrepare: procedure expose m.
parse arg cx, src, retOk
    res = sqlExec('prepare s'cx 'from :src', retOk)
    return sqlExec('describe input s'cx 'into :M.SQL.'cx'.I')
endProcedure sqlUpdatePrepare

/*-- execute a prepared update with the given arguments -------------*/
sqlUpdateExecute: procedure expose m.
parse arg cx retOk
    do ix=1 to arg()-1
        call sqlDASet cx , 'I', ix, arg(ix+1)
        end
    res = sqlExec('execute s'cx 'using descriptor :M.SQL.'cx'.I',
                  , retOk)
    m.sql.cx.updateCount = sqlErrd.3
    return res
endProcedure sqlUpdateExecute

/*--- sql call statement --------------------------------------------*/
sqlCall: procedure expose m.
parse arg cx, src, retOk
    call sqlReset cx
    s = scanSrc(sql_call, src)
    if \ scanUntil(s, ' ') | translate(m.s.tok) <> 'CALL' then
        call scanErr s, 'no call'
    if \ scanUntil(s, '(') then
        call scanErr s, 'not ( after call'
    prc = strip(m.s.tok)
    s2 = ''
    call scanLit s, '('
    do ax=1
        call scanSpaceOnly s
        if scanString(s, "'") then do
            m.sql.cx.var.ax = m.s.tok
            call scanSpaceOnly s
            end
        else if scanUntil(s, ',)') then
            m.sql.cx.var.ax = strip(m.s.tok)
        else
            call scanErr s, 'value expected in call list'
        s2 = s2', :m.sql.'cx'.var.'ax
        if scanLit(s, ')') then
            leave
        if \ scanLit(s, ',') then
            call scanErr s, 'missing ,) in call list'
        end
    m.sql.cx.var.0 = ax
    call scanSpaceOnly s
    if \ scanEnd(s) then
        call scanErr s, 'call does not end after )'
 say cx 'a1='m.sql.cx.var.1 'a2='m.sql.cx.var.2 'a3='m.sql.cx.var.3
    res = sqlExec('call' prc'('substr(s2, 3)')', 466 retOk)
 say cx 'a1='m.sql.cx.var.1 'a2='m.sql.cx.var.2 'a3='m.sql.cx.var.3
    if res  \== 466 then
        return res
    cC = substr(sqlErrMc, lastPos('ff'x, sqlErrMc)+1)
    rs = 'SQL.'cx'.RESULTSET'
    m.rs = 100+cx
    m.rs.0 = cc
    m.rs.act = 0
    lc = ''
    do rx=1 to cc
       lc = lc', :m.'rs'.'rx
       end
    call sqlExec0 'ASSOCIATE LOCATORS ('substr(lc, 3)')' ,
               'WITH PROCEDURE' prc
    if sqlNextResultSet(cx) then
        return 0
    else
        return err('no resultset')
endProcedure sqlCall

/*--- switch to next resultset, return false if none ----------------*/
sqlNextResultSet: procedure expose m.
parse arg cx
    rs = 'SQL.'cx'.RESULTSET'
    if m.rs <= 100 | m.rs.act >= m.rs.0 then
        return 0
    ax = m.rs.act + 1
    m.rs.act = ax
    call sqlResetCrs cx
    call sqlexec0 'allocate c'm.rs 'cursor for result set :m.'rs'.'ax
    CALL SQLEXEC0 'DESCRIBE CURSOR :C'm.rs 'INTO :M.SQL.'cx'.D'
    call sqlFetchVars cx
    return 1
endProcedure sqlNextResultSet

/*-- execute a query, update or call --------------------------------*/
sqlExecute: procedure expose m.
parse arg cx, src, retOk
    f = translate(word(src, 1))
    bx = pos('(', f)
    if bx > 0 then
        f = left(f, max(1, bx-1))
    m.sql.cx.fun = f
    if f == 'SELECT' | f == 'WITH' | f == '(' then
        return sqlQuery(cx, src, , retOk)
    else if f == 'CALL' then
        return sqlCall(cx, src, retOk)
    else
        return sqlUpdate(cx, src, retOk)
endProcedure sqlExecute

/*--- describe table and return sqlDA -------------------------------*/
sqlDescribeTable: procedure expose m.
parse upper arg tb, force
    if force == 1 | \ datatype(m.sql.table.tb.sqlD, 'n') then
         call sqlExec 'describe table :tb into :M.SQL.TABLE.'tb
    return 'SQL.TABLE.'tb
endProcedure sqlDescribeTable

/*--- return select column list for table tb
      omit Blobs (blobMax < 0) or substr(blob, 1, blobMax) ----------*/
sqlColList: procedure expose m.
parse arg tb al, blobMax
    sd = sqlDescribeTable(tb)
    bs = ''
    lst = ''
    if al \== '' & right(al, 1) \== '.' then
        al = al'.'
    do sx=1 to m.sd.sqld
        if wordPos(m.sd.sx.sqlType, '404 405 408 409 412 413') < 1 then
            lst = lst',' al || m.sd.sx.sqlName
        else do
            bs = bs m.sd.sx.sqlName
            if blobMax >= 0 then
                lst = lst', length('al || m.sd.sx.sqlName')' ,
                                          m.sd.sx.sqlName'Len' ,
                     || ', substr('al  || m.sd.sx.sqlName ,
                     || ', 1,' blobMax')' m.sd.sx.sqlName
            end
        end
    m.sd.colList = substr(lst, 3)
    m.sd.blobs = strip(bs)
    return substr(lst, 3)
endProcedure sqlColList

/*--- use describe output to generate column names,
        ''         use names from ca (rexxified)
        nms+       use names, check ca for null values
        ?('?'?nm)+ use names, check for null if preceeded by ?
        :...       use string as is
                fetchVariables and sqlNull names --------------------*/
sqlFetchVars: procedure expose m.
parse arg cx, src, cd
    if abbrev(src, ':') then do
        m.sql.cx.fetchVars = src
        m.sql.cx.fetchCode = cd
        m.sql.cx.fetchFlds = ''
        return
        end
    if src <> '' then do
        ff = src
        end
    else do
        ff = ''
        do kx=1 to m.sql.cx.d.sqlD
             ff = sqlNiceVarsApp(ff, m.sql.cx.d.kx.sqlName)
             end
        end
    m.sql.cx.fetchFlds = ff
    if m.sql.cx.d.sqlD <> words(ff) then
        call err 'sqlFetchVars sqlD='m.sql.cx.d.sqlD ,
                '<>' words(ff) 'fields of' ff
    sNu = ''
    sFe = ''
    do kx=1 to m.sql.cx.d.sqlD
        nm = word(ff, kx)
        sFe = sFe', :m.dst.'nm
        if m.sql.cx.d.kx.sqlType // 2 then do
            sFe = sFe' :m.dst.'nm'.sqlInd'
            sNu = sNu 'if m.dst.'nm'.sqlInd < 0 then' ,
                   'm.dst.'nm '= m.sqlNull;'
            end
        end
    m.sql.cx.fetchVars = substr(sFe, 3)
    m.sql.cx.fetchCode = sNu cd
    return
endProcedure sqlFetchVars

/*--- append next column name
          ensure name is unique and rexx compatible -----------------*/
sqlNiceVarsApp: procedure expose m.
parse arg old, nx rest
    upper nx
    cx = verifId(nx)
    if cx > 0 then /* avoid bad characters for classNew| */
        nx = left(nx, cx-1)
    if nx <> '' & wordPos(nx, old) < 1 then
        return old nx
    else
        return old  'COL' || (words(old) + 1)
endProcedure sqlNiceVarsApp

/*--- 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

sqlCommit: procedure expose m.
     return sqlExec0('commit')
endProcedure sqlCommit

/*** sql.4: diverse helpers ******************************************/
/*-- fetch all rows to stem and close -------------------------------*/
sqlFetch2St: procedure expose m.
parse arg cx, dst, retOK
    do sx=1 while sqlFetch(cx, dst'.'sx)
       end
    res = sx-1
    m.dst.0 = sx-1
    call sqlClose cx
    return m.dst.0
endProcedure sqlFetch2St

/*-- execute a query, copy result to stem ---------------------------*/
sql2St: procedure expose m.
parse arg src, dst, feVa, retOK
    cx = m.sql_defCurs
    res = sqlQuery(cx, src, feVa, retOk)
    return sqlFetch2St(cx, dst, retOk)
endProcedure sql2St

/*-- return first column of the only row and close ------------------*/
sqlFetch2One: procedure expose m.
parse arg cx, dst, retNone
    f1 = sqlFetch(cx, dst)
    if f1 == 1 then
        f2 = sqlFetch(cx, dst'.2')
    if f1 >= 0 then
         call sqlClose cx
    else do
        say 'sqlFetch2One sqlCode='f1
        call sqlClose cx, '*'
        end
    if f1 \== 1 then
        if retNone \== '' then
            return substr(retNone, 2)
        else
            call err 'sqlFetch2One: no row returned'
    else if f2 == 1 then
        call err 'sqlFetch2One: more than 1 row'
    else if f2 \== 0 then
        call err 'sqlFetch2One second fetch sqlCode='f2
    if m.sql.cx.fetchFlds == '' then do
        c1 = substr(word(m.sql.cx.fetchVars, 1), 2)
        res = value(c1)
        return res
        end
    c1 = word(m.sql.cx.fetchFlds, 1)
    return m.dst.c1
endProcedure sqlFetch2One

/*-- execute a query and return first column of the only row
           if > 1 row fail, if 0 rows return arg(3) or fail ---------*/
sql2One: procedure expose m.
parse arg src, dst, feVa, retOk, retNone
    cx = m.sql_defCurs
    call sqlQuery cx, src, feVa, retOk
    return sqlFetch2One(cx, dst, retNone)
endProcedure sql2One

/*--- execute the given sql plus a commit
         until no more rows are updated -----------------------------*/
sqlUpdComLoop: procedure expose m.
parse arg src, retOk
    cx = m.sql_defCurs
    upds = 0
    if retOk == '' then
        retOk = 100
    do coms=0
        cd = sqlExecute(crs, src, retOk)
        if m.sql.crs.updateCount < 1 then do
            return sqlMsgLine( , upds, src, coms 'commits')
            end
        upds = upds + m.sql.crs.updateCount
        call sqlCommit
        if coms // 20 = 19 then
            say sqlMsgLine(time(), upds, src, (coms+1) 'commits')
        end
endProcedure sqlUpdComLoop

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

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

/*--- mark a cursor as closed ---------------------------------------*/
sqlFreeCursor: procedure expose m.
parse arg cx
    if substr(m.sql_cursors, cx, 1) \== 'u' then
         call err 'sqlFreeCursor('cx') not in use :'m.sql_cursors
    m.sql_cursors = overlay(' ', m.sql_cursors, cx)
    return
endProcedure sqlFreeCursor

/* copy sql end   ****************************************************/
/* copy dsnList begin *************************************************
     csi interface: see dfs managing catalogs chapt. 11
         returncode/reason see message IDC3009I
**********************************************************************/
/*--- specify dsn mask and fields to start a csi catalog search -------
      arguments:
          m       objectPointer
          dsnMask specifies the dsns with wildcards:
              %  1 character
              *  0 - n character in one level
              ** 0 - n levels
          fields a (space separated) list of field names ------------*/
csiOpen: procedure expose m.
parse arg m, dsnMask, fields
    m.m.fld.0 = words(fields)
    ffix = d2c(m.m.fld.0, 2)
    do x=1 to m.m.fld.0
        m.m.fld.x = translate(word(fields, x))
        ffix = ffix || left(m.m.fld.x, 8)
        end
    if dsnMask \== '' & right(dsnMask, 1) \== ' ' ,
          & pos('*', dsnMask) < 1 & length(dsnMask) < 42 then
        dsnMask = dsnMask'.**'
    m.m.filt = left(dsnMask, 149) ,
             || left('Y', 3) ,        /* resume offset 149      */
             || ffix                  /* csiNumEn offset 152    */

    WORKLEN = 1024 * 64
    m.m.work = D2C(WORKLEN,4) || COPIES('00'X,WORKLEN-4)
    m.m.pos = workLen + 1
    return
endProcedure csiOpen

/*--- put the next dsn into m.o and m.o.* (for other fields)
      return 1 if next dsn exists 0 otherwise -----------------------*/
csiNext: procedure expose m.
parse arg m, o
    usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET AMOUNT OF WORK AREA USED*/
    px = m.m.pos
    do forever
        if px > usedL then do
            if substr(m.m.filt, 150, 1) \== 'Y' then do
                m.m.pos = px
                m.o = ''
                return 0
                end
            reason = left('', 4)
            ADDRESS LINKPGM 'IGGCSI00  reason m.'m'.filt  m.'m'.work'
            if rc == 0 & substr(reason, 3, 2) == '0000'x then
                nop
            else if rc == 4 & substr(reason, 3, 2) == '0464'x then
                say 'data set entry with error'
            else
                call err 'call csi returns' rc,
                             'rc' c2d(substr(reason, 4,1)),
                             'reason' c2d(substr(reason, 3,1)),
                             'module' substr(reason, 1,2)
            usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET WORK AREA USED */
            numFD = C2D(SUBSTR(m.m.work,13,2)) /* no flds + 1 */
            if numFd <> m.m.fld.0 + 1 then
                call err 'csiNumFd' numFd 'not' m.m.fld.0 '+' 1
            px = 15
            iterate
            end
        eType =  substr(m.m.work, px+1, 1)
        m.o = strip(substr(m.m.work, px+2, 44), 't')
        flag = substr(m.m.work, px, 1)
        /* say 'eType' eType m.o 'flag' c2x(flag) */
        if eType == '0' then do
            if flag \== '00'x & flag \== '40'x then
                call err 'flag' c2x(flag) 'for catalog' m.o
            px = px + 50    /* length of catalog entry  */
            iterate
            end
        else do
            if \ abbrev(x2b(c2x(flag)), '101') then
                call err 'call csi entry flag' x2b(c2x(flag)),
                             'rc' c2d(substr(m.m.work, px+49,1)),
                             'reason' c2d(substr(m.m.work, px+48,1)),
                             'module' substr(m.m.work, px+46, 2),
                             'for entry' m.o,
                             'see qw IDC3009I'
            py = px + 46
            tl = c2d(substr(m.m.work, py, 2))
            pl = py + 4
            pf = py + m.m.fld.0 * 2 + 4
            do fx = 1 to m.m.fld.0
                fi = m.m.fld.fx
                fl = c2d(substr(m.m.work, pl, 2))
                m.o.fi = substr(m.m.work, pf, fl)
                if fi = 'MGMTCLAS' then
                    m.o.fi = substr(m.o.fi, 3, c2d(left(m.o.fi ,2)))
                else if wordPos(fi, 'COMUDSIZ NOBYTTRK') > 0 then
                    m.o.fi = utc2d(m.o.fi)
                pf = pf + fl
                pl = pl + 2
                end
            if py + tl <> pf then
                call err 'length mismatch for entry' m.o
            m.m.pos = pf
            return 1
            end
        end
endProcedure csiNext

/*--- if dsn is arcived return 'arcive'
      if dsn is tape return 'tape'
      otherwise return managment class ------------------------------*/
csiArcTape: procedure expose m.
parse arg vo, cl, dt, dsn
        if vo = '' then
            say err '||| no volume for dsn' dsn
        else if vo = 'ARCIVE' | vo = 'MIGRAT' then
            res = 'arcive'
        else if cl <> '' then
            res = cl
        else if abbrev(vo, 'SHR') then
            res = 'SHR'
        else
            res = 'tape'
        if   res = 'arcive' then
            return res
      /*if   abbrev(res, 'ar') \= abbrev(dt, '00'x) , */
        if   abbrev(res, 'ta') \= abbrev(c2x(left(dt, 1)), '7') ,
           | (left(res, 1) >= 'A') \= abbrev(dt, '30'x) then
           say '||| mismatch cl' cl 'vo' vo 'dt' c2x(dt) 'dsn' dsn
        return res
endProcedure csiArcTape

/*--- check if a dataset is archive -----------------------------------
          returns 'ok'                    if dataset on disk
                  'not'                   if dataset is not catalogued
                  'arc'                   if dataset archived
                  listDsi errorMsg        otherwise -----------------*/
dsnArc: procedure expose m.
parse upper arg dsn
    lc = listDsi("'"strip(dsn)"' noRecall")
    if lc = 0 then
        return 'ok'
    else if lc=4 & sysReason = 19 then  /* multiple volumes */
        return 'ok'
    else if lc=16 & sysReason = 5 then
        return 'notCat'
    else if lc=16 & sysReason = 9 then
        return 'arc'
    else
        return 'listDsi cc='lc', sysReason='sysReason ,
                          'm2='sysMsgLvl2', m1='sysMsgLvl1
endProcedure dsnArc

/*--- dsnList: csi or csm to stem -----------------------------------*/
dsnList: procedure expose m.
parse arg oo, aMsk, rzPref
    parse value dsnCsmSys(aMsk) with rz '/' msk
    if msk \== '' & right(msk, 1) \== ' ' ,
          & pos('*', msk) < 1 & length(msk) < 42 then
        msk = msk'.**'
    if rz == '*' then do
        call csiOpen dsnList_csi, msk
        do ox=1 while csiNext(dsnList_csi, oo'.'ox)
            end
        end
    else do
        pre = copies(rz'/', rzPref \== 0)
        call adrCsm 'dslist system('rz') dsnMask('msk') short', 4
        do ox=1 to stemSize
            m.oo.ox = pre || dsName.ox
            end
        end
    m.oo.0 = ox-1
    return m.oo.0
endProcedure dsnList

/*--- mbrList with listDS or csm to stem ----------------------------*/
mbrList: procedure expose m.
parse arg m, pds
    parse value dsnCsmSys(translate(dsn2jcl(pds))) with sys '/' dsn
    msk = strip(dsnGetMbr(dsn))
    if msk == '*' then
        msk = ''
    dsn = dsnSetMbr(dsn)
    if sys \== '*' then
        return csmMbrList(m, sys, dsn, msk)
    if adrTso(listDS "'"dsn"'" members, 8) <> 0 then
        mx = -99
    else if m.tso_trap.1 <> dsn then
        call err 'mbrList dsn='dsn '<> trap.1='m.tso_trap.1
    else if m.tso_trap.2 <> '--RECFM-LRECL-BLKSIZE-DSORG' then
        call err 'mbrList dsn='dsn 'trap.2='m.tso_trap.2
    else do
        parse var m.tso_trap.3 ,
            m.m.RECFM m.m.LRECL m.m.BLKSIZE m.m.DSORG .
        oy = m.tso_trap.0 + 99
        mFound = 0
        mx = 0
        do ox=4 to m.tso_trap.0
            if mFound then do
                if msk \== '' then
                    if \ match(strip(m.tso_trap.ox), msk) then
                        iterate
                mx = mx + 1
                m.m.mx = strip(m.tso_trap.ox)
                end
            else
                mFound = m.tso_trap.ox == '--MEMBERS--'
            end
        if \ mFound then
            mx = -98
        end
    m.m.0 = mx
    return mx
endProcedure mbrList

/*--- return whether a dsn exists -----------------------------------*/
dsnExists: procedure expose m.
parse upper arg aDsn
    parse value dsnCsmSys(aDsn) with rz '/' dsn
    if rz == '*' then
        return sysDsn("'"dsn"'") == 'OK'
    else if dsnGetMbr(dsn) <> '' then
        return csmMbrList(tso_dsnExits, rz, dsnSetMbr(dsn) ,
                  , dsnGetMbr(dsn)) == 1
    else do
        lc = adrCsm('dslist system('rz') dsnMask('dsn') short', 4)
        if stemsize = 0 | stemSize = 1 then
            return stemSize
        call err 'csmExists stemSize='stemsize 'for dsn='aDsn
        end
endProcedure dsnExists

/*--- copy members / datasets ---------------------------------------
      fr, to from or to dsn with or without member
      mbrs: space separated list of mbr or old>new
      opts
      *  all members from lib to lib
      &  members as defined in mbrs argument
      -  sequentiel (or library WITH member)
      *- if * fails then do - from fr to to
      &- if & fails then do - from fr(mbr) to to
---------------------------------------------------------------------*/
dsnCopy: procedure expose m.
parse upper arg fr opt . , to toPl, mbrs
    op1 = '?'
    if opt \== '' then do
        parse upper arg opt fr .
        if pos(left(opt, 1), 'WTC?') > 0 then
            parse var opt op1 2 opt
        end
    if opt == '-' then do
        if mbrs \== '' then
            call err 'op1 -  but mbrs not empty' mbrs
        end
    else do
        fMb = dsnGetMbr(fr)
        fr = dsn2jcl(dsnSetMbr(fr))
        tMb = dsnGetMbr(to)
        to = dsn2jcl(dsnSetMbr(to))
        if mbrs = '' then
            if fMb = '' then
                to = dsnSetMbr(to, tMb)
            else if tMb = '' then
                mbrs = fMb
            else
                mbrs = fMb'>'tMb
        else if fMb \== '' | tMb \== '' then
            call err 'fr='fr 'to='to 'but with mbrs='mbrs
        if mbrs = '' then
            o2 = left('*', tMb = '')'-'
        else if words(mbrs) = 1 & pos('>', mbrs) < 1 then
            o2 = if(verify(mbrs, '*?', 'm') > 0, '*', '&')'-'
        else
            o2 = '&'
        if opt == '' then
            opt = o2
        else if pos(opt, o2) == 0 then
            call 'bad opt' opt 'not in' o2
        end

    if abbrev(opt, '*') then do
        mbrs = ''
        do mx=1 to mbrList(tso_dsnCopy, fr'('fMb')')
            mbrs = mbrs m.tso_dsnCopy.mx
            end
        if m.tso_dsnCopy.0 > 0 then
            opt = '&'
        else if m.tso_dsnCopy.0 = 0 then do
            say 'nothing copied, no members in' fr
            return
            end
        else if substr(opt, 2, 1) == '-' then
            opt = '-'
        else
            return err(fr 'is not a library')
        end
         /* currently we use csm, which calls IBM Utilities
               for us, which seems not to be easy do to directly */
    if op1 == 'C' | op1 == '?' then do
        r = csmCop2(op1 opt, fr, to toPl, mbrs)
        if datatype(r, 'n') then
            return r
        op1 = r
        end
    if op1 == 'W' | op1 == 'T' then           /* use read and write,
                                                 allows reformatting */
        return dsnCopW(op1 opt, fr, to toPl, mbrs)
    call err 'dsnCopy bad opt' op1 opt
endProcedure dsnCopy

dsnCopW: procedure expose m. i.
parse arg o1 o2, fr, to tPl, mbrs
    if words(mbrs) > 1 then do
        do mx=1 to words(mbrs)
            call dsnCopW o1 o2, fr, to tPl, word(mbrs, mx)
            end
        return words(mbrs)
        end
    parse var tPl tA1 ':' tA2
    if \ abbrev(o2, '&') then do
        parse value dsnAlloc(fr, , 'readDD') with fDD fFr
        tAt = strip(tA1 ':'firstNS(tA2, ':D'fDD))
        parse value dsnAlloc(to tAt, , 'writeDD') with tDD tFr
        end
    else do
        parse value strip(mbrs) with fMb '>' tMb
        fr = dsnSetMbr(fr, fMb)
        parse value dsnAlloc(fr, , 'readDD') with fDD fFr
        tAt = strip(tA1 ':'firstNS(tA2, ':D'fDD))
        to = dsnSetMbr(to, firstNS(tMb, fMb))
        parse value dsnCsmSys(to) with rz '/' .
        if o2 = '&-' & rz == '*' then do
            r2 = sysDsn("'"to"'")
            if r2 == 'OK' | r2 == 'MEMBER NOT FOUND' ,
                 | r2 == 'DATASET NOT FOUND' then
                nop
            else if r2 ,
            == 'MEMBER SPECIFIED, BUT DATASET IS NOT PARTITIONED' then
                to = dsnSetMbr(to)
            else
                call err 'sysDsn(to='to')' r2
            end
        parse value dsnAlloc(to tAt, , 'writeDD') with tDD tFr
        if o2 = '&-' & rz \== '*' then do
            if m.tso_dsorg.tDD <> 'PO' then do
                call tsoFree tFr
                to = dsnSetMbr(to)
                parse value dsnAlloc(to tAt, , 'writeDD') with tDD tFr
                end
            end
        end
    cnt = 0
    trunc = 0
    do while readDD(fDD, i., 500)
        cnt = cnt + i.0
        call writeDD tDD, i., , o1 == 'T'
        if m.tso_rc then
            trunc = 1
        end
    call tsoClose fDD
    if cnt = 0 then
        call tsoOpen tDD, 'W'
    call tsoClose tDD
    call tsoFree fFr tFr
    say 'copied' cnt 'recs from' fr 'to' to copies('truncation',trunc)
    return cnt
endProcedure dsnCopW

dsnDel: procedure expose m.
parse upper arg aDsn, aMbrs
    parse value dsnCsmSys(dsn2jcl(aDsn)) with sys '/' dsn
    mbrs = dsnGetMbr(dsn) aMbrs
    dsn = dsnSetMbr(dsn)
    if sys \== '*' then
        return csmDel(sys, dsn, mbrs)
    if mbrs = '' then do
        dRc = adrTso("delete '"dsn"'", 8)
        end
    else do
        call dsnAlloc 'dd(deldd)' dsn
        do mx=1 to words(mbrs)
            m1 = word(mbrs, mx)
            dRc = adrTso("delete '"dsn"("m1")' file(delDD)", 8)
            if dRc <> 0 then do
                if pos('IDC3330I **' m1' ', m.tso_trap) < 1 then
                    leave
                say 'member not found and not deleted:' dsn'('m1')'
                dRc = 0
                end
            end
        call tsoFree deldd
        end
    if dRc = 0 then
        return 0
    if pos('IDC3012I ENTRY' dsn 'NOT FO', m.tso_trap) >= 1 then do
        say 'dsn not found and not deleted:' dsn
        return 4
        end
    call err 'adrTso rc='m.tso_rc 'stmt='m.tso_stmt m.tso_trap
    return 8
endProcedure dsnDel
/* copy dsnList end   ************************************************/
/* copy csm begin *****************************************************
    interface to csm,
        it is integrate with adrTso, eg. dsnAlloc , 'RZ3/..' uses csm
**********************************************************************/
/*--- execute a single csmExec command ------------------------------*/
adrCsm:
    return csmEx2('csmExec' arg(1), arg(2))

/*--- execute a single csmAppc start command
      arg(1)=pgm, arg(2)=parm, arg(3)=rest, arg(4)=retOk ------------*/
csmAppc:
    appc_rc = 0 /* csmAppc erstellt diese Variable nicht immmer| */
    appc_msg.0 = 0
    if 0 <> csmEx2('csmAppc start pgm('arg(1)')' ,
                  copies("parm("quote(arg(2), "'")")",
                        , arg(2) <> '') arg(3) , arg(4)) then
        ggRc = m.tso_rc
    else if appc_rc = 0 then
        return 0
    else do
        ggRc = appc_rc
        m.csm_err = ''
        m.csm_errMsg = 'tso_rc=0'
        end
    ggMsg = 'csmAppc rc='ggRc 'appc_rc='appc_rc ,
        'reason='appc_reason 'state_c='appc_state_c appc_state_f ,
        '\n  SUBSYS_TSR15='subsys_tsr15 'tsRc='SUBSYS_TSRC  ,
                 'abend='subsys_tsAbend 'reason='subsys_tsReason
        do ggCsmIx=1 to appc_msg.0
            ggMsg = ggMsg '\n   ' appc_msg.ggCsmIx
            end
    m.csm_errMsg = ggMsg'\n'm.csm_errMsg
    return ggRc
endRoutine csmAppc

/*--- execute single command in arg(1) -----------------------------*/
csmEx2:
    if wordPos(translate(word(arg(1), 2)), 'COPY MBRLIST') > 0 then
       ggTO = ''
    else if pos('TIMEOUT(' , translate(arg(1))) > 0 then
       ggTO = ''
    else if symbol('m.csm_timeOut') == 'VAR' then
        ggTO = 'timeout('m.csm_timeOut')'
    else
        ggTO = 'timeout(30)'
    ggStart = time()
    if adrTso(arg(1) ggTO, '*') == 0 then
        return 0
    if pos('CSMXC43E NO CONNECTION TO S', m.tso_trap) > 0 ,
         | pos('CSMXC36E TIMEOUT LIMIT EXCEEDED DURING LOGON TO' ,
            , m.tso_trap) > 0 then
               /* CSMXC43E NO CONNECTION TO SYSPLEX:RR2 AVAILABLE
                  CSMXC36E TIMEOUT LIMIT EXCEEDED DURING LOGON TO SYS*/
        m.csm_err = 'noConn'
    else if pos('IKJ56225I', m.tso_trap) > 0             ,
               & ( pos('ALREADY IN USE', m.tso_trap) > 0 ,
                 | pos('CATED TO ANOTH', m.tso_trap) > 0) then
               /* 5 IKJ56225I DATA SET A540769.TMP.TEXT ALREADY IN USE,
                  6 IKJ56225I DATA SET IS ALLOCATED TO ANOTHER JOB */
        m.csm_err = 'inUse'
    else if pos('CSMAP47E TP timed out', m.tso_trap) > 0 then
        m.csm_err = 'timeout'
    else
        m.csm_err = ''
    m.csm_errMsg = strip('csm' m.csm_err) 'rc='m.tso_rc ,
            '\nstmt='m.tso_stmt m.tso_trap ,
            '\ntime='ggStart '-' time()
    if pos('*', arg(2)) <= 0 & wordPos(m.tso_rc, arg(2)) <= 0 then
        call err m.csm_errMsg
    return m.tso_rc
endRoutine csmEx2

csmDel: procedure expose m.
parse upper arg rz, dsn, aMbrs
    mbrs = dsnGetMbr(dsn) aMbrs
    lib = dsnSetMbr(dsn)
    dd = tsoDD(csmDel, 'a')
    if mbrs = '' then do
        dRc = adrCsm("allocate system("rz") dataset('"lib"')" ,
                         "disp(del) ddname("dd")", 8)
        end
    else do
        dRc = adrCsm("allocate system("rz") dataset('"lib"')" ,
                         "disp(shr) ddname("dd")", 8)
        if dRc == 0 then do
            do mx=1 to words(mbrs)
                m1 = word(mbrs, mx)
                dRc = adrCsm("mDelete ddName("dd") member("m1")", 8)
                if dRc <> 0 then do
                    if pos('CSMEX77E Member:'m1  'not f', m.tso_trap) ,
                            < 1 then
                        leave
                  say 'member not found, not deleted:' rz'/'dsn'('m1')'
                  dRc = 0
                  end
                end
            end
        end
    if dRc = 0 then
        return tsoFree(dd)
    if pos('CSMSV29E DATA SET' lib 'NOT IN CAT', m.tso_trap) >0 then do
        say 'dsn not found and not deleted:' rz'/'dsn
        call tsoFree dd
        return 4
        end
    eMsg = 'rc='m.tso_rc 'stmt='m.tso_stmt':' m.tso_trap
    call tsoFree dd
    return err('csmDel' eMsg)
endProcedure csmDel
/*--- copy members / datasets
      Vorlage: csrxUtil ---------------------------------------------*/
csmCopy: procedure expose m.
parse upper arg fr, to, mbrs
    say 'please use dsnCopy instead of depreceated csmCopy'
    return dsnCopy(fr, to, mbrs)

csmCop2: procedure expose m.
parse upper arg o1 o2, fr, to tA1 ':' tA2, mbrs
    frDD = tsoDD('csmFrDD', 'a')
    tAt =  strip(tA1 firstNS(tA2, ':D'frDD))
    toDD = tsoDD('csmToDD', 'a')
    mbr1 = abbrev(o2, '&') & words(mbrs) = 1
    if mbr1 then do
        parse value strip(mbrs) with fMb '>' tMb
        call csmAlloc fr'('fMb')', frDD, 'shr'
        tM2 = firstNS(tMb, copies(fMb, o2 <> '&-'))
        call csmAlloc dsnSetMbr(to, tM2), toDD, 'shr', , tAt
        end
    else do
        call csmAlloc fr, frDD, 'shr'
        call csmAlloc to, toDD, 'shr', , tAt
        end
    if      m.tso_recFM.frDD <> m.tso_recFM.toDD ,
          | m.tso_lRecL.frDD <> m.tso_lRecL.toDD then do
        call tsoFree frDD toDD
        return if(m.tso_lRecL.frDD <= m.tso_lRecL.toDD, 'W', 'T')
        end
    inDD = tsoDD('csmInDD', 'a')
    i.0 = 0
    if abbrev(o2, '&') & \ mbr1 then do
        i.0 = words(mbrs)
        do mx=1 to i.0
            parse value word(mbrs, mx) with mF '>' mT
            if mF = '' then
                call err 'bad mbr or mbrOld>mbrNew' word(mbrs, mx),
                          'in csmCopy('fr',' to','mbrs')'
            else if mT = '' then
                i.mx = ' S M='mF
            else
                i.mx = ' S M=(('mF','mT'))'
            end
        end
    if i.0 <= 0 then do
        call adrTso 'alloc dd('inDD') dummy'
        end
    else do
        call tsoAlloc ,inDD, 'NEW', , ':F'
        call writeDD inDD, 'I.', i.0
        call tsoCLose inDD
        end
    outDD = tsoDD('csmOuDD', 'a')
    call dsnAlloc('dd('outDD') new ::V137')
    cmdu = 'CSMUTIL CSM,COPYREPLACE,DD(,,,,'inDD','outDD ,
               ||  ',,'frDD','toDD'),MARC(0)'
    cRc = adrTso(cmdU, '*')
    if cRc <> 0 then do
        call readDD outDD, o.
        call tsoClose outDD
        say 'rc='cRc',' o.0 'outputlines for' cmdU
        do ox=1 to o.0
            say o.ox
            end
        end
    call tsoFree frDD toDD inDD outDD
    if cRc <> 0 then
        call err 'csmCopy rc='cRc
    return cRc
endProcedure csmCopy

csmAlloc: procedure expose m.
parse arg sysDsn, dd, disp, rest, nn, retRc
    upper dd disp
    parse value dsnCsmSys(sysDsn) with sys '/' dsn
    m.tso_dsn.dd = sys'/'dsn
    if disp = '' then
        disp = 'shr'
    else if words(disp) = 2 then
        disp = word(disp, 2)
    a1 = "SYSTEM("sys") DDNAME("dd")"
    if dsn == 'INTRDR' then do
        a1 = a1 'sysout(T) writer(intRdr)'
        end
    else do
        if dsn <> '' then do
            a1 = a1 "DATASET('"dsnSetMbr(dsn)"')"
            mbr = dsnGetMbr(dsn)
            if mbr <> '' then
                a1 = a1 'MEMBER('mbr')'
            end
        if abbrev(disp, 'SYSOUT(') then
            a1 = a1 disp
        else
            a1 = a1 "DISP("disp")"
        end
    isNew = wordPos(disp, 'NEW MOD CAT') > 0
    if isNew & nn \== '' then
        rest = dsnCreateAtts('-'dsn , nn) rest
    cx = pos(' UCOUNT(', ' 'translate(rest))
    if cx > 0 then do
         rest = left(rest, cx) || 'nitCnt(' || substr(rest, cx+7)
         end
    cx = pos(' RECFM(', ' 'translate(rest))
    if cx > 0 then do
        recFm = substr(rest, cx+6, 1)
        cy = pos(')', rest, cx)
        if cy > cx then
            rest = left(rest, cx+5) || space(substr(rest,cx+6,cy-cx-6),
                               , 0) || substr(rest,cy)
        end
    cx = pos(' CYLINDERS ', ' 'translate(rest)' ')
    if cx > 0 then do
        rest = delStr(rest, cx+8, 1)
        end
    cx = pos(' CYL ', ' 'translate(rest)' ')
    if cx > 0 then
        rest = insert('inder', rest, cx+2)
    if isNew then
         if pos(' BLKSIZE(', ' 'translate(rest)) <= 0 then do
             /* without blkSize csm will fail to read for rec < 272 */
             cx = pos(' LRECL(', ' 'translate(rest))
             lrecl = substr(rest, cx+6,
                           , max(0, pos(')', rest, cx+6) - cx - 6))
             blk = 32760
             if datatype(lRecl ,'n') & translate(recfm) = 'F' then
                 blk = blk - blk // lRecL
             rest = rest 'blkSize('blk')'
             end
    noRetry = retRc <> '' | isNew | nn == ''
    alRc = adrCsm('allocate' a1 rest, if(noRetry, retRc, '*'))
    m.tso_dsorg.dd = subsys_dsOrg
    m.tso_recFM.dd = subsys_recFM
    m.tso_blkSize.dd = subsys_blkSize
    m.tso_lRecL.dd = subsys_lRecL
    if alRc = 0 then
        return 0
    m.tso_dsnNF.dd = pos('CSMSV29E DATA SET' dsnSetMbr(dsn) ,
                         'NOT IN CATALOG', m.tso_trap) > 0
    if noRetry | \ m.tso_dsnNF.dd then
        if pos('*', retRc) > 0 | wordPos(alRc, retRc) > 0 then
            return alRc
        else
            return err(m.csm_errMsg)
    say 'csmAlloc rc' alRc 'for' a1 rest '...trying to create'
    call csmAlloc sysDsn, dd, 'CAT', rest ,nn
    call adrTso 'free  dd('dd')'
    return adrCsm('allocate' a1 rest)
endProcedure csmAlloc

csmLikeAtts: procedure expose m.
parse upper arg rz '/' dsn
    lc = adrCsm('dslist system('rz') dsnMask('dsnSetMbr(dsn)')')
    if stemsize <> 1 then
        call err 'csmLikeAtts stemSize='stemsize 'for dsn='dsn
    if abbrev(dsOrg.1, 'PO') then
        r = 'dsorg(po) dsnType(library)'
    else if abbrev(dsOrg.1, 'PS-') then
        r = 'dsorg(PS)'
    else
        r = 'dsorg('dsOrg.1')'
    r = r 'mgmtClas('mgmtClas.1')'                       ,
       /* 'dataClas('dataClas.1')'   */                  ,
          'recFM('strip(translate('1 2 3', recFm.1, '123'))')'  ,
          'lRecl('lRecl.1')'                         ,
          'space('fUnit2I('b', tracksused.1) ,
           || ',' fUnit2I('b', tracks.1)') tracks'
    /*    if \ datatype(tracksused.1, 'n') then do
              if \ datatype(tracks.1, 'n') then
                  r = r 'space('tracks.1',' tracks.1')'
              if \ datatype(tracks.1, 'n') then
                  tracks.1 = tracksUsed.1   */

    return r
endProcedure csmLikeAtts

csmMbrList: procedure expose m.
parse arg m, sys, dsn, msk
              /* attention mbrList dataset(....)
                 does not cleanup proberly if dsn is NOT PO
                 and much later on follow errors appear
                 which are hard to debug| */
    if dataType(dsnAlloc(sys'/'dsn, , mbrLisDD, 8), 'n') then do
        say sys dsn
        say m.tso_trap
        m.m.dsnNF = m.tso_dsnNF.mbrLisDD
        if \ m.m.dsnNF then
            call err m.csm_errMsg
        m.m.0 = -99
        end
    else do
        m.m.dsnNF   = 0
        m.m.RECFM   = m.tso_RECFM.mbrLisDD
        m.m.LRECL   = m.tso_LRECL.mbrLisDD
        m.m.BLKSIZE = m.tso_BLKSIZE.mbrLisDD
        m.m.DSORG   = m.tso_DSORG.mbrLisDD
        if m.m.DSORG \== 'PO' then
            m.m.0 = -98
        else do
            if msk <> '' then
                msk = 'member('translate(msk, '%', '?')')'
            call adrCsm "mbrList ddName(mbrLisDD)" msk ,
                        "index(' ') short"
            m.m.0 = mbr_name.0
            do mx=1 to mbr_name.0
                m.m.mx = strip(mbr_name.mx)
                end
            end
        call tsoFree mbrLisDD
        end
    return m.m.0
endProcedure csmMbrList

/*--- execute a rexx (under tso) in another rz
           here we use rexx TPSYSIKJ which was written for
           jcl procedure RM@IKJ01
arguments
rz   which rz to run rexx
proc the (remote) procedure library to use
cmd  the tso command to execute
---------------------------------------------------------------------*/
/*--- execute a rexx (under tso) in another rz
          directly (without TPSYSIKJ) -------------------------------*/
csmExRx: procedure expose m.
parse arg rz, proc, cmd, keepTsPrt, retOk
    do cx=1 to (length(cmd)-1) % 68       /* split tso cmd in linews */
        cmd.cx = substr(cmd, 68*cx-67,68)'-'
        end
    cmd.cx = substr(cmd, 68*cx-67)
    cmd.0 = cx
                                       /* alloc necessary dd */
    call dsnAlloc rz"/"proc "dd(rmSyPro) rmtDDn(sysProc)"
    call dsnAlloc rz"/tmp.tsin new dd(rmTsIn) rmtDdn(sysTsIn) ::f "
    call tsoOpen rmTsIn, 'w'           /* write tso cmd */
    call writeDD rmTsIn, cmd.
    call tsoClose rmtsin
    call dsnAlloc rz"/tmp.tsPrt new dd(rmTsPrt) rmtDdn(sysTsPrt)",
                    "::v"
    call dsnAlloc rz"/tmp.rmt new dd(rmtSys)"
    m.csm_exRxMsg = ''
    m.csm_exRxRc = csmappc("csmexec" ,
         , "select tsocmd('csmappc allocate plu(*.rmtSys)" ,
           "tpname(sysikj) dealloc')", , "*")
    if m.csm_exRxRc <> 0 then do /* handle csm error */
        call readDD 'rmTsPrt', 'M.CSM_TSPRT.', '*'
        call tsoClose rmTsPrt
        msg = '\n'left('remote sysTsPrt' ,
              m.csm_tsprt.0 'lines begin ', 79, '-')
        do lx=1 to min(100, m.csm_tsPrt.0)
             msg = msg'\n'strip(m.csm_tsPrt.lx, 't')
             end
        l2 = max(lx, m.csm_tsPrt.0-99)
        if l2 > lx then
            msg = msg'\n'left('remote sysTsPrt' ,
              m.csm_tsprt.0 'lines' l2 'to' m.csm_tsPrt.0, 79, '-')
        do lx=l2 to m.csm_tsPrt.0
             msg = msg'\n'strip(m, m.csm_tsPrt.lx, 't')
             end
        m.csm_exRxMsg = 'cmsExRx rc='ggRc m.csm_errMsg || msg ,
              '\n'left('remote sysTsPrt' ,
                  m.csm_tsprt.0 'lines end ', 79, '-')
    /*  call sayNl m.csm_exRxMsg */
        end
    call tsoFree rmSyPro rmtSys rmtsIn copies(rmTsPrt, keepTsPrt\==1)
    if pos(m.csm_exRxRc, 0 4) < 1 then do /* handle csm error */
        if pos('*', retOk) > 0 | wordPos(m.csm_exRxRc, retOk) > 0 then
            call sayNl m.csm_exRxMsg
        else
            call err m.csm_exRxMsg
        end
    return m.csm_exRxRc
endProcedure csmExRx


csmExWsh: procedure expose m.
parse arg rz, rdr, opt
    w = oNew(m.class_csmExWsh, rz, rdr, opt)
    call pipeWriteAll w
    return

csmExWshOpen: procedure expose m.
parse arg m, opt
     rz = m.m.rz
     if opt \== '<' then
         call err 'csmExWshOpen('opt') not read'
     a1 = dsnAlloc(rz"/tmp.wsh new dd(rmtWsh) rmtDdn(wsh) ::v",,, '*')
     if datatype(a1, 'n') then do
          call sayNl 'adrTso rc='a1 'stmt='m.tso_stmt m.tso_trap
          say 'trying to free'
          call tsoFree 'rmtSys rmTsPrt rmTsIn rmSyPro rmtWsh rmtOut'
          call dsnAlloc rz"/tmp.wsh new dd(rmtWsh) rmtDdn(wsh) ::v"
          end
     wsh = jOpen(file('dd(rmtWsh)'), '>')
     call jWriteNow wsh, in2file(m.m.rdr)
     call jClose wsh
     parse var m.m.wOpt oOpt wSpec
     if wSpec = '' then
         wSpec = '@'
     o2 = firstNS(oOpt, 'v')
     if oOpt == 'e' then do
         o2 = 'v'
         wSpec = '$#outFmt e $#'wSpec
         end
     if o2 == 'p' then do
         fo = file('dd(rmTsPrt)')
         end
     else do
         call dsnAlloc rz"/tmp.out new dd(rmtOut) rmtDdn(out) ::"o2
         fo = file('dd(rmtOut)')
         end
     if oOpt == 'e' then
         m.m.deleg = csvIntRdr(csvF2VRdr(fo))
     else
         m.m.deleg = fo
     say 'cmsExWsh sending to' rz wSpec
     if abbrev(m.myLib, A540769) then
         m.m.exRxRc = csmExRx(rz, m.myLib, m.myWsh wSpec,
             , o2 == 'p' , '*')
     else
         m.m.exRxRc = csmExRx(rz, 'DSN.DB2.EXEC', 'WSH' wSpec,
             , o2 == 'p' , '*')
     call tsoFree 'rmtWsh'
     call jOpen m.m.deleg, opt
     m.fo.free = m.fo.dd
     return m
endProcedure csmExWshOpen

csmIni: procedure expose m.
    if m.csm_ini == 1 then
        return
    m.csm_ini = 1
    call catIni
    call classNew 'n CsmExWsh u JRWDeleg', 'm'                   ,
        , "jReset m.m.deleg = ''; m.m.rz = arg; m.m.rdr = arg2"  ,
                               "; m.m.wOpt = arg(4)"             ,
        , "jOpen call csmExWshOpen m, opt"                       ,
        , "jClose call jClose m.m.deleg;" ,
              "if pos(m.m.exRxRc,0 4)<1 then call err m.csm_ExRxMsg;",
              "else say 'csm execute wsh rc =' m.m.exRxRc"
    return
endProcedure csmIni

/* copy csm end ******************************************************/
/* copy timing begin *************************************************/
timing: procedure expose m.
parse arg typ, c2, txt
    e1 = time('E')
    c1 = strip(sysvar('syscpu'))
    s1 = sysvar('syssrv')
    if typ == '' then
        return strip(f('%c ela=%5i cpu=%8.3i su=%9i' ,
            , time(), e1, c1, s1) txt)
    if symbol('m.timing_ela') \== 'VAR' then
        call err 'timing('typ',' c2',' txt') ohne ini'
    if symbol('m.timing.typ.ela') \== 'VAR' then do
        m.timing.typ.ela = 0
        m.timing.typ.cpu = 0
        m.timing.typ.su  = 0
        m.timing.typ.cnt = 0
        m.timing.typ.cn2 = 0
        if symbol('m.timing_types') == 'VAR' then
            m.timing_types = m.timing_types typ
        else
            m.timing_types = typ
        if symbol('m.timing_say') \== 'VAR' then
            m.timing_say = 0
        end
    m.timing.typ.ela = m.timing.typ.ela + e1 - m.timing_ela
    m.timing.typ.cpu = m.timing.typ.cpu + c1 - m.timing_cpu
    m.timing.typ.su  = m.timing.typ.su  + s1 - m.timing_su
    m.timing.typ.cnt = m.timing.typ.cnt + 1
    if c2 \== '' then
       m.timing.typ.cn2 = m.timing.typ.cn2 + c2
    m.timing_ela = e1
    m.timing_cpu = c1
    m.timing_su  = s1
    if m.timing_say then
            say left(typ, 10)right(m.timing.typ.cn2, 10) ,
                'ela='m.timing.typ.ela ,
                'cpu='m.timing.typ.cpu 'su='m.timing.typ.su txt
    return
endProcedure timing

timingSummary: procedure expose m.
    say 'timing summary' time()
    do tx = 1 to words(m.timing_types)
        typ = word(m.timing_types, tx)
        say left(typ, 10)right(m.timing.typ.cnt,  7)       ,
                      || right(m.timing.typ.cn2,  7)       ,
                         'cpu='right(m.timing.typ.cpu, 10) ,
                         'su='right(m.timing.typ.su, 10)
        end
    return
endProcedure timingSummary
/* copy timing end   *************************************************/
/* copy ii begin ********* Installation Info *************************/
iiini: procedure expose m.
    if m.ii_ini == 1 then
        return
    m.ii_ini = 1
    m.ii_ds.org = ORG.U0009.B0106.MLEM43
    m.ii_ds.db2 = DSN.DB2
    m.ii_rz = ''
    i = 'RZ0 0 T S0 RZ1 1 A S1'  ,
        'RZX X X X2 RZY Y Y Y2 RZZ Z Z Z2'  ,
        'RQ2 Q Q Q2 RR2 R R R2 RZ2 2 B S2 RZ4 4 D S4'
    do while i <> ''
        parse var i rz ch pl sys i
        if rz <> RZ0 & rz <> RZ1 then
            m.ii_rz = strip(m.ii_rz rz)
        m.ii_rz2c.rz = ch
        m.ii_c2rz.ch = rz
        m.ii_rz2plex.rz = pl
        m.ii_plex2rz.pl = rz
        m.ii_rz2Sys.rz  = sys
        m.ii_sys2rz.sys = rz
        end
    i = 'DBTF T DTF DVTB V DTB DBOC C DOC DBTC B DBT DBIA A DIA' ,
        'DE0G E DE0 DEVG M DEV DPXG I DPX DX0G X DX0' ,
        'DPYG Y DPY DPZG N DPZ' ,
        'DBOF F DOF DVBP P DBP DP2G Q DP2 DBOL O DOL DP4G U DP4'
    do while i <> ''
        parse var i db ch mbr i
        m.ii_db2c.db = ch
        m.ii_c2db.ch = db
        m.ii_mbr2db.mbr = db
        m.ii_db2mbr.db  = mbr
        m.ii_db2Elar.db = wordPos(db, 'DVTB DVBP DEVG') > 0
        end
    m.ii_rz2db.rz0 = 'DBTC DBIA'
    m.ii_rz2db.rz1 = 'DBTF DVTB DBOC'
    m.ii_rz2db.rzx = 'DE0G DEVG DX0G DPXG'
    m.ii_rz2db.rzy = 'DE0G DEVG DPYG'
    m.ii_rz2db.rzz = 'DE0G DEVG DPZG'
    m.ii_rz2db.rq2 = 'DBOF DVBP DP2G'
    m.ii_rz2db.rr2 = 'DBOF DVBP DP2G'
    m.ii_rz2db.rz2 = 'DBOF DVBP DP2G'
    m.ii_rz2db.rz4 = 'DP4G DBOL'
    m.ii_rzDbCsmF  = 'RZ2/DVBP RR2/DVBP' /* 'RQ2/DVBP' */ ,
                     'RZZ/DEVG RZY/DEVG RZX/DEVG'
    m.ii_rzDbCsmT  = 'S25/DVBP R25/DVBP' /* 'Q25/DVBP' */ ,
                     'Z25/DEVG Y25/DEVG X25/DEVG'
    i = ''
    do rx=1 to words(m.ii_rz)
        rz = word(m.ii_rz, rx)
        i = i repAll(' 'space(m.ii_rz2db.rz, 1), ' ', ' 'rz'/')
        end
    m.ii_rzDb = space(i, 1)
    return
endProcedure iiIni

iiDS: procedure expose m.
parse arg nm
    return iiGet(ds, nm)

iiMbr2DbSys: procedure expose m.
parse arg mbr
    return iiGet(mbr2db, left(mbr, 3))

iiRz2C: procedure expose m.
parse arg rz
    return iiGet(rz2c, rz)

iiRz2P: procedure expose m.
parse arg rz
    return iiGet(rz2plex, rz)

iiRz2Dsn: procedure expose m.
parse arg rz
    return overlay('Z', rz, 2)

iiDBSys2C: procedure expose m.
parse arg db
    return iiGet(db2c, db)

iiSys2RZ: procedure expose m.
parse arg sys
    return iiGet(sys2rz, left(sys, 2))

iiRz2Sys: procedure expose m.
parse arg rz
    return iiGet(rz2sys, rz)

iiGet: procedure expose m.
parse upper arg st, key, ret
    s2 = 'II_'st
    if symbol('m.s2.key') == 'VAR' then
        return m.s2.key
    if m.ii_ini == 1 then
       if abbrev(ret, '^') then
           return substr(ret, 2)
       else
           return err('no key='key 'in II_'st, ret)
    call iiIni
    return iiGet(st, key, ret)
endProcedure iiGet

iiPut:procedure expose m.
parse upper arg rz '/' db
    rz = strip(rz)
    db = strip(db)
    call vPut 'rz', rz
    call vPut 'rzC', iiRz2C(rz)
    call vPut 'rzP', iiRz2P(rz)
    call vPut 'rzD', iiRz2Dsn(rz)
    call vPut 'dbSys', db
    if db <> '' then do
        call vPut 'dbSysC', iidbSys2C(db)
        call vPut 'dbSysElar', iiGet(db2Elar, db)
        end
    return 1
endProcedure iiPut

iiIxPut:procedure expose m.
parse arg ix
    call iiIni
    if ix > words(m.ii_rzDb) then
        return 0
    else
        return iiPut(word(m.ii_rzDb, ix))
endProcedure iiIxPut

ii2RzDb:procedure expose m.
parse arg a, forCsm
    r = ii2rzDbS(a, forCsm)
    if r \== '' then
        return r
    else
        return err('i}no rz/dbSys for' a)

ii2RzDbS:procedure expose m.
parse upper arg a, forCsm
    if pos('/', a) > 0 then
        parse var a r '/' d
    else if length(a) == 2 then
        parse var a r 2 d
    else
        parse var a d r
    myRz = sysvar(sysnode)
    call iiIni
    if r == '' then
        r2 = myRz
    else if length(r) <> 1 then
        r2 = r
    else do
        r2 = iiGet(plex2rz, r, '^')
        if r2 == '' then
            r2 = iiGet(c2rz, r, '^')
        end
    if length(d) == 4 then
        d2 = d
    else do
        if symbol('m.ii_rz2db.r2') \== 'VAR' then
            return ''
        if d == '' then do
            if myRz == 'RZ4' then
                d2 = 'DP4G'
            else if sysvar(sysnode) == 'RZX' then
                d2 = 'DX0G'
            else
                return ''
            end
        else do
            x = pos(d, m.ii_rz2db.r2)
            if x < 1 then
                return ''
            d2 = substr(m.ii_rz2db.r2,
                       , lastPos(' ', m.ii_rz2db.r2, x)+1,4)
            end
        end
    if r2 = myRz then
        return '*/'d2
    res = translate(r2'/'d2)
    if forCsm \==1 | wordPos(res, m.ii_rzDbCsmF) < 1 then
        return res
    else
        return word(m.ii_rzDbCsmT, wordPos(res, m.ii_rzDbCsmF))
endProcedure ii2RzDbS

/* copy ii end   ********* Installation Info *************************/
/* copy adrIsp begin *************************************************/
/*--- 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 m.tso_stmt, ggRet
    call outtrap m.tso_trap.
    address tso m.tso_stmt
    m.tso_rc = rc
    call outtrap off
    if m.tso_rc == 0 then
        return 0
    m.tso_trap = ''
    do ggXx=1 to min(7, m.tso_trap.0)
        m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
        end
    if m.tso_trap.0 > 7 then do
        if m.tso_trap.0 > 14 then
            m.tso_trap = m.tso_trap'\n............'
        do ggXx=max(8, m.tso_trap.0-6) to m.tso_trap.0
            m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
            end
        end
    m.tso_errL1 = 'adrTso rc='m.tso_rc 'stmt='m.tso_stmt
    if ggRet <> '*' & wordPos(m.tso_rc, ggRet) <= 0 then
        call err m.tso_errL1 m.tso_trap
    return m.tso_rc
endSubroutine adrTso

/*--- format dsn from tso format to jcl format
      replace any ~ by syspref or userid and necessary dots ---------*/
dsn2jcl: procedure expose m.
parse upper arg dsn ., addPrefix
    if left(dsn,1) = "'" then /* only remove apostrophs */
        return strip(dsn, 'b', "'")
    cx = pos('~', dsn)
    if cx < 1 then
        if addPrefix \== 1 then
            return dsn
    sp = sysvar('SYSPREF')
    if sp == '' then
        sp = userid()
    if cx < 1 then
        return sp'.'dsn
    do until cx == 0
        le = left(dsn, cx-1)
        if le \== '' & right(le, 1) \== '.' & right(le, 1) \== '/' then
            le = le'.'
        if cx == length(dsn) then
            return le || sp
        else
            dsn = le || sp'.' ,
                || substr(dsn, cx + 1 + (substr(dsn, cx+1, 1) == '.'))
        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 lib '(' . , mbr .
     if mbr = '' then
         return strip(lib)
     else
         return strip(lib)'('mbr')'
endProcedure dsnSetMbr

dsnGetMbr: procedure expose m.
parse arg dsn '(' mbr ')'
     if mbr = '' then
         return arg(2)
     else
         return strip(mbr)
endProcedure dsnGetMbr

dsnCsmSys: procedure expose m.
parse upper arg dsn, withStar
    if pos('/', dsn) < 1 then
        return copies('*/', withStar \== 0)dsn
    parse var dsn sys '/' d2
    if sys = '' | sys = sysvar(sysnode) then
        return copies('*/', withStar \== 0)d2
    else
        return dsn
endProcedure dsnCsmSys

/**********************************************************************
    io: read or write a dataset with the following callsequences:
        read:  tsoOpen...'R', readDD*,  tsoClose
        write: tsoOpen...'W', writeDD*, tsoClose

        readDD returns true if data read, false at eof
        do not forget that open is mandatory to write empty file|
**********************************************************************/

/*--- open dd for read (rw='R') or write (rw='W') -------------------*/
tsoOpen: procedure expose m.
parse upper arg dd, rw
    return adrTso('execio' 0 'disk'RW dd '(open)')
return /* end tsoOpen */

/*--- close dd -----------------------------------------------------*/
tsoClose: procedure expose m.
    parse upper arg dd
    return adrTso('execio 0 diskR' dd '(finis)')
endProcedure tsoClose

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

/*--- write to gg ggDD from stem ggSt, ggCnt records ----------------*/
writeDD:
    parse arg ggDD, ggSt, ggCnt, ggRetDD
    if ggCnt == '' then
        ggCnt = value(ggst'0')
    if adrTso('execio' ggCnt 'diskW' ggDD '(stem' ggSt')',
                 , 1 ggRetDD) = 1 then
        if wordPos(1, ggRetDD) < 1 then
            call err 'truncation on write dd' ggDD
    return
endSubroutine writeDD

/*--- 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
    parse value dsnAlloc('dd('m.m.dd')' m.m.dsn) with m.m.dd m.m.free
    call tsoOpen m.m.dd, 'R'
    return m
endProcedure readNxBegin

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

/*--- return the stem of the curr line, '' at end -------------------*/
readNxCur: procedure expose m.
parse arg m
    if m.m.cx <= m.m.0 then
        return m'.'m.m.cx
    else
        return ''
endProcedure readNxCur

/*--- return the position (line number) of reader
           plus le characters of the current line (default 50) ------*/
readNxPos: procedure expose m.
parse arg m, le
    if m.m.cx > m.m.0 then
        return 'line' (m.m.buf0x + m.m.cx)':after EOF'
    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 tsoClose m.m.dd
    call tsoFree m.m.free
    return
endProcedure readNxEnd

/*--- 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 arg m, spec
    upper spec
    m.m.dsn = ''
    m.m.dd = ''
    m.m.disp = ''
    do wx=1 by 1
        w = word(spec, wx)
        if w = '' | abbrev(w, ':') then
            leave
        else if wordPos(w, 'OLD SHR MOD NEW')>0 | abbrev(w,'SYSO') then
            m.m.disp = w
        else if abbrev('CATALOG', w, 3) | abbrev(DELETE, w, 3) then
            m.m.disp = di left(w, 3)
        else if abbrev(w, 'DD(') then
            m.m.dd = substr(w, 4, length(w)-4)
        else if abbrev(w, 'DSN(') then
            m.m.dsn = dsn2Jcl(substr(w, 5, length(w)-5))
        else if m.m.dsn == '' & (w = 'INTRDR' ,
                                | verify(w, ".~'/", 'm') > 0) then
            m.m.dsn = dsn2jcl(w)
        else if pos('(', w) > 0 then
            leave
        else if m.m.dd == '' then
            m.m.dd = w
        else
            leave
        end
    if pos('/', m.m.dsn) < 1 then
        m.m.sys = ''
    else do
        parse var m.m.dsn m.m.sys '/' m.m.dsn
        if m.m.sys == '*' | m.m.sys = sysvar(sysnode) then
            m.m.sys = ''
        end
    parse value subword(spec, wx) with at ':' nw
    m.m.attr = strip(at)
    m.m.new  = strip(nw)
    return m
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, dDi, dDD, timeOut
    x = max(1, arg() - 1)
    do rt=0
        res = dsnAlloc(spec, dDi, dDD, '*')
        if \ datatype(res, 'n') then
            return res
        if rt > timeOut & timeOut \== '' then
            return err('timeout allocating' spec time() '\n'm.tso_trap)
        if pos('DATA SET IS ALLOCATED TO ANOTHER', m.tso_trap) < 1 then
            return err('allocating' spec'\n'm.tso_trap)
        say time() 'sleep and retry alloc' spec
        call sleep 1, 0
        end
endProcedure dsnAllocWait

/*--- alloc a dsn or a dd
          spec dsnSpec
          dDi  default disposition
          dDD  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, dDi, dDD, retRc
    return dsnAllo2(dsnSpec(dsn_alloc, spec), dDi, dDD, retRc)

/*--- alloc a dsn or a dd, dsnSpec analysed in m --------------------*/
dsnAllo2: procedure expose m.
parse upper arg m, dDi, dDD, retRc
    m.tso_dsn.dd = ''
    if m.m.dd \== '' then
        dd = m.m.dd
    else if dDD \== '' then
        dd = dDD
    else
        dd = 'DD*'
    if m.m.dsn == '' & m.m.disp == '' & m.m.attr = '' then
        return dd          /* already allocated only use dd */
    dd = tsoDD(dd, 'a')    /* ensure it is free'd by errCleanup */
    if m.m.disp \== '' then
        di = m.m.disp
    else if dDi \== '' then
        di = dDi
    else
        di = 'SHR'
    if pos('(', m.m.dsn) < 1 then
        nop
    else if di = 'MOD' then
        call err 'disp mod for' na
    else
        di = 'SHR'
    if m.m.sys == '' then
        rx = tsoAlloc(m.m.dsn, dd, di, m.m.attr, m.m.new, retRc)
    else
        rx = csmAlloc(m.m.sys'/'m.m.dsn,dd, di, m.m.attr,m.m.new,retRc)
    if rx = 0 then
        return dd dd
    call tsoFree dd, 1, 1  /* over careful? would tsoDD , - suffice? */
    return rx
endProcedure dsnAlloc

/*--- find a free dd with prefix dd ---------------------------------*/
tsoDD: procedure expose m.
parse upper arg dd, f, noErr
    if m.err_ini \== 1 then
        call errIni  /* initialises tso_ddAll */
    if f == '-' then do
        ax = wordPos(dd, m.tso_ddAll)
        if ax > 0 then
            m.tso_ddAll = delWord(m.tso_ddAll, ax, 1)
        else if noErr \== 1 then
            call err 'tsoDD dd' dd 'not used' m.tso_ddAll
        end
    else if f <> 'A' then
        call err 'tsoDD bad fun' f
    else do
        if right(dd, 1) = '*' then do
            d0 = left(dd, length(dd)-1) || m.err_screen
            dd = d0
            do dx=1 while wordPos(dd, m.tso_ddAll) > 0
                dd = d0 || dx
                end
            end
        else if pos('?', dd) > 0 then
            dd = repAll(dd, '?', m.err_screen)
        if wordPos(dd, m.tso_ddAll) < 1 then
            m.tso_ddAll = strip(m.tso_ddAll dd)
        m.tso_dsn.dd = ''
        m.tso_dsOrg.dd = ''
        end
    return dd
endProcedure tsoDD

tsoAlloc: procedure expose m.
parse arg na, dd, disp, rest, nn, retRc
    dd = translate(dd)
    c = 'alloc dd('dd')' disp
    if na == '' then
        m.tso_dsn.dd = ''
    else if na \== 'INTRDR' then do
        c = c "DSN('"na"')"
        m.tso_dsn.dd = na
        end
    else do
        c = c "sysout(*) writer(intRdr)"
        m.tso_dsn.dd = '*intRdr'
        end
    if wordPos(disp, 'NEW MOD') > 0 & nn \== '' then
        c = c dsnCreateAtts(,nn)
    if adrTso(c rest, '*') = 0 then
        return 0
    if pos('IKJ56246I', m.tso_trap) > 0 then
        if pos('TED, FILE IN USE', m.tso_trap) > 0 then do
     /* IKJ56246I DATA SET A540769.WK.REXX NOT ALLOCATED, FILE IN USE*/
        say 'tsoAlloc dd' dd 'already in use:' substr(m.tso_trap, 3)
        say '.... trying to free'
        call tsoFree dd, 1
        say '.... retrying to allocate' c rest
        if adrTso(c rest, '*') = 0 then
            return 0
        end
    if nn \= '' & wordPos(disp, 'OLD SHR') > 0 ,
          & pos('IKJ56228I', m.tso_trap) > 0 ,
          & pos(' NOT IN CATALOG ', m.tso_trap) > 0 then do
       /* IKJ56228I DATA SET A540769.XY.REXX NOT IN CATALOG OR CATAL*/
        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
    if pos('*', retRc) < 1 & wordPos(m.tso_rc, retRc) < 1 then
        call err 'tsoAlloc rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
    call sayNl 'tsoAlloc rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
    return m.tso_rc
endProcedure tsoAlloc

dsnLikeAtts: procedure expose m.
parse upper arg dsn, isFile
     if isFile then do
         ddDsn = m.tso_dsn.dsn
         if lastPos('/', m.tso_dsn.dsn, 4) < 1 then
              return tsoLikeAtts(dsn, 1)
         dsn = m.tso_dsn.dsn
         end
     sx = lastPos('/', dsn, 4)
     if sx < 1 then
         return tsoLikeAtts(dsn, 0)
     else if abbrev(dsn, '*/') | abbrev(dsn, sysVar('SYSNODE')'/') then
         return tsoLikeAtts(substr(dsn, sx+1), 0)
     else
         return csmLikeAtts(dsn)
endProcedure dsnLikeAtts

tsoLikeAtts: procedure expose m.
parse arg dsn, isFile
    rc = listDsi("'"dsn"'" copies('FILE', isFile) "SMSINFO")
    if rc = 0 then
        r = ''
    else if rc = 4 & sysReason = 19 then do
        r = 'UCOUNT(30)' /* 7.8.13 corrected attribute */
        say 'creating' dsn 'with multi volume' mv
        end
    else if rc ^= 0 then
        call err 'listDsi rc' rc 'reason' sysReason,
                             sysMsgLvl1 sysMsgLvl2
    if right(sysDsSms, 7) == 'LIBRARY' | abbrev(sysDsSms, 'PDS') ,
                      | sysDsOrg = 'PO' then
         r = 'dsOrg(PO) DSNTYPE(LIBRARY)' r
    else
         r = "dsOrg("sysDSorg")" r
    if sysUnits = 'TRACK' then
        sysUnits = 'TRACKS'
    return r "MGMTCLAS("sysMgmtClass")",
        "DATACLAS("sysDataClass")" ,
        "RECFM("strip(translate('1 2 3', ' 'sysREcFM, '123'))")",
        "LRECL("SYSLRECL")",
        "SPACE("sysPrimary"," sysSeconds")" ,
            sysUnits || left('S', sysUnits == 'TRACK')
    /*  "blksize("sysBLkSIZE")" removed 3.4.13: let sms do the magic */
endProcedure tsoLikeAtts

tsoFree: procedure expose m.
parse arg ddList, tryClose, silent
    do dx=1 to words(ddList)
        dd = word(ddList, dx)
        if adrTso('free dd('dd')', '*') <> 0 then do
            if pos('IKJ56861I', m.tso_trap) > 0 & tryClose == 1 then
              if pos('NOT FREED, DATA SET IS OPEN', m.tso_trap),
                    > 0 then do
                  /* IKJ56861I  FILE A1 NOT FREED, DATA SET IS OPEN */
                say 'dataset open:' substr(m.tso_trap, 3)
                say '.... trying to close'
                if adrTso('execio 0 diskR' dd '(finis)', '*') = 0 then
                   call adrTso 'free dd('dd')', '*'
                end
            if m.tso_rc \== 0 then
                if silent \== 1 ,
                    | \ (pos('IKJ56247I FILE',m.tso_trap) > 0 ,
                        & pos('NOT FREED, IS NOT ALLOCATED' ,
                             , m.tso_trap) > 0) then
                   call sayNl m.tso_errL1 m.tso_trap
            end
        call tsoDD dd, '-', 1
        end
    return 0
endProcedure tsoFree

dsnCreateAtts: procedure expose m.
parse arg dsn, atts
    res = ''
    if dsn \== '' & \ abbrev(dsn, '-') then
        res = "dataset('"dsnSetMbr(dsn)"')"
    if abbrev(atts, ':') then do
        parse var atts a1 atts
        rl = substr(a1, 3)
        if abbrev(a1, ':F') then do
            if rl = '' then
                rl = 80
            res =  res "recfm("space(f b)") lrecl("rl")"
            end
        else if abbrev(a1, ':V') then do
            if rl = '' then
                rl = 32755 /* 32756 gives bad values in ListDSI | */
            res =  res "recfm("space(v b)") lrecl("rl")"
            end
        else if abbrev(a1, ':L') then
            res = res dsnLikeAtts(rl, 0)
        else if abbrev(a1, ':D') then
            res = res dsnLikeAtts(rl, 1)
        else
            call err 'dsnCreateAtt bad :' a1
        end
    aU = ' 'translate(atts)
    hasOrg = pos(' DSORG(', aU) > 0 | pos(' DSNTYPE(', aU) > 0
    hasMbr = pos('(', dsn) > 0
    if hasMbr & \ hasOrg then
        atts = atts 'dsorg(po) dsntype(library)'
    if hasOrg | hasMbr then do
        ww = DSORG DSNTYPE
        do wx=1 to words(ww)
            do forever
                cx = pos(' 'word(ww, wx)'(', ' 'translate(res))
                if cx == 0 then
                    leave
                cy = pos(')', res, cx)
                res = delstr(res, cx, cy+1-cx)
                end
            end
        end
    res = res atts
    aU = ' 'translate(res)
    if pos(' MGMTCLAS(', aU) < 1 then
        res = res 'mgmtclas(COM#A091)'
    if pos(' SPACE(', aU) < 1 then
        res = res 'space(1, 50) cylinders'
    return res
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)'
    call tsoFree word(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)'
    call tsoFree word(ggAlloc, 2)
    if ggSay == 1 | m.debug == 1 then
       say ggCnt 'records written to' ggDsnSpec
    return
endSubroutine writeDsn

tsoDsiMaxl:
    rc = listDsi(arg(1) 'FILE')
    if rc ^= 0 then
        call err 'listDsi rc' rc 'reason' sysReason,
                             sysMsgLvl1 sysMsgLvl2
    return SYSLRECL  - 4 * abbrev(sysRecFm, 'V')
endSubroutine tsoDsiMaxL
/* copy adrTso end ***************************************************/
/* copy csv begin ****************************************************/
/**** csvRdr reads a text file, in csv format
             and creates a class from column head in first line
      csvRdr#jRead returns the create objects ************************/
csv2ObjRdr: procedure expose m.
parse arg rdr, opt
    return oNew(jClassNew1sRdr('Csv2ObjRdr', 'm.m.opt = arg2' ,
            , 'call csv2ObjBegin m' ,
            , 'call csv2Obj m, rStem, $i'), rdr, opt)

csv2ObjBegin: procedure expose m.
parse arg m
    m.m.class = ''
    return m

csvRdrOpenFinish: procedure expose m.
parse arg m, ff
    if m.m.opt == 'u' then
        upper ff
    m.m.class = classNew("n* CsvF u f%v" ff)
    call classMet m.m.class, 'new'
    call classMet m.m.class, 'oFldD'
    return m
endProcedure csvRdrOpenFinish

csv2Obj: procedure expose m.
parse arg m, wStem, li
    if m.m.class == '' then
        return csvRdrOpenFinish(m, space(translate(li, ' ', ','), 1))
    call mAdd wStem, csv2O(mNew(m.m.class), m.m.class, li)
    return
endProcedure csv2Obj

/*--- convert csv line into object of class cl ----------------------*/
csv2o: procedure expose m.
parse arg m, cl, src
    ff = classMet(cl, 'oFldD')
    s = csv_2o_SCAN
    call scanSrc s, src
    do fx=1 to m.ff.0
        f1 = m || m.ff.fx
        if scanString(s, '"') then
            m.f1 = m.s.val
        else do
            call scanUntil s, ','
            m.f1 = m.s.tok
            end
        if scanEnd(s) then
            leave
        if \ scanLit(s, ',') then
            call scanErr s, ',' expected
        end
    return csv2Ofinish(m, cl, fx+1)
endProcedure csv2o

/*--- clear remaining fields and stems and mutate -------------------*/
csv2Ofinish: procedure expose m.
parse arg m, cl, fy
    call classClearStems cl, oMutate(m, cl)
    do fx=fy to m.cl.fldd.0
        f1 = m || m.cl.fldd.fx
        m.f1 = ''
        end
    return m
endProcedure csv2Ofinish

/**** csvWordRdr: similar to csvRdr, but input line format
             are quoted or unquoted words ****************************/
csvWordRdr: procedure expose m.
parse arg rdr, opt
    return oNew(jClassNew1sRdr('CsvWordRdr', 'm.m.opt = arg2' ,
            , 'call csvWordBegin m' ,
            , 'call csvWord m, rStem, $i'), rdr, opt)

csvWordBegin: procedure expose m.
parse arg m
    m.m.class = ''
    return m

csvWord: procedure expose m.
parse arg m, wStem, li
    if m.m.class == '' then
        return csvRdrOpenFinish(m, space(li, 1))
    call mAdd wStem, csvWord2O(mNew(m.m.class), m.m.class, li)
    return
endProcedure csvWord

csvWord2O: procedure expose m.
parse arg m, cl, src
    ff = cl'.FLDD'
    s = csv_2o_SCAN
    call scanSrc s, src
    do fx=1 to m.ff.0
        call scanSpaceOnly s
        if \ scanWord(s) then
            leave
        f1 = m || m.ff.fx
        m.f1 = m.s.val
        end
    return csv2Ofinish(m, cl, fx)
endProcedure csvWord2O

/**** csvColRdr: similar to csvRdr, but input format
             are fixed width columns *********************************/
/*--- create object for fixLenColumns format ------------------------*/
csvColRdr: procedure expose m.
parse arg rdr, opt
    return oNew(jClassNew1sRdr('CsvColRdr', 'm.m.opt = arg2' ,
            , 'call csvColBegin m' ,
            , 'call csvCol m, rStem, $i'), rdr, opt)

csvColBegin: procedure expose m.
parse arg m
    m.m.class = ''
    return m

csvCol: procedure expose m.
parse arg m, wStem, li
    if m.m.class == '' then do
        s = scanSrc(csv_colOpen, li)
        ff = ''
        do cx=1
            call scanWhile s, ' <>'
            if scanEnd(s) then
                leave
            call scanUntil s, ' <>'
            ff = ff m.s.tok
            call scanSpaceOnly s
            m.m.pEnd.cx = m.s.pos + (scanLook(s, 1) == '>')
            end
        m.m.pEnd.0 = cx-1
        call csvRdrOpenFinish m, ff
        return
        end
    call mAdd wStem, csvCol2O(m, mNew(m.m.class), m.m.class, li)
    return
endProcedure csvCol

csvCol2O: procedure expose m.
parse arg oo, m, cl, src
    ff = cl'.FLDD'
    cx = 1
    do fx=1 to m.oo.pEnd.0 - 1
        f1 = m || m.ff.fx
        m.f1 = strip(substr(src, cx, m.oo.pEnd.fx - cx))
        cx = m.oo.pEnd.fx
        end
    f1 = m || m.ff.fx
    m.f1 = strip(substr(src, cx))
    return csv2Ofinish(m, cl, fx+1)
endProcedure csvCol2O

/*--- csv4obj add a header line
          and objects of the same class in csv format ---------------*/
csv4ObjRdr: procedure expose m.
parse arg rdr, opt
    return oNew(jClassNew1sRdr('Csv4ObjRdr', ,
            , 'call csv4ObjBegin m' ,
            , 'call csv4Obj m, rStem, $i'), rdr, opt)

csv4ObjBegin: procedure expose m.
parse arg m
    m.m.class = ''
    return m
endProcedure csv4ObjBegin

csv4Obj: procedure expose m.
parse arg m, wStem, o
    if o == '' then do
        if m.m.class \== '' then
            call mAdd wStem, ''
        return
        end
    cl = objClass(o)
    if cl \== m.m.class then do
        if m.m.class \== '' then
            return err('class('o')='cl '<>' m.m.class)
        m.m.class = cl
        ff = classMet(cl, 'oFlds')
        if m.ff.0 < 1 then
            return err('no fields in' cl)
        t = ''
        do fx=1 to m.ff.0
            t = t','m.ff.fx
            end
        call mAdd wStem, substr(t, 2)
        m.m.oFldD = classMet(cl, 'oFldD')
        end
    call mAdd wStem, csv4O(o, m.m.oFldD, 0)
    return
endProcedure csv4Obj

/*--- return the csv string for an object ---------------------------*/
csv4o: procedure expose m.
parse arg o, ff, hasNull, oNull
    res = ''
    do fx=1 to m.ff.0
        of1 = o || m.ff.fx
        v1 = m.of1
        if hasNull & v1 == oNull then
            res = res','
        else if pos(',', v1) > 0 | pos('"', v1) > 0 | v1 == '' then
            res = res','quote(v1, '"')
        else
            res = res','v1
        end
    return substr(res, 2)
endProcedure csv4o
/*--- fill empty fieldds with value of previous row -----------------*/
csvE2PrevRdr: procedure expose m.
parse arg rdr, opt
    return oNew(jClassNew1sRdr('CsvE2PrevRdr', 'm.m.opt = arg2' ,
            , "m.m.prev = ''" ,
            , 'call csvE2Prev m, rStem, $i'), rdr, opt)

/*--- externalize o and add to wStem --------------------------------*/
csvE2Prev: procedure expose m.
parse arg m, wStem, o
    if o == '' then
        return
    ff = oFldD(o)
    hasData = 0
    do fx=1 to m.ff.0
        f1 = o || m.ff.fx
        if m.f1 \== '' then do
            hasData = 1
            iterate
            end
        if m.m.prev == '' then
           iterate
        p1 = m.m.prev || m.ff.fx
        m.f1 = m.p1
        end
    if \ hasData then
        return
    call mAdd wStem, o
    m.m.prev = o
    return
endProcedure csvE2Prev

csvColBegin: procedure expose m.
/**** csvExt externalises object into csvExt format
      including object cycles and classes
   csv+ protocoll, first field contains meta info ---------------------
   v,text               null or string
   w,text               w-string
   c name classAdr,flds class definition
   b name classAdr,     class forward declaration
   m name adr,text      method
   o classAdr adr,flds  object definition and output
   d classAdr adr,flds  object definition wihtout output
   f classAdr adr,      object forward declaration
   r adr,               reference = output of already defined objects
   * text               unchanged text including ' " ...
   * flds               csv flds
**********************************************************************/
csvExtRdr: procedure expose m.
parse arg rdr, opt
    return oNew(jClassNew1sRdr('CsvExtRdr', ,
            , 'call csvExtBegin m',
            , 'call csvExt m, rStem, $i'), rdr, opt)

csvExtBegin: procedure expose m.
parse arg m
    d = m'.DONE'
    call mapReset d, 'K'
    call mapPut d, m.class_class, 'class'
    call mapPut d, m.class_v, 'v'
    call mapPut d, m.class_w, 'w'
    call mapPut d, m.class_o, 'o'
    return m
endProcedure csvExtBegin

/*--- externalize o and add to wStem --------------------------------*/
csvExt: procedure expose m.
parse arg m, wStem, o
    c = objClass(o)
    if c == m.class_W then
        return mAdd(wStem, 'w,'substr(o, 2))
    if oKindOfString(o) then
        return mAdd(wStem, 'v,'o)
    if c == m.class_class then
        call csvExtClass m, wStem, o
    if m.m.done.o == 0 then do
        m.m.done.o = 1
        call mAdd wStem, 'f' csvExtClass(m, wStem, c) o','
        end
    if symbol('m.m.done.o') == 'VAR' then
        return mAdd(wStem, 'r' o',')
    return mAdd(wStem, 'o' c o || csvExtObjTx(m, wStem, o))
endProcedure csvExt

csvExtObjTx: procedure expose m.
parse arg m, wStem, o
    call mapAdd m'.DONE', o, 0
    c = objClass(o)
    if c \== m.class_class & pos(m.m.done.c, 12) < 1 then
        call csvExtClass m, wStem, c
    ff = classMet(c, 'oFldD')
    r = ''
    do fx=1 to m.ff.0
        c1 = m.ff.fx.class
        f1 = o || m.ff.fx
        v1 = m.f1
        if m.c1 == 'r' then do
            c2 = objClass(v1)
            if c2 == m.class_S then do
                v1 = s2o(v1)
                end
            else if \ (c2 == m.class_N | c2 == m.class_W) then do
                if m.m.done.v1 == 0 then do
                    m.m.done.v1 = 1
                    call mAdd wStem, 'f' c2 v1','
                    end
                if symbol('m.m.done.v1') \== 'VAR' then
                    call mAdd wStem, 'd' c2 v1 ,
                         || csvExtObjTx(m, wStem, v1)
                end
            end
        if pos(',', v1) > 0 | pos('"', v1) > 0 then
            r = r','quote(v1, '"')
        else
            r = r','v1
        end
    m.m.done.o = 2
    return r
endProcedure csvExtObjTx

csvExtClass: procedure expose m.
parse arg m, wStem, c
    res = mapGet(m'.DONE', c, '-')
    if res == 0 then do
        m.m.done.c = 1
        call mAdd wStem, 'b' if(m.c.name == '', '-', m.c.name) c','
        return c
        end
    if res == 1 then
        return c
    if res \== '-' then
        return res
    call mapAdd m'.DONE', c, 0
    ty = m.c
    res = if(m.c.name == '', '-', m.c.name) c
    if ty == 'u' then do
        res = 'c' res',u'
        if m.c.0 > 0 then do
            r = ''
            do cx=1 to m.c.0
                r = r','csvExtClassEx(m, wStem, m.c.cx)
                end
            res = res substr(r, 2)
            end
        end
    else if ty == 'm' & m.c.0 == 0 then
        res = 'm' res','m.c.met
    else
        res = 'c' res','csvExtClassEx(m, wStem, c)
    call mAdd wStem, res
    call mapPut m'.DONE', c, c
    return c
endProcedure csvExtClass

csvExtClassEx: procedure expose m.
parse arg m, wStem, c
    res = ''
    ch = c
    do forever
        g = mapGet(m'.DONE', c, '-')
        if g \== '-' then
            return strip(res g)
        else if m.ch == 'u' | m.ch == 'm' then
            return strip(res csvExtClass(m, wStem, ch))
        else if \ (length(m.ch) == 1 & pos(m.ch, 'fscr') >= 1,
                & m.ch.0 <= 1 & m.ch.met == '') then
            return err('csvExtClassEx bad cl' ch 'ty='m.ch,
                     'name='m.ch.name '.0='m.ch.0 'met='m.ch.met)
        res = strip(res m.ch m.ch.name)
        if m.ch.0 = 0 then
            return res
        ch = m.ch.1
        end
endProcedure csvExtClassEx

/*--- convert variable len recs to fixLen
       & = continuation, | end (to protect ' &|') -------------------*/
csvV2FRdr: procedure expose m.
parse arg rdr, arg
    return oNew(jClassNew1sRdr('CsvV2FRdr', 'm.m.maxLen = arg2',
            , 'call csvV2FBegin m, m.m.maxLen',
            , 'call csvV2F m, rStem, $i'), rdr, arg)

csvV2FBegin: procedure expose m.
parse arg m, maxL
    m.m.maxLen = word(maxL 55e55, 1)
    return m
endProcedure csvV2FBegin

csvV2F: procedure expose m.
parse arg m, wStem, line
    if line \== '' & pos(right(line, 1), ' &|') > 0 then
        line = line'|'
    if length(line) <= m.m.maxLen then
        return mAdd(wStem, line)
    do cx=1 by m.m.maxLen-1 to length(line)-m.m.maxLen
        call mAdd wStem, substr(line, cx, m.m.maxLen-1)'&'
        end
    return mAdd(wStem, substr(line, cx))
endProcedure csvV2F

/*--- f2v fixLen to variable len lines: &=continuation |=endMark ----*/
csvF2VRdr: procedure expose m.
parse arg rdr, arg
    return oNew(jClassNew1sRdr('CsvF2VRdr', ,
            , 'call csvF2VBegin m' ,
            , 'call csvF2V m, rStem, $i' ,
            , 'call csvF2VEnd m'), rdr, arg)

csvF2VBegin: procedure expose m.
parse arg m
    m.m.strt = ''
    return m
endProcedure csvF2VBegin

csvF2V: procedure expose m.
parse arg m, wStem, aLi
    li = strip(aLi, 't')
    if right(li, 1) == '&' then do
        m.m.strt = m.m.strt || left(li, length(li) - 1)
        return
        end
    if right(li, 1) == '|' then
        call mAdd wStem, m.m.strt || left(li, length(li) - 1)
    else
        call mAdd wStem, m.m.strt || li
    m.m.strt = ''
    return
endProcedure csvF2V

csvF2VEnd: procedure expose m.
parse arg m
    if m.m.strt \== '' then
        return err("csvF2vEnd but strt='"m.m.strt"'")
    return m
endProcedure csvF2VEnd

/*--- internalize objects in ext format -----------------------------*/
csvIntRdr: procedure expose m.
parse arg rdr, opt
    return oNew(jClassNew1sRdr('CsvIntRdr', ,
            , 'call csvIntBegin m',
            , 'call csvInt m, rStem, $i'), rdr, opt)

csvIntBegin: procedure expose m.
parse arg m
    m.m.forward = ''
    d = m'.DONE'
    call mapReset d, 'K'
    return
endProcedure csvIntBegin

csvInt: procedure expose m.
parse arg m, wStem, line
    parse var line hd ',' rest
    parse var hd h1 h2 h3 hr
    d = m'.DONE'
    if pos(h1, 'vwr') > 0 then do
        if m.m.forward \== '' then
            return err('csvInt: forward='m.m.forward 'not empty:' line)
        if h1 == 'v' & h2 == '' then
            return mAdd(wStem, rest)
        if h1 == 'w' & h2 == '' then
            return mAdd(wStem, m.o_escW || rest)
        if h1 \== 'r' | h2 == '' | h3 \== '' | rest \== '' then
            return err('csvInt: bad line' line)
        r = mapGet(d, h2, '')
        if r == '' then
            return err('csvInt: undefined reference' line)
        return mAdd(wStem, r)
        end
    if h3=='' | hr\=='' | length(h1)\==1 | pos(h1, 'bcmdfo') < 1 then
       return err('csvInt: bad line' line)
    if h1 == 'b' | h1 == 'f' then do
        if symbol('m.d.h3') == 'VAR' then
            return err('csvInt: forward already defined:' line)
        if h1 == 'b' then do
            if h2 == '-' then
                h2 = 'CsvForward'
            n = classNew('n' h2 || (m.class.0+1) 'u')
            m.n.met = h2'*'
            end
        else do
            cl = mapGet(d, h2, '')
            if cl == '' then
                return err('csvInt: undefined class:' line)
            n = mNew(cl)
            end
        call mapAdd d, h3, n
        m.m.forward = m.m.forward h3
        return
        end
    if h1 == 'm' then do
        n = classNew('m' h2 rest)
        return mapAdd(d, h3, n)
        end
    if h1 == 'c' then do
        rx = 1
        rr = ''
        do while rx <= length(rest)
            ry = pos(',', rest, rx+1)
            if ry < 1 then
                ry = length(rest)+1
            r1 = substr(rest, rx, ry-rx)
            rI = wordIndex(r1, words(r1))
            if rI == 1 & abbrev(r1, ',') then
                rI = 2
            rL = strip(substr(r1, rI))
            if length(rL) \== 1 | pos(rL, 'vwor') < 1 then do
                rL = mapGet(d, rL, '')
                if rL == '' then
                    return err('csvInt undef class' rL 'line:' line)
                end
            rr = rr || left(r1, rI-1)rL
            rx = ry
            end
        end
    fx = wordPos(h3, m.m.forward)
    if fx > 0 then do
        m.m.forward = strip(delWord(m.m.forward, fx, 1))
        n = mapGet(d, h3)
        if h1 == 'c' then do
            call classNew 'n=' m.n.name rr
            call classMet n, 'new'
            return
            end
        cl = 'CLASS'substr(n, 2, pos('.', n, 3)-2)
        if cl \== mapGet(d, h2) then
            return err('csvInt: forward class' cl 'mismatches' line)
        end
    else do
        if mapHasKey(m, d, h3) then
            return err('already defined:' line)
        if h1 == 'c' then do
            do while datatype(right(h2, 1), 'n')
                h2 = left(h2, length(h2)-1)
                end
            if h2 == '-' then
                h2 = 'CsvForward'
            s = ''
            cl = classNew(copies('n*' h2' ', h2 \== '-')rr)
            call classMet cl, 'new'
            return mapAdd(d, h3, cl)
            end
        cl = mapGet(d, h2, '')
        if cl == '' then
            return err('undefined class:' line)
        n = mNew(cl)
        call mapAdd d, h3, n
        end
    call csv2o n, cl, rest
    ff = classFldD(cl)
    do fx=1 to m.ff.0
        f1 = n || m.ff.fx
        c1 = m.ff.fx.class
        if m.c1 \== 'r' | m.f1 == '' | abbrev(m.f1, m.o_escW) then
            iterate
        t1 = mapGet(d, m.f1, '')
        if t1 == '' then
            return err('missing reference' fx m.f1 'in' line)
        m.f1 = t1
        end
    if h1 == 'o' then do
        if m.m.forward \== '' then
            call err 'forward not empty:' line
        call mAdd wStem, n
        end
    return
endProcedure csvInt

/* copy csv end   ****************************************************/
/* copy j begin *******************************************************
    the j framework
         jReset
         jOpen
         jClose
         jRead
         jWrite
**********************************************************************/
jRead: procedure expose m.
parse arg m
    ix = m.m.readIx + 1
    if ix > m.m.buf.0 then do
        if m.m.jReading \== 1 then
            return err('jRead('m') but not opened r')
        if \ jReadBuf(m, m'.BUF') then
            return 0
        ix = 1
        end
    m.m.readIx = ix
    m.m = m.m.buf.ix
    return 1
endProcedure jRead

jReadBuf: procedure expose m.
parse arg m, rStem
    interpret objMet(m, 'jRead')
    m.m.bufI0  = m.m.bufI0 + m.rStem.0
    return m.rStem.0 > 0
endProcedure jReadBuf

jReadVar: procedure expose m.
parse arg m, var
if arg() > 2 then call err '???  old interface' / 0
    if \ jRead(m) then
        return 0
    m.var = m.m
    return 1
endProcedure jReadVar

/*--- read next NonEmpty line ---------------------------------------*/
jReadNE: procedure expose m.
parse arg m
    do while jRead(m)
        if m.m <> '' then
            return 1
        end
    return 0
endProcedure jReadNE

/*--- read next lines to stem ---------------------------------------*/
jReadSt: procedure expose m.
parse arg m, st
    sx = 0
    if m.m.readIx >= m.m.buf.0 then do
        if jReadBuf(m, st) then
            return 1
        m.st.0 = 0
        return 0
        end
    do rx = m.m.readIx+1 to m.m.buf.0
        sx = sx + 1
        m.st.sx = m.m.buf.rx
        end
    m.m.readIx = m.m.buf.0
    m.st.0 = sx
    return sx > 0
endProcedure jReadSt

jReadObRe: procedure expose m.
parse arg m
if arg() > 1 then call err '???  old interface' / 0
    if jRead(m) then
        return m.m
    else
        return ''
endProcedure jReadObRe

jReadO: procedure expose m.
parse arg m
if arg() > 1 then call err '???  old interface'  /0
    return jRead(m)
endProcedure jReadO

jWrite: procedure expose m.
parse arg m, line
    ix = m.m.buf.0 + 1
    m.m.buf.0 = ix
    m.m.buf.ix = line
    if ix > m.m.bufMax then
        call jWriteBuf m
    return
endProcedure jWrite

/*--- write the buf to destination ----------------------------------*/
jWriteBuf: procedure expose m.
parse arg m
    if \ m.m.jWriting then
        return err('jWrite('m') but not opened w')
    wStem = m'.BUF'
    interpret objMet(m, 'jWriteMax')
    return
endProcedure jWriteBuf

jWriteSt: procedure expose m.
parse arg m, qStem
    interpret objMet(m, 'jWriteSt')
    return
endProcedure jWriteSt

jPosBefore: procedure expose m.
parse arg m, lx
    interpret objMet(m, 'jPosBefore')
    return m
endProcedure jPosBefore

jWriteAll: procedure expose m.
parse arg m, rdr
    rdr = o2file(rdr)
    met = objMet(m, 'jWriteAll')
    if \ m.m.jWriting then
        return err('jWriteAll('m',' rdr') but not opened w')
    interpret met
    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)
        if m.rdr.readIx == 1 then do
            call jWriteSt m, rdr'.BUF'
            m.rdr.readIx = m.rdr.buf.0
            end
        else
            call jWrite m, m.rdr
        end
    call jClose rdr
    return
endProcedure jWriteNow

/*--- reset JRW: fail if open, initialise ---------------------------*/
jReset0: procedure expose m.
parse arg m
    if m.m.jReading == 1 | m.m.jWriting == 1 then
        return err('still open jReset0('m')')
    m.m.jUsers = 0
    m.m.buf.0  = 0
    m.m.wriMax = 0
    call jCloseSet m
    return m
endProcedure jReset0

jCloseSet: procedure expose m.
parse arg m
    m.m.jReading = 0
    m.m.jWriting = 0
    m.m.readIx = 55e55
    m.m.bufMax = -55e55
    return m
endProcedure jCloseSet

jReset: procedure expose m.
parse arg m, arg, arg2
    interpret objMet(m, 'oResetNoMut')
    return m
endProcedure jReset

jOpen: procedure expose m.
parse arg m, opt
    met = objMet(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
            m.m.readIx = 0
            m.m.bufI0 = 0
            interpret met
            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
            m.m.bufI0 = 0
            m.m.bufMax = m.m.wriMax
            interpret met
            m.m.jWriting = 1
            end
        end
    m.m.jUsers = oUsers + 1
    return m
endProcedure jOpen

/*--- close JRW flush buffer if writing ... -------------------------*/
jClose: procedure expose m.
parse arg m
    oUsers = m.m.jUsers
    if oUsers = 1 then do
        if m.m.jWriting then do
            wStem = m'.BUF'
            interpret objMet(m, 'jWriteFlu')
            end
        interpret objMet(m, 'jClose')
        call jCloseSet m
        end
    else if oUsers < 1 then
        call err 'jClose' m 'but already closed'
    m.m.jUsers = oUsers - 1
    return m
endProcedure jClose

/*--- force physical close for errCleanup ---------------------------*/
jCloseClean: procedure expose m.
parse arg m
    if m.m.jUsers = 0 then
        return
    m.m.jUsers = 1
    return jClose(m)
endProcedure jCloseClean

/*--- 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 do
        call err '-sql in jCatLines'
        end
    f2 = '%##fCatFmt' fmt
    call jOpen m, m.j.cRead
    if \ jRead(m) then do
        call jClose m
        return f(f2'%#0')
        end
    res = f(f2'%#1', m.m)
    do while jRead(m)
        res = res || f(f2, m.m)
        end
    call jClose m
    return res || f(f2'%#r')
endProcedure jCatLines

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 classIni
    am = "call err 'call of abstract method"
    cLa= classNew('n JRWLazy u LazyRun', 'm',
        , "oReset" m.class_lazyRetMutate,
                   "'call jReset0 m;' classMet(cl, 'jReset')",
        , "jWriteMax return classMet(cl, 'jWrite') '; m.m.buf.0 = 0'" ,
        , "jWriteFlu return classMet(cl, 'jWriteMax')",
        , "jWriteSt  return 'if m.m.buf.0 <> 0" ,
                     "| m.qStem.0 < m.m.bufMax / 2  then do;" ,
                "call mAddSt m''.BUF'', qStem;" ,
                "if m.m.buf.0 > m.m.bufMax then do;'" ,
                     "classMet(cl, 'jWriteMax')'; end; end;",
              "else do; wStem = qStem;' classMet(cl, 'jWrite') ';end'",
        )
    c1 = classNew('n JRW u ORun, f JREADING v, f JWRITING v', 'm',
        , "METHODLAZY" cLa,
        , "jReset" ,
        , "jRead" am "jRead('m')'" ,
        , "jWrite" am "jWrite('m',' wStem')'" ,
        , "jWriteAll call jWriteNowImpl m, rdr",
        , "jWriteNow call jWriteNowImpl m, rdr",
        , "jOpen" am" jOpen('m',' opt')'" ,
        , "jClose" ,
        , "oRun call pipeWriteAll m",
        , "o2String return jCatLines(m, fmt)",
        , "o2File return m")
    call classNew 'n JRWDelegOC u JRW', 'm',
        , "jReset m.m.deleg = arg;" ,
        , "jOpen     call jOpen m.m.deleg, opt" ,
        , "jClose    call jClose m.m.deleg"
    call classNew 'n JRWDeleg u JRWDelegOC', 'm',
        , "jRead if \ jReadSt(m.m.deleg, rStem) then return 0",
        , "jWrite  call jWriteSt m.m.deleg, wStem" ,
    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 do wx=1 to m.wStem.0;say o2Text(m.wStem.wx,157);end",
        , "jOpen if \ abbrev(opt, m.j.cWri) then",
            "call err 'can only write JSay#jOpen('m',' opt')';"
    call classNew 'n JRWEof u JRW', 'm',
        , "jRead  return 0",
        , "jOpen if opt \=='<' then call err 'JRWEof#open('m',' opt')'"
    m.j.in = jOpen(oNew('JRWEof'), m.j.cRead)
    m.j.out = jOpen(oNew('JSay'), '>')
    m.j.say = m.j.out
    m.j.errRead  = "return err('jRead('m') but not opened r')"
    m.j.errWrite = "return err('jWrite('m',' line') but not opened w')"
    call classNew "n JBuf u JRW, f BUF s r", "m",
        , "jReset call jBufReset m, arg, arg2" ,
        , "jOpen call jBufOpen m, opt",
        , "jRead return 0",
        , "jWriteMax call err 'buf overflow'",
        , "jWriteFlu ",
        , "jWriteSt  call mAddSt m'.BUF', qStem" ,
        , "jWrite call mAddSt m'.BUF', wStem;" ,
              "if m.m.buf.0 > m.m.bufMax then call err 'buf overflow'",
        , "jPosBefore m.m.readIx = m.m.readIx+0;m.m.readIx = lx-1"
    return
endProcedure jIni

/*--- return a JRW from rdr or in -----------------------------------*/
in2File: procedure expose m.
    parse arg m
    interpret objMet(m, 'in2File')
    return err('in2File did not return')
endProcedure in2File
      /* jstr is part of out interface --> in2Str */
in2Str: procedure expose m.
    parse arg m, fmt
    interpret objMet(m, 'in2Str')
    return err('in2Str did not return')
endProcedure in2Str

in2Buf: procedure expose m.
parse arg m
    interpret objMet(m, 'in2Buf')
    return err('in2Buf did not return')
endProcedure in2Buf

in: procedure expose m.
    if arg() > 0 then call err '??? old interface'
    r = m.j.in
    m.in_ret = jRead(r)
    m.in = m.r
    return m.in_ret
endProcedure in

inVar: procedure expose m.
parse arg var
    return jReadVar(m.j.in, var)
endProcedure inVar

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

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

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

/*--- out (part of) the lines of a stem ----------------------------*/
outSt: procedure expose m.
parse arg st, fx, tx
    do lx=word(fx 1, 1) to word(tx m.st.0, 1)
        call out m.st.lx
        end
    return st
endProcedure outSt

outX: procedure expose m.
parse arg line
    if symbol('m.tst_m') \== 'VAR' then
        call jWrite m.j.out, line
    else
        call tstOut m.tst_m, line
    return 0
endProcedure out

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

/*--- jBuf: buffer read or write (supports datataypes) --------------*/
jBuf: procedure expose m.
    m = oNew(m.class_jBuf) /* calls jBufReset */
    do ax=1 to arg()
        m.m.buf.ax = arg(ax)
        end
    m.m.buf.0 = ax-1
    return m
endProcedure jBuf
/*--- jText: write text to deleg ------------------------------------*/
jText: procedure expose m.
parse arg rdr, opt
    return oNew(jClassNew1sRdr('JText', 'm.m.maxL = arg2' , ,
            , 'call mAdd rStem, o2Text($i, m.m.maxL)'),rdr, opt)

jBufReset: procedure expose m.
parse arg m
    call oMutate m, m.class_jBuf
    do ax=1 to arg() - 1
        m.m.buf.ax = arg(ax+1)
        end
    m.m.buf.0 = ax-1
    m.m.wriMax = 1e30
    return m
endProcedure jBufReset

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

jBufCopy:
parse arg rdr
    b = jOpen(jBuf(), m.j.cWri)
    call jWriteNow b, rdr
    return jClose(b)
endProcedure jBufCopy

jSingle: procedure expose m.
parse arg m
    call jOpen m, '<'
    one = jRead(m)
    two = jRead(m)
    call jClose m
    if \ one then
        if arg() < 2 then
            call err 'empty file in jSingle('m')'
        else
            return arg(2)
    if two then
        call err '2 or more recs in jSingle('m')'
    return m.m
endProcedure jSingle

/*--- lazily create a reader class for 1s protocol ------------------*/
jClassNew1sRdr: procedure expose m.
parse arg cla, reset, op, rd, cls
    return classNew('n?' cla 'u JRWDelegOC', 'm',
        , 'jReset m.m.delegSp = in2file(arg);' reset ,
        , 'jOpen m.m.deleg = in2file(m.m.delegSp);' ,
              'call jOpen m.m.deleg, opt;' op ,
        , 'jRead if \ jRdr1sRead(m, rStem,' ,
                   quote(repAll(rd, '$i', 'm.dg.buf.ix'), '"'),
                   ') then return 0' ,
        , 'jWrite call jRdr1sWrite m, wStem,' ,
                   quote(repAll(rd, '$i', 'm.wStem.wx'), '"'),
        , 'jClose' cls||left(';', cls <> '') 'call jClose m.m.deleg')
endProcedure jNewClassRdr1s

jRdr1sRead: procedure expose m.
parse arg m, rStem, add1s
    m.rStem.0 = 0
    dg = m.m.deleg
    do while jRead(dg)
        do ix = m.dg.readIx to m.dg.buf.0
            interpret add1s
            end
        m.dg.readIx = ix - 1
        if m.rStem.0 >= 100 then
            return 1
        end
    return m.rStem.0 > 0
endProcedure jRdr1sRead

jRdr1sWrite: procedure expose m.
parse arg m, wStem, add1s
    dg = m.m.deleg
    rStem = dg'.BUF'
    do wx=1 to m.wStem.0
        interpret add1s
        end
    if m.rStem.0 > m.dg.bufMax then
        call jWriteBuf dg
    return
endProcedure jRdr1sWrite


/*--- jTalkRdr: say strings, out objects ----------------------------*/
jTalkRdr: procedure expose m.
parse arg rdr, opt
    return oNew(jClassNew1sRdr('JTalkRdr', , ,
          , "if oKindOfString($i) then say o2string($i);" ,
            "else call mAdd rStem, $i"), rdr, opt)
/* copy j end ********************************************************/
/* copy o begin *******************************************************
    an object is register for a class in o2c
    a class has a list of parents in cParent
    a class has a methodTable cMet with lazy initialization
             a method generator
        otherwise an existing method is simply copied
**********************************************************************/
classInheritsOf: procedure expose m.
parse arg cl, sup
    cl = class4name(cl)
    sup = class4name(sup)
    if m.cl.inheritsOf \== 1 then do
        m.cl.inheritsOf = 1
        call classInheritsOfAdd cl, cl'.INHERITSOF'
        end
    return m.cl.inheritsOf.sup == 1
endProcedure classInheritsOf

classInheritsOfAdd: procedure expose m.
parse arg cl, trg, pa
    pa = classCycle(cl, pa)
    m.trg.cl = 1
    call assert "m.cl == 'u'"
    do cx=1 to m.cl.0
        c1 = m.cl.cx
        if m.c1 == 'u' then
            call classInheritsOfAdd c1, trg, pa
        end
    return
endProcedure classInheritsOf

classClear: procedure expose m.
parse arg cl, m
    do fx=1 to m.cl.fldd.0
        f1 = m || m.cl.fldd.fx
        m.f1 = ''
        end
    return classClearStems(cl, m)
endProcedure classClear

classClearStems: procedure expose m.
parse arg cl, m
    do sx=1 to m.cl.stmD.0
        s1 = m || m.cl.stmD.sx
        m.s1.0 = 0
        end
    return m
endProcedure classClearStems

classCopy: procedure expose m.
parse arg cl, m, t
    do fx=1 to m.cl.fldd.0
        ff = m || m.cl.fldd.fx
        tf = t || m.cl.fldd.fx
        m.tf = m.ff
        end
    do sx=1 to m.cl.stmD.0
        call classCopyStem m.cl.stmD.sx.class,
             , m || m.cl.stmD.sx, t || m.cl.stmD.sx
        end
    return t
endProcedure classCopy

classCopyStem: procedure expose m.
parse arg cl, m, t
    m.t.0 = m.m.0
    do sx=1 to m.t.0
        call classCopy cl, m'.'sx, t'.'sx
        end
    return 0
endProcedure classCopyStem

/*--- return true if src is a rexxVariable a, m.a.c etc. ------------*/
rxIsVar: procedure expose m.
parse arg src
    if pos(left(src, 1), m.ut_rxN1) > 0 then
        return 0
    else
        return verify(src, m.ut_rxId) = 0
endProcedure rxIsVar

/*--- return true if src is a rexxConstant rerpresenting its value --*/
rxIsConst: procedure expose m.
parse arg src, vars                                                   c
    if \ rxIsVar(src) then
        return 0
    srU = translate(src)
    if srU \== src then
        return 0
    srU = '.'srU'.'
    if pos('.GG', srU) > 0 then
        return 0
    if vars == '' then
        return 1
    upper vars
    do vx=1 to words(vars)
        if pos('.'word(vars, vx)'.', vars) > 0 then
            return 0
        end
    return 1
endProcedure rxIsConst

/*--- return rexx code m.cc or mGet('cc') ---------------------------*/
rxMGet: procedure expose m.
parse arg v1, cc, vars
    if cc == '' then
        return 'm.'v1
    else if rxIsConst(cc, vars) then
        return 'm.'v1'.'cc
    else
        return 'mGet('v1 || quote('.'cc, "'")')'
endProcedure rxMGet

/*--- print object --------------------------------------------------*/
objOut: procedure expose m.
parse m, pr, p1
    return classOutDone(m.class_O, m, pr, p1)

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

/*--- 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 do;
        if t = m.class_o then
             t = objClass(a)
        return outX(p1'done :'className(t) '@'a)
        end
    done.t.a = 1
    if t = m.class_O then do
        if a == '' then
            return outX(p1'obj null')
        t = objClass(a)
        if t = m.class_N | t = m.class_S then
            return outX(p1'obj has no class @'m.a)
        else
            return classOutDone(t, a, pr, p1'isA')
        end

    if t == m.class_V then
        return outX(p1'=' m.a)
    if t == m.class_W == 'w' then
        return outX(p1'}' substr(a, 2))
    if m.t == 'f' then
        return classOutDone(m.t.1, a'.'m.t.name, pr, p1'.'m.t.name)
    if m.t == 'r' then do
        if m.a == '' then
            return outX(p1'refTo :'className(m.t.1) '@null@')
        else
            return classOutDone(m.t.1, 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_V
        call outX 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 outX p1'stem' m.a.0
        do ux=1 to m.a.0
            call classOutDone m.t.1, 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.1, 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

/*--- mutate and reset an object for a class -----------------------*/
oReset: procedure expose m.
parse arg m, cl, arg, arg2
    interpret classMet(class4name(cl), 'oReset')
    return m
endProcedure oReset

/*--- create an an object of the class cl and reset it --------------*/
oNew: procedure expose m.
parse arg cl, arg, arg2
    interpret classMet(class4name(cl), 'new')
    return m
endProcedure oNew

/*--- return the class of object obj --------------------------------*/
objClass: procedure expose m.
parse arg m
    if arg() > 1 then
        return err('old objClass') / 0
    if symbol('m.o.o2c.m') == 'VAR' then
        return m.o.o2c.m
    else if abbrev(m, m.o_escW) then
        return m.class_w
    else if m \== '' then
        return m.class_S
    else
        return m.class_N
endProcedure objClass

oKindOf: procedure expose m.
parse arg obj, sup
    return classInheritsOf(objClass(obj), sup)

/*--- return the code of method met of object m ---------------------*/
objMet: procedure expose m.
parse arg m, met
    if symbol('m.o.o2c.m') == 'VAR' then
        cl = m.o.o2c.m
    else if abbrev(m, m.o_escW) then
        cl = m.class_w
    else if m \== '' then
        cl = m.class_S
    else
        cl = m.class_N
    if symbol('m.cl.method.met') == 'VAR' then
        return m.cl.method.met
    else
        return classMet(cl, met)    /* will do lazy initialisation */
endProcedure objMet

/*--- return true if obj is kind of string  -------------------------*/
oKindOfString: procedure expose m.
parse arg obj
     return objMet(obj, 'oKindOfString')

/*--- if obj is kindOfString return string
          otherwise return arg(2) or fail ---------------------------*/
oAsString: procedure expose m.
parse arg m
     interpret objMet(m, 'oAsString')

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

oFldD: procedure expose m.
parse arg m
    return objMet(m, 'oFldD')
endProcedure oFlds

oClear: procedure expose m.
parse arg m
    interpret objMet(m, 'oClear')
    return m
endProcedure oClear

/*--- copy object m of class cl to t --------------------------------*/
oClaCopy: procedure expose m.
parse arg cl, m, t
    interpret "drop cl;" classMet(cl, 'oCopy')
endProcedure oClaCopy

/*--- copy object m to t / create a new object if t=='' -------------*/
oCopy: procedure expose m.
parse arg m, t
    interpret objMet(m, 'oCopy')
endProcedure oCopy

oCopyGen: procedure expose m.
parse arg cl
    if cl == m.class_N | cl == m.class_S | cl == m.class_W then
        return 'return m'
    call classMet cl, 'new'
    do sx=1 to m.cl.stms.0
        s1 = m.cl.stms.sx
        call classMet m.cl.s2c.s1, 'oCopy'
        end
    return "if t=='' then t = mNew('"cl"');" ,
           "call oMutate t, '"cl"';" ,
           "return classCopy('"cl"', m, t)"
endProcedure oCopyGen

/*--- return a new instance of a subclass of Run
        with code code in method oRun -------------------------------*/
oRunner: procedure expose m.
parse arg code
    return oNew(classNew('n* ORun u ORun, m oRun' code))
endProcedure oRunner

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

/*--- run method oRun of object m No Procedure:
        ??? optimize: class only run ???
         use from caller unprotected---------------------------------*/
oRunNP: procedure expose m.
    interpret objMet(arg(1), 'oRun')
    return
endProcedure oRunNP

/*--- run method oRun and return output in new JBuf -----------------*/
oRun2File: procedure expose m.
parse arg rn
    b = jBuf()
    call pipe '+F' , b
    call oRun rn
    call pipe '-'
    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'  / 0
endProcedure o2File

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

/*--- return a short string representation of an object o=¢...! -----*/
o2Text: procedure expose m.
parse arg m, maxL, fmt
    if maxL == '' then
        maxL = 75
    interpret objMet(m, 'o2Text')
endProcedure o2Text

/*--- return a short string representation of the fields of an obj --*/
o2TexLR: procedure expose m.
parse arg m, maxL, le, ri
    if maxL == '' then
        maxL = 75
    interpret objMet(m, 'o2TexLR')
endProcedure o2TexLR

o2TextFlds: procedure expose m.
parse arg m, cl, maxL
    maxL = maxL - 3
    r = ''
    do fx=1 to m.cl.fldd.0
        c1 = m.cl.fldd.fx.class
        r = r || left(' ', fx > 1)substr(m.cl.fldd.fx, 2)
        if c1 = m.class_V then
            r = r'='
        else if m.c1 == 'r' then
            r = r'=>'
        else
            r = r'=?'c1'?'
        a1 = m || m.cl.fldd.fx
        r = r || m.a1
        if length(r) > maxL then
            return left(r, maxL)'...'
        end
    return r
endProcedure o2TextFlds

o2TextGen: procedure expose m.
parse arg cl, le, ri
    m1 = classMet(cl, 'o2String', '-')
    if m1 \== '-' then do
        if translate(word(m1, 1)) \== 'RETURN' then
            call err 'o2TextGen' className(cl)'#o2String return?:' m1
        return '__r = strip('subword(m1, 2)', "t");',
             'if length(__r) <= maxL then return __r;' ,
             'else return left(__r, maxL-3)"..."'
        end
    call classMet cl, 'oFlds'
    if le = '' & ri = '' then
        return "return o2TextFlds(m, '"cl"', maxL)"
    else
        return "return" le "|| o2TextFlds(m, '"cl"'" ,
              ", maxL - length("le") - length("ri")) || "ri
endProcedure o2TextGen

o2TextStem: procedure expose m.
parse arg st, to, maxL
     do sx=1 to m.st.0
         m.to.sx = o2Text(m.st.sx, maxL)
         end
     m.to.0 = m.st.0
     return to
endProcedure o2TextStem

/*--- cast a String to an object -----------------------------------*/
s2o: procedure expose m.
parse arg str
    return m.o_escW || str
endProcedure s2o
/* 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),
                     CLASS -> 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 address (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' (cu (',' cu)*)?
    cu = ce | c1* '%' c1* '%'? name+      (same type for each name)

    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
    m.o_escW = '!'
    call mapIni
    m.class.0 = 0
    call mapReset class_n2c  /* name to class */
    m.class_V = classNew('n v u', 'm',
          , "asString return m.m"    ,
          , "o2File return file(m.m)")
    m.class_W = classNew('n w u', 'm' ,
          , "asString return substr(m, 2)" ,
          , "o2File return file(substr(m,2))")
    m.class_O = classNew('n o u')

    m.class_C = classNew('n class u')
    call classNew 'n= class u v' ,
          , 'c u u f NAME v',           /* union or class */
          , 'c f u f NAME v',           /* field          */
          , 'c s u' ,                   /* stem           */
          , 'c c u f NAME v',           /* choice         */
          , 'c r u' ,                   /* reference      */
          , 'c m u f NAME v, f MET  v', /* method         */
          , 's r class'

    m.class_lazyRetMutate = "return 'call oMutate m, '''cl''';'"
    m.class_lazyRoot = classNew('n LazyRoot u', 'm',
          , "METHODLAZY" ,
          , "f2c    call classMet cl, 'oFlds'; return cl'.F2C'" ,
          , "f2x    call classMet cl, 'oFlds';",
                   "call mInverse cl'.FLDS', cl'.F2X';" ,
                   "return cl'.F2X'" ,
          , "oFlds  call classFldGen cl; return cl'.FLDS'" ,
          , "oFldD  call classMet cl, 'oFlds'; return cl'.FLDD'" ,
          , "o2Text return o2textGen(cl, 'm''=¢''', '''!''')",
          , "o2TexLR return o2textGen(cl, 'le', 'ri')",
          , "s2c    call classMet cl, 'oFlds'; return cl'.S2C'" ,
          , "stms   call classMet cl, 'oFlds'; return cl'.STMS'" ,
          , "in2Str return  classMet(cl, 'o2String')" ,
          , "in2File return classMet(cl, 'o2File')" ,
          , "in2Buf  return 'return jBufCopy('" ,
                      "classMetRmRet(cl,'o2File')')'",
          , "oKindOfString return classMet(cl, 'asString', '\-\')" ,
                      "\== '\-\'" ,
          , "oAsString if classMet(cl, 'oKindOfString')" ,
                "then return classMet(cl, 'asString');",
                "else return 'if arg() >= 2 then return arg(2)" ,
                "; else return err(m ''is not a kind of string" ,
                    "but has class' className(cl)''')'" ,
          , "o2String  return classMet(cl,'asString','\-\')" ,
          , "new    call mNewArea cl, 'O.'substr(cl,7);" ,
                    "return 'm = mNew('''cl''');'" ,
                            "classMet(cl,'oReset')",
          )
    call classNew 'n= LazyRoot u', 'm',
          , "oReset call classMet cl, 'oClear';" m.class_lazyRetMutate,
                    "'call classClear '''cl''', m;'" ,
          , "oResetNoMut return classRmFirstmt(" ,
                    "classMet(cl, 'oReset'), 'call oMutate ');" ,
          , "oClear call classMet cl, 'oFlds'" ,
                 "; return 'call classClear '''cl''', m'",
          , "oCopy  return oCopyGen(cl)"

    m.class_S = classNew('n String u', 'm',
          , 'asString return m' ,
          , 'in2Str return m' ,
          , 'in2File return jBuf(m)',
          , 'in2Buf return jBuf(m)')
    m.class_N = classNew('n Null u', 'm',
          , "asString return ''",
          , 'in2Str return o2String(m.j.in, fmt)',
          , "o2Text return ''",
          , 'in2File return m.j.in',
          , 'in2Buf return jBufCopy(m.j.in)')
    call classNew 'n LazyRun u LazyRoot', 'm',
          , "o2Text   return 'return m''=¢'className(cl)'!'''"
    call classNew 'n ORun u', 'm',
          , 'METHODLAZY' m.class_lazyRun,
          , 'oRun call err "call of abstract method oRun"',
          , 'o2File return oRun2File(m)',
          , 'o2String return jCatLines(oRun2File(m), fmt)'
    call mPut class_inheritMet'.'m.class_V, 0
    call mPut class_inheritMet'.'m.class_W, 0
    call mPut class_inheritMet'.'m.class_O, 0
    call mPut class_inheritMet'.'m.class_S, 0
    call mPut class_inheritMet'.'m.class_N, 0
    return
endProcedure classIni

/*--- remove first statement if src starts with strt ----------------*/
classRmFirStmt: procedure expose m.
parse arg src, strt
    if \ abbrev(src, strt) then
        return src
    return substr(src, pos(';', src)+2)

classNe1: procedure expose m.
parse arg n, ty, nm, refs, io
    ky = ty','nm','space(refs, 1)','strip(io)
    if ty == 'f' & abbrev('=', nm) then do
        if words(refs) = 1 & io == '' then
            return strip(refs)
        else
            call err 'bad field name:' ky
        end
    if n then
        if symbol('m.class_k2c.ky') == 'VAR' then
            return m.class_k2c.ky
    m.class.0 = m.class.0 + 1
    n = 'CLASS.'m.class.0
    call mapAdd class_n2c, n, n
    m.n = ty
    m.n.met = strip(io)
    if ty \== 'm' & io <> '' then
            call err "io <> '' ty: classNe1("ky")" /0
    if ty = 'u' then do
        m.n.met = nm
        if right(nm, 1) == '*' then
            nm = left(nm, length(nm)-1)substr(n, 7)
        end
    m.n.name = nm
    m.n.0 = words(refs)
    do rx=1 to m.n.0
        m.n.rx = word(refs, rx)
        end
    if length(ty) \== 1 | pos(ty, 'ufscrm') < 1 then
        call err 'bad type' ty': classNe1('ky')' /0
    else if nm == '' & pos(ty, 'm') > 0 then
        call err 'empty name: classNe1('ky')'
    else if nm \== '' & ty \== 'c' & verifId(nm) > 0 then
        call err 'bad name' nm': classNe1('ky')'
    else if nm \= '' & pos(ty, 'rs') > 0 then
        call err 'name for type' ty': classNe1('ky')'
    else if (pos(ty, 'fcsr') > 0 & m.n.0 \== 1) ,
          | (    ty == 'm' & m.n.0 \== 0) then
        call err m.n.0 'bad ref count in classNe1('ky')'
    return n
endProcedure classNe1

classNew: procedure expose m.
parse arg clEx 1 ty rest
    n = ''
    nm = ''
    io = ''
    refs = ''
    if wordPos(ty, 'n n? n* n=') > 0 then do
        nmTy = right(ty, 1)
        parse var rest nm ty rest
        if nmTy = '=' then do
            if \ mapHasKey(class_n2c, nm) then
                call err 'class' nm 'not defined: classNew('clEx')'
            n = mapGet(class_n2c, nm)
            end
        if ty \== 'u' then
            call err 'class name' nm 'without u: classNew('clEx')'
        if nmTy == '?' then do
            if mapHasKey(class_n2c, nm) then
                return mapGet(class_n2c, nm)
            end
        else if nmTy == '*' & arg() == 1 then do
            if mapHasKey(class_n2c, clEx) then
                return mapGet(class_n2c, clEx)
            end
        end
    else do
        nmTy = ''
        if arg() == 1 then
            if mapHasKey(class_n2c, clEx) then
                return mapGet(class_n2c, clEx)
        if length(ty) <> 1 | pos(ty, 'ufscmr') < 1 then
            return err('bad type' ty': classNew('clEx')')
        if pos(ty, 'fcm') > 0 then
            parse var rest nm rest
        if ty == 'm' then
            io = rest
        else if pos(ty, 'fsc') > 0 | (ty == 'r' & rest \== '') then
            refs = classNew(strip(rest))
        else if ty == 'r' then
            refs = m.class_O
        end
    if ty == 'u' then do
        lx = 0
        do while lx < length(rest)
            t1 = word(substr(rest, lx+1), 1)
            cx = pos(',', rest, lx+1)
            if cx <= lx | t1 == 'm' then
                cx = length(rest)+1
            one = strip(substr(rest, lx+1, cx-lx-1))
            lx=cx
            if pos('%', word(one, 1)) < 1 then
                refs = refs classNew(one)
            else do
                parse value translate(word(one, 1), ' ', '-') ,
                      with wBe '%' wAf '%' ww
                do wx=2 to words(one)
                    refs = refs classNew(wBe word(one, wx) wAf)
                    end
                end
            end
        pref = ''
        do ax=2 to arg()
            if length(arg(ax)) == 1 & arg(ax) \== ' ' then
                pref = arg(ax)' '
            else
                refs = refs classNew(pref || arg(ax))
            end
        end
    if nmTy == '=' then do
        if m.n \== ty | ty \== 'u' then
            call err 'n= mismatch'
        do ux=1 to words(refs)
            call mAdd n, word(refs, ux)
            end
        end
    else if nmTy == '*' then
        n = classNe1(0, ty, nm'*', refs, io)
    else
        n = classNe1(nmTy == '', ty, nm, refs, io)
    if arg() == 1 then
        call mapAdd class_n2c, clEx, n
/*  if nmTy == '*' & m.n.name == nm'*' then
        m.n.name = nm || substr(n, 6)   ??????? */
    if nmTy \== '' & nmTy \== '=' then
       call mapAdd class_n2c, m.n.name, n
    if nmTy == 'n' | nmTy == '?' then do
       v = 'CLASS_'translate(nm)
       if symbol('m.v') == 'VAR' then
           call err 'duplicate class' v
       m.v = n
       end
    return n
endProcedure classNew

/*--- return the name of a class if it exists otherwise the class ---*/
className: procedure expose m.
parse arg cl
    if \ mapHasKey(class_n2c, cl) then
        return 'notAClass:' cl
    c2 = mapGet(class_n2c, cl)
    if m.c2 = 'u' & m.c2.name \= '' then
        return m.c2.name
    else
        return cl
endProcedure className

/*--- 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

/*--- mutate object m to the class cl -------------------------------*/
oMutate: procedure expose m.
parse arg m, cl
    m.o.o2c.m = cl
    return m
endProcedure oMutate

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

/*--- find the code for a met in a class ---------------------------*/
classMet: procedure expose m.
parse arg cl, met
    if symbol('m.cl.method.met') == 'VAR' then
        return m.cl.method.met
    if symbol('m.cl.method.methodLazy') == 'VAR' then do
                                     /* build lazy method */
        m.cl.method.met = "call err 'building lazy method" cl"#"met"'"
        m.cl.method.met = classMetLazy(m.cl.method.methodLazy, cl, met)
        if m.cl.method.met \== '\-\' then
            return m.cl.method.met
        drop m.cl.method.met
        if arg(3) \== '' then
            return arg(3)
        else
            return err('no method' met 'in class' className(cl))
        end
    if symbol('m.class_n2c.cl') \== 'VAR' then
        call err 'no class classMet('cl',' met')'
    if cl \== m.class_n2c.cl then
        return classMet(m.class_n2c.cl, met)
    if m.cl == 'u' then
        call classMetGen cl, cl'.'method
    if symbol('m.cl.method.methodLazy') \== 'VAR' then
        m.cl.method.methodLazy = m.class_lazyRoot
    return classMet(cl, met, arg(3))
endProcedure classMet

classMetLazy: procedure expose m.
parse arg build, cl, met
    if build = '' then
        return '\-\'
    cd = classMet(build, met, '\-\')
    if abbrev(cd, '?') then
           return err('? met' cd 'b='build cl'#'met) / 0
    else if cd \== '\-\' then
        interpret cd
    else
        return cd
endProcedure classMetLazy

classMetRmRet: procedure expose m.
parse arg cl, met
    cd = classMet(cl, met)
    if word(cd, 1) == 'return' then
        return subword(cd, 2)
    else
        return cd
endProcedure classMetRmRet
/*--- generate all methods for a class recursively ------------------*/
classMetGen: procedure expose m.
parse arg aC, trg, pa
    pa = classCycle(aC, pa)
    if m.aC \== 'u' then
        call err 'cl not u:' m.aC aC
    do cx=1 to m.aC.0                      /* methods directly in cl */
        cl = m.aC.cx
        if pos(m.cl, 'ufscr') > 0 then
            iterate
        if m.cl \== 'm' then
            call err 'bad cla' cl m.cl
        m1 = m.cl.name
        if symbol('m.trg.m1') == 'VAR' then
            nop
        else
            m.trg.m1 = m.cl.met
        end
    do cx=1 to m.aC.0                      /* inherited methods */
        cl = m.aC.cx
        if m.cl == 'u' & m.class_inheritMet.cl \== 0 then
            call classmetGen cl, trg, pa
        end
    return
endProcedure classmetGen

classCycle: procedure expose m.
parse arg cl, pa
    if wordPos(cl, pa) < 1 then
        return pa cl
    call err classCycle cl pa / 0
endProcedure classCycle

classFlds: procedure expose m.
parse arg cl
    return classMet(cl, 'oFlds')
endProcedure classFlds

classFldD: procedure expose m.
parse arg cl
    return classMet(cl, 'oFldD')
endProcedure classFldD

classFldGen: procedure expose m.
parse arg cl
    m.cl.fldS.0 = 0
    m.cl.fldS.self = 0
    m.cl.fldD.0 = 0
    m.cl.stmS.0 = 0
    m.cl.stmS.self = 0
    m.cl.stmD.0 = 0
    return classFldAdd(cl, cl)
endPorcedure classFldGen

/*--- add the the fields of class cl to stem f ----------------------*/
classFldAdd: procedure expose m.
parse arg f, cl, nm, pa
    pa = classCycle(cl, pa)
    if cl == m.class_V | cl == m.class_W | cl == m.class_O ,
             | m.cl == 'r' then
             return classFldAdd1(f'.FLDD', f'.FLDS', f'.F2C', cl, nm,
                  , if(cl=m.class_W, m.o_escW, ''))
    if m.cl = 's' then do
        if m.cl.1 == '' then
            call err 'stem null class'
        return classFldAdd1(f'.STMD', f'.STMS', f'.S2C', m.cl.1, nm, 0)
        end
    if m.cl = 'f' then
        return classFldAdd(f, m.cl.1, nm ,
          || left('.', m.cl.name \== '' & nm \== '') || m.cl.name, pa)
    do tx=1 to m.cl.0
        call classFldAdd f, m.cl.tx, nm, pa
        end
    return 0
endProcedure classFldAdd

classFldAdd1: procedure expose m.
parse arg fd, fs, f2, cl, nm, null
    if symbol('m.f2.nm') == 'VAR' then
        if m.f2.nm == cl then
            return 0
        else
            return err('f2 mismatch f2='f2 Nm 'cl='cl 'old='m.f2.nm)
    m.f2.nm = cl
    cc = mAdd(fd, left('.', nm \== '')nm)
    m.cc.class = cl
    if nm == '' then do
        m.fs.self = 1
        m.fs.self.class = cl
   /*   call mMove fa, 1, 2
        m.fa.1 = ''
        call mPut fa'.SELF', 1 */
        end
    else do
        cc = mAdd(fs, nm)
        m.cc.class = cl
        end
    return 0
endProcedure classFldAdd1

/* copy class 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.ut_alfDot, '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 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 = mapAdr(a, ky, 'a')
    if vv == '' then
        return err('duplicate in mapAdd('a',' ky',' val')')
    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 = mapAdr(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 mapAdr(a, ky, 'g') \== ''
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 =  mapAdr(a, ky, 'g')
    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 = mapAdr(a, ky, 'g')
    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 = 247 - 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 = mapAdr(a, ky, 'g')
            if adr \== '' then do
                ha = left(adr, length(adr) - 2)
                do i = 1 to m.ha.0
                     vv = ha'v'i
                     drop m.ha.i m.vv
                     end
                 drop m.ha.0
                 end
            end
        end
    m.st.0 = 0
    return a
endProcedure mapClear

/*--- return the value pointer for a key, '' if non existing
    f = 'g' return address if exists otherwise ''
        'p' return address if exists otherwise newly added address
        'a' return ''      if exists otherwise newly added address --*/
mapAdr: procedure expose m.
parse arg a, ky, f
    if length(ky) + length(a) < 247 then do
        res = a'.'ky
        if symbol('m.res') == 'VAR' then
            return copies(res, f \== 'a')
        else if f == 'g' then
            return ''
        end
    else do
        len = length(ky)
        q = len % 2
        ha = a'.'len || left(ky, 80) || substr(ky,
            , len % 2 - 40, 85-length(len)-length(a)) || right(ky, 80)
        if symbol('M.ha.0') == 'VAR' then do
            do i=1 to m.ha.0
                if m.ha.i == ky then
                    return copies(ha'v'i, f \== 'a')
                end
            end
        else do
            i = 1
            end
        if f == 'g' then
            return ''
        if i > 9 then
            call err 'overflow long key' y 'in map' a 'hash' ha'.K.'i
        m.ha.0 = i
        m.ha.i = ky
        res = ha'v'i
        end
    if m.map_keys.a \== '' then
        call mAdd m.map_keys.a, ky
    return res
endProcedure mapAdr

/* copy map 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>.** and m.<mbr>_**: every rexx Module (copy) should only
               allocate these addresses to avoid address conficts
               with <mbr> the name of therexx 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
    ax = m.m_area.0 + 1
    m.m_area.0 = ax
    m.m_area.ax = nm
    if adr == '=' then
        adr = nm
    else if adr == '' then
        adr = 'M.'ax
    if symbol('m.m_2a.nm') == 'VAR' then
        call err 'area name' nm 'already used'
    if symbol('m.m_2a.adr') == 'VAR' then
        call err 'adr' adr 'for area' nm 'already used'
    m.m_2a.adr = adr
    m.m_2a.nm  = adr
    m.adr.0 = 0
    m.m_free.adr.0 = 0
    return nm
endProcedure mNewArea

mNew: procedure expose m. ggArea
parse arg name
    if symbol('m.m_2a.name') \== 'VAR' then
        call err 'area' name 'does not exists'
    adr = m.m_2a.name
    if m.m_free.adr.0 > 0 then do
        fx = m.m_free.adr.0
        m.m_free.adr.0 = fx-1
        return m.m_free.adr.fx
        end
    m.adr.0 = m.adr.0 + 1
    return adr'.'m.adr.0
endProcedure mNew

mFree: procedure expose m.
parse arg m
    adr = left(m, lastPos('.', m)-1)
    fx = m.m_free.adr.0 + 1
    m.m_free.adr.0  = fx
    m.m_free.adr.fx = m
    return ''
endProcedure mFree
/*--- iterate over all allocate elements of an area -----------------*/
mIterBegin: procedure expose m.
parse arg nm
    return m.m_2a.nm'.0'
endProcedure mIterBegin

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

/*--- get m.a -------------------------------------------------------*/
mGet: procedure expose m.
parse arg a
    return m.a
endProcedure mGet

/*--- put value v into m.a ------------------------------------------*/
mPut: procedure expose m.
parse arg a, v
    m.a = v
    return v
endProcedure mPut

/*--- 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

/*--- create the inverse map of a stem ------------------------------*/
mInverse: procedure expose m.
parse arg a, i
    do x=1 to m.a.0
        v = m.a.x
        m.i.v = x
        end
    return m.a.0
endProcedure inverse

/*--- add to m.dst.* a (sub)sequence of m.src.* ---------------------*/
mAddSt: procedure expose m.
    parse arg dst, src, fx , tx
    dx = m.dst.0
    if fx == '' then
        fx = 1
    if tx == '' then
        tx = m.src.0
    do sx = fx to tx
        dx = dx + 1
        m.dst.dx = m.src.sx
        end
    m.dst.0 = dx
    return dst
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 with separator ---------------------------*/
mCat: procedure expose m.
parse arg st, sep
    if m.st.0 < 1 then
        return ''
    res = m.st.1
    do sx=2 to m.st.0
        res = res || sep || m.st.sx
        end
    return res
endProcedure mCat

mIni: procedure expose m.
    if m.m_ini == 1 then
        return
    m.m_ini = 1
    call utIni
    m.m_area.0 = 0
    call mNewArea
    return
endProcedure mIni

/* copy m end ********************************************************/
/* copy fTab begin ****************************************************
    output Modes: t = tableMode 1 line per object with fixed colums th
                  c = colMode   1 line per column/field of object

    we build a format for each column
             and a set of title lines, one sequence printed before
                                     , one sequence printed after
    lifeCycle fTab           sql

        fTabReset            sqlFTabReset
        fTabAdd *               fTabAdd *       add col info
                             sqlFTabOthers ?
        fTabGenTab or fTabGenCol
        fTabBegin                                      header lines
        fTab1 * / tTabCol *
        fTabEnd                                        trailer lines
    primary data for each col
        .col     : column (rexx) name plus aDone
        .done    : == 0 sqlFtabOthers should add it again
        .fmt     : format
        .labelLo : long  label for multi line cycle titles
        .labelSh : short label for singel title line (colwidth)
        .tit.*   : title line piece for this col
**********************************************************************/
fTabIni: procedure expose m.
    if m.fTab_ini == 1 then
        return
    m.fTab_ini = 1
    call classIni
    m.fTab_class = classNew("n FTab u ORun, m oRun call fTab m")
    return
endProcedure fTabIni

fTabReset: procedure expose m.
parse arg m, m.m.titBef, m.m.titAft, m.m.opt
    call fTabIni
    if m.m.titBef == '' & m.m.titaft == '' then do
        m.m.titBef = 'c 1'
        m.m.titAft = '1 c'
        end
    if m.m.titBef == '-' then
        m.m.titBef = ''
    if m.m.titAft == '-' then
        m.m.titAft = ''
    m.m.generated = ''
    m.m.verbose = pos('s', m.m.opt) < 1 /* not silent */
    m.m.0 = 0
    m.m.set.0 = 0
    return fTabResetCols(oMutate(m, m.fTab_class))
endProcedure fTabReset

/*--- clean out all cols of ftab, but keep settings -----------------*/
fTabResetCols: procedure expose m.
parse arg m
    m.m.0 = 0
    return m

/*--- for column cx set title tx ------------------------------------*/
fTabSetTit: procedure expose m.
parse arg m, cx, tx, t1
    m.m.generated = ''
    if tx > m.m.cx.tit.0 then do
        do xx=m.m.cx.tit.0+1 to tx-1
            m.m.cx.tit.xx = ''
            end
        m.m.cx.tit.0 = tx
        end
    m.m.cx.tit.tx = t1
    return m
endProcedure fTabSetTit

/*--- set default atts for a col name ------------------------------*/
fTabSet: procedure expose m.
parse arg m, c1 aDone, f1, sh, lo
    sx = m.m.set.0 + 1
    m.m.set.0 = sx
    m.m.set.sx = c1 aDone
    m.m.set.sx.fmt = f1
    m.m.set.sx.labelSh = sh
    m.m.set.sx.labelLo = lo
    m.m.set.c1 = sx
    return
endProcedure fTabSet

/*--- add a column --------------------------------------------------
       m, rexxName done, fmt, labelShort, labelLong, titles... ------*/
fTabAdd: procedure expose m.
parse arg m, rxNm aDone
    m.m.generated = ''
    cx = m.m.0 + 1
    m.m.0 = cx
    cc = m'.'cx
    m.cc.col = rxNm
    m.cc.done = aDone \== 0
parse arg  , , m.cc.fmt, m.cc.labelSh, m.cc.labelLo
    if rxNm == '=' | rxNm == 0 | rxNm == 1 then
        call err 'bad rxNm' rxNm
    if \ (aDone == '' | aDone == 0 | aDone == 1) then
        call err 'bad aDone' aDone
    m.cc.tit.0 = max(arg()-4, 1)
    m.cc.tit.1 = ''
    do tx=2 to m.cc.tit.0
        m.cc.tit.tx = arg(tx+4)
        end
    return cc
endProcedure fTabAdd

/*--- complete column info-------------------------------------------*/
fTabColComplete: procedure expose m.
parse arg m
    do cx=1 to m.m.0
        nm = m.m.cx.col
        f1 = m.m.cx.fmt
        if f1 = '' then
            m.m.cx.fmt = '@.'nm'%-8C'
        else do
            px = pos('%', f1)
            ax = pos('@', f1)
            if px > 0 & (ax <= 0 | ax >= px) then
                m.m.cx.fmt = left(f1, px-1)'@.'nm || substr(f1, px)
            end
        if m.m.cx.labelLo = '' then
            if nm = '' then
                m.m.cx.labelLo = '='
            else
                m.m.cx.labelLo = nm
        if m.m.cx.labelSh = '' then
            m.m.cx.labelSh = m.m.cx.labelLo
        end
    return
endProcedure fTabColComplete

/*--- generate line formats and title lines -------------------------*/
fTabGenTab: procedure expose m.
parse arg m, sep
    if m.m.generated == '' then
        call fTabColComplete m
    m.m.tit.0 = words(m.m.titBef m.m.titAft) + 5
    do tx=1 to m.m.tit.0
        m.m.tit.tx = ''
        end
    f = ''
    tLen = 0
    do kx=1 to m.m.0
       rxNm = m.m.kx.col
       call mPut 'F_TEMP'left('.', rxNm\=='')rxNm, m.m.kx.labelSh
       t1 = f(m.m.kx.fmt, 'F_TEMP')
       m.m.kx.len = length(t1)
       if pos(strip(t1), m.m.kx.labelSh) < 1 then /* corrupted| */
           t1 = left(left('', max(0, verify(t1, ' ') -1)) ,
                 || m.m.kx.labelSh, length(t1))
       m.m.kx.tit.1 = t1
       if kx = 1 then do
           f = m.m.kx.fmt
           end
       else do
           tLen = tLen + length(sep)
           f = f || sep || m.m.kx.fmt
           end
       m.m.kx.start = tLen+1
       do tx=1 to m.m.kx.tit.0
           if m.m.kx.tit.tx \== '' then
               if tx > 1 | pos('-', m.m.opt) < 1 then
                   m.m.tit.tx = left(m.m.tit.tx, tLen) ,
                       || strip(m.m.kx.tit.tx, 't')
               else if \ abbrev(m.m.kx.tit.tx, ' ') then
                   m.m.tit.tx = left(m.m.tit.tx, tLen, '-') ,
                       || strip(m.m.kx.tit.tx, 't')
               else
                   m.m.tit.tx = left(m.m.tit.tx, tLen, '-') ,
                          || right(strip(m.m.kx.tit.tx),
                                , length(m.m.kx.tit.tx), '-')
           end
       tLen = tLen + m.m.kx.len
       end
    m.m.len = tLen
    if pos('-', m.m.opt) > 0 then
        m.m.tit.1 = left(m.m.tit.1, tLen +3, '-')
    m.m.fmt = fGen('%>', f)

    cSta = m.m.tit.0+3          /* compute cycle titles */
    cycs = ''
    cyEq = 1
    do cEnd=cSta until kx > m.m.0
            /*try with cycle lines for cSta to cEnd */
        cycs = cycs cEnd
        cx = cSta
        firstRound = 1
        do kx=1 to m.m.0
            if firstRound then
                m.m.tit.cx =  left('', m.m.kx.start-1)m.m.kx.labelLo
            else if length(m.m.tit.cx) <= m.m.kx.start - 2 then
                m.m.tit.cx = left(m.m.tit.cx, m.m.kx.start - 1) ,
                             || m.m.kx.labelLo
            else
                leave
            if cyEq then
               cyEq = translate(m.m.kx.labelLo) ,
                    = translate(m.m.kx.labelSh)
            cx = cx + 1
            if cx > cEnd then do
                cx = cSta
                firstRound = 0
                end
            end
        end
    m.m.cycles = strip(cycs)
    if cyEq & words(cycs) <=  1 then
        m.m.cycles = ''
    m.m.generated = m.m.generated't'
    return
endProcedure fTabGenTab

/*--- generate column format ----------------------------------------*/
fTabGenCol: procedure expose m.
parse arg m
    if m.m.generated == '' then
        call fTabColComplete m
    do kx=1 to m.m.0
        t = m.m.kx.labelLo
        l = if(m.m.kx.labelSh == t, , m.m.kx.labelSh)
        f = lefPad(lefPad(strip(l), 10) t, 29)
        if length(f) > 29 then
           if length(l || t) < 29 then
               f = l || left('', 29 - length(l || t))t
           else
               f = lefPad(strip(l t), 29)
        g = strip(m.m.kx.fmt)
        o = right(g, 1)
        if pos(o, 'dief') > 0 then
            f = f '@'m.m.kx.col'%12e @'m.m.kx.col'%18c'
        else if o = 'C' then
            f = f left(g, length(g)-1)'c'
        else
            f = f g
        m.m.kx.colFmt = f
        end
    m.m.generated = m.m.generated'c'
    return
endProcedure fTabGenCol

/*--- output all of rdr in tab format -------------------------------*/
fTab: procedure expose m.
parse arg m, rdr
    if pos('a', m.m.opt) < 1 then
        i = rdr
    else do
        i = in2Buf(rdr)
        if m.i.buf.0 > 0 then
            call fTabDetect m, i'.BUF'
        end
    if pos('o', m.m.opt) > 0 then do
        call pipeWriteAll i
        end
    else if pos('c', m.m.opt) > 0 then do
        if pos('c', m.m.generated) < 1 then
            call fTabGenCol m
        i = jOpen(in2file(i), '<')
        do rx=1 while jRead(i)
            call out left('--- row' rx '',  80, '-')
            call fTabCol m, m.i
            end
        call out left('--- end of' (rx-1) 'rows ', 80, '-')
        call jClose i
        end
    else do
        call fTabBegin m
        call fAll m.m.fmt, i
        return fTabEnd(m)
        end
    return m
endProcedure fTab

/*--- output object i in col format ---------------------------------*/
fTabCol: procedure expose m.
parse arg m, i
    do cx=1 to m.m.0
        call out f(m.m.cx.colFmt, i)
        end
    return 0
endProcedure fTabCol

fTabBegin: procedure expose m.
parse arg m
    if pos('t', m.m.generated) < 1 then
        call fTabGenTab m, ' '
    return fTabTitles(m, m.m.titBef)
endProcedure fTabBegin

fTabEnd: procedure expose m.
parse arg m
    return fTabTitles(m, m.m.titAft)

fTabTitles: procedure expose m.
parse arg m, list
    list = repAll(list, 'c', m.m.cycles)
    do tx=1 to words(list)
        t1 = word(list, tx)
        call out m.m.tit.t1
        end
    return m
endProcedure fTabTitles

/*--- auto tables detect format from data ---------------------------*/
/*--- detect fmt from a rdr and write it formatted to stdOut --------*/
fTabAuto: procedure expose m.
parse arg m, rdr
    if m == '' then
        m = fTabReset(f_auto, 1, , 'a')
    else if pos('a', m.m.opt) < 1 then
        m.m.opt = 'a'm.m.opt
    return fTab(m, rdr)
endProcedure fTabAuto

/*--- generate format for all fields of a stem of objects -----------*/
fTabDetect: procedure expose m.
parse arg m, b
    do cx=1 to m.m.0
        rxNm = m.m.cx.col
        done.rxNm = m.m.cx.done
        if m.m.cx.fmt == '' then
            m.m.cx.fmt = fTabDetectFmt(b, left('.', rxNm \== '')rxNm)
        end
    ff = oFldD(m.b.1)
    do fx=1 to m.ff.0
        rxNm = substr(m.ff.fx, 2)
        if done.rxNm \== 1 then do
             cc = fTabAdd(m, rxNm)
             m.cc.fmt = fTabDetectFmt(b, left('.', rxNm \== '')rxNm)
             end
        end
    return
endProcedure fTabDetect

/*--- detect format for one field in stem st ------------------------*/
fTabDetectFmt: procedure expose m.
parse arg st, suf
    lMa = -1
    rMa = -1
    bMa = -1
    aDiv = 0
    nMi =  9e999
    nMa = -9e999
    eMi =  9e999
    eMa = -9e999
    eDa = 2
    dMa = -9e999
    do sx=1 to m.st.0
        v = mGet(m.st.sx || suf)
        lMa = max(lMa, length(strip(v, 't')))
        rMa = max(rMa, length(strip(v, 'l')))
        bMa = max(bMa, length(strip(v, 'b')))
        if \ dataType(v, 'n') then do
            if length(v) > 100 then
                aDiv = 99
            else if aDiv <=3 then
                if aDiv.v \== 1 then do
                    aDiv.v = 1
                    aDiv = aDiv + 1
                    end
            iterate
            end
        v = strip(v)
        nMi = min(nMi, v)
        nMa = max(nMa, v)
        ex = verify(v, 'eEfF', 'm')
        if ex > 0 then do
            eMa = max(eMa, substr(v, ex+1))
            eMi = min(eMi, substr(v, ex+1))
            v = left(v, ex-1)
            do while pos(left(v,1), '+-0') > 0
                v = substr(v, 2)
                end
            eDa = max(eDa, length(v) - (pos('.', v) > 0))
            end
        dx = pos('.', v)
        if dx > 0 then do
            do while right(v, 1) == 0
                v = left(v, length(v)-1)
                end
            dMa = max(dMa, length(v)-dx)
            end
        end
    if nMi > nMa | aDiv > 3 then
        newFo = '-'max(1, (lMa+0))'C'
    else if eMi <= eMa then do
        newFo = ' ' || (eDa+max(length(eMa), length(eMi))+3) ,
             || '.'||(eDa-1)'e'
        end
    else do
        be = max(length(trunc(nMi)), length(trunc(nMa)))
        if dMa <= 0 then
            newFo = max(be, bMa)'I'
        else
            newFo = max(be+1+dMa, bMa)'.'dMa'I'
        end
    return '%'newFo
endProcedure fTabDetectFmt

/* copy fTab end   ***************************************************/
/* copy f begin ******************************************************/
/*--- format with the given format ggA1, ggA2, etc. -----------------*/
f: procedure expose m.
parse arg ggFmt, ggA1, ggA2
    if symbol('M.f_gen.ggFmt') \== 'VAR' then
        call fGen ggFmt, ggFmt
    interpret m.f_gen.ggFmt
endProcedure f

fImm: procedure expose m.
parse arg ggFmt, ggA1
    interpret m.f_gen.ggFmt
endProcedure fImm

fCache: procedure expose m.
parse arg a, fmt
    if a \== '%>' then do
        if symbol('M.f_gen.a') == 'VAR' then
            if m.f_gen.a \== fmt then
                call err 'fCache('a',' fmt') already' m.f_gen.a
        end
    else do
        if symbol('m.f_gen0') == 'VAR' then
            m.f_gen0 = m.f_gen0 + 1
        else
            m.f_gen0 = 1
        a =  '%>'m.f_gen0
        end
    m.f_gen.a = fmt
    return a
endProcedure fCache

/*--- compile format fmt put in the cache with address a
          this procedure handles precompile and calls fGenF ---------*/
fGen: procedure expose m.
parse arg a, fmt
    if a \== '%>' then
        if symbol('M.f_gen.a') == 'VAR' then
            return a
    r3 = right(fmt, 3)
    if abbrev(r3, '%#') then do
        if substr(r3, 3) = '' then
            call err 'fGen bad suffix' fmt
        if right(a, 3) \== r3 then
            call err 'adr fmt mismatch' a '<->' fmt
        fmt = left(fmt, length(fmt) - 3)
        a = left(a, length(a) - 3)
        if symbol('m.a') == 'VAR' then
            call err 'base already defined' arg(2)
        end
    if \ abbrev(fmt, '%##') then
        return fCache(a, fGenF(fmt))
    parse var fmt '%##' fun ' ' rest
    interpret 'return' fun'(a, rest)'
endProcedure fGen

/*--------------------------------------------------------------------
 Format generator    should be compatible with fPrint|
 <<<< + extension of fPrint, - in fPrint but not implemented

   %%  %@ the escaped char
   ('@' argN? '.'? field)?'%' flags? width? ('.' precision)? specifier

 specifier: is the most significant one and defines the type

 - c  Character rigPad or lefPad, prec ==> substr(..., prec)
 -  C Cut %-nC = left(v,n), %nC = right(v,n) %n.mC = substr(m, n)
 - hH Characters in hex
 - iI Signed decimal integer (padded or cut)
 - eE Scientific notation (mantissa/exponent) using e character 3.92e+2
 - S  Strip (both)
 - txy time date formatting from format x to format y see fTstGen
 - kx  units Formatting x = t=time d=decimal b=binary (k) B=binary (kB)
 Flags:
 - -  Left-justify within the given field width; Right is default
 - +  print '+' before non negative numbers
 -' ' print ' ' before non negative numbers
 - /  cut to length

 preprocessor implemented in fGen
   %##fun fmt  format by function fun
   %>          address only
---------------------------------------------------------------------*/
fGenF: procedure expose m.
parse arg fmt
    if symbol('m.f_s_0') \== 'VAR' then
        m.f_s_0 = 1
    else
        m.f_s_0 = m.f_s_0 + 1
    f_s = 'F_S_'m.f_s_0
    call scanSrc f_s, fmt
    ax = 0
    cd = ''
    cp = ''
    do forever
        txt = fText(f_s)
        if txt \== '' then
            cd = cd '||' quote(txt, "'")
        if scanEnd(f_s) then
            leave
        if \ scanLit(f_s, '@') then do
            ax = ax + 1
            af = ''
            hasDot = 0
            end
        else do
            if scanWhile(f_s, '0123456789') then
                ax = m.f_s.tok
            else if ax < 1 then
                ax = 1
            hasDot = scanLit(f_s, '.')
            af = fText(f_s)
            end
        if \ scanLit(f_s, '%') then
            call scanErr f_s, 'missing %'
        call scanWhile f_s, '-+ /'
        flags = m.f_s.tok
        call scanWhile f_s, '0123456789'
        len = m.f_s.tok
        if \ scanLit(f_s, '.') then
            prec  = ''
        else do
            call scanWhile f_s, '0123456789'
            prec = m.f_s.tok
            end
        call scanChar f_s, 1
        sp = m.f_s.tok
        if ax < 3 | ass.ax == 1 then
            aa = 'ggA'ax
        else do
            aa = 'arg(' || (ax+1) || ')'
            if af \== '' then do
                 cp = cp 'ggA'ax '=' aa';'
                 aa = 'ggA'ax
                 ass.ax = 1
                 end
            end
        if af \== '' | hasDot then
            aa = rxMGet(aa, af)
        if sp == 'c' then do
            if prec \== '' then
                aa = 'substr('aa',' prec')'
            if len == '' then
                cd = cd '||' aa
            else if pos('-', flags) > 0 then
                cd = cd '|| lefPad('aa',' len')'
            else
                cd = cd '|| rigPad('aa',' len')'
            end
        else if sp == 'C' then do
            if prec \== '' then do
                cd = cd '|| substr('aa',' prec
                if len == '' then
                    cd = cd')'
                else
                    cd = cd',' len')'
                end
            else if len == '' then
                cd = cd '||' aa
            else if pos('-', flags) > 0 then
                cd = cd '|| left('aa',' len')'
            else
                cd = cd '|| right('aa',' len')'
            end
        else if sp == 'H' then
            cd = cd "|| fH("aa"," len',' (pos('-', flags) > 0)')'
        else if sp == 'h' then
            cd = cd "|| translate(fH("aa", '"siL"'),'abcdef','ABCDEF')"
        else if sp == 'i' then
            cd = cd "|| fI("aa"," len", '"flags"'," firstNS(prec, 0)")"
        else if sp == 'I' then
            cd = cd "|| fI("aa"," len", '/"flags"'," firstNS(prec, 0)")"
        else if sp == 'E' | sp == 'e' then do
            if len == '' then
                len = 8
            if prec = '' then
                prec = len - 6
            cd = cd "|| fE("aa"," len"," prec", '"sp"', '"flags"')"
            end
        else if sp = 'S' then
            cd = cd '|| strip('aa')'
        else if sp = 't' then do
            call scanChar f_s, 2
            cd = cd '||' fTstGen(m.f_s.tok, aa)
            end
        else if sp = 'k' then do
            call scanChar f_s, 1
            if pos(m.f_s.tok, 'tdbBiI') < 1 then
                call scanErr f_s, "bad unit type" m.f_s.tok
            if pos('+', flags) > 0 then
                pl = ", '+'"
            else if pos(' ', flags) > 0 then
                pl = ", ' '"
            else
                pl = ''
            cd = cd "|| fUnit('"m.f_s.tok || len"."prec"¢"pl"'," aa")"
            end
        else if sp == '(' then do
            c1 = aa
            do until m.f_s.tok = '%)'
                sx = m.f_s.pos
                do until m.f_s.tok == '%,' | m.f_s.tok == '%)'
                    call scanUntil f_s, '%'
                    if \ scanLit(f_s, '%,', '%)', '%') then
                         call scanErr f_s, '%( not closed'
                    end
                c1 = "fImm('"fGen('%>', substr(m.f_s.src, sx,
                              , m.f_s.pos - sx - 2))"'," c1")"
                end
            cd = cd '||' c1
            end
        else do
            call scanErr f_s, 'bad % clause'
            call scanBack f_s, '%'sp
            leave
            end
        end
    if \ scanEnd(f_s) then
        call scanErr f_s, "bad specifier '"m.f_s.tok"'"
    m.f_s_0 = m.f_s_0 - 1
    if cd \== '' then
        return strip(cp 'return' substr(cd, 5))
    else
        return "return ''"
endProcedure fGenF

fText: procedure expose m.
parse arg f_s
    res = ''
    do forever
        if scanUntil(f_s, '@%') then
            res = res || m.f_s.tok
        if scanLit(f_s, '%%', '%@') then
            res = res || substr(m.f_s.tok, 2)
        else if scanLit(f_s, '%>', '%##') then
            res = res || m.f_s.tok
        else
            return res
        end
endProcedure fText

fAll: procedure expose m.
parse arg fmt, rdr
    i = jOpen(in2File(rdr), '<')
    do while jRead(i)
        call out f(fmt, m.i)
        end
    call jClose i
    return
endProcedure fAll

/*--- format character2hex (if not sql null) ------------------------*/
fH: procedure expose m.
parse arg v, l, leftJ
    if v \== m.sqlNull then
        v = c2x(v)
    if length(v) > l then
        return v
    else if leftJ \== 1 then
        return right(v, l)
    else
        return left(v, l)
endProcedure fH

/*--- format integer or fixPoint Decimal ----------------------------*/
fI: procedure expose m.
parse arg v, l, flags, d
    if \ datatype(v, 'n') then
        return fRigLeft(strip(v), l, flags)
    v = format(v, , d, 0)
    if pos('+', flags) > 0 then
        if \ abbrev(v, '-') then
            v = '+'v
    if length(v) > l then
        if pos('/', flags) > 0 then
            return left('', l, '*')
        else
            return v
    return fRigLefPad(v, l, flags)
endProcedure fI

/*--- format with exponent l=total output len
                           d=number of digits after . in mantissa
                           c=exponent character
                           flags: - to ouput text left justified
    differences: exponent is always printed but without +
                 overflow ==> expand exponent, *****, 0e-999 --------*/
fE: procedure expose m.
parse arg v, l, d, c, flags
    if \ datatype(v, 'n') then
        return fRigLeft(v, l, flags)
    if pos(' ', flags) < 1 then
        if v >=  0 then
            if pos('+', flags) > 0 then
                return '+'substr(fE(v, l, d, c, ' 'flags), 2)
            else
                return substr(fE(v, l+1, d+1, c, ' 'flags), 2)
    x = format(v, 2, d, 7, 0)
    m = 2 + d + (d>0)
    call assert "length(x) == m+9", 'm x length(x)'
    if substr(x, m+1) = '' then
        return left(x, m)c || left('', l-m-1, 0)
    call assert "substr(x, m+1, 1) == 'E'","exponenent in x not at m"
    y = verify(x, '0', 'n',  m+3)
    call assert 'y>0'
    if substr(x, m+1, 2) == 'E+' then do
        if m+10-y <= l-m-1 then
             return left(x,m)c || right(x, l-m-1)
        z = l - 4 - (m+10-y)
        end
    else if substr(x, m+1, 2) == 'E-' then do
        if m+10-y <= l-m-2 then
             return left(x,m)c'-'right(x, l-m-2)
        z = l - 5 - (m+10-y)
        end
    else
        call err 'bad x' x
    if z >= -1 & max(0, z) < d then
        return fE(v, l, max(0, z), c, flags)
    else if substr(x, m+1, 2) == 'E-' then
        return left(x,1)'0'c'-'left('', l-4, 9)
    else
        return left('', l, '*')
endProcedure fE

/*--- right or left with truncation ---------------------------------*/
fRigLeft: procedure expose m
parse arg s, len, flags
    if length(s) = len then
        return s
    else if pos('-', flags) > 0 | length(s) > len then
        return left(s, len)
    else
        return right(s, len)
endProcedure fRigLefPad

/*--- right or left pad without truncation --------------------------*/
fRigLefPad: procedure expose m
parse arg s, len, flags
    if pos('-', flags) > 0 then
        if length(strip(s, 't')) >= len then
            return strip(s, 't')
        else
            return left(s, len)
    else
        if length(strip(s, 'l')) >= len then
            return strip(s, 'l')
        else
            return right(s, len)
endProcedure fRigLefPad

/*-- return char i+1 from codes cc ----------------------------------*/
fI2C: procedure expose m.
parse arg i, cc
    if i >= length(cc) then
        call err 'no code for fI2C('i',' cc')'
    return substr(cc, i+1, 1)

/*-- return pos-1 for char c in codes -------------------------------*/
fC2I: procedure expose m.
parse arg c, codes
    res = pos(c, codes)
    if res > 0 then
        return res - 1
    call err 'not  a code fI2C('c',' codes')'

/*--- generate timestamp format, derive from %t.. ------------------*/
fTstGen: procedure expose m.
parse arg ft, s
    fmt = '%t'ft
    if symbol('M.f_gen.fmt') \== 'VAR' then
        m.f_gen.fmt = 'return' fTstGe2(ft, 'ggA1')
    code =  m.f_gen.fmt
    if \ abbrev(code, 'return ') then
        call err 'fTstGen' ft 'bad code' code
    if pos('ggA1', code) == lastPos('ggA1', code) ,
              | verify(s, '()', 'm') < 1 then
        return repAll(substr(code, 8), 'ggA1', s)
    else
        return "fImm('"fmt"'," s")"
endProcedure fTstGen

/*--- generate timestamp formats: from format c to format d ---------*/
fTstGe2: procedure expose m.
parse arg c 2 d, s
    if pos(c, ' jJLlu') > 0 then do /* special cases */
        if c == ' ' then do  /* get current timestamp */
            if pos(d, 'sMAnY ') > 0 then
                return fTstGen('n'd, "date('S') time()")
            else if pos(d, 'DdEeJj') > 0 then
                return fTstGen('D'd, "date('S')")
            else if pos(d, 'tH') > 0 then
                return ftstGen('t'd, "time()")
            else if pos(d, 'T') > 0 then
                return fTstGen('T'd, "time('L')")
            else
                return fTstGen('N'd, "date('S') time('L')")
            end
        if c == 'j' then           /* via date D */
            return fTstGen('D'd, "date('s'," s", 'J')")
        if c == 'J' then
            return fTstGen('D'd, "date('s'," s", 'B')")
        call timeIni               /* via db2 timestamp */
        if c == 'L' then
            return fTstGen('S'd, 'timeLRSN2LZT('s')')
        if c == 'l' then
            return fTstGen('S'd, 'timeLRSN2LZT(c2x('s'))')
        if c == 'u' then
            return fTstGen('S'd, 'timeLRSN2LZT(timeUniq2lrsn('s'))')
        end

    if symbol('m.f_tstFo.c')=='VAR' & symbol('m.f_tstFo.d')=='VAR' then
        return ftstGFF(m.f_tstFo.c, m.f_tstFo.d, s)
    if m.f_tstIni == 1 then
        call err "bad timestamp from or to format '"c || d"'"
        /*--- initialize f_tst --------------------------------------*/
    m.f_tstIni = 1
    call utIni
    m.f_tstScan = 0
    a = 'F_TSTFO.'
    m.f_tstN0   =   'yz345678 hi:mn:st'
    m.f_tstN    =   'yz345678 hi:mn:st.abcdef'
    m.f_tstS0   =   'yz34-56-78-hi.mn.st'
    m.f_tstS    =   'yz34-56-78-hi.mn.st.abcdef'
        /*---------- picture characters not in DB2 timestamp
                     Y: year//25 A = 2000 Y=2024
                     Z: year//20 A = 2010                to deimplement
                     M: month B=Januar ...,
                     A: first digit of day A=0, D=30
                     B: day 1=1 10=A 31=V                 deimplemented
                     H: hour first digit  A=0 B=10 C=20 D=30
                     I: hour 1=A, 10=K 23=X               deimplemented
                     jjjjj: Julian
                     JJJJJJ: base date (days since 1.1.0001)
                     llllllllll: 10 Byte LRSN
                     LL...: 10 Byte LRSN as 20 HexCharacters
                     uuuuuuuu: db2 Utility Unique
                     qr: minuten//10, sec ==> aa - xy  base 25 ------*/
    m.f_tstPics =   'yz345678himnstabcdefYZMAHIjJlLuqr'
    m.f_tstZero =   '00010101000000000000???AAA??00?AA'
    call mPut a'S',  m.f_tstS
    call mPut a's',  m.f_tstS0
    call mPut a' ',  m.f_tstS0
    call mPut a'D', 'yz345678'
    call mPut a'd',   '345678'
    call mPut a't',            'hi.mn.st'
    call mPut a'T',            'hi:mn:st.abcdef'
    call mPut a'E', '78.56.yz34'
    call mPut a'e', '78.56.34'
    call mPut a'Y',    'YM78Imqr'
    call mPut a'Z',      'ZM78'    /* deimplement */
    call mPut a'M',    'M78himns'
/*  call mPut a'I',    'M78Imnst'   */
    call mPut a'A',    'A8himnst'
/*  call mPut a'B',    'YMBImnst'   */
    call mPut a'H',           'Himnst'
    call mPut a'n',  m.f_tstN0
    call mPut a'N',  m.f_tstN
    call mPut a'j', 'jjjjj' /* julian date 34jjj        */
    call mPut a'J', 'JJJJJJ' /* day since 1.1.00: 6 digits     */
    call mPut a'l', copies('l', 10) /*LRSN out 10 Byte, input var*/
    call mPut a'L', copies('L', 20) /* LRSN in hex */
    call mPut a'u', 'uuuuuuuu' /* Unique */
    return fTstGe2(c || d, s) /* retry after initialisation */
endProcedure fTstGe2

/*--- nest source s into code (at $)
      if source is not simpe and used several times then
          use fImm to avoid muliple evaluations ---------------------*/
fTstNest: procedure expose m.
parse arg code, s
    if pos('$', code) == lastPos('$', code) ,
              | verify(s, '(). ', 'm') < 1 then
        return repAll(code, '$', s)
    a = fCache('%>', 'return' repAll(code, '$', 'ggA1'))
    return "fImm('"a"'," s")"
endProcedure fTstFi

/*--- return rexx code for timestamp conversion
      from pic f to pic aT for source s -----------------------------*/
fTstgFF: procedure expose m.
parse arg f, aT, s
    m.f_tstScan = m.f_tstScan + 1
    a = f_tstScan || m.f_tstScan
    call scanSrc a, aT
    cd = ''
    pc = '' /* permutations and constants */
    do until t == ''
        c1 = '' /* a rexx function / expression */
        p1 = '' /* permutations and constants */
        tPos = m.a.pos
        call scanChar a, 1
        t = m.a.tok
        if pos(t, f' .:-') > 0 then do
            call scanVerify a, f' .:-', 'n'
            p1 = t || m.a.tok         /* permutate pics or constants */
            end
        else if pos(t, m.f_tstPics) <= 0 then do
            p1 = m.a.tok                                /* constants */
            end
        else if t == 'y' then do                             /* year */
            if scanLit(a, 'z34') then do
                if pos('34', f) > 0 then
                    c1 = "timeYear24(substr("s "," pos('34', f)", 2))"
                else if pos('Y', f) > 0 then
                    c1 = "timeY2Year(substr("s "," pos('Y', f)", 1))"
                end
            end
        else if t == '3' then do
            if scanLit(a, '4') then
                if pos('Y', f) > 0 then
                    c1 = "substr(timeY2Year(substr("s,
                            "," pos('Y', f)", 1)), 3)"
            end
        else if t == 'Y' then do
            if pos('34', f) > 0 then
                c1 = "timeYear2Y(substr("s "," pos('34', f)", 2))"
            end
        else if t == 'Z' then do
            if pos('34', f) > 0 then
                c1 = "timeYear2Z(substr("s "," pos('34', f)", 2))"
            end
        else if t == '5' then do                            /* month */
            if scanLit(a, '6') then
                if pos('M', f) > 0 then
                    c1 = "timeM2Month(substr("s"," pos('M', f)", 1))"
            end
        else if t == 'M' then do
            if pos('56', f) > 0 then
                c1 = "timeMonth2M(substr("s"," pos('56', f)", 2))"
            end
        else if t == '7' then do                              /* day */
            if scanLit(a, '8') then
                c1 = fTstGetDay(f, s)
            end
        else if t == 'A' then do
            if scanLit(a, '8') then do
                c1 = fTstGetDay(f, s)
                if c1 \== '' then
                    c1 = fTstNest("fI2C(left($, 1), 'ABCD')" ,
                                 || "right($, 1)", c1)
                end
            end
        else if t == 'h' then do                             /* hour */
            if scanLit(a, 'i') then
                c1 = fTstGetHour(f, s)
            end
        else if t == 'n' then do                             /* hour */
            if scanLit(a, 'i') then
                c1 = fTstGetHour(f, s)
            else if pos('qr', f) > 0 then do
                call scanLit a, 'st', '.st', ':st', 's', '.s', ':s'
                c1 = "fqr2ms(substr("s"," pos('qr', f)", 2)" ,
                    || ", '"left(m.a.tok, abbrev(m.a.tok, '.') ,
                                        | abbrev(m.a.tok, ':'))"')"
                if right(m.a.tok, 1) \== 't' then
                    c1 = "left("c1"," 1 + length(m.a.tok)")"
                end
            end
        else if t == 'H' then do
            if scanLit(a, 'i') then do
                c1 = fTstGetHour(f, s)
                if c1 \== '' then
                    c1 = fTstNest("fI2C(left($, 1), 'ABC')" ,
                                 || "right($, 1)", c1)
                end
            end
        else if t == 'I' then do
            c1 = fTstGetHour(f, s)
            if c1 \== '' then
                c1 = "fI2C("c1", m.ut_uc25)"
            end
        else if t == 'j' then do                           /* julian */
            if scanLit(a, 'jjjj') then
                c1 = "time2jul(" fTstGFF(f, 'yz34-56-78', s)")"
            end
        else if t == 'J' then do                  /* day since 1.1.1 */
            if scanLit(a, 'JJJJJ') then
                c1 = "date('B'," fTstGFF(f, 'yz345678', s)", 's')"
            end
        else if t == 'l' then do                     /* 10 byte lrsn */
            if scanLit(a, copies('l', 9)) then
                c1 = "x2c(timeLZT2LRSN("fTstGFF(f, m.f_tstS, s)"))"
            end
        else if t == 'L' then do                   /* lrsn in 20 hex */
            if scanLit(a, copies('L', 19)) then
                c1 = "timeLZT2LRSN("fTstGFF(f, m.f_tstS, s)")"
            end
        else if t == 'u' then do            /* 8 byte utility unique */
            if scanLit(a, 'uuuuuuu') then
                c1 = "timeLrsn2Uniq(timeLZT2LRSN(",
                        || fTstGFF(f, m.f_tstS, s)"))"
            end
        else if t == 'q' then do            /* 8 byte utility unique */
            if scanLit(a, 'r') then
                if pos('n', f) > 0 then do
                    c1 = "fms2qr(substr("s"," pos('n', f)", 1),"
                    if pos('st', f) > 0 then
                        c1 = c1 "substr("s"," pos('st', f)", 2))"
                    else if pos('s', f) > 0 then
                        c1 = c1 "substr("s"," pos('s', f)", 1)'0')"
                    else
                        c1 = c1 "0)"
                    end
            end

        if pos(t, 'lLu') > 0 then
            call timeIni
        if c1 == '' & p1 == '' & t \== '' then /* nothing -> zero */
            p1 = translate(substr(m.a.src, tPos, m.a.pos-tPos),
                   , m.f_tstZero, m.f_tstPics)

        pc = pc || p1
        if (c1 \== '' | t == '') & pc \== '' then do/*append pc to cd*/
            if verify(pc, m.f_tstPics, 'm') == 0 then
                cd = cd '||' quote(pc, "'")
            else if pc == f then
                cd = cd '||' s
            else if pos(pc, f) > 0 then
                cd = cd "|| substr("s"," pos(pc, f)"," length(pc)")"
            else
                cd = cd "|| translate('"pc"'," s", '"f"')"
            pc = ''
            end
        if c1 \== '' then                         /* append pc to cd */
            cd = cd '||' c1
        end
    m.f_tstScan = m.f_tstScan - 1
    if cd == '' then
        return "''"
    else
        return substr(cd, 5)
endProcedure fTstGFF

/*--- return code for day, d1Only = first digit only ----------------*/
fTstGetDay: procedure expose m.
parse arg f, s
    if pos('78', f) > 0 then
        return  "substr("s"," pos(78, f)", 2)"
    if pos('A', f) > 0 then
        if pos('8', f) > 0 then
            return "fc2i(substr("s"," pos('A', f)", 1), 'ABCD')",
                || "substr("s"," pos('8', f)", 1)"
    return ''
endProcedure fTstGetDay

/*--- return code for hour in 2 digits ------------------------------*/
fTstGetHour: procedure expose m.
parse arg f, s
    if pos('hi', f) > 0 then
        return "substr("s"," pos('hi', f)", 2)"
    if pos('Hi', f) > 0 then
        return "fC2I(substr("s"," pos('Hi', f)", 1), 'ABC')" ,
                 || "substr("s"," pos('Hi', f) + 1", 1)"
    if pos('I', f) > 0 then
        return "right(fC2I(substr("s"," pos('I', f)", 1)," ,
                     "m.ut_uc25), 2, 0)"
    return ''
endProcedure fTstGetHour

fms2qr: procedure expose m.
parse arg m, s
    t =  (m // 10) * 60 + s
    return substr(m.ut_uc25, t %  25 + 1,1),
        || substr(m.ut_uc25, t // 25 + 1,1)


fqr2ms: procedure expose m.
parse arg q, sep
    v = pos(left(q, 1), m.ut_uc25) * 25 ,
      + pos(substr(q, 2, 1), m.ut_uc25) - 26
    return (v % 60) || sep || right(v // 60, 2, 0)

fWords: procedure expose m.
parse arg fmt, wrds
    f2 = '%##fCatFmt' fmt
    if wrds = '' then
        return f(f2'%#0')
    res = f(f2'%#1', word(wrds, 1))
    do wx=2 to words(wrds)
        res = res || f(f2, word(wrds, wx))
        end
    return res || f(f2'%#r')
endProcedure fWords

fCat: procedure expose m.
parse arg fmt, st
    return fCatFT(fmt, st, 1, m.st.0)

fCatFT: procedure expose m.
parse arg fmt, st, fx, tx
    f2 = '%##fCatFmt' fmt
    if tx < fx then
        return f(f2'%#0')
    res = f(f2'%#1', m.st.fx)
    do sx=fx+1 to tx
        res = res || f(f2, m.st.sx)
        end
    return res || f(f2'%#r')
endProcedure fCatFT

fCatFmt: procedure expose m.
parse arg adr, fmt
    v.m = ''    /* middle */
    v.l = ''    /* left */
    v.r = ''    /* right */
    v.a = '%c'  /* all rows */
    nm = M
    cx = 1
    do forever        /* split clauses */
        cy = pos('#', fmt, cx)
        if cy < 1 then do
            v.nm = substr(fmt, cx)
            leave
            end
        v.nm = substr(fmt, cx, cy-cx)
        nm = translate(substr(fmt, cy+1, 1))
        cx = cy+2
        end
    if symbol('v.2') \== 'VAR' then  /* second and following */
        v.2 = v.M || v.a
    adr = fGen(adr, v.2)
    if symbol('v.0') \== 'VAR' then  /* empty */
        v.0 = v.l || v.r
    call fGen adr'%#0', v.0
    if symbol('v.1') \== 'VAR' then /* first row */
        v.1 = v.l || v.a
    call fGen adr'%#1', v.1
    call fGen adr'%#r', v.R
    return adr
endProcedure fCatFmt

/*--- format with units seconds ==> 3d13 oder 5.43e6 ==> 5M43 -------*/
fUnit: procedure expose m.
parse arg uFmt, v /* scale, aLen, aPrec, plus */
    uF = 'F_UNIT.'uFmt                 /* address of (global) format */
    if symbol('m.uF.0') \== 'VAR' then
         call fUnitGen uFmt
    if \ dataType(v, 'n') then
        return right(v, m.uF.len)
    uS = uF'!' || (v >= 0)               /* address of signed format */
    v = abs(v)                /* always get rid also of sign of -0 | */


    do fx=11 to m.uF.0-1 while v >= m.uS.fx.lim1     /* search range */
        end
    if fx = 11 & v <> trunc(v) then do
        do fx=10 by -1 to m.uF.min while v < m.uS.fx.lim1
            end
        fx = fx + 1
        end

    do fx=fx to m.uF.0                              /* try to format */
        uU = uS'.'fx
        w = format(v * m.uU.fact, , m.uU.prec)    /* address of Unit */
        if pos('E-', w) > 0 then
            w = format(0, , m.uU.prec)
        if w < m.uU.lim2 then do
            if m.uU.kind == 'r' then
                x = m.uS.sign || w || m.uU.unit
            else if m.uU.kind == 'm' then
                x = m.uS.sign || (w % m.uU.mod) || m.uU.mUnit ,
                    || right(w // m.uU.mod, m.uF.len2, 0)
            else
                call err 'bad kind' m.uU.kind 'in uU' uU
            if length(x) <= m.uF.len then
                return right(x, m.uF.len)
            end
        end
    return left(m.uS.sign, m.uF.len, '+')
endProcedure fUnit

/*--- generate all format entries for given scale -------------------*/
     aLen = total len, pLen =len of +, sLen = len of current sign ---*/
fUnitGen: procedure expose m.
parse arg uFmt
parse arg scale 2 aMid '¢' plus
parse var aMid aLen '.' aPrec
    if pos('!', uFmt) > 0 then
        call err 'bad fUnit format' uFmt
    sc = 'F_SCALE.'scale
    uF = 'F_UNIT.'uFmt                 /* address of (global) format */
    if symbol('m.sc.0') \== 'VAR' then do
        call fUnitIni
        if symbol('m.sc.0') \== 'VAR' then
            call err 'bad scale' sc 'for fUnitGen('uFmt')'
        end

    hasM = scale = 't'
    if aPrec == '' then
        if scale = 't' then
            aPrec = 2
        else
            aPrec = 0
    if aLen = '' then
        if scale = 't' then
            aLen = length(plus) + 3 + aPrec
        else
            aLen = aPrec + (aPrec >= 0) + 4 + length(plus)
    m.uF.len2  = aPrec
    if hasM then
        aPrec = 0
    m.uF.len = aLen
    m.uF.0   = m.sc.0
    m.uF.min = m.sc.min
    do geq0=0 to 1
        uS = uF'!'geq0                   /* address of signed format */
        if geq0 then do
            m.uS.sign = plus
            end
        else do
            m.uS.sign = '-'
            end
        sLen = length(m.uS.sign)
        dLen = aLen - sLen - hasM
        limR = '1e' || (aLen -sLen - hasM - (aPrec > 0) - aPrec)
        limM = '1e' || (aLen - m.uF.len2 - 1 - sLen)
        do ix=m.sc.0 by -1 to m.sc.min
            uU = uS'.'ix                      /* address of one unit */
            m.uU.unit = m.sc.ix.unit
            m.uU.fact = m.sc.ix.fact
            m.uU.val  = m.sc.ix.val
            m.uU.kind = m.sc.ix.kind
            m.uU.Len  = aLen
            m.uU.prec = aPrec
            if m.uU.kind = 'r' then do
                m.uU.lim2 = limR
                m.uU.lim1 = limR * m.uU.val
                end
            else do
                iy = ix + 1
                iz = ix + 2
                m.uU.mUnit = m.sc.iy.unit
                m.uU.mod   = m.sc.iy.val % m.sc.ix.val
                m.uU.wid2  = aPrec
                if iz <= m.sc.0 & m.sc.iz.kind == 'm' then
                    m.uU.lim1  = m.sc.iz.val
                else
                    m.uU.lim1 = limM * m.sc.iy.val
                m.uU.lim2  = m.uU.lim1 % m.uU.val
                end
            end
        end
    return
endProcedure fUnitGen

fUnitIni: procedure expose m.
    if m.f_unit_ini == 1 then
        return
    m.f_unit_ini = 1
      /*  0    5   10    5   20 */
    iso = '    afpnum kMGTPE   '
    sB = f_Scale'.b'
    sD = f_Scale'.d'
    sT = f_Scale'.t'
    fB = 1
    fD = 1
    call fUnitIni2 sB, 11, ' ', 'r', fB
    m.sB.0   =  17
    m.sB.min =  11
    call fUnitIni2 sD, 11, ' ', 'r', fD
    m.sD.0   = 17
    m.sd.min =  5
    do x=1 to 6
        fB = fB * 1024
  /*    call fUnitIni2 sB, (11-x), substr(iso, 11-x, 1), 'r', fB*/
        call fUnitIni2 sB, (11+x), substr(iso, 11+x, 1), 'r', 1/fB
        fD = fD * 1000
        call fUnitIni2 sD, (11+x), substr(iso, 11+x, 1), 'r', 1/fD
        call fUnitIni2 sD, (11-x), substr(iso, 11-x, 1), 'r', fD
        end
    kilo = 'k'
    m.sB.u2v.k = m.sB.u2v.kilo
    m.sD.u2v.k = m.sD.u2v.kilo
    m.sT.0   =  16
    m.sT.min =  11
    call fUnitIni2 sT, 11, ' ', 'm', 100
    call fUnitIni2 sT, 12, 's', 'm',   1
    call fUnitIni2 sT, 13, 'm', 'm', 1/60
    call fUnitIni2 sT, 14, 'h', 'm', 1/3600
    call fUnitIni2 sT, 15, 'd', 'm', 1/3600/24
    call fUnitIni2 sT, 16, 'd', 'r', 1/3600/24
    return 0
endProcedure fUnitIni

fUnitIni2: procedure expose m.
parse arg sc, ix, u, ki, fa
    sb = sc'.'ix
    m.sb.kind = ki
    m.sb.fact = fa
    m.sb.unit = u
    m.sb.val     = 1 / fa
    if m.sb.fact > 1 then
        m.sb.fact = format(fa, , 0)
    else
        m.sb.val  = format(m.sb.val, , 0)
    m.sc.u2v.u = m.sb.val
    return
endProcedure fUnitIni2

fUnitsF1I0: procedure expose m.
parse arg sc, ix
    si = sc'.'ix
parse arg , , m.si.kind, aU, m.si.fact,
                , m.si.lim2, m.si.len,
                , m.si.mod, m.si.len2
    m.si.unit = aU
    m.sc.u2f.aU = ''
    if \ datatype(ix, 'n') then
        return si
    m.sc.u2f.aU = 1 / m.si.fact
    if symbol('m.sc.0') \== 'VAR' then do
        m.sc.0   = ix
        m.sc.min = ix
        end
    else do
        m.sc.0   = max(ix, m.sc.0)
        m.sc.min = min(ix, m.sc.min)
        end
    return si
endProcedure fUnitsF1I0

fUnit2I: procedure expose m.
parse arg b, v
    v = strip(v)
    if datatype(v, 'n') then
        return v
    u = right(v, 1)
    key = f_Scale'.' || b'.U2V.'u
    if symbol('m.key') == 'VAR' then
        return strip(left(v, length(v)-1)) * m.key
    if m.f_unit_ini \== 1 then
        return fUnit2I(b, v, fUnitIni())
    call err 'bad unit' u 'or base' b 'for' v
endProcedure fUnit2I
/* copy f end   ******************************************************/
/* copy err begin *** errorhandling, messages, help    ***************/
errIni: procedure expose m.
    if m.err_ini == 1 then
        return
    m.err_ini     = 1
    call utIni
    m.err_saySay  = 1
    m.err_sayOut  = 0
    m.err_handler  = ''
    m.err_handler.0 = 0
    m.err_cleanup = '\?'
    m.err_opt     = ''
    m.err_nest    = 0
    parse source m.err_os .
    m.tso_ddAll   = ''
    m.err_ispf    = 0
    m.err_screen  = 0
    if m.err_os \== 'LINUX' then do
        address tso 'profile MsgId'   /* brauchen wir in tsoAlloc| */
        if sysVar('sysISPF') = 'ACTIVE' then do
            m.err_ispf = 1
            address ispExec 'vget (zScreen zScreenD zScreenW) shared'
            m.err_screen = zScreen
            m.err_screenD = zScreenD
            m.err_screenW = zScreenW
            end
        end
    return
endProcedure errIni

/* configure err ----------------------------------------------------*/
errReset: procedure expose m.
    call errIni
parse arg m.err_opt, m.err_handler
    upper m.err_opt
    call errSetSayOut '-'
    m.err_handler.0 = 0
    if pos('I', m.err_opt) > 0 & m.err_ispf then
        address ispExec 'control errors return'
    return
endSubroutine errReset

/*--- set sayOut and sysSay -----------------------------------------*/
errSetSayOut: procedure expose m.
parse upper arg flags
    if flags \== '-' then
        m.err_opt = space(translate(m.err_opt, '  ' ,'OS')flags, 0)
    m.err_sayOut = pos('O', m.err_opt) > 0
    m.err_saySay = pos('S', m.err_opt) > 0 | \ m.err_sayOut
    return
endProcedure errSetSayOut

/*--- set rc for ispf: ------------------------------------------------
    if a cmd is run by ispStart, its RC is ignored,
         but ISPF passes the value of the shared variable zIspfRc
         back as return code
---------------------------------------------------------------------*/
errSetRc: procedure expose m.
parse arg zIspfRc
    if m.err_ispf then
        address ispExec vput 'zIspfRc' shared
    return zIspfRc
endProcedure errSetRc

/* push error handler -----------------------------------------------*/
errHandlerPush: procedure expose m.
parse arg aH
    ex = m.err_handler.0 + 1
    m.err_handler.0 = ex
    m.err_handler.ex = m.err_handler
    m.err_handler = aH
    return
endProcedure errHandlerPush
/* push error handler return Constant value -------------------------*/
errHandlerPushRet: procedure expose m.
parse arg rv
    call errHandlerPush "return '"rv"'"
    return
/* pop  error handler -----------------------------------------------*/
errHandlerPop: procedure expose m.
    if m.err_handler.0 < 1 then
        call err 'errHandlerPop but err_handler.0='m.err_handler.0
    ex = m.err_handler.0
    m.err_handler = m.err_handler.ex
    m.err_handler.0 = ex - 1
    return
endProcedure errHandlerPop
/* pop  error handler -----------------------------------------------*/
errHandlerCall:
    interpret m.err_handler
    m.err_handlerReturned = 0
    return ''
endProcedure errHandlerCall
/*--- error routine: abend with message -----------------------------*/
err:
    parse arg ggTxt, ggOpt
    if abbrev(ggOpt, '^') then
        return substr(ggOpt, 2)
    call errIni
    ggNx = m.err_nest + 1
    m.err_nest = ggNx
    m.err_nest.ggNx = ggTxt
    if ggNx \== 1 & ggNx \== 2 then do ggNx=ggNx by -1 to 1
        say '  error nesting.'ggNx '==>' m.err_nest.ggNx
        end
    drop err handler cleanup opt call return stackHistory
    if ggOpt == '' & m.err_handler <> '' then do
        m.err_handlerReturned = 1
        ggRet = errHandlerCall()
        ggDoR = m.err_handlerReturned
        m.err_handlerReturned = 1
        if ggDoR then do
            m.err_nest = m.err_nest - 1
            return ggRet
            end
        end
    call errSay ggTxt
    call errCleanup
    if ggOpt == '' then
        ggOpt = m.err_opt
    upper ggOpt
    if pos('T', ggOpt) > 0  then do
        trace ?r
        say 'trace ?r in err'
        end
    if pos('H', ggOpt) > 0 & m.err_cat == 'f' then
        x = show + stackHistory + by + bad + arithmetic + conversion
    call errSay ' }errorhandler exiting with exit(12)'
    m.err_nest = m.err_nest - 1
    exit errSetRc(12)
endSubroutine err

/*--- error routine: user message cleanup exit ----------------------*/
errAddCleanup: procedure expose m.
parse arg code
    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
    call errIni
    cx = pos('\?'code'\?', m.err_cleanup)
    if cx > 0 then
        m.err_cleanup = delStr(m.err_cleanup, cx, length(code)+2)
    return
endProcedure errRmCleanup

errCleanup: procedure expose m.
    call errIni
    m.err_saySay  = 1
    m.err_sayOut  = 0

    if m.err_cleanup <> '\?' then do
        do while m.err_cleanup <> '\?'
            cx = pos('\?', m.err_cleanup, 3)
            c1 = substr(m.err_cleanup, 3, cx-3)
            m.err_cleanup = substr(m.err_cleanup, cx)
            say 'errCleanup doing' c1
            interpret c1
            end
        say 'errCleanup end doing err_cleanup'
        end
    if m.tso_ddAll <> '' then
        call tsoFree m.tso_ddAll, 1
    return
endProcedure errCleanup

/*--- say an errorMessage msg with pref and linesplits --------------*/
errSay: procedure expose m.
parse arg msg
    return errSaySt(splitNl(err_l, 0, errMsg(msg)))

errSaySt: procedure expose m.
parse arg st
    if m.err_saysay | \ m.err_sayOut then
        call saySt st
    if m.err_sayOut & \ ( m.err_saySay & m.j.out == m.j.say) then
        call outSt st
    return st
endProcedure errSaySt

/*--- prefix an errormessage with pref,
          split it into lines at \n to stem m.err -------------------*/
errMsg: procedure expose m.
parse arg msg
    m.err_cat = 'f'
    do while substr(msg, 2, 1) == '}'
        parse var msg m.err_cat '}' msg
        end
   if m.err_cat == ' ' | m.err_cat == 'o' then
        return msg
   pTxt = ',fatal error,error,input error,scanErr,warning,onlyMsg,'
   px = pos(','m.err_cat, pTxt)
   if px < 1 then do
       px = 1
       m.err_cat = 'f'
       end
   pre = substr(pTxt, px+1, pos(',', pTxt, px+2)-px-1)
   if m.err_cat == 's' then
       return pre msg
   parse source . . s3 .              /* current rexx */
   return pre 'in' s3':' msg
endProcedure errMsg

/*--- fill stem st with lines of msg separated by \n ----------------*/
splitNl: procedure expose m.
parse arg st, sx, msg
    bx = 1
    sx = firstNS(sx, 1)
    do lx=sx+1 to sx+999
        ex = pos('\n', msg, bx)
        if ex < bx then
            leave
        m.st.lx = substr(msg, bx, ex-bx)
        bx = ex+2
        end
    m.st.lx = substr(msg, bx)
    m.st.0 = lx
    return st
endProcedure splitNl

/*--- out msg lines separated by \n ---------------------------------*/
outNL: procedure expose m.
parse arg msg
    return outSt(splitNl(err_outNl, 0, msg))

/*--- say msg lines separated by \n ---------------------------------*/
sayNl: procedure expose m.
parse arg msg
    return saySt(splitNl(err_outNl, 0, msg))

/*--- say (part of) the lines of a stem -----------------------------*/
saySt: procedure expose m.
parse arg st, fx, tx
    do lx=firstNS(fx, 1) to firstNS(tx, m.st.0)
        say strip(m.st.lx, 't')
        end
    return st
endProcedure saySt

/*--- say a trace message if m.trace is set -------------------------*/
debug: procedure expose m.
parse arg msg
    if m.debug == 1 then
        say 'debug:' msg
    return
endProcedure debug

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

/*--- assert that the passed rexx expression evaluates to true ------*/
assert:
    interpret 'if ('arg(1)') == 1 then return 1'
    interpret 'call err' quote('assert failed' arg(1)':' arg(2) '==>'),
                        arg(2)
endProcedure assert

/*--- abend with Message after displaying help ----------------------*/
errHelp: procedure expose m.
parse arg msg
    call errSay 'i}'msg
    call help 0
    call err 'i}'msg
endProcedure errHelp

/*--- display the first comment block of the source as help ---------*/
help: procedure expose m.
parse arg doClear
    if doClear \== 0 then
        address tso 'clear'
    parse source . . s3 .
    say right(' help for rexx' s3, 72, '*')
    do lx=1 while pos('/*', sourceLine(lx)) < 1
        if lx > 10 then
            return err('initial commentblock not found for help')
        end
    doInc = 1
    ho = m.err_helpOpt
    do lx=lx+1 to sourceline() while pos('*/', sourceline(lx)) = 0
        li = strip(sourceLine(lx), 't')
        cx = lastPos('{', li)
        if cx > 0 then do
            if length(ho) = 1 then
                doInc = cx = length(li) | pos(ho, li, cx+1) > 0
            li = left(li, cx-1)
            end
        if doInc then
            say li
        end
    say right(' end help for rexx' s3, 72, '*')
    return 4
endProcedure help

/* copy err end   ****************************************************/
/* copy ut begin  ****************************************************/
utIni: procedure expose m.
    if m.ut_ini == 1 then
        return
    m.ut_ini = 1
    m.ut_Num    = '0123456789'
                /* 012345678901234567890123456789 */
    m.ut_lc     = 'abcdefghijklmnopqrstuvwxyz'
    m.ut_uc     = translate(m.ut_lc)
    m.ut_uc25   = left(m.ut_uc, 25)
    m.ut_alfa   = m.ut_lc || m.ut_uc
    m.ut_alfNum = m.ut_alfa || m.ut_Num
    m.ut_alfDot = m.ut_alfNum || '.'
    m.ut_alfId  = m.ut_alfNum'_' /* avoid rexx allowed @ # $ ¬ . | ? */
    m.ut_alfIdN1 = m.ut_Num       /* not as first character */
    m.ut_rxId   = m.ut_AlfNum'@#$?' /* charset puff mit ¬*/
    m.ut_rxDot  = '.'m.ut_rxId
    m.ut_rxN1   = '.0123456789'
    m.ut_space  = '05'x' '         /* with space: space and Tab char */
    m.ut_alfPrint = m.ut_alfNum'+-*/=()¢!{}<> .:,;?|''"%&#@$£\_'
    m.ut_numUc = m.ut_num || m.ut_uc
    m.ut_base64 = m.ut_uc || m.ut_lc || m.ut_Num'+-'
    m.ut_alfLC  = m.ut_lc   /* backward compatibility */
    m.ut_alfUC  = m.ut_uc   /* backward compatibility */
    return
endProcedure utIni
/*--- 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

/*--- strip and returnn first argument not space --------------------*/
firstNS: procedure expose m.
    do ax=1 to arg()
        if arg(ax) <> '' then
            return strip(arg(ax))
        end
    return ''
endProcedure firstNS

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

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

/*--- left without truncation ---------------------------------------*/
lefPad: procedure expose m
parse arg s, len
    if length(strip(s, 't')) >= len then
        return strip(s, 't')
    return left(s, len)
endProcedure lefPad

/*--- right without truncation --------------------------------------*/
rigPad: procedure expose m
parse arg s, len
    if length(strip(s, 'l')) >= len then
        return strip(s, 'l')
    return right(s, len)
endProcedure rigPad

/*--- 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

/*--- translate to lowercase ----------------------------------------*/
ut2lc: procedure expose m.
parse arg s
    return translate(s, m.ut_lc, m.ut_uc)

/*--- verify an id --------------------------------------------------*/
verifId: procedure expose m.
    parse arg src, extra
    if pos(left(src, 1), m.ut_alfIdN1) > 0 then
        return 1
    else
        return verify(src, m.ut_alfId || extra, 'n')

/*--- 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
    do ax = 2 by 2 to arg()
        src = repAl2(src, src, arg(ax), arg(ax+1))
        end
    return src
endProcedure repAll

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

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
/*--- character to decimal '0140'x --> 256+64=320 -------------------*/
utc2d: procedure expose m.
parse arg ch
    cx = length(ch) // 3
    if cx = 0 then
        cx = 3
    res = c2d(left(ch, cx))
    do cx=cx+1 by 3 to length(ch)
        res = res * 16777216 + c2d(substr(ch, cx, 3))
        end
    return res
endProcedure utc2d

utInter: procedure expose m.
    interpret arg(1)
    return
endProcedure utInter
/* copy ut end *******************************************************/
}¢--- A540769.WK.REXX(WSHCOPY) cre=2016-10-26 mod=2016-10-26-09.51.11 A540769 ---
/* copy wshCopy begin ************************************************/
wshIni: procedure expose m.
    call compIni
    call sqlIni
    call fTabIni
    call csmIni
    return
endProcedure wshIni

/*--- call hooks and/or compile wsh
      finally execute any generated code ----------------------------*/
wshRun: procedure expose m.
parse arg m, spec, inp
    m.m.info = 'compile'
    r = wshHookComp(m, spec, inp)
    m.m.info = 'run'
    if r \== '' then
        call oRun r
    return
endProcedure wshRun

/*--- call hooks, handle $# clauses, compile wsh
      return generated code as ORunner or ''-------------------------*/
wshHookComp: procedure expose m.
parse arg m, spec, inp
    if m == '' then do
        if symbol('m.wsh_new') \== 'VAR' then
            m.wsh_new = 1
        else
            m.wsh_new = m.wsh_new + 1
        m = 'wsh_new'm.wsh_new
        end
    m.m.in   = inp
    m.m.comp = ''
    m.m.kind = '@'
    m.m.out  = ''
    m.m.wshEnd = 0
    run = ''
    rest = strip(spec)
    if abbrev(rest, '$#') then
        rest = strip(substr(rest, 3))
    workDone = 0
    do until m.m.comp \== '' | (workDone & rest = '')
        if pos(left(rest, 1), '<>') > 0 then do
            parse var rest s2 r2
            end
        else do
            workDone = 1
            parse var rest s2 '$#' r2
            end
        run = run wshHook(m, strip(s2), rest)
        rest = strip(r2)
        end
    if m.m.comp \== '' then do
        c = m.m.comp
        s = m.c.scan
        do while \ m.m.wshEnd
             if \ scanLit(s, '$#') then
                     leave
             call scanChar s
             sp2 = m.s.tok
             run = run wshHook(m, sp2, sp2)
             end
        call compEnd c, left(m.m.kind, \ m.m.wshEnd)
        end
    run = space(run, 1)
    if words(run) <= 1 then
        return run
    else
        return oRunner('call oRun' repAll(run, ' ', '; call oRun '))
endProcedure wshHookComp

/*--- compile wsh until eof or unknown syntax -----------------------*/
wshHook: procedure expose m.
parse arg m, spec, specAll
    parse var spec sp1 spR
    if pos(left(sp1, 1), '<>') > 0 then
        return wshHookRedir(m, sp1 spR)
    if verifId(sp1) > 0 | sp1 == '' then
        return wshCompile(m, specAll)
    if wordPos(sp1, 'out end version') <= 0 then do
        cd = "return wshHook_"sp1"(m, '"strip(spR)"')"
     /* say 'interpreting hook' cd */
        interpret cd
        end
    c = m.m.comp
    s = m.c.scan
    if c == '' then
        call err 'wshHook before compiler created:' spec
    else if sp1 == 'out' then do
        m.m.out = scanPos(s)
        m.m.wshEnd = 1
        end
    else if sp1 == 'end' then
        call scanNlUntil s, '$#out'
    else if m.s.tok == 'version' then
        call scanErr s, 'implement version'
    return ''
endProcedure wshHook

/*--- initialize compiler if necessary and compile one unit ---------*/
wshCompile: procedure expose m.
parse arg m, spec
    spec = strip(spec, 'l')
    if m.m.comp == '' then
        call wshIni
    if pos(left(spec, 1), m.comp_chKind'*') > 0 then
        parse var spec m.m.kind 2 spec
    if m.m.comp == '' then do
        c = comp(m.m.in)
        m.m.comp = c
        call compBegin c, spec
        end
    else do
        c = m.m.comp
        call scanBack m.c.scan, spec
        end
    return compile(c, m.m.kind)
endProcedure wshCompile

/*--- redirection hook ----------------------------------------------*/
wshHookRedir: procedure expose m.
parse upper arg m,  op 2 dsn
    call pipeIni
    f = ''
    if op == '<' then
        call pipe '+f', , file(dsn)
    else if op \== '>' then
        call err 'bad op' op 'in wshHookRedir' op || dsn
    else do
        if pos('>', dsn) > 0 then
            parse var dsn f '>' dsn
        else if verify(dsn, '.~/', 'm') > 0 then
            nop
        else if abbrev(dsn, 'E') | abbrev(dsn, 'VV') ,
                                  | abbrev(dsn, 'VF') then
            parse var dsn f 2 dsn
        else
            f = 'E'
        dsn = strip(dsn)
        if dsn \== '' & verify(dsn, '.~/:', 'm') == 0 then
            dsn = '::'dsn
        if f <> '' then
             call pipe '+F', fEdit(dsn, f)
        else
             call pipe '+F', file(dsn)
        end
    m.m.pipeCnt = m.m.pipeCnt + 1
    return ''
endProcedure wshHookRedir
/* copy wshCopy end   ************************************************/
}¢--- A540769.WK.REXX(WSHLOB) cre=2016-07-25 mod=2016-07-25-11.35.59 A540769 ---
with u(s, l) as
(
  select cast('text eins            ....|' as clob(2 g) ), 0
    from sysibm.sysDummyE
  union all select '<'|| l || ' ' || s
                 ||'?'|| l || '?' || s
                      || l || '>', l+1
    from u where l < 16
)
select l, length(s), cast(s as clob(66666)) s50   from u
;
select varchar(substr(s, 1, 2000), 2000) l from u
;
select l, length(s) from u
}¢--- A540769.WK.REXX(WSHOL2) cre=2014-04-15 mod=2014-04-15-14.51.10 A540769 ---
/* rexx ****************************************************************
  wsh: walter's rexx shell                                   version 2.2
  interfaces:                                                    9. 2.14
      edit macro: for adhoc evaluation or programming
              either block selection: q or qq and b or a
              oder mit Directives ($#...) im Text
      wsh i:  tso interpreter
      batch:  input in dd wsh
      docu:   http://chw20025641/host/db2wiki/pmwiki.php?n=Main.Wsh
      syntax: http://chw20025641/host/db2wiki/pmwiki.php?n=Main.WshSyn
--- history ------------------------------------------------------------
19.03.14 ii = installation Info
*********/ /*** end of help ********************************************
 9.01.14 walter: redesign formatting (fmt eliminiert), csm.div.p0.exec
 3.12.13 walter: db2 interface radikal geputzt
 3.10.13 walter: uCount fuer TSO <-> unitCount fuer Csm
23. 9.13 walter: ws2 syntax
 6. 2.13 w.keller csmCopy aendert mgmtClas für neue lokale Files
23. 1.13 w.keller sqlErrHandler sowie sqlRx und sql
11. 6.12 w.keller sqlUpdComLoop
23. 5.12 w.keller fix sqlStmt: drop accepts -204
31. 3.12 w.keller sql Query interface incl. sql über CSM
10. 2.12 w.keller div catTb* und eLong
 2. 6.11 w.keller sql error with current location and dsnTiar
 2. 5.11 w.keller sqlStmt etc..
16. 3.11 w.keller basic new r '' ==> r m.class.classO
28. 2.11 w.keller compFile by exprBlock, fix $@do forever, |
 7. 2.11 w.keller cleanup block / with sqlPush....
 2. 2.11 w.keller simplified syntax, ast for blocks, ? for help
19. 6.10 w.keller open/close stacked mit jUsers, framed eliminiert
19. 5.10 w.keller class wird direkt erzeugt ohne temporary
18. 4.10 w.keller scanUtilInto ==> testCase noch einbauen
17. 4.10 w.keller oMutate in fileTsoList eingebaut
14. 1.10 w.keller batch interface no longer dies on openIfNotYet
                  CSM.RZ1.P0.EXEC korrigiert
***********************************************************************/

/*--- main code wsh --------------------------------------------------*/
    call errReset 'hI'
    call pipeIni  /* without tstClass2 gives different result */
    parse arg spec
    isEdit = 0
    if spec = '' & m.err.ispf then do /* z/OS edit macro */
        isEdit = adrEdit('macro (spec) NOPROCESS', '*') == 0
        if isEdit then do
            call adrEdit '(d) = dataset'
            call adrEdit '(m) = member'
            m.editDsn = dsnSetMbr(d, m)
            if spec = '' & m.editDsn = 'A540769.WK.REXX(WS3)' then
                spec = 't'
            end
        end
    if spec = '?' then
        return help()
    call utIni
    f1 = spec
    rest = ''
    call wshIni
    inp = ''
    out = ''
    if m.err.os == 'TSO' then do
        if isEdit then do
            parse value wshEditBegin(spec) with inp out
            end
        else if sysvar('sysEnv') = 'FORE' then do
            end
        else do
            inp = file('dd(wsh)')
            useOut = listDsi('OUT FILE')
            if \ (useOut = 16 & sysReason = 2) then
                out = file('dd(out)')
            end
        end
    else if m.err.os == 'LINUX' then do
        inp = file('&in')
        out = file('&out')
        end
    else
        call err 'implement wsh for os' m.err.os
    m.wshInfo = 'compile'
    call compRun spec, inp, out, wshInfo
    if isEdit then
        call wshEditEnd
exit 0

wshIni: procedure expose m.
    call compIni
    call sqlIni
    call scanWinIni
    return
endProcedure wshIni

tstRts: procedure expose m.
    call wshIni
    call sqlConnect dbaf
    call sqlQuery 3, "select * from sysibm.sysTableSpaceSTats" ,
                    "where dbName = 'MF01A1A' and name = 'A150A'",
                    "order by partition  asc"
    do while sqlFetch(3, rr)
        say f('@.DBNAME%-8C.@NAME%-8C @PARTITION %4C' ,rr)
        end
    call sqlDisconnect
endProcedure tstRts

/*--- test hook ----------------------------------------------------*/
wshHook_T: procedure expose m.
parse arg cmp
    rest = strip(scanLook(m.cmp.scan))
    call jClose m.cmp.scan
    if rest = '' then do /* default */
        call tstfTst
        return 0
        call sqlConnect DBAF
        call catColCom 'cmnBatch', 'DSN_PGROUP_TABLE',
                     , 'cmnBatch', 'DSN_PGROUP_TABLE_new'
        call sqlDisConnect DBAF
        return 0
        end
    c = ''
    do wx=1 to words(rest)
        c = c 'call tst'word(rest, wx)';'
        end
    if wx > 2 then
        c = c 'call tstTotal;'
    say 'wsh interpreting' c
    interpret c
    exit 0
endProcedure wshTst

/*--- i hook: interpret user input: rexx, expr, data or shell -------*/
wshHook_I: procedure expose m.
parse arg cmp
    inp = strip(scanLook(m.cmp.scan))
    call jClose m.cmp.scan
    mode = '*'
    do forever
        if pos(left(inp, 1), '/;:*@.-=') > 0 then
            parse var inp mode 2 inp
        if mode == '/' then
            exit 0
        mode = translate(mode, ';', ':')
        if inp <> '' then do
            say 'as' mode 'interpreting' inp
            if mode = ';' then
                interpret inp
            else if mode = '*' then
                interpret 'say' inp
            else do
                call errReset 'h', 'say "******* intercepting error";',
                    'call errSay ggTxt; return "";'
                call oRun compile(comp(jBuf(inp)), mode)
                call errReset 'h'
                end
            end
        say 'enter' mode 'expression, / for end, ; or * for Rexx' ,
                                                 '@ . - =  for wsh'
        parse pull inp
        end
endProcedure wshInter

/*--- sql hook -----------------------------------------------------*/
wshHook_S: procedure expose m.
parse arg cmp
    s = m.cmp.scan
    ki = '='
    call scanVerify s, m.m.chSpa
    if scanVerify(s, m.comp.chKind) then
        ki = left(m.s.tok, 1)
    call scanChar s
    rest = m.s.tok
    call scanNl s
    dbSy = word(rest, 1)
    if abbrev(dbSy, '-') | \ (length(dbSy) = 4 ,
                   | (length(dbsy) = 8 & substr(dbSy,4,1) == '/')) then
        dbSy = ''
    else
        rest = subWord(rest, 2)
    call sqlConnect dbSy
    r = oRunner(compAst2Rx(cmp, ';', compUnit(cmp, ki, '$#')))
    call sqlStmtsOpt r, rest
    call sqlDisConnect
    return ''
endProcedure wshHook_s


wshEditBegin: procedure expose m.
parse arg spec
    dst = ''
    li = ''
    m.wsh.editHdr = 0
    pc = adrEdit("process dest range Q", 0 4 8 12 16)
    if pc = 16 then
        call err 'bad range must be q'
    if pc = 0 | pc = 8 then do
        call adrEdit "(rFi) = lineNum .zFrange"
        call adrEdit "(rLa) = lineNum .zLrange"
    /*  say 'range' rFi '-' rLa */
        end
    else do
        rFi = ''
    /*  say 'no range' */
        end
    if pc = 0 | pc = 4 then do
        call adrEdit "(dst) = lineNum .zDest"
    /*  say 'dest' dst */
        dst = dst + 1
        end
    else do
    /*  say 'no dest' */
        if adrEdit("find first '$#out' 1", 4) = 0 then do
            call adrEdit "(dst) = cursor"
    /*      say '$#out' dst   */
            call adrEdit "(li) = line" dst
            m.wsh.editHdr = 1
            end
        end
    m.wsh.editDst = dst
    m.wsh.editOut = ''
    if dst \== '' then do
        m.wsh.editOut = jOpen(jBufTxt(), '>')
        if m.wsh.editHdr then
            call jWrite m.wsh.editOut, left(li, 50) date('s') time()
        end
    if rFi == '' then do
        call adrEdit "(zLa) = lineNum .zl"
        if adrEdit("find first '$#' 1", 4) = 0 then do
            call adrEdit "(rFi) = cursor"
            call adrEdit "(li) = line" rFi
            if abbrev(li, '$#out') | abbrev(li, '$#end') then
                rFi = 1
            if rFi < dst & dst \== '' then
                rLa = dst-1
            else
                rLa = zLa
            end
        else do
            rFi = 1
            rLa = zLa
            end
        end
    /*  say 'range' c1 'rc' pc':' rFi '-' rLa   'after' dst */

    m.wsh.editIn = jOpen(jBuf(), m.j.cWri)
    do lx=rFi to rLa
        call adrEdit "(li) = line" lx
        call jWrite m.wsh.editIn, li
        end
    call errReset 'h',
             , 'return wshEditErrH(ggTxt, ' rFi',' rLa')'
    return jClose(m.wsh.editIn) m.wsh.editOut
endProcedure wshEditBegin

wshEditEnd: procedure expose m.
    call errReset 'h'
    if m.wsh.editOut == '' then
        return 0
    call jClose(m.wsh.editOut)
    lab = wshEditInsLinSt(m.wsh.editDst, 0, , m.wsh.editOut'.BUF')
    call wshEditLocate max(1, m.wsh.editDst-7)
    return 1
endProcedure wshEditEnd

wshEditLocate: procedure
parse arg ln
    call adrEdit '(la) = linenum .zl'
    call adrEdit 'locate '  max(1, min(ln, la - 37))
    return
endProcedure wshEditLocate

wshEditErrH: procedure expose m.
parse arg ggTxt, rFi, rLa
    call errCleanup
    call errReset 'h'
    call errMsg ' }'ggTxt
    call mMove err, 1, 2
    isScan = 0
    if wordPos("pos", m.err.4) > 0 ,
        & pos(" in line ", m.err.4) > 0 then do
        parse var m.err.4 "pos " pos .     " in line " lin":"
        if pos = '' then do
            parse var m.err.4 " line " lin":"
            pos = 0
            end
        isScan = lin \== ''
        end
    m.err.1 = '***' m.wshInfo 'error ***'
    if m.wshInfo=='compile' & isScan then do
        do sx=1 to m.err.0
            call out m.err.sx
            end
        lab = rFi + lin
        if pos \= '' then
            lab = wshEditInsLin(lab, 'msgline', right('*',pos))
        lab = wshEditInsLinSt((rFi+lin),0, 'msgline', err)
        call wshEditLocate rFi+lin-25
        end
    else do
        if m.wsh.editOut \== '' then do
            do sx=1 to m.err.0
                call jWrite m.wsh.editOut, m.err.sx
                end
            lab = wshEditInsLinSt(m.wsh.editDst, 0, ,
                , m.wsh.editOut'.BUF')
            call wshEditInsLinSt m.wsh.editDst, m.wsh.editHdr,
                , msgline, err
            call wshEditLocate max(1, m.wsh.editDst-7)
            end
        else do
            do sx=1 to m.err.0
                say m.err.sx
                end
            end
        end
    call errCleanup
    exit
endSubroutine wshEditErrH

wshEditInsLinCmd: procedure
parse arg wh
    if dataType(wh, 'n')  then do
        if adrEdit("label" wh "= .a", 0 8 12) \= 12 then
            return 'line_before .a ='
        else
            return 'line_after .zl ='
        end
    else if left(wh, 1) == '.' then
        return 'line_before' wh '='
    else
        return wh
endProcedure wshEditInsLinCmd

wshEditInsLin: procedure
parse arg wh, type
    cmd = wshEditInsLinCmd(wh)
    do ax=3 to arg()
        li = strip(arg(ax), 't')
        if li == '' then
            iterate
        if translate(type) = 'MSGLINE' then do while length(li) > 72
            sx = lastPos(' ', li, 72)
            if sx < 10 then
                sx = 72
            one = left(li, sx)
            li = '    'strip(substr(li, sx))
            call adrEdit cmd type "(one)"
            end
        call adrEdit cmd type "(li)", 0 4
        end
    return cmd
endProcedure wshEditInsLin

wshEditInsLinSt: procedure expose m.
parse arg wh, pl, type, st
    if wh == '' then do
        do ox=1 to m.st.0
            say m.st.ox
            end
        return ''
        end
    wh = wh + pl
    cmd = wshEditInsLinCmd(wh)
    do ax=1 to m.st.0
        call wshEditInsLin cmd, type, m.st.ax
        end
    return cmd
endProcedure wshEditInsLinSt


/*** end wsh, begin all copies ****************************************/
/* copy tstAll begin  *************************************************/
/*----------- neu, noch versorgen |||||-------------------------------*/
tstWiki:
    call mapReset docs, 'k'
    call addFiles docs, 'n', '/media/wkData/literature/notes'
    call addFiles docs, 'd', '/media/wkData/literature/docs'

    in = jOpen(file('wiki.old'), '<')
    out = jOpen(file('wiki.new'), '>')
    abc = '(:abc: %l%'
    do cx=1 to length(m.ut_alfLC)
        c1 = substr(m.ut_alfLC, cx, 1)
        abc = abc '¢¢#'c1 '|' c1'!!'
        end
    call jWrite out, abc ':)'
    inTxt = 0
    li = m.i
    do lx=1 while jRead(in, i)
        if 0 then
            say length(m.i) m.i
        if m.i = '' then
            iterate
        li = m.i
        do forever
            bx = pos('¢=', li)
            if bx < 1 then
                leave
            ex = pos('=!', li)
            if ex <= bx then
                call err '=! before ¢= in' lx li
            li = left(li, bx-1)substr(li,bx+2, ex-bx-2)substr(li,ex+2)
            end
        li = strip(li)
        if abbrev(li, '|') then do
            w = word(substr(li, 2), 1)
            call jWrite out, '¢¢#'w'!! {$:abc}'
            call jWrite out, '|||' substr(li, 2)
            inTxt=1
            iterate
            end
        if \ inTxt then do
            call jWrite out, li
            iterate
            end
        if \ (abbrev(li, '->') | abbrev(li, '#') ,
                | abbrev(li, '¢')) then do
            call jWrite out, '-<' li
            iterate
            end
        cx = 1
        if substr(li, cx, 2) == '->' then
            cx = verify(li, ' ', 'n', cx+2)
        hasCross = substr(li, cx, 1) == '#'
        if hasCross then
            cx = verify(li, ' ', 'n', cx+1)
        ex = verify(li, '!:\, ', 'm', cx)
        ex = ex - (substr(li, ex, 1) \== '!')
        hasBr = substr(li, cx, 1) == '¢'
        if \ hasBr then
            w = substr(li, cx, ex+1-cx)
        else if substr(li, ex, 1) == '!' then
            w = substr(li, cx+1, ex-1-cx)
        else
            call err 'br not closed' substr(w, cx+1, ex-1-cx) 'in' lx li
        hasPdf = right(w, 4) == '.pdf'
        if hasPdf then
            w = left(w, length(w)-4)
        if verify(w, '#?', 'm') > 0 then do
            w = translate(w, '__', '#?')
            say '*** changing to' w 'in' lx li
            end
        o = '-< {def+'w'}'
        o = '-< ¢¢'w'!!'
        k = translate(w)
        if k.k == 1 then
            say '*** doppelter key' k 'in:' lx left(li,80)
        k.k = 1
        dT = ''
        if mapHasKey(docs, k) then do
            parse value mapGet(docs, k) with dT dC dN
            call mapPut docs, k, dT (dC+1) dN
            do tx=1 to length(dT)
                t1 = substr(dT, tx, 1)
                o = o '¢¢Lit'translate(t1)':'word(dN, tx) '|' t1 '!!'
                end
            end
        qSeq = 'nd'
        qq = left(qSeq, 1)
        qx = 0
        do forever
            qx = pos('@'qq, li, qx+1)
            if qx < 1 then do
                qq = substr(qSeq, 1 + pos(qq, qSeq), 1)
                qx=0
                if qq = '' then
                    leave
                else
                    iterate
                end
            if pos(qq, dT) < 1 then do
                say '*** @'qq 'document not found:' lx li
                iterate
                end
            do qb = qx-1 by -1 while substr(li, qb, 1) == ' '
                end
            do qe = qx+2 by 1 while substr(li, qe, 1) == ' '
                end
            if substr(li, qb, 1) == '.' & substr(li, qe, 1) == '.' then
                li = left(li, qb)substr(li, qe+1)
            else
                li = left(li, qb) substr(li, qe)
            end
        o = o':' strip(substr(li, ex+1+(substr(li,ex+1,1)==':')))
        if 0 then say left(li, 30) '==>' left(o, 30)
        call jWrite out, o
        end
    dk = mapKeys(docs)
    do dx=1 to m.dk.0
        parse value mapGet(docs, m.dk.dx) with dT dC dN
        if dC < 1 then
            say '*** document not used:' dT dC dn
        end
    call jClose in
    call jClose out
    return
endProcedure tstWiki

addFiles: procedure expose m.
parse arg m, ty, file
    fl = jOpen(fileList(file(file)), '<')
    do while jRead(fl, fi1)
        nm = substr(m.fi1, lastPos('/', m.fi1)+1)
        k = translate(left(nm, pos('.', nm)-1))
        if \ mapHasKey(m, k) then do
            call mapAdd m, k, ty 0 nm
            end
        else do
            parse value mapGet(m, k) with dT dC dN
            call mapPut m, k, dT || ty 0 dN nm
            end
        end
    call jClose fl
    return
endProcedure addFiles

tstAll: procedure expose m.
    say 'tstAll ws2 25.2.13...............'
    call tstBase
    call tstComp
    call tstDiv
    if m.err.os = 'TSO' then do
        call tstZos
        call tstTut0
        end
    return 0
endProcedure tstAll

/* copx tstZos begin **************************************************/
tstZOs:
    call tstTime
    call tstII
    call sqlIni
    call tstSqlRx
    call tstSql
    call tstSqlCsm
    call tstSqlC
    call tstSqlCsv
    call tstSqlRxUpd
    call tstSqlUpd
    call tstSqlUpdPre
    call tstSqlE
    call tstSqlB
    call tstSqlO1
    call tstSqlO2
    call tstSqlStmt
    call tstSqlStmts
    call tstSqlUpdComLoop
    call tstSqls1
    call tstSqlO
    call tstSqlFTab
    call tstTotal
    return
endProcedure tstZOs

tstWshBatch:
    call adrTso 'alloc dd(WSH) shr dsn(WK.TEXT(WSHBATCH))'
    call wshBatch
    return adrTso('free dd(WSH)')

tstLmdTiming:
parse arg lev
    say timing() lev
    call lmdBegin abc, lev
    c = 0
    do while lmdNext(abc, st.)
        c = c + st.0
        end
    call lmdEnd   abc
    say timing() lev 'with group - without reading' c
    call adrIsp 'lmdinit listid(lmdId) level('lev')'
    do c=0 while adrIsp('lmdlist listid(&lmdId) dataset(abc)', 8) = 0
        end
    call adrIsp 'lmdfree listid(&lmdId)'
    say timing() lev 'with list' c
    return
endProcedure lmdTiming

tstCsi: procedure expose m.
    if 0 then do
        call lmd 'A540769.*K'
        call tstCsiCla 'A540769.WK.REXX'
        call tstCsiCla 'A540769.AAA.DATASETS'
        call tstCsiCla 'A540769.RRR.DATASETS'
        end
    if 0 then do
        call tstCsiOpNx 'A540769.WK.*E*'
        call tstCsiOpNx 'A540769.AAA.DATASETS'
        call tstCsiOpNx 'A540769.RRR.DATASETS'
        end
    if 1 then do
        call tstCsiNxCl 'A540769.WK.**'
        call tstCsiNxCl 'DBTF.M*.**'
        call tstCsiNxCl 'DBTF.BE*.**'
        end
    return
tstCsi: procedure expose m.
 /* call lmd 'A540769.*K' */
    call tstCsi1   'A540769.WK.REXX'
    call tstCsi1   'A540769.AAA.DATASETS'
    call tstCsi1   'A540769.RRR.DATASETS'
    return

tstCsiCla:
parse arg ds
    say ds '-->' csiCla(ds)
    return

tstCsiOpNx: procedure expose m.
parse arg ds
    m = 'NUE123'
    s = 'res89'
    flds = 'devtyp volser mgmtclas comudsiz NOBYTTRK UDATASIZ HARBA'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    do while csiNext(m, s)
        say m.s     'dev' c2x(m.s.devTyp) ,
                    'vol' m.s.volSer 'cla' m.s.mgmtclas,
                    'comuDsiz' m.s.comuDsiz 'noBytTrk' m.s.noBytTrk,
                    'udatasiz' c2x(m.s.udatasiz) ,
                    'harba' c2x(m.s.harba)
        end
    return

tstCsiNxCl: procedure expose m.
parse arg ds
    m = 'ABC123'
    s = 'efg89'
    flds = 'devtyp volser mgmtclas'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    say timing() 'begin'
    do i=1 while csiNext(m, s)
        nn = csiArcTape(m.s.volser, m.s.mgmtClas, m.s.devtyp, m.s)
/*      oo = csiCla(strip(m.s))
        if oo <> nn then
            say nn '<>' oo m.s
 */     if i // 1000 = 0 then
            say timing() i nn m.s
        end
    say timing() (i-1) nn m.s
    return
endProcedure tstCsiNxCl

listCatClass: procedure expose m.
parse upper arg dsn
    rt = adrTso("listcat volume entry('"dsn"')", 4)
    /* say 'listct rc =' rt 'lines' m.tso_trap.0 */
    cl = ''
    vo = ''
    if word(m.tso_trap.1, 3) \== dsn then
        call out 'err ??? for dsn' dsn 'bad first line' m.tso_trap.1
    else if pos('NOT FOUND', m.tso_trap.1) > 0 then
        return 'notFound'
    else if word(m.tso_trap.1, 1)\== 'NONVSAM' then
        call out 'err ??? for dsn' dsn 'bad first line' m.tso_trap.1
    do tx=2 to m.tso_trap.0 while vo = '' ,
              & left(m.tso_trap.tx, 1) = ' '
     /* say m.tso_trap.tx */
        p = pos('MANAGEMENTCLASS-', m.tso_trap.tx)
        if p > 0 then
            vo = strip(word(substr(m.tso_trap.tx, p+16), 1), 'l', '-')
        p = pos('VOLSER--', m.tso_trap.tx)
        if p > 0 then
            vo = strip(word(substr(m.tso_trap.tx, p+6), 1), 'l', '-')
        p = pos('DEVCLASS--', m.tso_trap.tx)
            dt = strip(word(substr(m.tso_trap.tx, p+8), 1), 'l', '-')
        end
   /*  say 'lc' cl 'vo' vo 'dt' dt 'dsn' dsn */
    if vo = '' then
        call out '??? err no volume for dsn' dsn
    else if vo = 'ARCIVE' then
        res =  'arcive'
    else if cl = '' then
        res = 'tape'
    else
        res = cl
    if   abbrev(res, 'ar') \= abbrev(dt, "X'0") ,
       | abbrev(res, 'ta') \= abbrev(dt, "X'7") ,
       | (left(res, 1) >= 'A') \= abbrev(dt, "X'3") then
       call out 'err ??? mismatch cl' cl 'vo' vo 'dt' dt 'dsn' dsn
    return res
endProcedure listCatClass

/* copx tstZos end   **************************************************/
/* copx tstDiv begin **************************************************/
tstDiv:
    call tstSort
    call tstMatch
    call tstTotal
    return
endProcedure tstDiv


tstSort: procedure expose m.
    call tstSortComp
    call tstSortComp '<<='
    call tstSortComp 'm.aLe <<= m.aRi'
    call tstSortComp 'if 1 then cmp = m.aLe <<= m.aRi; else call err sd'
    return
endProcedure tstSort

tstSortComp: procedure expose m.
parse arg cmp
/*
$=/tstSort/
    ### start tst tstSort #############################################
    sort 29  c ACHT DREI DREIZEHN EINS ELF FUENF M.I.25 M.I.26 M.I.27 M+
    ..I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI ZWOELF 0 1 1 1 2 2+
    . 3 3 4 4
    sort 22  c ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27 M.I.29 NEUN VIERZ+
    EHN ZEHN ZWOELF 0 1 1 1 2 2 3 3 4 4
    sort 15  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1 1 2 2 3 3 4 4
    sort 8  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1
    sort 1  M.I.29
$/tstSort/ */
/*
$=/tstSortAscii/
    ### start tst tstSortAscii ########################################
    sort 29  0 1 1 1 2 2 3 3 4 4 ACHT DREI DREIZEHN EINS ELF FUENF M.I.+
    25 M.I.26 M.I.27 M.I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI Z+
    WOELF c
    sort 22  0 1 1 1 2 2 3 3 4 4 ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27+
    . M.I.29 NEUN VIERZEHN ZEHN ZWOELF c
    sort 15  0 1 1 1 2 2 3 3 4 4 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 8  0 1 1 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 1  M.I.29
$/tstSortAscii/ */
    say '### start with comparator' cmp '###'
    if m.err.os == 'LINUX' then
        call tst t, "tstSortAscii"
    else
        call tst t, "tstSort"
    call mAdd mCut(i, 0), eins, zwei, drei, vier, fuenf, sechs,
        ,sieben, acht, neun, zehn, elf, zwoelf, dreizehn, vierzehn
    call mAdd i, 1, 2, 3, 4, 4, 3, 2, 1, 0, 1,
        , 'M.I.25', 'M.I.26', 'M.I.27', 'c', 'M.I.29'

    do yy = m.i.0 by -1 to 1
        do x = 0 to yy
            m.i.0 = x
            call sort i, o, cmp
            m = ''
            la = ''
            if x <> m.o.0 then
                call err 'size mismatch' x '<>' m.o.0
            do y=1 to m.o.0
                m = m m.o.y
                if \ (la << m.o.y) then
                    call err 'sort mismatch' yy x y '\' la '<<' m.o.y
                end
            end
        if yy // 7 = 1 then
           call tstOut t, 'sort' yy m
        do x = 2 to yy
            x1 = x-1
            m.i.x1 = m.i.x
            end
        end
    call tstEnd t
    return
endProcedure tstSort
tstMatch: procedure expose m.
/*
$=/tstMatch/
    ### start tst tstMatch ############################################
    match(eins, e?n*) 1 1 2,i,s trans(E?N*) EiNs
    match(eins, eins) 1 1 0 trans(EINS) EINS
    match(e1nss, e?n*) 1 1 2,1,ss trans(?*) 1ss
    match(eiinss, e?n*) 0 0 -9
    match(einss, e?n *) 0 0 -9
    match(ein s, e?n *) 1 1 2,i,s trans(E?N *) EiN s
    match(ein abss  , ?i*b*) 1 1 3,e,n a,ss   trans(?I*B*) eIn aBss  .
    match(ein abss wie gehtsssxdirx und auch , ) 0 0 -9
    match(ies000, *000) 1 1 1,ies trans(*000) ies000
    match(xx0x0000, *000) 1 1 1,xx0x0 trans(*000) xx0x0000
    match(000x00000xx, 000*) 1 1 1,x00000xx trans(000*) 000x00000xx
    match(000xx, *0*) 1 1 2,00,xx trans(ab*cd*ef) ab00cdxxef
$/tstMatch/ */
    call tst t, "tstMatch"
    call tstOut t, matchTest1('eins', 'e?n*'                        )
    call tstOut t, matchTest1('eins', 'eins'                        )
    call tstOut t, matchTest1('e1nss', 'e?n*', '?*'                 )
    call tstOut t, matchTest1('eiinss', 'e?n*'                      )
    call tstOut t, matchTest1('einss', 'e?n *'                      )
    call tstOut t, matchTest1('ein s', 'e?n *'                      )
    call tstOut t, matchTest1('ein abss  ', '?i*b*'                 )
    call tstOut t, matchTest1('ein abss wie gehtsssxdirx und auch ' )
    call tstOut t, matchTest1('ies000', '*000'                      )
    call tstOut t, matchTest1('xx0x0000', '*000'                    )
    call tstOut t, matchTest1('000x00000xx', '000*'                 )
    call tstOut t, matchTest1('000xx', '*0*', 'ab*cd*ef'            )
    call tstEnd t
return

matchTest1:
parse arg w, m, m2
    r = 'match('w',' m')' match(w, m) match(w, m, vv) m.vv.0
    do x=1 to m.vv.0
        r = r','m.vv.x
        end
    if m2 = '' then
        m2 = translate(m)
    if m.vv.0 >= 0 then
        r = r 'trans('m2')' matchTrans(m2, vv)
    return r
endProcedure matchTest1

tstIntRdr: procedure expose m.
    i.1 = "//A540769J JOB (CP00,KE50),'DB2 REO',"                       00010000
    i.2 = "//         MSGCLASS=T,TIME=1440,"                            00020000
    i.3 = "//         NOTIFY=&SYSUID,REGION=0M,SCHENV=DB2"              00030003
    i.4 = "//*MAIN CLASS=LOG"                                           00040003
    i.5 = "//S1       EXEC PGM=IEFBR14"                                 00040003
    call writeDsn 'RR2/intRdr', i., 5, 1
    return
endProcedure tstIntRdr

tstII: procedure expose m.
/*
$=/tstII/
    ### start tst tstII ###############################################
    iiRz2C(RZ2)       2
    *** err: no rz=R?Y in ii II_RZ2C
    iiRz2C(R?Y)       0
    iiRz2C(RZY)       Y
    iiDbSys2C(de0G)   E
    *** err: no dbSys=D??? in ii II_DB2C
    iiDbSys2C(d???)   0
    iiDbSys2C(DBOF)   F
    iiSys2RZ(S27)     RZ2
    iiMbr2DbSys(DBP5) DVBP
    ii_rz             RZ1 RZ2 RR2 RZ4 RZX RZY RZZ
    ii_dbSys.rzx      DE0G DEVG DPXG DX0G
$/tstII/
*/
    call tst t, 'tstII'
    call tstOut t, 'iiRz2C(RZ2)      '  iiRz2C(RZ2)
    call tstOut t, 'iiRz2C(R?Y)      '  iiRz2C(R?Y)
    call tstOut t, 'iiRz2C(RZY)      '  iiRz2C(RZY)
    call tstOut t, 'iiDbSys2C(de0G)  '  iiDbSys2C('de0G')
    call tstOut t, 'iiDbSys2C(d???)  '  iiDbSys2C('d???')
    call tstOut t, 'iiDbSys2C(DBOF)  '  iiDbSys2C('DBOF')
    call tstOut t, 'iiSys2RZ(S27)    '  iiSys2RZ(S27)
    call tstOut t, 'iiMbr2DbSys(DBP5)'  iiMbr2DbSys(DBP5)
    call tstOut t, 'ii_rz            '  m.ii_rz
    call tstOut t, 'ii_dbSys.rzx     '  m.ii_dbSys.rzx
    call tstEnd t
    return
endProcedure tstII

tstTime: procedure
/*         Winterzeit dez 2011
$=/tstTime/
    ### start tst tstTime #############################################
    Lrsn2Lzt(C5E963363741) 2010-05-01-12.34.55.789008
    Achtung: output ist abhaengig von Winter/Sommerzeit und LeapSecs
    timeZone 7200.00000 leapSecs 25.0000000
    2jul(2011-03-31-14.35.01.234567)  11090
    Lrsn2Gmt(C5E963363741) 2010-05-01-10.35.20.789008
    Lrsn2Lzt(C5E963363741) 2010-05-01-12.34.55.789008
    gmt2Lrsn(2011-03-31-14.35.01.234567) C78D87B86E38
    lzt2Lrsn(2011-03-31-14.35.01.234567) C78D6CFDD13C
    Lrsn2Gmt(gmt2Lrsn(2011-03-31-14.35.01.234567) 2011-03-31-14.35.01.2+
    34560
    gmt2Lrsn(Lrsn2Gmt(C5E963363741) C5E963363741
    Lrsn2LZt(LZt2Lrsn(2011-03-31-14.35.01.234567) 2011-03-31-14.35.01.2+
    34560
    LZt2Stc(Lrsn2LZt(C5E963363741)( C5E963363741
$/tstTime/
*/
    call jIni
    call tst t, 'tstTime'
    t1 = '2011-03-31-14.35.01.234567'
    s1 = 'C5E963363741'
    call out 'Lrsn2Lzt('s1')' timeLrsn2Lzt(s1)
    call out ,
     'Achtung: output ist abhaengig von Winter/Sommerzeit und LeapSecs'
    call out 'timeZone' m.timeZone * m.timeStckUnit ,
             'leapSecs' m.timeLeap * m.timeStckUnit
    call timeReadCvt 1
    call out '2jul('t1') ' time2jul(t1)
    call out 'Lrsn2Gmt('s1')' timeLrsn2Gmt(s1)
    call out 'Lrsn2Lzt('s1')' timeLrsn2Lzt(s1)
    call out 'gmt2Lrsn('t1')' timeGmt2Lrsn(t1)
    call out 'lzt2Lrsn('t1')' timeLzt2Lrsn(t1)
    call out 'Lrsn2Gmt(gmt2Lrsn('t1')' timeLrsn2Gmt(timeGmt2Lrsn(t1))
    call out 'gmt2Lrsn(Lrsn2Gmt('s1')' timeGmt2Lrsn(timeLrsn2Gmt(s1))
    call out 'Lrsn2LZt(LZt2Lrsn('t1')' timeLrsn2LZt(timeLZt2Lrsn(t1))
    call out 'LZt2Stc(Lrsn2LZt('s1')(' timeLZt2Lrsn(timeLrsn2LZt(s1))
    call tstEnd t
    return
endProcedure tstTime
/* copx tstDiv end   **************************************************/

/* copx tstSql begin **************************************************/
tstSqlUtils: procedure expose m.
    call sqlConnect 'DBAF'
    id = 'A540769.dsnUtils'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "TEMPLATE TCOPYD",
            "DSN('&SSID..&JO..&US..P&PART(2)..D&DATE(3)..T&TIME.')",
            "DATACLAS(NULL12) MGMTCLAS(COM#A011) STORCLAS(FAR$N)",
            "SPACE (150,3750) TRK UNCNT 59;",
         "listdef abc include tablespace DA540769.A002* partlevel;",
         "listdef mf  include tablespace MF01A1A.A110A partlevel;",
         "copy list abc copyddn(tcopyd) shrlevel change;"
    st = translate(st)
    call sqlExec "call SYSPROC.DSNUTILS ( :id, :rst,",
                   ":st,:retcode, :ANY" copies(',:e,:e,:z',12) ")"
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.DSNUTILS'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    exit
endProcedure tstSqlUtils

tstSqlStored: procedure expose m.
    call sqlConnect 'DBAF'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "abc"
    call sqlExec "call SYSPROC.COMMAND ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.COMMAND'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStored

tstSqlStoredWK: procedure expose m.
    call sqlConnect 'DBAF'
 /* st = "direct wkUtiSub"
    rst = 'NO'
    say 'before call st='st 'rst='rst
    call sqlExec "call A540769.WKUTILSUB ( :st, :rst)"
    say 'after call st='st 'rst='rst
 */ rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    prc = 'DB2UTIL.DB2UTIL'
    st = "DA540769.A2*" /*  DA540769.A1*" */
    say  "call" prc "("st", ...)"
    call sqlExec "call" prc "(:st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE' prc
 /* say 'results' results  */
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say ''
    say '***** utility output'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say '***** end output'
    call sqlDisconnect
    return 0
endProcedure tstSqlStoredWK

tstSqlStoredSQL1: procedure expose m.
    call sqlConnect 'DBIA'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "DA540769.A2* DA540769.A1*"
    call sqlExec "call A540769.WKSQL1 ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE A540769.WKUTIL'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStoredSQL1

tstSqlTriggerTiming:
    parse upper arg tb ni
    m.noInsert = ni == 0
    cnt = 200000
    if tb = '' then
        TB = 'GDB9998.TWK511TRI'
    call sqlConnect dbaf
    say timing() 'noInsert' m.noInsert 'tb' tb
    call sql2St 'select max(pri) MX from' tb, cc
    if m.cc.1.mx == m.sqlNull then
        m.cc.1.mx = 0
    von = m.cc.1.mx + 1
    bis = m.cc.1.mx + cnt
    say m.cc.0 'max' m.cc.1.mx 'von' von 'bis' bis 'count' cnt tb
    if right(tb, 2) = 'A1' then do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long, par)' ,
                   'values (?, ?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    else do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long)' ,
                   'values (?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    /*  call sqlLn 5,,, 'select * from' tb 'where pri >=' von */
    call sqlCommit
    say timing()
    call sqlDisconnect
    return
endProcedure tstSqlTriggerTiming

tstSqlRx: procedure expose m.
/*
$=/tstSqlRx/
    ### start tst tstSqlRx ############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s7 into :M.SQL.7.D from :src
    .    e 3: with into :M.SQL.7.D = M.SQL.7.D
    fetchA 1 ab=m.abcdef.123.AB=abc ef=efg
    fetchA 0 ab=m.abcdef.123.AB=abc ef=efg
    fetchB 1 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchB 0 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
    fetchBT 1 SYSTABLES
    fetchBT 0 SYSTABLES
    fetchBi 1 SYSINDEXES
    fetchBi 0 SYSINDEXES
$/tstSqlRx/ */
    call tst t, "tstSqlRx"
    call sqlRxConnect
    cx = 7
    call sqlRxQuery cx, 'select * from sysdummy'
    call sqlRxQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlRxFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    call sqlRxClose cx
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlRxQuery cx, sql, '?ab cd ?ef ?gh'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlRxFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    call sqlRxClose cx
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlRxQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlRxFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    call sqlRxClose cx
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call sqlQueryPrep cx, "select name" ,
                        "from sysibm.sysTables" ,
                        "where creator = 'SYSIBM' and name = ?",':m.nm'
    call sqlQueryArgs cx, 'SYSTABLES'
    call out 'fetchBT' sqlRxFetch(cx) m.nm
    call out 'fetchBT' sqlRxFetch(cx) m.nm
    call sqlRxClose cx
    call sqlQueryArgs cx, 'SYSINDEXES'
    call out 'fetchBi' sqlRxFetch(cx) m.nm
    call out 'fetchBi' sqlRxFetch(cx) m.nm
    call tstEnd t
    call sqlRxDisconnect
    return
endProcedure tstSqlRx

tstSql: procedure expose m.
/*
$=/tstSql/
    ### start tst tstSql ##############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s7 into :M.SQL.7.D from :src
    .    e 3: with into :M.SQL.7.D = M.SQL.7.D
    fetchA 1 ab=m.abcdef.123.AB=abc ef=efg
    fetchA 0 ab=m.abcdef.123.AB=abc ef=efg
    fetchB 1 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchB 0 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
    sql2St 1 st.0=1
    sql2St:1 a=a b=2 c=--- d=d
    sql2One a
    sql2One a=a b=2 c=--- d=d
    fetchBT 1 SYSTABLES
    fetchBT 0 SYSTABLES
    fetchBi 1 SYSINDEXES
    fetchBi 0 SYSINDEXES
$/tstSql/ */
    call tst t, "tstSql"
    call sqlConnect
    cx = 7
    call sqlQuery cx, 'select * from sysdummy'
    call sqlQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    call sqlClose cx
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlQuery cx, sql, '?ab cd ?ef ?gh'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    call sqlClose cx
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    call sqlClose cx
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call out 'sql2St' sql2St(sql, st) 'st.0='m.st.0
    do i=1 to m.st.0
        call out 'sql2St:'i ,
            'a='m.st.i.a 'b='m.st.i.b 'c='m.st.i.c 'd='m.st.i.d
        end
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    drop m.st.a m.st.b m.st.c m.st.d m.st.0
    call out 'sql2One' sql2One(sql, st)
    call out 'sql2One' ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
    drop m.st.a m.st.b m.st.c m.st.d m.st.0
    call sqlQueryPrep cx, "select name" ,
                        "from sysibm.sysTables" ,
                        "where creator = 'SYSIBM' and name = ?",':m.nm'
    call sqlQueryArgs cx, 'SYSTABLES'
    call out 'fetchBT' sqlFetch(cx) m.nm
    call out 'fetchBT' sqlFetch(cx) m.nm
    call sqlClose cx
    call sqlQueryArgs cx, 'SYSINDEXES'
    call out 'fetchBi' sqlFetch(cx) m.nm
    call out 'fetchBi' sqlFetch(cx) m.nm
    call tstEnd t
    call sqlDisconnect
    return
endProcedure tstSql

tstSqlCsm: procedure expose m.
/*
$=/tstSqlCsm/
    ### start tst tstSqlCsm ###########################################
    *** err: SQLCODE = -204: S100447.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: subsys = DE0G, host = RZZ
    *** err: fetchVars with : for csm: :m.dst.ab, :m.dst.ef
    fetchA 0 ab=m.abcdef.123.AB=M.abcdef.123.AB ef=M.abcdef.123.EF
    fetchA 0 ab=m.abcdef.123.AB=M.abcdef.123.AB ef=M.abcdef.123.EF
    fetchB 1 ab=a cd=2 ef=--- ind=M.abc.Def.123.EF.SQLIND gh=d ind=M.ab+
    c.Def.123.GH.SQLIND
    fetchB 0 ab=a cd=2 ef=--- ind=M.abc.Def.123.EF.SQLIND gh=d ind=M.ab+
    c.Def.123.GH.SQLIND
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
$/tstSqlCsm/ */
    call tst t, "tstSqlCsm"
    call sqlConnect 'RZZ/DE0G'
    cx = 7
    call sqlCsmQuery cx, 'select * from sysdummy'
    call sqlCsmQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlCsmFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlCsmQuery cx, sql, '?ab cd ?ef ?gh'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlCsmFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlCsmQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlCsmFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call tstEnd t
    call sqlDisconnect
    return
endProcedure tstsqlCsm

tstSqlCSV: procedure expose m.
/*
$=/tstSqlCSV/
    ### start tst tstSqlCSV ###########################################
    NAME,CREATOR,MITCOM,MITQUO,MITNU,COL6
    SYSTABLES,SYSIBM  ,"a,b","a""b",1,8
    SYSTABLESPACE,SYSIBM  ,"a,b","a""b",---,8
    SYSTABLESPACESTATS,SYSIBM,"a,b","a""b",---,6
$/tstSqlCSV/ */
    call csvIni
    call sqlConnect
    call tst t, "tstSqlCSV"
    r = csvWrt(sqlRdr("select name, creator, 'a,b' mitCom",
         ", 'a""b' mitQuo" ,
         ", case when name='SYSTABLES' then 1 else null end mitNu" ,
         ",length(creator)" ,
              "from sysibm.sysTables" ,
              "where creator = 'SYSIBM' and name like 'SYSTABLES%'",
              "fetch first 3 rows only"))
    call pipeWriteAll r
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlCsv

tstSqlB: procedure expose m.
/*
$=/tstSqlB/
    ### start tst tstSqlB #############################################
    #jIn 1# select strip(name) "tb", strip(creator) cr
    #jIn 2# , case when name = 'SYSTABLES' then 1 else null end
    #jIn 3# from sysibm.sysTables
    #jIn 4# where creator = 'SYSIBM' and name like 'SYSTABLES%'
    #jIn 5# .
    #jIn 6# order by name
    #jIn 7# fetch first 3 rows only
    #jIn eof 8#
    dest1.fet: SYSTABLES SYSIBM 1
    dest2.fet: SYSTABLESPACE SYSIBM ---
    dest3.fet: SYSTABLESPACESTATS SYSIBM ---
$/tstSqlB/ */
    call tst t, "tstSqlB"
    cx = 9
    call sqlConnect
    call jIni
    call mAdd mCut(t'.IN', 0),
      , 'select strip(name) "tb", strip(creator) cr' ,
      ,     ", case when name = 'SYSTABLES' then 1 else null end" ,
      ,   "from sysibm.sysTables" ,
      ,   "where creator = 'SYSIBM' and name like 'SYSTABLES%'", ,
      ,   "order by name",
      ,   "fetch first 3 rows only"
     call sqlQuery cx
     do qx=1 while sqlFetch(cx, 'dest'qx'.fet')
         dst = 'dest'qx'.fet'
         call out dst':' m.dst.tb m.dst.cr m.dst.col3
         drop m.dst.tb m.dst.cr m.dst.col3
         end
     call sqlClose cx
     call sqlDisconnect
     call tstEnd t
     return
endProcedure tstSqlB

tstSqlO: procedure expose m.
/*
$=/tstSqlO/
    ### start tst tstSqlO #############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s10 into :M.SQL.10.D from :src
    .    e 3: with into :M.SQL.10.D = M.SQL.10.D
    REQD=Y col=123 case=--- .sqlInd:-1 col5=anonym geburri=1956-04-01+
    -06.00.00.000000
$/tstSqlO/
*/
    call sqlConnect
    call tst t, "tstSqlO"
    call sqlStmt 'set current schema = A540769';
    call sqlStmt 'select * from sysdummy';
    r = sqlRdr( ,
          "select d.*, 123, timestamp('01.04.1956','06:00:00')" ,
                             '"geburri walter",',
                    'case when 1=0 then 1 else null end caseNull,',
                    "'anonym'" ,
               'from sysibm.sysdummy1 d')
    call jOpen r, '<'
    do while assNN('o', jReadO(r))

        call out 'REQD='m.o.IBMREQD 'col='m.o.col2,
                  'case='m.o.CASENULL '.sqlInd:'m.o.caseNull.sqlInd,
                  'col5='m.o.col5,
                  'geburri='m.o.GEBURRI
        end
    call jClose r
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlO

tstSqlFTab: procedure expose m.
/*
$=/tstSqlFTab/
    ### start tst tstSqlFTab ##########################################
    UPDATESTATSTIME----------------NACTIVE------NPAGES-EXTENT-LOADRLAST+
    TIME--------------REORGLASTTIME--------------REORGINSERT-REORGDELET+
    E-REORGUPDATE-REORGUNCLUS-REORGDISORG-REORGMASSDE-REORGNEARIN-REORG+
    FARIND-STATSLASTTIME--------------STATSINSERT-STATSDELETE-STATSUPDA+
    TE-STATSMASSDE-COPYLASTTIME---------------COPYUPDATED-COPYCHANGES-C+
    OPYUP-COPYUPDATETIME-------------I---DBID---PSID-PARTIT-INSTAN---SP+
    ACE-TOTALRO-DATASIZ-UNCOMPR-DBNAME-------NAME---------REORGCL-REORG+
    SC-REORGHA-HASHLASTUS-DRI-L-STATS01----
    --- modified
    allg vorher                     others vorher
    db-------ts---------part---inst-UPDATESTATSTIME----------------NACT+
    IVE------NPAGES-EXTENT-LOADRLASTTIME--------------REORGLASTTIME----+
    ----------REORGINSERT-REORGDELETE-REORGUPDATE-REORGUNCLUS-REORGDISO+
    RG-REORGMASSDE-REORGNEARIN-REORGFARIND-STATSLASTTIME--------------S+
    TATSINSERT-STATSDELETE-STATSUPDATE-STATSMASSDE-COPYLASTTIME--------+
    -------COPYUPDATED-COPYCHANGES-COPYUP-COPYUPDATETIME-------------I-+
    --DBID---PSID---SPACE-TOTALRO-DATASIZ-UNCOMPR-REORGCL-REORGSC-REORG+
    HA-HASHLASTUS-DRI-L-STATS01----
    db-------ts---------part---inst-UPDATESTATSTIME----------------NACT+
    IVE------NPAGES-EXTENT-LOADRLASTTIME--------------REORGLASTTIME----+
    ----------REORGINSERT-REORGDELETE-REORGUPDATE-REORGUNCLUS-REORGDISO+
    RG-REORGMASSDE-REORGNEARIN-REORGFARIND-STATSLASTTIME--------------S+
    TATSINSERT-STATSDELETE-STATSUPDATE-STATSMASSDE-COPYLASTTIME--------+
    -------COPYUPDATED-COPYCHANGES-COPYUP-COPYUPDATETIME-------------I-+
    --DBID---PSID---SPACE-TOTALRO-DATASIZ-UNCOMPR-REORGCL-REORGSC-REORG+
    HA-HASHLASTUS-DRI-L-STATS01----
    allg nachher                    others nachher
    DBNAME                   INSTANCE                                  +
    .    NPAGES                                        REORGLASTTIME   +
    .                                   REORGUPDATES                   +
    .     REORGMASSDELETE                     STATSLASTTIME            +
    .                          STATSUPDATES                            +
    .           COPYUPDATEDPAGES               COPYUPDATETIME          +
    .            PSID                   DATASIZE                REORGSC+
    ANACCESS            DRIVETYPE
    .         NAME                   UPDATESTATSTIME                   +
    .                 EXTENTS                                          +
    .            REORGINSERTS                        REORGUNCLUSTINS   +
    .                  REORGNEARINDREF                                 +
    .   STATSINSERTS                        STATSMASSDELETE            +
    .                        COPYCHANGES                               +
    .    IBMREQD         SPACE                   UNCOMPRESSEDDATASIZE  +
    .  REORGHASHACCESS        LPFACILITY
    .                  PARTITION                                NACTIVE+
    .                        LOADRLASTTIME                             +
    .                        REORGDELETES                        REORGD+
    ISORGLOB                      REORGFARINDREF                       +
    .              STATSDELETES                        COPYLASTTIME    +
    .                                   COPYUPDATELRSN                 +
    .     DBID                  TOTALROWS               REORGCLUSTERSEN+
    S        HASHLASTUSED     STATS01
$/tstSqlFTab/
*/
    call tst t, 'tstSqlFTab'
    call sqlConnect
    call sqlQuery 17, 'select * from sysibm.sysTablespaceStats' ,
                "where dbName = 'xxxDB06' and name = 'SYSTSTAB'"
    call sqlFTabReset abc, 17, 1,     ,  12
    call sqlFTabDef      abc, 492, '%7e'
    call sqlFTabOthers abc
    call sqlfTab abc
    call sqlClose 17
    call out '--- modified'
    call sqlQuery 17, 'select * from sysibm.sysTablespaceStats' ,
                "where dbName = 'xxxDB06' and name = 'SYSTSTAB'"
    call sqlFTabReset abc, 17, 2 1, 1 3 'c', 12
    call sqlFTabDef      abc, 492, '%7e'
    call sqlFTabAdd      abc, DBNAME, '%-8C', 'db', 'allg vorher'  ,
                                                  , 'allg nachher'
    call sqlFTabAdd      abc, NAME  , '%-8C', 'ts'
    call sqlFTabAdd      abc, PARTITION , , 'part'
    call sqlFTabAdd      abc, INSTANCE  , , 'inst'
    call fTabAddTit      abc, 2,                    'others vorher'
    call fTabAddTit      abc, 3,                    'others nachher'
    call sqlFTabOthers abc
    call sqlFTab abc
    call sqlClose 17
    call tstEnd t
    return
endProcedure tstSqlFTab

tstSqlC: procedure expose m.
/*
$=/tstSqlCRx/
    ### start tst tstSqlCRx ###########################################
    *** err: SQLCODE = -104: ILLEGAL SYMBOL "?". SOME SYMBOLS THAT MIGH+
    T
    .    e 1:     BE LEGAL ARE: . <IDENTIFIER> JOIN INNER LEFT RIGHT FU+
    LL CROSS ,
    .    e 2:     HAVING GROUP
    .    e 3: src select * from sysibm?sysDummy1
    .    e 4:   >    >>>pos 21 of 30>>>
    .    e 5: sql = select * from sysibm?sysDummy1
    .    e 6: stmt = prepare s9 into :M.SQL.9.D from :src
    .    e 7: with into :M.SQL.9.D = M.SQL.9.D
    *** err: SQLCODE = -204: NONONO.SYSDUMMY1 IS AN UNDEFINED NAME
    .    e 1: sql = select * from nonono.sysDummy1
    .    e 2: stmt = prepare s9 into :M.SQL.9.D from :src
    .    e 3: with into :M.SQL.9.D = M.SQL.9.D
    sys  ==> server CHSKA000DP4G    .
    fetched a1=abc, i2=12, c3=---
    .  I1 C2  .
    .   1 eins
    2222 zwei
$/tstSqlCRx/
$=/tstSqlCCsm/
    ### start tst tstSqlCCsm ##########################################
    *** err: SQLCODE = -104: ILLEGAL SYMBOL "?". SOME SYMBOLS THAT MIGH+
    T
    .    e 1:     BE LEGAL ARE: . <IDENTIFIER> JOIN INNER LEFT RIGHT FU+
    LL CROSS ,
    .    e 2:     HAVING GROUP
    .    e 3: src select * from sysibm?sysDummy1
    .    e 4:   >    >>>pos 21 of 30>>>
    .    e 5: sql = select * from sysibm?sysDummy1
    .    e 6: subsys = DE0G, host = RZZ
    *** err: SQLCODE = -204: NONONO.SYSDUMMY1 IS AN UNDEFINED NAME
    .    e 1: sql = select * from nonono.sysDummy1
    .    e 2: subsys = DE0G, host = RZZ
    sys RZZ/DE0G ==> server CHROI00ZDE0G    .
    fetched a1=abc, i2=12, c3=---
    .  I1 C2  .
    .   1 eins
    2222 zwei
$/tstSqlCCsm/ */
    sqlBuf = jBuf("select 1 i1, 'eins' c2 from sysibm.sysDummy1",
      , "union all select 2222 i1, 'zwei' c2 from sysibm.sysDummy1")
    do tx=1 to 2
        if tx = 1 then do
            call tst t, "tstSqlCRx"
            sys = ''
            end
        else do
            call tst t, "tstSqlCCsm"
            sys =  'RZZ/DE0G'
            end
        call sqlConnect sys
        cx = 9
        call sqlQuery cx, 'select * from sysibm?sysDummy1'
        call sqlQuery cx, 'select * from nonono.sysDummy1'
        call sqlQuery cx, "select 'abc' a1, 12 i2, current server srv",
                     ", case when 1=0 then 1 else null end c3",
                 "from sysibm.sysDummy1"
        do while sqlFetch(cx, dst)
            call out 'sys' sys '==> server' m.dst.srv
            call out 'fetched a1='m.dst.a1', i2='m.dst.i2', c3='m.dst.c3
            end
        call fTabAuto , sqlRdr(sqlBuf)
        call sqlDisconnect
        call tstEnd t
        end
    return
endProcedure tstSqlC

tstSqlUpd: procedure expose m.
/*
$=/tstSqlUpd/
    ### start tst tstSqlUpd ###########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table (update session.dgtt  set c2 = 'u' +
    || c2)
    stmt = prepare s9 into :M.SQL.9.D from :src
    with into :M.SQL.9.D = M.SQL.9.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstSqlUpd/ */
    call tst t, "tstSqlUpd"
    cx = 9
    qx = 3
    call sqlConnect
    call sqlUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call sqlUpdate,"insert into session.dgtt" ,
                    "values(1, 'eins', '2012-04-01 06.07.08')"
    call sqlUpdate,"insert into session.dgtt" ,
                    "values(2, 'zwei', '2012-02-29 15:44:33.22')"
    call out 'insert updC' m.sql..updateCount
    call sqlUpdate,"insert into session.dgtt" ,
                      "select i1+10, 'zehn+'||strip(c2), t3+10 days",
                           "from session.dgtt"
    call out 'insert select updC' m.sql..updateCount
    call sqlQuery cx, 'select d.*' ,
               ', case when mod(i1,2) = 1 then 1 else null end grad' ,
               'from session.dgtt d'
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlClose cx
    call sqlQuery cx, "select * from final table (update session.dgtt",
                   " set c2 = 'u' || c2)"
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSqlUpd
tstSqlUpdPre: procedure expose m.
/*
$=/tstSqlUpdPre/
    ### start tst tstSqlUpdPre ########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table ( update session.dgtt set c2 = ? ||+
    . c2)
    stmt = prepare s5 into :M.SQL.5.D from :src
    with into :M.SQL.5.D = M.SQL.5.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstSqlUpdPre/ */
    call tst t, "tstSqlUpdPre"
    cx = 5
    qx = 3
    call sqlConnect
    call sqlUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call sqlUpdPrep 5, "insert into session.dgtt values (?, ?, ?)"
    call sqlUpdArgs 5, 1, 'eins', '2012-04-01 06.07.08'
    call sqlUpdArgs 5, 2, 'zwei', '2012-02-29 15:44:33.22'
    call out 'insert updC' m.sql.5.updateCount
    call sqlUpdPrep 5,"insert into session.dgtt" ,
                      "select i1+?, 'zehn+'||strip(c2), t3+? days",
                           "from session.dgtt"
    call sqlUpdArgs 5, 10, 10
    call out 'insert select updC' m.sql.5.updateCount
    call sqlQueryPrep cx, 'select d.*' ,
               ', case when mod(i1,2) = ? then 0+? else null end grad' ,
               'from session.dgtt d'
    call sqlQueryArgs cx, 1, 1
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlClose cx
    call sqlQueryPrep cx, "select * from final table (" ,
              "update session.dgtt set c2 = ? || c2)"
    call sqlQueryArgs cx, "u"
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSqlUpdPre
tstsqlRxUpd: procedure expose m.
/*
$=/tstsqlRxUpd/
    ### start tst tstsqlRxUpd #########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table (update session.dgtt set c2 = 'u' +
    || c2)
    stmt = prepare s9 into :M.SQL.9.D from :src
    with into :M.SQL.9.D = M.SQL.9.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstsqlRxUpd/ */
    call tst t, "tstsqlRxUpd"
    cx = 9
    qx = 3
    call sqlRxConnect
    call sqlRxUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call sqlRxUpdate,"insert into session.dgtt" ,
                    "values(1, 'eins', '2012-04-01 06.07.08')"
    call sqlRxUpdate,"insert into session.dgtt" ,
                    "values(2, 'zwei', '2012-02-29 15:44:33.22')"
    call out 'insert updC' m.sql..updateCount
    call sqlRxUpdate,"insert into session.dgtt" ,
                      "select i1+10, 'zehn+'||strip(c2), t3+10 days",
                           "from session.dgtt"
    call out 'insert select updC' m.sql..updateCount
    call sqlRxQuery cx, 'select d.*' ,
               ', case when mod(i1,2) = 1 then 1 else null end grad' ,
               'from session.dgtt d'
    do qx=qx+1 while sqlRxFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlRxClose cx
    call sqlRxQuery cx, "select * from final table",
                 "(update session.dgtt set c2 = 'u' || c2)"

    do qx=qx+1 while sqlRxFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlRxClose cx
    call sqlRxDisconnect
    call tstEnd t
    return
endProcedure tstsqlRxUpd

tstSqlE: procedure expose m.
/*
$=/tstSqlE/
    ### start tst tstSqlE #############################################
    *** err: SQLCODE = -713: THE REPLACEMENT VALUE FOR CURRENT SCHEMA I+
    S
    .    e 1:     INVALID
    .    e 2: sql = set current schema = 'sysibm'
    .    e 3: stmt = execute immediate :src
    -713 set schema ''
    0 set schema
    0 select
    fetch=1 SYSIBM
$/tstSqlE/
*/
    call sqlConnect
    call tst t, "tstSqlE"
    call tstOut t, sqlExecute(3, "set current schema = 'sysibm'") ,
                                 "set schema ''"
    call tstOut t, sqlExecute(3, " set current schema = sysibm ") ,
                                 "set schema"
    call tstOut t, sqlExecute(3, " select current schema c"      ,
                                      "from sysibm.sysDummy1") 'select'
    call tstOut t, 'fetch='sqlFetch(3, aa) m.aa.c
    call sqlClose 3
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlE
tstSqlO: procedure expose m.
/*
$=/tstSqlO/
    ### start tst tstSqlO #############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: stmt = prepare s7 from :src
    .    e 2: with from :src = select * from sysdummy
    REQD=Y col=123 case=--- .sqlInd:-1 col5=anonym geburri=1956-04-01+
    -06.00.00.000000
$/tstSqlO/
*/
    call sqlConnect
    call sqlStmt 'set current schema = A540769';
    call tst t, "tstSqlO"
    src = 'select * from sysdummy'
    call sqlExec 'prepare s7 from :src'
    r = sqlRdr( ,
          "select d.*, 123, timestamp('01.04.1956','06:00:00')" ,
                             '"geburri walter",',
                    'case when 1=0 then 1 else null end caseNull,',
                    "'anonym'" ,
               'from sysibm.sysdummy1 d')
    call jOpen r, '<'
    do while assNN('o', jReadO(r))

        call out 'REQD='m.o.IBMREQD 'col='m.o.col2,
                  'case='m.o.CASENULL '.sqlInd:'m.o.caseNull.sqlInd,
                  'col5='m.o.col5,
                  'geburri='m.o.GEBURRI
        end
    call jClose r
    call tstEnd t
    return
endProcedure tstSqlO

tstSqlUpdComLoop: procedure expose m.
/*
$=/tstSqlUpdComLoop/
    ### start tst tstSqlUpdComLoop ####################################
    sqlCode 0: declare global temporary table session.dgtt (i1 int) on +
    commit ....
    sqlCode 0, 123 rows inserted: insert into session.dgtt select row_n+
    umber()....
    CNT
    123
    1 rows fetched: select count(*) cnt from session.dgtt
    123 rows deleted, 10 commits: delete from session.dgtt d where i1 i+
    n (sele....
    C
    0
    1 rows fetched: select count(*) cnt from session.dgtt
$/tstSqlUpdComLoop/ */
    call tst t, "tstSqlUpdComLoop"
    call sqlConnect
    call out sqlStmt("declare global temporary table session.dgtt",
                           "(i1 int) on commit preserve rows")
    call out sqlStmt("insert into session.dgtt",
       "select row_number() over() from sysibm.sysTables",
           "fetch first 123 rows only")
    call out sqlStmt("select count(*) cnt from session.dgtt")
    call out sqlUpdComLoop("delete from session.dgtt d where i1 in",
       "(select i1 from session.dgtt fetch first 13 rows only)")
    call out sqlStmt("select count(*) cnt from session.dgtt")
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlUpdComLoop

tstSqlO1: procedure expose m.
/*
$=/tstSqlO1/
    ### start tst tstSqlO1 ############################################
    tstR: @tstWriteoV2 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART
    tstR: @tstWriteoV3 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART_HIST
    tstR: @tstWriteoV4 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLES
    tstR: @tstWriteoV5 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLESPACE
    --- writeAll
    tstR: @tstWriteoV6 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART
    tstR: @tstWriteoV7 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART_HIST
    tstR: @tstWriteoV8 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLES
    tstR: @tstWriteoV9 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLESPACE
$/tstSqlO1/
*/
    call sqlConnect
    call tst t, "tstSqlO1"
    qr = sqlRdr("select strip(creator) cr, strip(name) tb",
                     "from sysibm.sysTables",
                     "where creator='SYSIBM' and name like 'SYSTABL%'",
                     "order by 2 fetch first 4 rows only")
    call jOpen qr, m.j.cRead
    call mAdd t.trans, className(m.qr.type) '<tstSqlO1Type>'
    do while assNN('ABC', jReadO(qr))
        if m.qr.rowCount = 1 then do
            cx = m.qr.cursor
            end
        call outO abc
        end
    call jClose qr
    call out '--- writeAll'
    call pipeWriteAll qr
    call tstEnd t
    return 0
endProcedure tstSqlO1

tstSqlO2: procedure expose m.
/*
$=/tstSqlO2/
    ### start tst tstSqlO2 ############################################
    CR     TB                RR
    SYSIBM SYSTABLEPART       1
    SYSIBM SYSTABLEPART_HIST  4
    SYSIBM SYSTABLES          9
    SYSIBM SYSTABLESPACE     16
$/tstSqlO2/
*/
    call sqlConnect
    call tst t, "tstSqlO2"
    call pipe '+N'
    call out    "select strip(creator) cr, strip(name) tb,"
    call out         "(row_number()over())*(row_number()over()) rr"
    call out         "from sysibm.sysTables"
    call out         "where creator='SYSIBM' and name like 'SYSTABL%'"
    call out         "order by 2 fetch first 4 rows only"
    call pipe 'N|'
    call sqlSel
    call pipe 'P|'
    call fTabAuto fTabReset(abc, 1)
    call pipe '-'
    call tstEnd t
    return 0
endProcedure tstSqlO2
tstSqlS1: procedure expose m.
/*
$=/tstSqlS1/
    ### start tst tstSqlS1 ############################################
    select c, a from sysibm.sysDummy1
    tstR: @tstWriteoV2 isA :<cla sql c a>
    tstR:  .C = 1
    tstR:  .A = a
    select ... where 1=0
    tstR: @ obj null
$/tstSqlS1/
*/
    call sqlIni
    call tst t, "tstSqlS1"
    call sqlConnect
    s1 = jSingle( ,
        sqlRdr("select count(*) c, 'a' a from sysibm.sysdummy1"))
    call mAdd t.trans, className(objClass(s1)) '<cla sql c a>'
    call out 'select c, a from sysibm.sysDummy1'
    call tstWriteO t, s1
    call out 'select ... where 1=0'
    call tstWriteO t, jSingle( ,
        sqlRdr("select*from sysibm.sysdummy1 where 1=0"), '')
    call tstEnd t
    return
endProcedure tstSqlS1
tstSqlStmt: procedure expose m.
/*
$=/tstSqlStmt/
    ### start tst tstSqlStmt ##########################################
    *** err: SQLCODE = -713: THE REPLACEMENT VALUE FOR CURRENT SCHEMA I+
    S
    .    e 1:     INVALID
    .    e 2: sql = set current schema = 'sysibm'
    .    e 3: stmt = execute immediate :src
    sqlCode -713: set current schema = 'sysibm'
    sqlCode 0: set current schema =  sysibm
    tstR: @tstWriteoV2 isA :<sql?sc>
    tstR:  .C = SYSIBM
    1 rows fetched: select current schema c  from sysDummy1
    tstR: @tstWriteoV3 isA :<sql?sc>
    tstR:  .C = SYSIBM
    1 rows fetched: (select current schema c from sysDummy1)
$/tstSqlStmt/
*/
    call sqlConnect
    call tst t, "tstSqlStmt"
    cn = className(classNew('n* SQL u f C v'))
    call mAdd t.trans, cn '<sql?sc>'
    call tstOut t, sqlStmt("set current schema = 'sysibm'")
    call tstOut t, sqlStmt("  set current schema =  sysibm ")
    call tstOut t, sqlStmt("   select current schema c  from sysDummy1",
                           , ,'o')
    call tstOut t, sqlStmt("  (select current schema c from sysDummy1)",
                           , ,'o')
    call tstEnd t
    return
endProcedure tstSqlStmt
tstSqlStmts: procedure expose m.
/*
$=/tstSqlStmts/
   ### start tst tstSqlStmts #########################################
   *** err: SQLCODE = -104: ILLEGAL SYMBOL "BLABLA". SOME SYMBOLS THAT
   .    e 1:     MIGHT BE LEGAL ARE: <ERR_STMT> <WNG_STMT> GET SQL SAV+
   EPOINT HOLD
   .    e 2:     FREE ASSOCIATE
   .    e 3: src blabla
   .    e 4:   > <<<pos 1 of 6<<<
   .    e 5: sql = blabla
   sqlCode -104: blabla
   sqlCode 0: set current schema=  sysIbm
   C
   1
   1 rows fetched: select count(*) "c" from sysDummy1  with    ur
   C
   1
   1 rows fetched: select count(*) "c" from sysDummy1  with    ur
   #jIn 1# set current -- sdf
   #jIn 2# schema = s100447;
   sqlCode 0: set current  schema = s100447
   #jIn eof 3#
$/tstSqlStmts/ */
    call sqlConnect
    call scanReadIni
    call scanWinIni
    call tst t, "tstSqlStmts"
    call sqlStmts "blabla ;;set current schema=  sysIbm "
    b = jBuf('select count(*) "c" from sysDummy1 --com' ,
             ,'with  /* comm */ ur;')
    call sqlStmts b
    call sqlStmts b, , '-sql72'
    call mAdd mCut(t'.IN', 0), 'set current -- sdf', 'schema = s100447;'
    call sqlStmts
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlStmts

tstCatTb:
/*
$=/tstCatTb/
    ### start tst tstCatTb ############################################
    ..
    select * from sysibm.SYSDUMMY1  .
    IBMREQD
    I .
    Y .
    I .
    IBMREQD
$/tstCatTb/
*/
    call sqlConnect
    call tst t, 'tstCatTb'
    call sqlCatTb 'sysDummy1'
    call sqlCatTb 'SYSTableSpaceStats',
             , "name = 'A403A1' and dbName = 'DA540769'"
    call tstEnd t
    return
endProcedure tstCatTb

tstSqlDisDb: procedure expose m.
    call sqlDsn di, 'DP4G', '-dis db(*) sp(*)' ,
                    'restrict advisory limit(*)', 12
    m.oo.0 = 0
    call sqlDisDb oo, di
    say 'di.0' m.di.0 '==> oo.0' m.oo.0
    trace ?r
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE)
    say 'DB2PDB6.RR2HHAGE  ==>' ix m.oo.ix.sta
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE, 3)
    say 'DB2PDB6.RR2HHAGE.3  ==>' ix m.oo.ix.sta
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE,22)
    say 'DB2PDB6.RR2HHAGE.22 ==>' ix m.oo.ix.sta
    return
endProcedure tstSqlDisDb

/* copx tstSql end  ***************************************************/
/* copx tstComp begin **************************************************
    test the wsh compiler
***********************************************************************/
tstComp: procedure expose m.
    call compIni
    call tstCompDataConst
    call tstCompDataVars
    call tstCompShell
    call tstCompPrimary
    call tstCompExpr
    call tstCompFile
    call tstCompStmt
    call tstCompDir
    call tstCompObj
    call tstCompORun
    call tstCompDataIO
    call tstCompPipe
    call tstCompPip2
    call tstCompRedir
    call tstCompComp
    call tstCompColon
    call tstCompTable
    call tstCompSyntax
    if m.err.os == 'TSO' then
        call tstCompSql
    call tstTotal
    return
endProcedure tstComp

tstComp1: procedure expose m.
parse arg ty nm cnt
    c1 = 0
    if cnt = 0 | cnt = '+' then do
        c1 = cnt
        cnt = ''
        end
    call jIni
    src = jBuf()
    call jOpen src, m.j.cWri
    do sx=2 to arg()
        call jWrite src, arg(sx)
        end
    call tstComp2 nm, ty, jClose(src), , c1, cnt
    return
endProcedure tstComp1

tstComp2: procedure expose m.
parse arg nm, spec, src, compSt
    call compIni
    call tst t, nm, compSt
    if src == '' then do
        src = jBuf()
        call tst4dp src'.BUF', mapInline(nm'Src')
        end
    m.t.moreOutOk = abbrev(strip(arg(5)), '+')
    oldErr = m.err.count
    cmp = comp(src)
    call out 'compile' spec',' (m.src.buf.0) 'lines:' m.src.buf.1
    r = compile(cmp, spec)
    noSyn = m.err.count = oldErr
    coErr = m.t.err
    say if( noSyn, "compiled", "*** syntaxed") r ":" objMet(r, 'oRun')

    cnt = 0
    do ax = 5 to max(arg(), 5) while m.t.err = coErr & noSyn
        a1 = strip(arg(ax))
        if a1 == '' & arg() >= 5 then
            iterate
        if abbrev(a1, '+') then do
            m.t.moreOutOk = 1
            a1 = strip(substr(a1, 2))
            end
        if datatype(a1, 'n') then
            cnt = a1
        else if a1 \== '' then
            call err 'tstComp2 bad arg('ax')' arg(ax)
        if cnt = 0 then do
            call mCut 'T.IN', 0
            call out "run without input"
            end
        else  do
            call mAdd mCut('T.IN', 0),
                ,"eins zwei drei", "zehn elf zwoelf?",
                , "zwanzig 21 22 23 24 ... 29|"
            do lx=4 to cnt
                call mAdd 'T.IN', left(lx "line" lx, lx+9, '-')';'
                end
            call out "run with" cnt "inputs"
            end
        m.t.inIx = 0
        call oRun r
        end
    call tstEnd t
    return
endProcedure tstComp2

tstCompDataConst: procedure expose m.
/*
$=/tstCompDataConst/
    ### start tst tstCompDataConst ####################################
    compile =, 8 lines:       Lline one, $** asdf
    run without input
    .      Lline one, .
    line two..
    line threecontinued on 4
    line five  fortsetzung
    line six   fortsetzung
$/tstCompDataConst/ */
    call tstComp1 '= tstCompDataConst',
        , '      Lline one, $** asdf',
        , 'line two.',
        , 'line three$*+ bla bla' ,
        , 'continued on 4',
        , 'line five $*( und so',
        , 'weiter $abc $? $''$*)'' $"$*)" und weiter $*) fortsetzung',
        , 'line six  $*( und $*( $** $*( so',
        , 'weiter $abc $? $*)'' $"$*)" und weiter $*) fortsetzung'

/*
$=/tstCompDataConstBefAftComm1/
    ### start tst tstCompDataConstBefAftComm1 #########################
    compile =, 3 lines:     $*(anfangs com.$*)       $*(plus$*) $** x
    run without input
    the only line;
$/tstCompDataConstBefAftComm1/ */
    call tstComp1 '= tstCompDataConstBefAftComm1',
        , '    $*(anfangs com.$*)       $*(plus$*) $** x',
        , 'the only line;',
        , '      $*(end kommentar$*)              '

/*
$=/tstCompDataConstBefAftComm2/
    ### start tst tstCompDataConstBefAftComm2 #########################
    compile =, 11 lines:     $*(anfangs com.$*)       $*(plus$*) $*+ x
    run without input
    the first non empty line;
    .      .
    befor an empty line with comments;
$/tstCompDataConstBefAftComm2/ */

    call tstComp1 '= tstCompDataConstBefAftComm2',
        , '    $*(anfangs com.$*)       $*(plus$*) $*+ x',
        , '    $*(forts Zeile com.$*)       $*(plus$*) $** x',
        , ' $*(comment 2. Zeile$*) $*(plus$*) $*+ x',
        , ' $*(forts Zeile com.$*) $*(plus$*) $** x',
        , 'the first non empty line;',
        , '      ',
        , 'befor an empty line with comments;',
        , ' $*(comment 4. Zeile$*) $*(plus$*) $*+ x',
        , ' $*(forts 4.Zeile com.$*) $*(plus$*) $** x',
        , '      $*(end kommentar$*)              $*+',
        , ' $*(forts end com.$*) $*(plus$*) $** x'
     return
endProcedure tstCompDataComm

tstCompDataVars: procedure expose m.
/*
$=/tstCompDataVars/
    ### start tst tstCompDataVars #####################################
    compile =, 5 lines:       Lline one, $** asdf
    run without input
    .      Lline one, .
    lline zwei output
    lline 3 .
    variable v1 = valueV1 ${v1}= valueV1; .
    .      $-.{""$v1} = valueV1; .
$/tstCompDataVars/ */
    call tstComp1 '= tstCompDataVars',
        , '      Lline one, $** asdf',
        , '   $$ lline zwei output',
        , 'lline 3 $=v1= valueV1 ' ,
        , 'variable v1 = $v1 $"${v1}=" ${  v1  }; ',
        , '      $"$-.{""""$v1} =" $-.{$""$"v1"}; '
    return
endProcedure tstCompDataVars

tstCompShell: procedure expose m.
/*
$=/tstCompShell3/
    ### start tst tstCompShell3 #######################################
    compile @, 8 lines: call tstOut "T",  "abc" $-¢2*3$! "efg"$-¢2*3$!"+
    hij"
    run without input
    abc 6 efg6hij
    insert into A540769x.tqt002 with n(n) as ( select 4 from sysibm.s
    insert into A540769x.tqt002 with n(n) as ( select 4 from sysibm.s  +
    .   union all .
    abc 6 efg6hij
$/tstCompShell3/ */
    call tstComp1 '@ tstCompShell3',
        , 'call tstOut "T",  "abc" $-¢2*3$! "efg"$-¢2*3$!"hij"',
        , 'ix=3' ,
        , 'call tstOut "T","insert into A540769x.tqt002" ,',
        ,     '"with n(n) as ( select" $-¢ix+1$! "from sysibm.s"',
        , 'call tstOut "T","insert into A540769x.tqt002"  ,  ',
        ,    '"with n(n) as ( select" $-¢ix+1$! "from sysibm.s" , ' ,
        ,    '"    union all "' ,
        , '$$ abc $-¢2*3$! efg$-¢2*3$!hij',
/*
$=/tstCompShell/
    ### start tst tstCompShell ########################################
    compile @, 12 lines:   $$  Lline one, $** asdf
    run without input
    Lline one,
    lline zwei output
    v1 = valueV1 ${v1}= valueV1|
    REXX OUT L5 CONTINUED L6 CONTINUED L7
    L8 ONE
    L9 TWO
    valueV1
    valueV1 valueV2
    out  valueV1 valueV2
    SCHLUSS
$/tstCompShell/ */
    call tstComp1 '@ tstCompShell',
        , '  $$  Lline one, $** asdf',
        , '   $$ lline zwei output',
        , '        $=v1= valueV1 ' ,
        , '$$       v1 = $v1 $"${v1}=" ${  v1  }| ' ,
        , 'call out rexx out l5, ' ,
        , '     continued l6 , ' ,
        , '     continued l7   ' ,
        , 'call out l8 one    ' ,
        , 'call out l9 two$=v2=valueV2  ',
        , '$$- $v1  $$- $v1 $v2   ',
        , 'call out   "out "     $v1 $v2   ',
        , '$$-   schluss    '
/*
$=/tstCompShell2/
    ### start tst tstCompShell2 #######################################
    compile @, 13 lines: $@do j=0 to 1 $@¢ $$ do j=$j
    run without input
    do j=0
    after if 0 $@¢ $!
    after if 0 $=@¢ $!
    do j=1
    if 1 then $@¢ a
    a2
    if 1 then $@=¢ b
    b2
    after if 1 $@¢ $!
    after if 1 $=@¢ $!
    end
$/tstCompShell2/ */
    call tstComp1 '@ tstCompShell2',
        , '$@do j=0 to 1 $@¢ $$ do j=$j' ,
        ,     'if $j then $@¢ ',
        ,          '$$ if $j then $"$@¢" a $$a2' ,
        ,          '$!',
        ,     'if $j then $@=¢ ',
        ,          '$$ if $j then $"$@=¢" b $$b2' ,
        ,          '$!',
        ,     'if $j then $@¢ $!' ,
        ,     '$$ after if $j $"$@¢ $!"' ,
        ,     'if $j then $@=¢ $!' ,
        ,     '$$ after if $j $"$=@¢ $!"' ,
        ,     '$!',
        , '$$ end'
    return
endProcedure tstCompShell

tstCompPrimary: procedure expose m.
    call compIni
/*
$=/tstCompPrimary/
    ### start tst tstCompPrimary ######################################
    compile =, 16 lines: Strings $"$""$""""$""" $'$''$''''$'''
    run without input
    Strings $"$""$" $'$''$'
    rexx $-¢ 3 * 5 $! = 15
    rexx $-¢ 5 * 7 $! = 35
    rexx $-// 7 * 11 $// = 77
    rexx $-/abcEf/ 11 * 13 $/abcEf/ = 143
    data  line three line four  bis hier
    shell line five line six bis hier
    var get   v1 value Eins, v1 value Eins .
    var isDef v1 1, v2 0 .
    #jIn eof 1#
    var read  >1 0 rr undefined
    #jIn eof 2#
    var read  >2 0 rr undefined
    no call abc$-¢4*5$! $-¢efg$-¢6*7$! abc20 EFG42
    brackets $-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$! 1000
    run with 3 inputs
    Strings $"$""$" $'$''$'
    rexx $-¢ 3 * 5 $! = 15
    rexx $-¢ 5 * 7 $! = 35
    rexx $-// 7 * 11 $// = 77
    rexx $-/abcEf/ 11 * 13 $/abcEf/ = 143
    data  line three line four  bis hier
    shell line five line six bis hier
    var get   v1 value Eins, v1 value Eins .
    var isDef v1 1, v2 0 .
    #jIn 1# eins zwei drei
    var read  >1 1 rr eins zwei drei
    #jIn 2# zehn elf zwoelf?
    var read  >2 1 rr zehn elf zwoelf?
    no call abc$-¢4*5$! $-¢efg$-¢6*7$! abc20 EFG42
    brackets $-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$! 1000
$/tstCompPrimary/ */
    call vRemove 'v2'
    call tstComp1 '= tstCompPrimary 3',
        , 'Strings $"$""$""""$"""' "$'$''$''''$'''",
        , 'rexx $"$-¢ 3 * 5 $! =" $-¢ 3 * 5 $!' ,
        , 'rexx $"$-¢ 5 * 7 $! =" $-¢ 5 * 7 $!' ,
        , 'rexx $"$-// 7 * 11 $// =" $-// 7 * 11 $//' ,
        , 'rexx $"$-/abcEf/ 11 * 13 $/abcEf/ ="',
            '$-/abcEf/ 11 * 13 $/abcEf/' ,
        , 'data $-=¢ line three',
        , 'line four $! bis hier'  ,
        , 'shell $-@¢ $$ line five',
        , '$$ line six $! bis hier' ,
        , '$= v1  =   value Eins  $=rr=undefined $= eins = 1 ',
        , 'var get   v1 $v1, v1 ${  v1  } ',
        , 'var isDef v1 ${? v${  eins  }  }, v2 ${?v2 } ',
        , 'var read  >1 ${> rr} rr $rr' ,
        , 'var read  >2 ${> rr} rr $rr',
        , 'no call $"abc$-¢4*5$! $-¢efg$-¢6*7$!"',
            'abc$-¢4*5$! $-¢efg$-¢6*7$!$!',
        , 'brackets $"$-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$!"',
            '$-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$!'
    return
endProcedure tstCompPrimary

tstCompExpr: procedure expose m.
    call compIni
/*
$=/tstCompExprStr/
    ### start tst tstCompExprStr ######################################
    compile -, 3 lines: $=vv=vvStr
    run without input
    vv=vvStr
    o2String($.vv)=vvStr
$/tstCompExprStr/ */
    call tstComp1 '- tstCompExprStr',
        , '$=vv=vvStr' ,
        , '"vv="$vv' ,
        , '$"o2String($.vv)="o2String($.vv)'
/*
$=/tstCompExprObj/
    ### start tst tstCompExprObj ######################################
    compile ., 5 lines: $=vv=vvStr
    run without input
    vv=
    vvStr
    s2o($.vv)=
    vvStr
$/tstCompExprObj/ */
    call tstComp1 '. tstCompExprObj',
        , '$=vv=vvStr' ,
        , '"!vv="', '$vv',
        , '$"s2o($.vv)="', 's2o($-vv)'
/*
$=/tstCompExprDat/
    ### start tst tstCompExprDat ######################################
    compile =, 4 lines: $=vv=vvDat
    run without input
    vv=vvDat
    $.vv= !vvDat
    $.-¢"abc"$!=!abc
$/tstCompExprDat/ */
    call tstComp1 '= tstCompExprDat',
        , '$=vv=vvDat' ,
        , 'vv=$vv',
        , '$"$.vv=" $.vv',
        , '$"$.-¢""abc""$!="$.-¢"abc"$!'

/*
$=/tstCompExprRun/
    ### start tst tstCompExprRun ######################################
    compile @, 3 lines: $=vv=vvRun
    run without input
    vv=vvRun
    o2string($.vv)=vvRun
$/tstCompExprRun/ */
    call tstComp1 '@ tstCompExprRun',
        , '$=vv=vvRun' ,
        , 'call out "vv="$vv',
        , 'call out $"o2string($.vv)="o2string($.vv)'
/*
$=/tstCompExprCon/
    ### start tst tstCompExprCon ######################################
    compile #, 2 lines: $$ in # drinnen
    run without input
    $$ in # drinnen
    call out "vv="$vv
$/tstCompExprCon/
$=/tstCompExprCo2/
    ### start tst tstCompExprCo2 ######################################
    compile #, 3 lines: $$ in # drinnen
    run without input
    $$ in # drinnen
    call out "vv="$vv
    nacgh $#@
$/tstCompExprCo2/
*/
    call tstComp1 '# tstCompExprCon',
        , '$$ in # drinnen' ,
        , 'call out "vv="$vv'

    call tstComp1 '# tstCompExprCo2',
        , '$$ in # drinnen' ,
        , 'call out "vv="$vv',
        , '$#@ $$ nacgh $"$#@"'

    return
endProcedure tstCompExpr

tstCompStmt: procedure expose m.
/*
$=/tstCompStmt1/
    ### start tst tstCompStmt1 ########################################
    compile @, 8 lines: $= v1 = value eins  $= v2  =- 3*5*7 .
    run without input
    data v1 value eins v2 105
    eins
    zwei
    drei
    vier
    fuenf
    elf
    . zwoelf  dreiZ  .
    . vierZ .
    fuenfZ
    lang v1 value eins v2 945
    oRun ouput 1
$/tstCompStmt1/ */
    call pipeIni
    call vPutO 'oRun', oRunner('call out "oRun ouput" (1*1)')
    call vRemove 'v2'
    call tstComp1 '@ tstCompStmt1',
        , '$= v1 = value eins  $= v2  =- 3*5*7 ',
        , '$$ data v1 $v1 v2 ${   v2  }',
        , '$$eins $@¢$$ zwei $$ drei  ',
        , '   $@¢   $!  $@//   $// $@/q r s /   $/q r s /',
             '       $@/eins/ $@¢ $$vier $! $/eins/ $! $$fuenf',
        , '$$elf $@=¢$@=¢ zwoelf  dreiZ  $!  ',
        , '   $@=¢   $! $@=¢ $@=¢ vierZ $! $! $! $$fuenfZ',
        , '$$- "lang v1" $v1 "v2" ${v2}*9',
        , '$@oRun'
/*
$=/tstCompStmt2/
    ### start tst tstCompStmt2 ########################################
    compile @, 1 lines: $@for qq $$ loop qq $qq
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    loop qq eins zwei drei
    #jIn 2# zehn elf zwoelf?
    loop qq zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    loop qq zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
$/tstCompStmt2/ */
    call tstComp1 '@ tstCompStmt2 3',
        , '$@for qq $$ loop qq $qq'

/*
$=/tstCompStmt3/
    ### start tst tstCompStmt3 ########################################
    compile @, 9 lines: $$ 1 begin run 1
    2 ct zwei
    ct 4 mit assign .
    run without input
    1 begin run 1
    3 run 3 ctV = ct 4 assign ctV|
    run 5 procCall $@prCa
    out in proc at 8
    run 6 vor call $@prCa
    out in proc at 8
    9 run end
    run with 3 inputs
    1 begin run 1
    3 run 3 ctV = ct 4 assign ctV|
    run 5 procCall $@prCa
    out in proc at 8
    run 6 vor call $@prCa
    out in proc at 8
    9 run end
$/tstCompStmt3/ */
    call tstComp1 '@ tstCompStmt3 3',
        , '$$ 1 begin run 1',
        , '$@ct $$ 2 ct zwei',
        , '$$ 3 run 3 ctV = $ctV|',
        , '$@ct $@=¢ct 4 mit assign $=ctV = ct 4 assign ctV $!',
        , '$$ run 5 procCall $"$@prCa" $@prCa',
        , '$$ run 6 vor call $"$@prCa"',
        , '$@prCa',
        , '$@proc prCa $$out in proc at 8',
        , '$$ 9 run end'

/*
$=/tstCompStmt4/
    ### start tst tstCompStmt4 ########################################
    compile @, 4 lines: $=eins=vorher
    run without input
    eins vorher
    eins aus named block eins .
$/tstCompStmt4/ */
    call tstComp1 '@ tstCompStmt4 0',
        , '$=eins=vorher' ,
        , '$$ eins $eins' ,
        , '$=/eins/aus named block eins $/eins/' ,
        , '$$ eins $eins'
/*
$=/tstCompStmtDo/
    ### start tst tstCompStmtDo #######################################
    compile @, 2 lines: ti=0$@do y=3 to 4 $@do 2 $@¢
    run without input
    y=3 ti1 z=7
    y=3 ti1 z=8
    y=3 ti2 z=7
    y=3 ti2 z=8
    y=4 ti3 z=7
    y=4 ti3 z=8
    y=4 ti4 z=7
    y=4 ti4 z=8
$/tstCompStmtDo/ */
    call tstComp1 '@ tstCompStmtDo', 'ti=0$@do y=3 to 4 $@do 2 $@¢',
     ,    'ti = ti + 1',
        '$@do $*(sdf$*) z $*(sdf$*) =7 to 8 $$ y=$y ti$-¢ti$! z=$z $!'

/*
$=/tstCompStmtDo2/
    ### start tst tstCompStmtDo2 ######################################
    compile @, 7 lines: $$ $-=/sqlSel/
    run without input
    select 1 abc select 2 abc after table .
$/tstCompStmtDo2/ */
    call tstComp1 '@ tstCompStmtDo2',
         , '$$ $-=/sqlSel/',
         ,     '$=ty = abc ',
         ,     '$@do tx=1 to 2 $@=/table/',
         ,          'select $tx $ty',
         , '$/table/',
         ,     '$=ty = abc',
         ,     'after table',
         '$/sqlSel/'
/*
$=/tstCompStmtWith/
    ### start tst tstCompStmtWith #####################################
    compile @, 3 lines: $@with $.vA $$ fEins=$FEINS fZwei=$FZWEI va&fEi+
    ns=${vA&FEINS}
    run without input
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
    fEins=2Eins fZwei=2Zwei va&fEins=1Eins
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
$/tstCompStmtWith/
*/
    cl = classNew('n? tstStmtWith u f FEINS v, f FZWEI v')
    v1 = onew(cl)
    m.v1.feins = '1Eins'
    m.v1.fzwei = '1Zwei'
    v2 = oNew(cl)
    m.v2.feins ='2Eins'
    m.v2.fzwei ='2Zwei'
    call vPutO 'vA', v1
    call vPutO 'vB', v2
    stmt = '$$ fEins=$FEINS fZwei=$FZWEI va&fEins=${vA&FEINS}'
    call tstComp1 '@ tstCompStmtWith',
         , '$@with $.vA' stmt ,
         , '$@with $vA $@¢' stmt ,
         , '$@with $vB ' stmt stmt '$!'
     return
endProcedure tstCompStmt

tstCompSyntax: procedure expose m.
    call tstCompSynPrimary
    call tstCompSynAss
    call tstCompSynRun
    return
endProcedure tstCompSyntax

tstCompSynPrimary: procedure expose m.

/*
$=/tstCompSynPri1/
    ### start tst tstCompSynPri1 ######################################
    compile @, 1 lines: a $ =
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $ =
    .    e 2: pos 3 in line 1: a $ =
    *** err: no class found for object 0
$/tstCompSynPri1/ */
    call tstComp1 '@ tstCompSynPri1 +', 'a $ ='

/*
$=/tstCompSynPri2/
    ### start tst tstCompSynPri2 ######################################
    compile @, 1 lines: a $. {
    *** err: scanErr primary not finished
    .    e 1: last token  scanPosition  {
    .    e 2: pos 5 in line 1: a $. {
$/tstCompSynPri2/ */
    call tstComp1 '@ tstCompSynPri2 +', 'a $. {'

/*
$=/tstCompSynPri3/
    ### start tst tstCompSynPri3 ######################################
    compile @, 1 lines: b $-  ¢  .
    *** err: scanErr primary not finished
    .    e 1: last token  scanPosition   ¢
    .    e 2: pos 5 in line 1: b $-  ¢
$/tstCompSynPri3/ */
    call tstComp1 '@ tstCompSynPri3 +', 'b $-  ¢  '

/*
$=/tstCompSynPri4/
    ### start tst tstCompSynPri4 ######################################
    compile @, 1 lines: a ${ $*( sdf$*) } =
    *** err: scanErr var name expected
    .    e 1: last token  scanPosition } =
    .    e 2: pos 17 in line 1: a ${ $*( sdf$*) } =
$/tstCompSynPri4/ */
    call tstComp1 '@ tstCompSynPri4 +', 'a ${ $*( sdf$*) } ='

/*
$=/tstCompSynFile/
    ### start tst tstCompSynFile ######################################
    compile @, 1 lines: $@.<$*( co1 $*) $$abc
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@.<$*( co1 $*) $$abc
    .    e 2: pos 1 in line 1: $@.<$*( co1 $*) $$abc
$/tstCompSynFile/ */
    call tstComp1 '@ tstCompSynFile +', '$@.<$*( co1 $*) $$abc'

    return
endProcedure tstCompSynPrimary

tstCompSynAss: procedure expose m.

/*
$=/tstCompSynAss1/
    ### start tst tstCompSynAss1 ######################################
    compile @, 1 lines: $=
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 3 in line 1: $=
$/tstCompSynAss1/ */
    call tstComp1 '@ tstCompSynAss1 +', '$='

/*
$=/tstCompSynAss2/
    ### start tst tstCompSynAss2 ######################################
    compile @, 2 lines: $=   .
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $=
$/tstCompSynAss2/ */
    call tstComp1 '@ tstCompSynAss2 +', '$=   ', 'eins'

/*
$=/tstCompSynAss3/
    ### start tst tstCompSynAss3 ######################################
    compile @, 2 lines: $=   $$
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition $$
    .    e 2: pos 6 in line 1: $=   $$
$/tstCompSynAss3/ */
    call tstComp1 '@ tstCompSynAss3 +', '$=   $$', 'eins'

/*
$=/tstCompSynAss4/
    ### start tst tstCompSynAss4 ######################################
    compile @, 1 lines: $=   eins
    *** err: scanErr = expected in assignment after $= var
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $=   eins
$/tstCompSynAss4/ */
    call tstComp1 '@ tstCompSynAss4 +', '$=   eins'

/*
$=/tstCompSynAss5/
    ### start tst tstCompSynAss5 ######################################
    compile @, 1 lines: $=  abc eins $$ = x
    *** err: scanErr = expected in assignment after $= var
    .    e 1: last token  scanPosition eins $$ = x
    .    e 2: pos 9 in line 1: $=  abc eins $$ = x
$/tstCompSynAss5/ */
    call tstComp1 '@ tstCompSynAss5 +', '$=  abc eins $$ = x'

/*
$=/tstCompSynAss6/
    ### start tst tstCompSynAss6 ######################################
    compile @, 1 lines: $=  abc =
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $=  abc =
$/tstCompSynAss6/ */
    call tstComp1 '@ tstCompSynAss6 +', '$=  abc ='

/*
$=/tstCompSynAss7/
    ### start tst tstCompSynAss7 ######################################
    compile @, 1 lines: $=  abc =..
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition ..
    .    e 2: pos 10 in line 1: $=  abc =..
$/tstCompSynAss7/ */
    call tstComp1 '@ tstCompSynAss7 +', '$=  abc =.'
    return
endProcedure tstCompSynAss

tstCompSynRun: procedure expose m.

/*
$=/tstCompSynRun1/
    ### start tst tstCompSynRun1 ######################################
    compile @, 1 lines: $@
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@
    .    e 2: pos 1 in line 1: $@
$/tstCompSynRun1/ */
    call tstComp1 '@ tstCompSynRun1 +', '$@'

/*
$=/tstCompSynRun2/
    ### start tst tstCompSynRun2 ######################################
    compile @, 1 lines: $@=
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@=
    .    e 2: pos 1 in line 1: $@=
$/tstCompSynRun2/ */
    call tstComp1 '@ tstCompSynRun2 +', '$@='

/*
$=/tstCompSynRun3/
    ### start tst tstCompSynRun3 ######################################
    compile @, 1 lines: $@: und
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@: und
    .    e 2: pos 1 in line 1: $@: und
    *** err: no class found for object 0
$/tstCompSynRun3/ */
    call tstComp1 '@ tstCompSynRun3 +', '$@: und'

/*
$=/tstCompSynFor4/
    ### start tst tstCompSynFor4 ######################################
    compile @, 1 lines: $@for
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $@for
$/tstCompSynFor4/ */
    call tstComp1 '@ tstCompSynFor4 +', '$@for'

/*
$=/tstCompSynFor5/
    ### start tst tstCompSynFor5 ######################################
    compile @, 2 lines: $@for
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $@for
$/tstCompSynFor5/ */
    call tstComp1 '@ tstCompSynFor5 +', '$@for', a

/*
$=/tstCompSynFor6/
    ### start tst tstCompSynFor6 ######################################
    compile @, 2 lines: a
    *** err: scanErr variable or named block after for
    .    e 1: last token  scanPosition .
    .    e 2: pos 15 in line 2:  b $@for   $$q
$/tstCompSynFor6/ */
    call tstComp1 '@ tstCompSynFor6 +', 'a', ' b $@for   $$q'

/*
$=/tstCompSynFor7/
    ### start tst tstCompSynFor7 ######################################
    compile @, 3 lines: a
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 11 in line 2:  b $@for a
$/tstCompSynFor7/ */
    call tstComp1 '@ tstCompSynFor7 +', 'a', ' b $@for a', '  $$q'

/*
$=/tstCompSynCt8/
    ### start tst tstCompSynCt8 #######################################
    compile @, 3 lines: a
    *** err: scanErr ct statement expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 8 in line 2:  b $@ct
$/tstCompSynCt8/ */
    call tstComp1 '@ tstCompSynCt8 +', 'a', ' b $@ct', '  $$q'

/*
$=/tstCompSynProc9/
    ### start tst tstCompSynProc9 #####################################
    compile @, 2 lines: a
    *** err: scanErr var or namedBlock expected after proc
    .    e 1: last token  scanPosition .
    .    e 2: pos 15 in line 2:  b $@proc  $$q
$/tstCompSynProc9/ */
    call tstComp1 '@ tstCompSynProc9 +', 'a', ' b $@proc  $$q'

/*
$=/tstCompSynProcA/
    ### start tst tstCompSynProcA #####################################
    compile @, 2 lines: $@proc p1
    *** err: scanErr proc statement expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $@proc p1
$/tstCompSynProcA/ */
    call tstComp1 '@ tstCompSynProcA +', '$@proc p1', '  $$q'

/*
$=/tstCompSynCallB/
    ### start tst tstCompSynCallB #####################################
    compile @, 1 lines: $@% ¢roc p1$!
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@% ¢roc p1$!
    .    e 2: pos 1 in line 1: $@% ¢roc p1$!
$/tstCompSynCallB/ */

    call tstComp1 '@ tstCompSynCallB +', '$@% ¢roc p1$!'

/*
$=/tstCompSynCallC/
    ### start tst tstCompSynCallC #####################################
    compile @, 1 lines: $@%¢call roc p1 !
    *** err: scanErr ending $! expected after ¢
    .    e 1: last token  scanPosition .
    .    e 2: pos 18 in line 1: $@%¢call roc p1 !
$/tstCompSynCallC/ */
    call tstComp1 '@ tstCompSynCallC +', '$@%¢call roc p1 !'

/*
$=/tstCompSynCallD/
    ### start tst tstCompSynCallD #####################################
    compile @, 2 lines: $@^¢call( $** roc
    *** err: scanErr ending $! expected after ¢
    .    e 1: last token  scanPosition .
    .    e 2: pos 18 in line 1: $@^¢call( $** roc
$/tstCompSynCallD/ */
    call tstComp1 '@ tstCompSynCallD +',
        ,'$@^¢call( $** roc' , ' $*( p1 $*) )'
    return
endProcedure tstCompSynRun

tstCompObj: procedure expose m.
    call tstReset t
    call oIni
    cl = classNew('n? tstCompCla u v, f FEINS v, f FZWEI v')
    do rx=1 to 10
        o = oNew(cl)
        m.tstComp.rx = o
        m.o = 'o'rx
        if rx // 2 = 0 then do
            m.o.fEins = 'o'rx'.1'
            m.o.fZwei = 'o'rx'.fZwei'rx
            end
        else do
            m.o.fEins = 'o'rx'.fEins'
            m.o.fZwei = 'o'rx'.2'
            end
        call mAdd 'T.TRANS', m.tstComp.rx '<o'rx'>'
        end

/*
$=/tstCompObjRef/
    ### start tst tstCompObjRef #######################################
    compile @, 13 lines: o1=m.tstComp.1
    run without input
    out .$"string" o1
    string
    out . o1
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .{ o2 }
    tstR: @<o2> isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    out .¢ o3 $!
    tstR: @<o3> isA :tstCompCla = o3
    tstR:  .FEINS = o3.fEins
    tstR:  .FZWEI = o3.2
    out .¢ o4 $!
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    out ./-/ o5 $/-/
    tstR: @<o5> isA :tstCompCla = o5
    tstR:  .FEINS = o5.fEins
    tstR:  .FZWEI = o5.2
$/tstCompObjRef/ */
m.ttt=1
    call tstComp1 '@ tstCompObjRef' ,
        , 'o1=m.tstComp.1',
        , 'o2 = m.tstComp.2' ,
        , '$$ out $".$""string""" o1 $$.$"string"',
        , '$$ out . o1 $$. o1',
        , '$$ out .{ o2 } $$.¢ o2 $!',
        , '$$ out .¢ o3 $"$!" $$.¢ ', ' m.tstComp.3 ', ' $!',
        , '$$ out .¢ o4 $"$!" $$.¢ ', ' m.tstComp.4 ', ' $!',
        , '$$ out ./-/ o5 $"$/-/" $$./-/  m.tstComp.5 ', ' $/-/'

/*
$=/tstCompObjRefPri/
    ### start tst tstCompObjRefPri ####################################
    compile @, 9 lines: $$ out .$"$.{o1}" $$.¢ m.tstComp.1 $!
    run without input
    out .$.{o1}
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .$.-{o2}
    <o2>
    out .$.={o3}
    . m.tstComp.3 .
    out .$.@{out o4}
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    tstWriteO kindOf ORun oRun end   >>>
    out .$.@¢$$abc $$efg$!
    tstWriteO kindOf ORun oRun begin <<<
    abc
    efg
    tstWriteO kindOf ORun oRun end   >>>
    out .$.@¢o5$!
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o5> isA :tstCompCla = o5
    tstR:  .FEINS = o5.fEins
    tstR:  .FZWEI = o5.2
    abc
    tstWriteO kindOf ORun oRun end   >>>
$/tstCompObjRefPri/ */
    call tstComp1 '@ tstCompObjRefPri' ,
        , '$$ out .$"$.{o1}" $$.¢ m.tstComp.1 $!',
        , '$$ out .$"$.-{o2}" $$.-¢ m.tstComp.2 $!',
        , '$$ out .$"$.={o3}" $$.=¢ m.tstComp.3 $!',
        , '$$ out .$"$.@{out o4}" $$.@@¢ call outO m.tstComp.4 $!',
    , '$$ out .$"$.@¢$$abc $$efg$!" $$.$.@@¢ $$abc ', ' ', ' $$efg $!',
        , '$$ out .$"$.@¢o5$!" $$.$.@@¢ $$.m.tstComp.5', '$$abc $!'

/*
$=/tstCompObjRefFile/
    ### start tst tstCompObjRefFile ###################################
    compile @, 7 lines: $$ out .$".<.¢o1!" $$.<.¢ m.tstComp.1 $!
    run without input
    out ..<.¢o1!
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .<$.-{o2}
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o2> isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$.<{o3}
    tstWriteO kindOf JRW jWriteNow begin <<<
    . m.tstComp.3 .
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$.<@{out o4}
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$<@¢$$abc $$efg$!
    tstWriteO kindOf JRW jWriteNow begin <<<
    abc
    efg
    tstWriteO kindOf JRW jWriteNow end   >>>
$/tstCompObjRefFile/ */

    call tstComp1 '@ tstCompObjRefFile' ,
        , '$$ out .$".<.¢o1!" $$.<.¢ m.tstComp.1 $!',
        , '$$ out .$"<$.-{o2}" $$<.¢ m.tstComp.2 $!',
        , '$$ out .$"$.<{o3}" $$<=¢ m.tstComp.3 $!',
        , '$$ out .$"$.<@{out o4}" $$<@¢ call outO m.tstComp.4 $!',
    , '$$ out .$"$<@¢$$abc $$efg$!" $$<@¢ $$abc ', ' ', ' $$efg $!'

/*
$=/tstCompObjFor/
    ### start tst tstCompObjFor #######################################
    compile @, 2 lines: $@do rx=1 to 3 $$. m.tstComp.rx
    run without input
    FEINS=o1.fEins FZWEI=o1.2
    FEINS=o2.1 FZWEI=o2.fZwei2
    FEINS=o3.fEins FZWEI=o3.2
$/tstCompObjFor/
*/
    call tstComp1 '@ tstCompObjFor' ,
        , '$@do rx=1 to 3 $$. m.tstComp.rx' ,
        , '$| $@forWith witx $$ FEINS=$FEINS FZWEI=$FZWEI'

/*
$=/tstCompObjRun/
    ### start tst tstCompObjRun #######################################
    compile @, 4 lines: $$ out .$"$@¢o1!" $$@¢ $$. m.tstComp.1 $!
    run without input
    out .$@¢o1!
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstWriteO kindOf ORun oRun end   >>>
    out .$<@¢$$abc $$efg$!
    tstWriteO kindOf JRW jWriteNow begin <<<
    abc
    efg
    tstWriteO kindOf JRW jWriteNow end   >>>
$/tstCompObjRun/ */
    call tstComp1 '@ tstCompObjRun' ,
        , '$$ out .$"$@¢o1!" $$@¢ $$. m.tstComp.1 $!',
    , '$$ out .$"$<@¢$$abc $$efg$!" $$<@¢ $$abc ', ' ', ' $$efg $!'

    m.t.trans.0 = 0
/*
$=/tstCompObj/
    ### start tst tstCompObj ##########################################
    compile @, 6 lines: o1=m.tstComp.1
    run without input
    out . o1
    tstR: @tstWriteoV1 isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .{ o2 }
    tstR: @tstWriteoV2 isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    out .¢ o1, o2!
    tstR: @tstWriteoV1 isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstR: @tstWriteoV2 isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
$/tstCompObj/ */
    call tstComp1 '@ tstCompObj' ,
        , 'o1=m.tstComp.1',
        , 'o2 = m.tstComp.2' ,
        , '$$ out . o1 $$. o1',
        , '$$ out .{ o2 } $$.¢ o2 $!',
    , '$$ out .¢ o1, o2!$; $@<.¢  m.tstComp.1  ', '  m.tstComp.2  $!'
    return
    m.t.trans.0 = 0
endProcedure tstCompObj

tstCompORun: procedure expose  m.
/*
$=/tstCompORun/
    ### start tst tstCompORun #########################################
    compile @, 6 lines: $@oRun
    run without input
    oRun arg=1, v2=, v3=, v4=
    oRun arg=1, v2=, v3=, v4=
    oRun arg=2, v2=-{1 arg only} oder?, v3=, v4=
    oRun arg=2, v2=!.{1 obj only} oder?, v3=, v4=
    oRun arg=3, v2={2 args}, v3=und zwei?, v4=
    oRun arg=4, v2={3 args}, v3=zwei, v4=und drei?
$/tstCompORun/ */
    call compIni
    call vPutO 'oRun', oRunner('parse arg , v2, v3, v4;',
        'call tstOut t, "oRun arg="arg()", v2="v2", v3="v3", v4="v4' )
    call tstComp1 '@ tstCompORun',
        , '$@oRun', '$@%¢oRun$!' ,
        , '    $@%¢oRun $"-{1 arg only}" oder?$!' ,
        , '    $@%-¢oRun $.".{1 obj only}" ''oder?''$! $=v2=zwei' ,
        , '    $@%-¢oRun $"{2 args}", "und" $v2"?"$!' ,
        , '    $@%-¢oRun $"{3 args}", $v2, "und drei?"$!'
    return
endProcedure tstCompORun

tstCompDataIO: procedure expose m.
/*
$=/tstCompDataHereData/
    ### start tst tstCompDataHereData #################################
    compile =, 13 lines:  herdata $@#/stop/    .
    run without input
    . herdata .
    heredata 1 $x
    heredata 2 $y
    nach heredata
    . herdata ¢ .
    heredata 1 xValue
    heredata 2 yValueY
    nach heredata ¢
    . herdata { .
    HEREDATA 1 xValue
    heredata 2 yValueY
    nach heredata {
$/tstCompDataHereData/ */
    call tstComp1 '= tstCompDataHereData',
        , ' herdata $@#/stop/    ',
        , 'heredata 1 $x',
        , 'heredata 2 $y',
        , '$/stop/ $$ nach heredata',
        , ' herdata ¢ $@=/stop/    ',
        , '$=x=xValue$=y=yValueY',
        , 'heredata 1 $x',
        , 'heredata 2 $y',
        , '$/stop/ $$ nach heredata ¢',
        , ' herdata { $@/st/',
        , '; call out heredata 1 $x',
        , '$$heredata 2 $y',
        , '$/st/ $$ nach heredata {'
/*
$=/tstCompDataIO/
    ### start tst tstCompDataIO #######################################
    compile =, 5 lines:  input 1 $@.<-=¢$dsn $*+.
    run without input
    . input 1 .
    readInp line 1                       .
    readInp line 2                       .
    . nach dsn input und nochmals mit & .
    readInp line 1                       .
    readInp line 2                       .
    . und schluiss..
$/tstCompDataIO/ */
    dsn = word(tstPdsMbr(tstFilename('lib37', 'r'), 'readInp'), 1)
    dsnFB = strip(dsn tstFB('::F37', 0))
    b = jBuf(tstFB('readInp line 1', 37),
            ,tstFB('readInp line 2', 37))
    extFd = tstFB('&dsn('dsn') dd(xyz)', 0)
    extFd = tstFB(dsn 'dd(xyz) ::f', 0)
    if extFd = '' then
        extFd = dsn
    call jCat file(dsnFB), b
    call vPut 'dsn', dsn
    say 'dsn' dsn 'extFD' extFD'?'
    call tstComp1 '= tstCompDataIO',
        , ' input 1 $@.<-=¢$dsn $*+',
        , tstFB('::f', 0) '$!',
        , ' nach dsn input und nochmals mit & ' ,
        , '         $@.<"'extFD'"',
        , ' und schluiss.'
    return
endProcedure tstCompDataIO

tstObjVF: procedure expose m.
parse arg v, f
    obj  = oNew(classNew('n? TstClassVF u v, f FLD1 v'))
    m.obj = if(f=='','val='v, v)
    m.obj.fld1 = if(f=='','FLD1='v, f)
    return obj
endProcedure tstObjVF

tstCompFile: procedure expose m.
/*
$=/tstCompFileBloSrc/
    $=vv=value-of-vv
    ###file from empty # block
    $@<#¢
        $!
    ###file from 1 line # block
    $@<#¢
    the only $ix+1/0 line $vv
    $!
    ###file from 2 line # block
    $@<#¢
        first line /0 $*+ no comment
        second and last line $$ $wie
    $!
    ===file from empty = block
    $@<=¢     $*+ comment
        $!
    ===file from 1 line = block
    $@<=¢ the only line $!
    ===file from 2 line = block
    $@<=¢ first line$** comment
        second and last line  $!
    ---file from empty - block
    $@<-/s/
        $/s/
    ---file from 1 line - block
    $@<-/s/ the only "line" (1*1) $/s/
    ---file from 2 line = block
    $@<-// first "line" (1+0)
        second   and   "last  line" (1+1)  $//
    ...file from empty . block
    $@<.¢
        $!
    ...file from 1 line . block
    $@<.¢ tstObjVF('v-Eins', '1-Eins') $!
    ...file from 2 line . block
    $@<.¢ tstObjVF('v-Elf', '1-Elf')
        tstObjVF('zwoelf')  $!
    ...file from 3 line . block
    $@<.¢ tstObjVF('einUndDreissig')
            s2o('zweiUndDreissig' o2String($vv))
            tstObjVF('dreiUndDreissig')  $!
    @@@file from empty @ block
    $@<@¢
        $!
    $=noOutput=before
    @@@file from nooutput @ block
    $@<@¢ nop
        $=noOutput = run in block $!
    @@@nach noOutput=$noOutput
    @@@file from 1 line @ block
    $@<@¢ $$. tstObjVF('w-Eins', 'w1-Eins') $!
    @@@file from 2 line @ block
    $@<@¢ $$.tstObjVF('w-Elf', 'w1-Elf')
        y='zwoelf' $$-y  $!
    @@@file from 3 line @ block
    $@<@¢ $$.tstObjVF('w einUndDreissig')    $$ +
    zweiUndDreissig $$ 33 $vv$!
    {{{ empty ¢ block
    $@<¢     $!
    {{{ empty ¢ block with comment
    $@<¢    $*+ abc
          $!
    {{{ one line ¢ block
    $@<¢ the only $"¢...$!" line $*+.
        $vv $!
    {{{ one line -¢ block
    $@<-¢ the only $"-¢...$!"  "line" $vv  $!
    {{{ empty #¢ block
    $@<#¢
$!
    {{{ one line #¢ block
    $@<#¢ the only $"-¢...$!"  "line" $vv $¢vv${x}$!
$!
$/tstCompFileBloSrc/ */
/*
$=/tstCompFileBlo/
    ### start tst tstCompFileBlo ######################################
    compile =, 72 lines: $=vv=value-of-vv
    run without input
    ###file from empty # block
    ###file from 1 line # block
    the only $ix+1/0 line $vv
    ###file from 2 line # block
    first line /0 $*+ no comment
    second and last line $$ $wie
    ===file from empty = block
    ===file from 1 line = block
    . the only line .
    ===file from 2 line = block
    . first line
    second and last line  .
    ---file from empty - block
    ---file from 1 line - block
    THE ONLY line 1
    ---file from 2 line = block
    FIRST line 1
    SECOND AND last  line 2
    ...file from empty . block
    ...file from 1 line . block
    tstR: @tstWriteoV1 isA :TstClassVF = v-Eins
    tstR:  .FLD1 = 1-Eins
    ...file from 2 line . block
    tstR: @tstWriteoV2 isA :TstClassVF = v-Elf
    tstR:  .FLD1 = 1-Elf
    tstR: @tstWriteoV3 isA :TstClassVF = val=zwoelf
    tstR:  .FLD1 = FLD1=zwoelf
    ...file from 3 line . block
    tstR: @tstWriteoV4 isA :TstClassVF = val=einUndDreissig
    tstR:  .FLD1 = FLD1=einUndDreissig
    zweiUndDreissig value-of-vv
    tstR: @tstWriteoV5 isA :TstClassVF = val=dreiUndDreissig
    tstR:  .FLD1 = FLD1=dreiUndDreissig
    @@@file from empty @ block
    @@@file from nooutput @ block
    @@@nach noOutput=run in block
    @@@file from 1 line @ block
    tstR: @tstWriteoV6 isA :TstClassVF = w-Eins
    tstR:  .FLD1 = w1-Eins
    @@@file from 2 line @ block
    tstR: @tstWriteoV7 isA :TstClassVF = w-Elf
    tstR:  .FLD1 = w1-Elf
    zwoelf
    @@@file from 3 line @ block
    tstR: @tstWriteoV8 isA :TstClassVF = val=w einUndDreissig
    tstR:  .FLD1 = FLD1=w einUndDreissig
    zweiUndDreissig
    33 value-of-vv
    {{{ empty ¢ block
    {{{ empty ¢ block with comment
    {{{ one line ¢ block
    . the only ¢...$! line value-of-vv .
    {{{ one line -¢ block
    THE ONLY -¢...$! line value-of-vv
    {{{ empty #¢ block
    {{{ one line #¢ block
    . the only $"-¢...$!"  "line" $vv $¢vv${x}$!
$/tstCompFileBlo/ */
    call tstComp2 'tstCompFileBlo', '='
    m.t.trans.0 = 0

/*
$=/tstCompFileObjSrc/
    $=vv=value-vv-1
    $=fE=<¢ $!
    $=f2=.$.<.¢s2o("f2 line 1" o2String($vv))
         tstObjVF("f2 line2") $!
    ---empty file $"$@<$fE"
    $@fE
    ---file with 2 lines $"$@<$f2"
    $@.<.f2
    $=vv=value-vv-2
    ---file with 2 lines $"$@<$f2"
    $@.<.f2
    $= dsn =- word(tstPdsMbr(tstFilename('libvb', 'r'), 'fileObj'),1) +
                tstFB('::V', 0)
    $@¢
        fi=jOpen(file($dsn),'>')
        call jWrite fi, 'line one on' $"$dsn"
        call jWrite fi, 'line two on' $"$dsn"
        call jClose fi
    $!
    ---file on disk out
    $@.<.dsn
$/tstCompFileObjSrc/ */
/*
$=/tstCompFileObj/
    ### start tst tstCompFileObj ######################################
    compile =, 20 lines: $=vv=value-vv-1
    run without input
    ---empty file $@<$fE
    ---file with 2 lines $@<$f2
    f2 line 1 value-vv-1
    tstR: @tstWriteoV1 isA :TstClassVF = val=f2 line2
    tstR:  .FLD1 = FLD1=f2 line2
    ---file with 2 lines $@<$f2
    f2 line 1 value-vv-1
    tstR: @tstWriteoV1 isA :TstClassVF = val=f2 line2
    tstR:  .FLD1 = FLD1=f2 line2
    ---file on disk out
    line one on $dsn
    line two on $dsn
$/tstCompFileObj/ */
    call tstComp2 'tstCompFileObj', '='

    return
endProcedure tstCompFile

tstCompPipe: procedure expose m.
/*
$=/tstCompPipe1/
    ### start tst tstCompPipe1 ########################################
    compile @, 1 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    (1 eins zwei drei 1)
    #jIn 2# zehn elf zwoelf?
    (1 zehn elf zwoelf? 1)
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    (1 zwanzig 21 22 23 24 ... 29| 1)
    #jIn eof 4#
$/tstCompPipe1/ */
    call tstComp1 '@ tstCompPipe1 3',
        , ' call pipePreSuf "(1 ", " 1)"'
/*
$=/tstCompPipe2/
    ### start tst tstCompPipe2 ########################################
    compile @, 2 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    ¢2 (1 eins zwei drei 1) 2!
    ¢2 (1 zehn elf zwoelf? 1) 2!
    ¢2 (1 zwanzig 21 22 23 24 ... 29| 1) 2!
$/tstCompPipe2/ */
    call tstComp1 '@ tstCompPipe2 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| call pipePreSuf "¢2 ", " 2!"'

/*
$=/tstCompPipe3/
    ### start tst tstCompPipe3 ########################################
    compile @, 3 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <3 ¢2 (1 eins zwei drei 1) 2! 3>
    <3 ¢2 (1 zehn elf zwoelf? 1) 2! 3>
    <3 ¢2 (1 zwanzig 21 22 23 24 ... 29| 1) 2! 3>
$/tstCompPipe3/ */
    call tstComp1 '@ tstCompPipe3 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| call pipePreSuf "¢2 ", " 2!"',
        , ' $| call pipePreSuf "<3 ", " 3>"'

/*
$=/tstCompPipe4/
    ### start tst tstCompPipe4 ########################################
    compile @, 7 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <3 ¢222 ¢221 ¢21 ¢20 (1 eins zwei drei 1) 20! 21! 221! 222! 3>
    <3 ¢222 ¢221 ¢21 ¢20 (1 zehn elf zwoelf? 1) 20! 21! 221! 222! 3>
    <3 ¢222 ¢221 ¢21 ¢20 (1 zwanzig 21 22 23 24 ... 29| 1) 20! 21! 221!+
    . 222! 3>
$/tstCompPipe4/ */
    call tstComp1 '@ tstCompPipe4 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| $@¢    call pipePreSuf "¢20 ", " 20!"',
        ,        ' $| call pipePreSuf "¢21 ", " 21!"',
        ,        ' $| $@¢      call pipePreSuf "¢221 ", " 221!"',
        ,                 ' $| call pipePreSuf "¢222 ", " 222!"',
        ,     '$!     $! ',
        , ' $| call pipePreSuf "<3 ", " 3>"'
    return
endProcedure tstCompPipe


tstCompPip2: procedure expose m.
/*
$=/tstCompPip21/
    ### start tst tstCompPip21 ########################################
    compile @, 3 lines:  $<¢ zeile eins .
    run without input
    (1  zeile eins  1)
    (1    zeile zwei  1)
    run with 3 inputs
    (1  zeile eins  1)
    (1    zeile zwei  1)
$/tstCompPip21/ */
    call tstComp1 '@ tstCompPip21 3',
        , ' $<¢ zeile eins ' ,
        , '   zeile zwei $!' ,
        , ' call pipePreSuf "(1 ", " 1)"'
/*
$=/tstCompPip22/
    ### start tst tstCompPip22 ########################################
    compile @, 3 lines: if ${>i1} then $@¢
    run without input
    #jIn eof 1#
    nachher
    run with 3 inputs
    #jIn 1# eins zwei drei
    <zeile 1: eins zwei drei>
    <zwei>
    nachher
$/tstCompPip22/ */
    call tstComp1 '@ tstCompPip22 3',
        , 'if ${>i1} then $@¢'          ,
        , ' $$ zeile 1: $i1 $$ zwei $| call pipePreSuf "<",">" $!',
        , ' $$ nachher '
    return
endProcedure tstCompPip2

tstCompRedir: procedure expose m.
/*
$=/tstCompRedir/
    ### start tst tstCompRedir ########################################
    compile @, 6 lines:  $=eins=<@¢ $@for vv $$ <$vv> $! .
    run without input
    #jIn eof 1#
    output eins .
    output piped zwei .
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    output eins <eins zwei drei> <zehn elf zwoelf?> <zwanzig 21 22 23 2+
    4 ... 29|>
    output piped zwei ab<eins zwei drei>yz ab<zehn elf zwoelf?>yz ab<zw+
    anzig 21 22 23 24 ... 29|>yz
$/tstCompRedir/ */
    call pipeIni
    call vRemove 'eins'  /* alte Variable loswerden */
    dsn = word(tstPdsMbr(tstFilename('libvb', 'r'), 'redir1'), 1)
    call vPut 'dsn', dsn
    call tstComp1 '@ tstCompRedir 3' ,
        , ' $=eins=<@¢ $@for vv $$ <$vv> $! ',
        , ' $$ output eins $-=¢$eins$!$; ',
        , ' $@for ww $$b${ww}y ',
        , '     $>$-¢ $dsn $! 'tstFB('::v', 0),
        ,         '$| call pipePreSuf "a", "z" $<.$eins',
        , ' $; $$ output piped zwei $-=¢$@<dsn$! '
    return
endProcedure tstCompRedir

tstCompComp: procedure expose m.
/*
$=/tstCompCompShell/
    ### start tst tstCompCompShell ####################################
    compile @, 5 lines: $$compiling shell $; $= rrr =.$.^compile $<@#/+
    aaa/
    run without input
    compiling shell
    running einmal
    RUN 1 COMPILED einmal
    #jIn eof 1#
    running zweimal
    RUN 1 COMPILED zweimal
    #jIn eof 2#
    run with 3 inputs
    compiling shell
    running einmal
    RUN 1 COMPILED einmal
    #jIn 1# eins zwei drei
    compRun eins zwei dreieinmal
    #jIn 2# zehn elf zwoelf?
    compRun zehn elf zwoelf?einmal
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    compRun zwanzig 21 22 23 24 ... 29|einmal
    #jIn eof 4#
    running zweimal
    RUN 1 COMPILED zweimal
    #jIn eof 5#
$/tstCompCompShell/ */
    call tstComp1 '@ tstCompCompShell 3',
        ,  "$$compiling shell $; $= rrr =.$.^compile $<@#/aaa/",
        ,  "call out run 1*1*1 compiled $cc; $@for v $$ compRun $v$cc",
        ,  "$/aaa/ $;",
        ,  "$=cc=einmal $$ running $cc $@rrr",
        ,  "$=cc=zweimal $$ running $cc $@rrr"
/*
$=/tstCompCompData/
    ### start tst tstCompCompData #####################################
    compile @, 5 lines: $$compiling data $; $= rrr =. $.^¢compile =$!  +
    $<@#/aaa/
    run without input
    compiling data
    running einmal
    call out run 1*1*1 compiled einmal
    running zweimal
    call out run 1*1*1 compiled zweimal
    run with 3 inputs
    compiling data
    running einmal
    call out run 1*1*1 compiled einmal
    running zweimal
    call out run 1*1*1 compiled zweimal
$/tstCompCompData/ */
    call tstComp1 '@ tstCompCompData 3',
        ,  "$$compiling data $; $= rrr =. $.^¢compile =$!  $<@#/aaa/",
        ,  "call out run 1*1*1 compiled $cc",
        ,  "$/aaa/ $;",
        ,  "$=cc=einmal $$ running $cc $@rrr",
        ,  "$=cc=zweimal $$ running $cc $@rrr"
    return
endProcedure tstCompComp

tstCompDir: procedure expose m.
/*
$=/tstCompDirSrc/
  'in src v1='$v1
  $#@ call out 'src @ out v1='$v1
  $#. $*(komm$*) s2o('src . v1=')
       $v1
  $#-
    'src - v1='$v1
  $#=
    src = v1=$v1
$/tstCompDirSrc/

$=/tstCompDir/
    ### start tst tstCompDir ##########################################
    compile @call out 'before v1='$v1 $=v1=eins $#. s2o('. v1='$-v1) $#+
    @ call out '@ v1='$v1 $#= = v1=$v1 $#- '- v1='$v1 $#-, 8 lines: 'in+
    . src v1='$v1
    run without input
    before v1=v1Before
    .. v1=eins
    @ v1=eins
    = v1=eins .
    - v1=eins
    in src v1=eins
    src @ out v1=eins
    src . v1=
    eins
    src - v1=eins
    src = v1=eins
$/tstCompDir/ */
    call vPut 'v1', 'v1Before'
    call tstComp2 'tstCompDir', "@call out 'before v1='$v1 $=v1=eins" ,
        "$#. s2o('. v1='$-v1) $#@ call out '@ v1='$v1" ,
        "$#= = v1=$v1 $#- '- v1='$v1 $#-"
/*
$=/tstCompDirPiSrc/
  zeile 1 v1=$v1
  zweite Zeile vor $"$@$#-"
  $#@  $@proc pi2 $@-¢
  $'zeile drei nach $@$#- v1='v1
  vierte und letzte Zeile $!
$/tstCompDirPiSrc/ */
/*
$=/tstCompDirPi/
    ### start tst tstCompDirPi ########################################
    compile @call pipePreSuf '<','>' $=v1=eiPi $<.$pi2 $#=, 5 lines: ze+
    ile 1 v1=$v1
    run without input
    <zeile drei nach $@$#- v1=V1>
    <VIERTE UND LETZTE ZEILE>
    zeile 1 v1=eiPi
    zweite Zeile vor $@$#-
$/tstCompDirPi/ */
    call tstComp2 'tstCompDirPi',
            , "@call pipePreSuf '<','>' $=v1=eiPi $<.$pi2 $#="
    return
endProcedure tstCompDir

tstCompColon: procedure expose m.
/*
$=/tstCompColon1/
    ### start tst tstCompColon1 #######################################
    compile :, 12 lines: vA = valueVonA
    run without input
    vA = valueVonA
    vA=valueVonA vB=valueVonB vC=valueVonC
    vC=valueVonC vD=valueVonD vE=valueVonvE
    vF=6
$/tstCompColon1/ */
    call tstComp1 ': tstCompColon1',
        , 'vA = valueVonA' ,
        , ' $$ vA = $vA' ,
        , '        * kommentar ' ,
        , '=vB=- "valueVonB"' ,
        , '=/vC/valueVonC$/vC/' ,
        , ' $$ vA=$vA vB=$vB vC=$vC' ,
        , '=/vD/valueVonD',
        , '$/vD/ vE=valueVonvE' ,
        , '        * kommentar ' ,
        , ' $$ vC=$vC vD=$vD vE=$vE',
        , 'vF=- 2*3 $=vG=@@¢ $$ vF=$vF$!' ,
        , '@vG'

/*
$=/tstCompColon2/
    ### start tst tstCompColon2 #######################################
    compile :, 7 lines: ix=0
    run without input
    #jIn eof 1#
    proc p1 arg(2) total 0 im argumentchen
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <<for 1 -> eins zwei drei>>
    <<for 2 -> zehn elf zwoelf?>>
    <<for 3 -> zwanzig 21 22 23 24 ... 29|>>
    proc p1 arg(2) total 3 im argumentchen
$/tstCompColon2/

*/
    call tstComp1 ': tstCompColon2 3',
        , 'ix=0' ,
        , 'for v @:¢ix=- $ix+1' ,
        , ' $$ for $ix -> $v' ,
        , '! | @¢call pipePreSuf "<<",">>"',
        , '$! @%¢p1 total $ix im argumentchen$!',
        , 'proc @:/p1/$$- "proc p1 arg(2)" arg(2)' ,
        , '/p1/'
/*
$=/tstCompColon3/
    ### start tst tstCompColon3 #######################################
    compile :, 11 lines: tc3Eins=freeVar1
    run without input
    tc3Eins=freeVar1 o2&tc3Eins= o2&tc3Zwei=
    tc3Eins=freeVar1 o2&tc3Eins=with3Eins o2&tc3Zwei=with3Zwei
    tc3Eins=freeVar1 o2&tc3Eins=ass4Eins o2&tc3Zwei=with5Zwei
    o3&tc3Eins=ass4Eins o3&tc3Zwei=with5 o3 Zwei
    tc3Eins=freeVar1 o2&tc3Eins=ass4Eins o2&tc3Zwei=with5Zwei
$/tstCompColon3/
*/
    call classNew 'n? TstCompColon3 u f tc3Eins v, f tc3Zwei v'
    showO2 = 'tc3Eins=$tc3Eins' ,
            'o2&tc3Eins=${o2&tc3Eins} o2&tc3Zwei=${o2&tc3Zwei}'
    showO3 = 'o3&tc3Eins=${o3&tc3Eins} o3&tc3Zwei=${o3&tc3Zwei}'
    call tstComp1 ': tstCompColon3',
        , 'tc3Eins=freeVar1' ,
     , 'o2 =. oNew("TstCompColon3")' ,
        , '$$' showO2 ,
        , 'with $o2 $@:¢tc3Eins = with3Eins',
        ,     'tc3Zwei = with3Zwei',
        ,    '! $$' showO2 ,
        , '{o2&tc3Eins} = ass4Eins',
        , 'with $o2 $=tc3Zwei = with5Zwei',
        , '$$' showO2 ,
        , 'with o3 =. oCopy($o2) $=tc3Zwei = with5 o3 Zwei',
        , '$$' showO3 '$$' showO2
    return
endProcedure tstCompColon

tstCompTable: procedure expose m.
/*
$=/tstCompTable1/
    ### start tst tstCompTable1 #######################################
    compile :, 6 lines: table $*( sdf $*)   .
    run without input
    tstR: @tstWriteoV2 isA :<TstCT1Class>
    tstR:  .fEins = v1
    tstR:  .fZwei = valueZwei
    tstR:  .fDrei = undDrei
    zweite
    tstR: @tstWriteoV3 isA :<TstCT1Class>
    tstR:  .fEins = w1
    tstR:  .fZwei = wZwei
    tstR:  .fDrei = wwwDrei
$/tstCompTable1/

 */
    cl = classNew('n* CompTable u f fEins v, f fZwei v, f fDrei v')
    call tstComp1 ': tstCompTable1',
        , 'table $*( sdf $*)   ' ,
        , 'fEins   fZwei $*(....$*) fDrei  ' ,
        , '$@¢call mAdd t.trans, className("'cl'") "<TstCT1Class>"$!',
        , '    v1     valueZwei undDrei     ' ,
        , '$$ zweite',
        , ' w1 wZwei                    wwwDrei     '


/*
$=/tstCompWithNew/
    ### start tst tstCompWithNew ######################################
    compile :, 12 lines: withNew nn $*( sdf $*)  $@:¢  .
    run without input
    tstR: @tstWriteoV2 isA :<TstCT1Class>
    tstR:  .fEins = withNewValue fEins
    tstR:  .fZwei = withNewValue fZwei
    tstR:  .fDrei = withNewValuel drei
    tstR: @tstWriteoV3 isA :<TstCT1Class>
    tstR:  .fEins = withNewValue fEinsB
    tstR:  .fZwei = withNewValue fZweiB
    tstR:  .fDrei = withNewValue fDreiB
    tstR: @tstWriteoV4 isA :<TstCT1Class>
    tstR:  .fEins = withValue fEinsC
    tstR:  .fZwei = .
    tstR:  .fDrei = withValue fDreiC
$/tstCompWithNew/
*/
    call tstComp1 ': tstCompWithNew',
        , 'withNew nn $*( sdf $*)  $@:¢  ' ,
        , 'fEins = withNewValue fEins' ,
        , 'fZwei = withNewValue fZwei' ,
        , '$@¢call mAdd t.trans, className("'cl'") "<TstCT1Class>"',
        , '$@:¢   fDrei = withNewValuel drei $! $!! $$ $.nn' ,
        , 'withNew out $*( sdf $*)  $@:¢  ' ,
        , 'fEins = withNewValue fEinsB' ,
        , 'fZwei = withNewValue fZweiB',
        , 'fDrei = withNewValue fDreiB',
        , '! with out =. oNew('cl') $@:¢',
        , 'fEins = withValue fEinsC' ,
        , 'fDrei = withValue fDreiC $!'

    return
endProcedure tstCompTable

tstCompSql: procedure expose m.
/*
$=/tstCompSqlSrc/
$@=¢
   select strip(creator) cr, strip(name) tb,
            (row_number()over())*(row_number()over()) rr
       from sysibm.sysTables
       where creator='SYSIBM' and name like 'SYSTABL%'
       order by 2 fetch first 4 rows only
$!
$| call sqlSel
$| call fTabAuto
$/tstCompSqlSrc/
$=/tstCompSql/
    ### start tst tstCompSql ##########################################
    compile @, 9 lines: $@=¢
    run without input
    CR     TB                RR
    SYSIBM SYSTABLEPART       1
    SYSIBM SYSTABLEPART_HIST  4
    SYSIBM SYSTABLES          9
    SYSIBM SYSTABLESPACE     16
$/tstCompSql/
*/
    call sqlConnect
    call tstComp2 'tstCompSql', '@'

    call sqlDisConnect
    return
endProcedure tstCompFile
tstTut0: procedure expose m.
/*
$=/tstTut01Src/
$#=
$*+>.fSub()                               Kommentar
$*+>~tmp.jcl(t)                           Kommentar
$*+@=¢                                    Kommentar
$=subsys=DP4G
$=db=DA540769
$=ts=A977A
$*+@<~wk.jcl(jc)                          Kommentar
??* ?-¢sysvar(sysnode) date() time()?!ts=$ts 10*len=$-¢length($ts)*10$!
//P02     EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769C.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
  $@¢if right($ts, 2) == '7A' then $@=¢
    FULL YES
  $! else
    $$ $''    FULL NO
  $!
    SHRLEVEL CHANGE
$*+!                                      Kommentar
$#out                                              20140319 16:53:24
$/tstTut01Src/
$=/tstTut01/
    ### start tst tstTut01 ############################################
    compile , 28 lines: $#=
    run without input
    ??* ?-¢sysvar(sysnode) date() time()?!ts=A977A 10*len=50
    //P02     EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A540769C.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977A*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    FULL YES
    SHRLEVEL CHANGE
$/tstTut01/
$=/tstTut02Src/
$#@
$**>.fSub()
$**@¢
$=subsys=DP4G
$=db=DA540769
$=jx=0
$@do tx = 976 to 977 $@=¢
    $=ts=A$tx
    $=jx=-$jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$**!
$#out                                              20101229 13
$/tstTut02Src/
$=/tstTut02/
    ### start tst tstTut02 ############################################
    compile , 28 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut02/
$=/tstTut03Src/
$#@
$=subsys=DP4G
$@:¢table
      db         ts
      DGDB9998   A976
      DA540769   A977
$!
$** $| call fTabAuto
$**    $#end
$|
$=jx=0
$@forWith o $@=¢
    $=jx=-$jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$#out
$/tstTut03Src/
$=/tstTut03/
    ### start tst tstTut03 ############################################
    compile , 31 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut03/
$=/tstTut04Src/
$#@
$=subsys=DP4G
$=db=DA540769
call sqlConnect $subsys
$@=¢  select dbName  db , tsName  ts
          from sysibm.sysTables
          where creator = 'SYSIBM' and name like 'SYSINDEXPAR%'
          order by name desc
$!
$| call sqlSel
$** $| call fTabAuto
$** $#end
$|
$=jx=0
$@forWith o $@=¢
    $=jx=-$jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$TS    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $DB.$TS*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
call sqlDisConnect
$#out                                              20101229
$/tstTut04Src/
$=/tstTut04/
    ### start tst tstTut04 ############################################
    compile , 35 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CSYSHIST     EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DSNDB06 .SYSHIST *   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CSYSTSIPT    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DSNDB06 .SYSTSIPT*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut04/
$=/tstTut05Src/
$#:
subsys = DP4G
lst =<:¢withNew out :¢
    db = DGDB9998
    ts =<:¢table
             ts
             A976
             A977
    $!
    db = DA540769
    <|/ts/
             ts
             A976
             A975
    /ts/
!
$** $$. $lst
$** $@ct $@¢$=tool =. $.compile@ $<~.wk.rexx(wshtut06)$!
$** $@$tool
$@do sx=1 to ${lst.0} $@¢
    $=db = ${lst.$sx.db}
    $** $$. ${lst.$sx}
    $@do tx=1 to ${lst.$sx.ts.0} $@=¢
        $*+ $$. ${lst.$sx.ts.$tx}
        $=ts= ${lst.$sx.ts.$tx.ts}
        $@¢ say $-=¢subsys $subsys db $db ts $ts $! $!
        $@copy()
        $!
    $!
$@ct $@¢
cl = classNew('n? DbTsList u s' ,
     classNew('n? DbTs u f db v, f ts s' ,
     classNew('n? Ts u f ts v')))
$=lst=. oNew(cl)
$!
$@proc copy $@=¢
$@ct $=jx=0
$=jx=-$jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$#out                                              201012
$/tstTut05Src/
$=/tstTut05/
    ### start tst tstTut05 ############################################
    compile , 56 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407693 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407693.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407694 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA975    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407694.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A975*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut05/
   tstTut06   ==> platz für copy aus tut05
$=/tstTut07Src/
$**$>.fEdit()
call sqlConnect dp4g
$@:¢table
   ts
VTCASHFLOW
VTCASHFLOWAMOUNT
VTINSTRFLATDATA
$!
$| $@=¢
    select name, statstime, strip(dbName) || '.' || strip(tsName) dbts
       from sysibm.sysTables
       where creator = 'VDPS2' and name in
  $=co=(
  $@forWith t $@=¢
                                           $co '$ts'
      $=co=,
  $!
                                           )
$!
$| call sqlSel
$** $| call fTabAuto
$|
$=jx=0
$@forWith t $@=¢
$=jx=-$jx+1
//A540769$jx JOB (CP00,KE50),
//       'CATALOG',MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//STEP$jx EXEC PGM=DSNUTILB,TIME=1440,
//   PARM=(DP4G,'A540769$jx.RUNSTA'),
//   REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//UTPRINT  DD SYSOUT=*
//SYSOUT   DD SYSOUT=*
//RNPRIN01 DD SYSOUT=*
//SYSIN DD *
  LISTDEF LST#STA   INCLUDE TABLESPACE $DBTS
   OPTIONS EVENT (ITEMERROR, SKIP)

   RUNSTATS TABLESPACE LIST LST#STA
         SHRLEVEL CHANGE
           INDEX(ALL KEYCARD)
           REPORT YES UPDATE ALL
$!
call sqlDisconnect
$#out                                              20101231 11:56:23
$/tstTut07Src/
$=/tstTut07/
$=/tstTut07/
    ### start tst tstTut07 ############################################
    compile , 47 lines: $**$>.fEdit()
    run without input
    //A5407691 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP1 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407691.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV27A1T.VDPS329
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
    //A5407692 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP2 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407692.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV28A1T.VDPS390
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
    //A5407693 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP3 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407693.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV21A1T.VDPS004
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
$/tstTut07/
$=/tstTut08Src/
$/tstTut08Src/
$=/tstTut08/
$/tstTut08/
*/
    call sqlIni
    call sqlDisconnect '*'
    call tstComp2 'tstTut01'
    call tstComp2 'tstTut02'
    call tstComp2 'tstTut03'
    if m.err.os == 'TSO' then do
        call tstComp2 'tstTut04'
        /* call tstComp2 'tstTut05' */
     /* call tstComp2 'tstTut07'  ???? anderes Beispiel ???? */
        end
    call tstTotal
    return
endProcedure tstTut0
/* copx tstComp end   *************************************************/
/* copx tstBase begin **************************************************
     test the basic classes
***********************************************************************/
tstBase: procedure expose m.
    call tstTstSay
    call oIni
    call scanIni
    call tstO
    call tstM
    call tstUtc2d
    call classIni
    call tstF
    call tstFtst
    call tstMCat
    call tstMap
    call tstMapVia
    call tstClass
    call tstClass2
    call tstOEins
    call jIni
    call tstJSay
    call tstJ
    call tstJ2
    call tstJCatSql
    call catIni
    call tstCat
    call pipeIni
    CALL TstEnv
    CALL TstEnvCat
    call tstPipe
    call tstPipeS
    call tstEnvVars
    call tstvWith
    call tstTotal
    call tstPipeLazy
    call tstEnvClass
    call tstFile
    call tstFileList
    call tstFE
    call tstFTab
    call tstFmt
    call tstfUnits
    call tstTotal
    call tstSb
    call tstSb2
    call tstScan
    call ScanReadIni
    call tstScanRead
    call tstScanUtilInto
    call tstScanWin
    call tstScanSQL
    call tstTotal
    return
endProcedure tstBase

/*--- test the tst Module, check the output visually  ----------------*/
tstTstSay: procedure expose m.
    call tstIni
    oldErr = m.tst.err
    oldNames = m.tst.errNames
    say '+++ tstTstSay start with' oldErr 'totErrs and',
            m.tst.tests 'tests'
/*
$=/tstTstSayEins/
    ### start tst tstTstSayEins #######################################
    test eins einzige testZeile
$/tstTstSayEins/
$=/tstTstSayZwei/
    ### start tst tstTstSayZwei #######################################
    zwei 1. testZeile
    zwei 2. und letzte testZeile
$/tstTstSayZwei/
$=/tstTstSayDrei/
    ### start tst tstTstSayDrei #######################################
    drei 1. testZeile vor empty Zeile
    ..
    drei 3. testZeile vor 10 space
    .          .
    drei 5. testZeile ziemlich lang 66                                 +
    .                                77  5   10   15++++++++++++++++++++
    .+++++++++++++++++++++++++++++++++++++++++++.
$/tstTstSayDrei/
*/
    call tst x, 'tstTstSayEins'
    call tstOut x, "test eins einzige testZeile"
    call tstEnd x, 'err 0'


    call tst x, 'tstTstSayZwei'
    call tstOut x, "zwei 1. testZeile"
    call tstOut x, "zwei 2. und letzte testZeile"
    call tstEnd x, 'err 0'

    call tst x, 'tstTstSayZwei'
    call tstOut x, "zwei 1. testZeile "    /* ein space zuviel */
    call tstOut x, "zwei 2. und letzte testZeile"
    call tstOut x, "zwei 3. zuviel"
    call tstEnd x, 'err 3'

    call tst y, 'tstTstSayDrei'
    call tstOut y, 'drei 1. testZeile vor empty Zeile'
    call tstOut y, ''
    call tstOut y, 'drei 3. testZeile vor 10 space'
    call tstOut y, left('', 10)
    call tstOut y, 'drei 5. testZeile ziemlich lang',
                left(66, 66) left('77  5   10   15', 77, '+')
    call tstEnd y, 'err 0'
    if m.y.err <> 0 then
        call err '+++ tstTstSay errs' m.x.err 'expected' 0
    if m.tst.err <> oldErr + 3 then
        call err '+++ tstTstSay totErrs' m.tst.err 'expected' 3
    say '+++ tstTstSay end Ok with' m.tst.err 'totErrs and' ,
        m.tst.tests 'tests'
    m.tst.err = oldErr
    m.tst.errNames = oldNames
    return
endProcedure tstTstSay

tstMark: procedure expose m.
parse arg m, msg
    if symbol('m.m') == 'VAR' then
        m.m = msg';' m.m
    else
        m.m = msg 'new'
    return m
endProcedure tstMark
tstM: procedure expose m.
/*
$=/tstMa/
    ### start tst tstMa ###############################################
    mNew() 1=newM1 2=newM2
    mNew(tst...) 2=2 new 3=4; 3; 1 new 4=5 new
    iter 4; 3; 1 new
    iter 2 new
    iter 5 new
$/tstMa/
*/
    call tst t, 'tstMa'
    m1 = mNew()
    m2 = mNew()
    m.m1 = 'newM1'
    m.m2 = 'newM2'
    call tstOut t, 'mNew() 1='m.m1 '2='m.m2
    call mNewArea 'tst'm1
    t1 = tstMark(mNew('tst'm1), '1')
    t2 = tstMark(mNew('tst'm1), '2')
    call mFree tstMark(t1, '3')
    t3 = tstMark(mNew('tst'm1), '4')
    t4 = tstMark(mNew('tst'm1), '5')
    call tstOut t, 'mNew(tst...) 2='m.t2 '3='m.t3 '4='m.t4
    i = mIterBegin('tst'm1)
    do while assNN('i', mIter(i))
        call tstOut t, 'iter' m.i
        end
    call tstEnd t
/*
$=/tstM/
    ### start tst tstM ################################################
    symbol m.b LIT
    symbol m.a LIT
    mAdd a A.2
    mAdd a A.3
    m.a: 3: 1=eins 2=zwei 3=drei 4=M.A.4
    m.c: 5: 1=c vorAddSt a 2=eins 3=zwei 4=drei 5=c nacAddSt a 6=M.C.6
$/tstM/ */


    drop m.b m.a m.a.0 m.a.1 m.a.2
    call tst t, 'tstM'
    call tstOut t, 'symbol m.b' symbol('m.b')
    m.b = 1
    call tstOut t, 'symbol m.a' symbol('m.a')
    call tstOut t, 'mAdd a' mAdd(mCut(a, 0), 'eins', 'zwei')
    call tstOut t, 'mAdd a' mAdd(a, 'drei')
    call tstOut t, 'm.a:' m.a.0': 1='m.a.1 '2='m.a.2 '3='m.a.3 '4='m.a.4
    call mAdd mCut(c, 0), 'c vorAddSt a'
    call mAddSt c, a
    call mAdd c, 'c nacAddSt a'
    call tstOut t, 'm.c:' m.c.0': 1='m.c.1 '2='m.c.2 '3='m.c.3,
                    '4='m.c.4 '5='m.c.5 '6='m.c.6
    call tstEnd t
    return
endProcedure tstM

tstMCat: procedure expose m.
/*
$=/tstMCat/
    ### start tst tstMCat #############################################
    mCat(0, )                     =;
    mCat(0, 1)                    =;
    mCat(0, 112222%,%,%,11)       =;
    mCat(0, 231%,%c%,1%,2)        =;
    mCat(0, 33341%,%c2@%c%,1%,333)=;
    mCat(0, 45%,1%c2@%c3@%c%,%,4) =;
    mCat(1, )                     =eins;
    mCat(1, 1)                    =eins;
    mCat(1, 112222%,%,%,11)       =eins11;
    mCat(1, 231%,%c%,1%,2)        =1eins2;
    mCat(1, 33341%,%c2@%c%,1%,333)=1eins2eins333;
    mCat(1, 45%,1%c2@%c3@%c%,%,4) =1eins2eins3eins4;
    mCat(2, )                     =einszwei;
    mCat(2, 1)                    =eins1zwei;
    mCat(2, 112222%,%,%,11)       =eins112222zwei11;
    mCat(2, 231%,%c%,1%,2)        =1eins231zwei2;
    mCat(2, 33341%,%c2@%c%,1%,333)=1eins2eins33341zwei2zwei333;
    mCat(2, 45%,1%c2@%c3@%c%,%,4) =1eins2eins3eins451zwei2zwei3zwei4;
    mCat(3, )                     =einszweidrei;
    mCat(3, 1)                    =eins1zwei1drei;
    mCat(3, 112222%,%,%,11)       =eins112222zwei112222drei11;
    mCat(3, 231%,%c%,1%,2)        =1eins231zwei231drei2;
    mCat(3, 33341%,%c2@%c%,1%,333)=1eins2eins33341zwei2zwei33341drei2dr+
    ei333;
    mCat(3, 45%,1%c2@%c3@%c%,%,4) =1eins2eins3eins451zwei2zwei3zwei451d+
    rei2drei3drei4;
$/tstMCat/ */
    call mIni
    call tst t, "tstMCat"
     m.qq.1 = "eins"
     m.qq.2 = "zwei"
     m.qq.3 = "drei"
     do qx = 0 to 3
         m.qq.0 = qx
         call tstMCat1 qx
         call tstMCat1 qx, '1'
         call tstMCat1 qx, '112222%,%,%,11'
         call tstMCat1 qx, '231%,%c%,1%,2'
         call tstMCat1 qx, '33341%,%c2@%c%,1%,333'
         call tstMCat1 qx, '45%,1%c2@%c3@%c%,%,4'
         end
     call tstEnd t
     return
endProcedure tstMCat

tstMCat1: procedure expose m.
parse arg m.qq.0, fmt
    call out left("mCat("m.qq.0"," fmt")", 30)"="mCat(qq, fmt)";"
return
endProcedure tstMCat1

tstMap: procedure expose m.
/*
$=/tstMap/
    ### start tst tstMap ##############################################
    mapNew m keys m-keys 0
    map m zwei --> 2
    map m Zwei is not defined
    map stem m-keys 4
    map m eins --> 1
    map m zwei --> 2
    map m drei --> 3
    map m vier --> 4
    *** err: duplicate key eins in map m
    map m zwei is not defined
    q 2 zw dr
    map stem Q 2
    map Q zw --> 2Q
    map Q dr --> 3Q
    map stem m 3
    map m eins --> 1
    map m zwei --> 2PUT
    map m vier --> 4PUT
    *** err: duplicate key zwei in map m
    tstMapLong eins keys 3
    tstMapLong zweiMal keys 48
    tstMapLong dreiMal keys 93
    tstMapLong vier    keys 138
    tstMapLong <fuenf> keys 188
    tstMap clear keys 0
    inline1 3
    inline1 1 ==    inline1 eins==
    inline1 2 ====
    inline1 3 ==    inline1 drei==
    inline2 1 1 ==    inline2 eins==
    inline3 ?
$/tstMap/ */
/*
$=/tstMapInline1/
    inline1 eins

    inline1 drei
$/tstMapInline1/ */
/*
$=/tstMapInline2/
    inline2 eins
$/tstMapInline2/ */

    call tst t, 'tstMap'
    m = mapNew('K')
    ky = mapKeys(m)
    call mAdd t'.TRANS', m 'm', ky 'm-keys'
    call tstOut t, 'mapNew' m 'keys' ky m.ky.0
    call mapAdd m, 'eins', 1
    call mapAdd m, 'zwei', 2
    call mapAdd m, 'drei', 3
    call mapAdd m, 'vier', 4
    call tstMapShow m, 'zwei'
    call tstMapShow m, 'Zwei'
    call tstMapShowSt m, mapKeys(m)
    call mapAdd m, 'eins', 1
    call mapReset m, '='
    call tstMapShow m, 'zwei'
    call mapAdd m, 'eins', 1
    call mapAdd m, 'zwei', 2
    call mapPut m, 'zwei', 2Put
    call mapPut m, 'vier', 4Put
    call mapReset q, '='
    call mapAdd q, 'zw', 2q
    call mapAdd q, 'dr', 3q
    call tstOut t, 'q' m.q.0 m.q.1 m.q.2
    call tstMapShowSt q, mapKeys(q)
    call tstMapShowSt m, mapKeys(m)
    call mapAdd m, 'zwei', 2addDup
    call tstMapLong m, 'eins'      ,201, 2000, -2, 2
    call tstMapLong m, 'zweiMal'   ,201, 2000, -2, 2
    call tstMapLong m, 'dreiMal'   ,201, 2000,  2,-2
    call tstMapLong m, 'vier   '   ,2010, 201, -2, 2
    call tstMapLong m, '<fuenf>'   ,2010, 201,  2,-2
    call mapClear m
    keys = mapKeys(m)
    call tstOut t, 'tstMap clear keys' m.keys.0
    i = mapInline('tstMapInline1')
    call tstOut t, 'inline1' m.i.0
    do x=1 to m.i.0
        call tstOut t, 'inline1' x '=='m.i.x'=='
        end
    i = mapInline('tstMapInline2')
    call tstOut t, 'inline2' m.i.0 '1 =='m.i.1'=='
    call tstOut t, 'inline3' mapInline('tstMapInline3', 'r')'?'
    call tstEnd t
    return
endProcedure tstMap

tstMapLong: procedure expose m.
parse arg m, w, f1, t1, f2, t2
    if f1 < t1 then
        b1 = 201
    else
        b1 = -201
    if f2 < t2 then
        b2 = 1
    else
        b2 = -1
    lo = copies(w, 2100 % length(w))
    keys = mapKeys(m)
    keyCn = m.keys.0
    call tstOut t, 'tstMapLong' w 'keys' keyCn
    do x = f1 by b1 to t1
        do y = x+f2 by b2 to x+t2
            k = left(lo, y)
            if mapHasKey(m, k) then
                call err 'mapLong hasKey before' w y
            call mapAdd m, k, w y
            if \ mapHasKey(m, k) then
                call err 'mapLong \ hasKey after' w y
            if mapGet(m, k) \== w y then
                call err 'mapLong \ get <> ' w y
            keys = mapKeys(m)
            if keyCn + 1 \= m.keys.0 then
                call err 'mapLong keys .0 <> ' w y
            keyCn = m.keys.0
            if k \== m.keys.keyCn then
                call err 'mapLong keys . ' keyCn '<>' w y
            end
        end
    return
endProcedure tstMapLong

tstMapVia: procedure expose m.
/*
$=/tstMapVia/
    ### start tst tstMapVia ###########################################
    map M K --> A
    mapVia(m, K)      A
    *** err: missing m.A at 3 in mapVia(M, K|)
    mapVia(m, K|)     M.A
    mapVia(m, K|)     valAt m.a
    mapVia(m, K|)     valAt m.a
    *** err: missing m.A.aB at 5 in mapVia(M, K|aB)
    mapVia(m, K|aB)   M.A.aB
    mapVia(m, K|aB)   valAt m.A.aB
    *** err: missing m.valAt m.a at 4 in mapVia(M, K||)
    mapVia(m, K||)    M.valAt m.a
    mapVia(m, K||)    valAt m.valAt m.a
    mapVia(m, K||F)   valAt m.valAt m.a.F
$/tstMapVia/ */
    call tst t, 'tstMapVia'
    u = 'A.aB'
    v = 'valAt m.a'
    drop m.a m.u m.v m.v.f
    call mapReset m, 'K'
    call mapAdd m, k, a
    call tstMapShow m, k
    call tstOut t, 'mapVia(m, K)     ' mapVia(m, 'K')
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    m.a = v
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    call tstOut t, 'mapVia(m, K|aB)  ' mapVia(m, 'K|aB')
    m.u = 'valAt m.'u
    call tstOut t, 'mapVia(m, K|aB)  ' mapVia(m, 'K|aB')
    call tstOut t, 'mapVia(m, K||)   ' mapVia(m, 'K||')
    v = m.a
    m.v = 'valAt m.'v
    m.v.f = 'valAt m.'v'.F'
    call tstOut t, 'mapVia(m, K||)   ' mapVia(m, 'K||')
    call tstOut t, 'mapVia(m, K||F)  ' mapVia(m, 'K||F')
    call tstEnd t
    return
endProcedure tstMapVia

tstMapShow: procedure expose m.
parse arg a, key
    if mapHasKey(a, key) then
        call tstOut t, 'map' a key '-->' mapGet(a, key)
    else
        call tstOut t, 'map' a key 'is not defined'
    return
endProcedure tstMapShow

tstMapShowSt: procedure expose m.
parse arg a, st
    call tstOut t, 'map stem' st m.st.0
    do wx=1 to m.st.0
        call tstMapShow a, m.st.wx
        end
    return
endProcedure tstMapShow

tstClass2: procedure expose m.
/*
$=/tstClass2o2/
    ### start tst tstClass2 ###########################################
    @CLASS.5 isA :class = u
    . choice u union
    .  .NAME = class
    .  stem 7
    .   .1 refTo @CLASS.1 :class = u
    .    choice v union
    .    choice c union
    .     .NAME = u
    .     .CLASS refTo @CLASS.15 :class = s
    .      choice s .CLASS refTo @CLASS.12 done :class @CLASS.12
    .   .2 refTo @CLASS.6 :class = c
    .    choice c union
    .     .NAME = v
    .     .CLASS refTo @CLASS.7 :class = u
    .      choice u stem 0
    .   .3 refTo @CLASS.8 :class = c
    .    choice c union
    .     .NAME = w
    .     .CLASS refTo @CLASS.7 done :class @CLASS.7
    .   .4 refTo @CLASS.9 :class = c
    .    choice c union
    .     .NAME = o
    .     .CLASS refTo @CLASS.7 done :class @CLASS.7
    .   .5 refTo @CLASS.10 :class = c
    .    choice c union
    .     .NAME = s
    .     .CLASS refTo @CLASS.11 :class = f
    .      choice f union
    .       .NAME = CLASS
    .       .CLASS refTo @CLASS.12 :class = r
    .        choice r .CLASS refTo @CLASS.5 done :class @CLASS.5
    .   .6 refTo @CLASS.13 :class = c
    .    choice c union
    .     .NAME = r
    .     .CLASS refTo @CLASS.11 done :class @CLASS.11
    .   .7 refTo @CLASS.14 :class = c
    .    choice c union
    .     .NAME = u
    .     .CLASS refTo @CLASS.15 :class = s
    .      choice s .CLASS refTo @CLASS.12 done :class @CLASS.12
    .   .8 refTo @CLASS.16 :class = c
    .    choice c union
    .     .NAME = n
    .     .CLASS refTo @CLASS.17 :class = u
    .      choice u stem 2
    .       .1 refTo @CLASS.18 :class = f
    .        choice f union
    .         .NAME = NAME
    .         .CLASS refTo @CLASS.1 done :class @CLASS.1
    .       .2 refTo @CLASS.15 done :class @CLASS.15
    .   .9 refTo @CLASS.19 :class = c
    .    choice c union
    .     .NAME = f
    .     .CLASS refTo @CLASS.20 :class = u
    .      choice u stem 2
    .       .1 refTo @CLASS.18 done :class @CLASS.18
    .       .2 refTo @CLASS.11 done :class @CLASS.11
    .   .10 refTo @CLASS.21 :class = c
    .    choice c union
    .     .NAME = c
    .     .CLASS refTo @CLASS.20 done :class @CLASS.20
    .   .11 refTo @CLASS.22 :class = c
    .    choice c union
    .     .NAME = m
    .     .CLASS refTo @CLASS.23 :class = u
    .      choice u stem 2
    .       .1 refTo @CLASS.18 done :class @CLASS.18
    .       .2 refTo @CLASS.24 :class = f
    .        choice f union
    .         .NAME = MET
    .         .CLASS refTo @CLASS.1 done :class @CLASS.1
$/tstClass2o2/

$=/tstClass2/
    ### start tst tstClass2 ###########################################
    @CLASS.4 isA :class = u
    . choice u union
    .  .NAME = class
    .  stem 7
    .   .1 refTo @CLASS.1 :class = u
    .    choice u union
    .     .NAME = v
    .     stem 2
    .      .1 refTo @CLASS.20 :class = m
    .       choice m union
    .        .NAME = o2String
    .        .MET = return m.m
    .      .2 refTo @CLASS.108 :class = m
    .       choice m union
    .        .NAME = o2File
    .        .MET = return file(m.m)
    .   .2 refTo @CLASS.5 :class = c
    .    choice c union
    .     .NAME = u
    .     .CLASS refTo @CLASS.6 :class = u
    .      choice u union
    .       .NAME = .
    .       stem 2
    .        .1 refTo @CLASS.7 :class = f
    .         choice f union
    .          .NAME = NAME
    .          .CLASS refTo @CLASS.1 done :class @CLASS.1
    .        .2 refTo @CLASS.8 :class = s
    .         choice s .CLASS refTo @CLASS.9 :class = r
    .          choice r .CLASS refTo @CLASS.4 done :class @CLASS.4
    .   .3 refTo @CLASS.10 :class = c
    .    choice c union
    .     .NAME = f
    .     .CLASS refTo @CLASS.11 :class = u
    .      choice u union
    .       .NAME = .
    .       stem 2
    .        .1 refTo @CLASS.7 done :class @CLASS.7
    .        .2 refTo @CLASS.12 :class = f
    .         choice f union
    .          .NAME = CLASS
    .          .CLASS refTo @CLASS.9 done :class @CLASS.9
    .   .4 refTo @CLASS.13 :class = c
    .    choice c union
    .     .NAME = s
    .     .CLASS refTo @CLASS.12 done :class @CLASS.12
    .   .5 refTo @CLASS.14 :class = c
    .    choice c union
    .     .NAME = c
    .     .CLASS refTo @CLASS.11 done :class @CLASS.11
    .   .6 refTo @CLASS.15 :class = c
    .    choice c union
    .     .NAME = m
    .     .CLASS refTo @CLASS.16 :class = u
    .      choice u union
    .       .NAME = .
    .       stem 2
    .        .1 refTo @CLASS.7 done :class @CLASS.7
    .        .2 refTo @CLASS.17 :class = f
    .         choice f union
    .          .NAME = MET
    .          .CLASS refTo @CLASS.1 done :class @CLASS.1
    .   .7 refTo @CLASS.18 :class = c
    .    choice c union
    .     .NAME = r
    .     .CLASS refTo @CLASS.12 done :class @CLASS.12
$/tstClass2/ */

    call classIni
    call tst t, 'tstClass2'
    call classOut , m.class.class
    call tstEnd t
    return
endProcedure tstClass2

tstClass: procedure expose m.
/*
$=/tstClass/
    ### start tst tstClass ############################################
    Q u =className= tstClassTf12
    Q.eins.zwei v ==> M.Q.eins.zwei
    *** err: bad type v: classNew(v tstClassTf12)
    *** err: bad type v: classBasicNew(v, tstClassTf12, )
    R u =className= uststClassTf12
    R u =className= uststClassTf12in
    R u =className= tstClassTf12
    R.eins.zwei v ==> M.R.eins.zwei
    R s =stem.0= 2
    R.1 r ==> M.R.1 :CLASS.3
    R.1 u =className= tstClassTf12
    R.1.eins.zwei v ==> M.R.1.eins.zwei
    R.2 r ==> M.R.2 :CLASS.3
    R.2 u =className= tstClassTf12
    R.2.eins.zwei v ==> M.R.2.eins.zwei
    S u =className= TstClass7
    S s =stem.0= 2
    S.1 u =className= TstClass7s
    S.1.eins v ==> M.S.1.eins
    S.1 m =met-metA--> say "metA"
    S.1 m =met-metB--> say "metB"
    S.2 u =className= TstClass7s
    S.2.zwei v ==> M.S.2.zwei
    S.2 m =met-metA--> say "metA"
    S.2 m =met-metB--> say "metB"
    class of mutate qq tstClassTf12
$/tstClass/ */

    f = 'eins.zwei'
    e = 'eins'
    z = 'zwei'
    drop m.q.f m.r.f m.r.0 m.r.1 m.r.1.f m.r.2 m.r.2.f
    drop m.s.0 m.s.1 m.s.1.e m.s.2 m.s.2.z
    call classIni
    call tst t, 'tstClass'
    t1  =  classNew('n? tstClassTf12 u f eins f zwei v')
    call tstClassOut t, t1, q
    z = m.class.0
    if class4name('tstClassB', '') == '' then do
        t2 = classNew('n tstClassB u n tstClassC u tstClassTf12,',
            's u v tstClassTf12')
        end
    else do /*  the second time we would get a duplicate error */
        call tstOut t, '*** err: bad type v: classNew(v tstClassTf12)'
        call tstOut t, '*** err: bad type v:' ,
            'classBasicNew(v, tstClassTf12, )'
        end
    t2 = classNew('n? uststClassTf12 u' ,
           'n? uststClassTf12in u tstClassTf12',
        , classNew('s u r, tstClassTf12'))
    m.r.0 = 2
    call tstClassOut t, t2, r
    t3 = classNew('n? TstClass7 u s',
         classNew('n? TstClass7s u c 1 f eins v, c 2 f zwei v',
            ,'m', 'metA say "metA"', 'metB say "metB"'))
    m.s.0 = 2
    m.s.1 = 1
    m.s.2 = 2
    call tstClassOut t, t3, s
    call oMutatName qq, 'tstClassTf12'
    tt = objClass(qq)
    call tstOut t, 'class of mutate qq' className(tt)
    call tstEnd t
    return
endProcedure tstClass

tstClassOut: procedure expose m.
parse arg o, t, a
    if wordPos(t, m.class.classV m.class.classW m.class.classO) > 0 then
        return tstOut(o, a m.t.name '==>' m.a)
    if m.t == 'r' then
        return tstOut(o, a m.t '==>' m.a ':'m.t.class)
    if m.t == 'u' & m.t.name \== '' then
        call tstOut o, a m.t '=className=' m.t.name
    if m.t == 'f' then
        return tstClassOut(o, m.t.class, a'.'m.t.name)
    if m.t = 'u' then do
        do ux=1 to m.t.0
            call tstClassOut o, m.t.ux, a
            end
        return 0
        end
    if m.t = 's' then do
        call tstOut o, a m.t '=stem.0=' m.a.0
        do ux=1 to m.a.0
            call tstClassOut o, m.t.class, a'.'ux
            end
        return 0
        end
    if m.t = 'c' then do
        if m.t.name = m.a then
            call tstClassOut o, m.t.class, a
         return 0
        end
    if m.t = 'm' then
        return tstOut(o, a m.t '=met-'m.t.name'-->' m.t.met)
    call err 'bad class type' m.t
endProcedure tstClassOut


tstO: procedure expose m.
/*
$=/tstO/
    ### start tst tstO ################################################
    oIsCla(TstOCla1) 0
    TstOCla1 -
    oIsCla(TstOCla1) 1
    TstOCla1 -
    oIsCla(TstOCla1) 1
    TstOCla1 contents of met1
    TstOCla1.met2 -
    TstOCla2.met1 contents of met1
    TstOCla2.met2 contents of met2
    TstOCla1.TstOMet3 -
    TstOCla1.TstOMet3 generated met TstOCla1:TstOMet3 code...;
    TstOCla2.TstOMet3 generated met TstOCla2:TstOMet3 code...;
    tstOObj1.met1 -
    tstOObj1.met1 contents of met1
$/tstO/
*/
    call mIni
    call tst t, 'tstO'
    call oIni
    c1 = 'TstOCla1'
    c2 = 'TstOCla2'
    m1 = 'met1'
    m2 = 'met2'
    m3 = 'TstOMet3'
    lg = m.o.lazyGen
    call tstOut t, 'oIsCla('c1')' oIsCla(c1)
    call tstOut t, c1 oClaMet(c1, 'met1', '-')
    call oAddCla c1
    call tstOut t, 'oIsCla('c1')' oIsCla(c1)
    call tstOut t, c1 oClaMet(c1, 'met1', '-')
    call oAddMet c1, m1, 'contents of met1'
    call tstOut t, 'oIsCla('c1')' oIsCla(c1)
    call tstOut t, c1 oClaMet(c1, m1, '-')
    call oAddCla c2, c1
    call oAddMet c2, 'met2', 'contents of met2'
    call tstOut t, c1'.met2' oClaMet(c1, 'met2', '-')
    call tstOut t, c2'.'m1 oClaMet(c2, m1, '-')
    call tstOut t, c2'.met2' oClaMet(c2, 'met2', '-')
    call tstOut t, c1'.'m3 oClaMet(c1, m3, '-')
    call oAddMet lg, m3,
            , "return 'generated met' cl':'me 'code...;'"
    call tstOut t, c1'.'m3 oClaMet(c1, m3, '-')
    call tstOut t, c2'.'m3 oClaMet(c2, m3, '-')
    o1 = 'tstOObj1'
    o2 = 'tstOObj2'
    call tstOut t, o1'.met1' objMet(o1, 'met1', '-')
    call oMutate o1, c1
    call tstOut t, o1'.met1' objMet(o1, 'met1', '-')
    call tstEnd t
    drop m.o.cParent.c1 m.o.cMet.c1.m1 m.o.cMet.c1.m2 m.o.cMet.c1.m3
    drop m.o.cParent.c2 m.o.cMet.c2.m1 m.o.cMet.c2.m2 m.o.cMet.c2.m3
    drop m.o.o2c.o1                                   m.o.cMet.lg.m3
    return
endProcedure tstO


tstOEins: procedure expose m.
/*
$=/tstOEins/
    ### start tst tstOEins ############################################
    class method calls of TstOEins
    .  met Eins.eins M
     FLDS of <obj e of TstOEins> .FEINS, .FZWEI
     methodcalls of object e of TstOEins
    .  met Eins.eins <obj e of TstOEins>
    .  met Eins.zwei <obj e2 of TstOEins>
    *** err: no method nein in class TstOEins of object <obj e+
    . of TstOEins>
    *** err: no class found for object noObj
    class method calls of TstOEins
    .  met Elf.zwei M
    FLDS of <obj f of TstOElf> .FEINS, .FZWEI, .FELF
    methodcalls of object f of TstOElf
    .  met Eins.eins <obj f of TstOElf>
    .  met Elf.zwei <obj f of TstOElf>
    .  met Elf.drei <obj f of TstOElf>
    oCopy c1 of class TstOEins, c2
    C1 u =className= TstOEins
    C1.FEINS v ==> M.C1.FEINS
    C1.FZWEI v ==> M.C1.FZWEI
    C1 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C1 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    C2 u =className= TstOEins
    C2.FEINS v ==> M.C1.FEINS
    C2.FZWEI v ==> M.C1.FZWEI
    C2 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C2 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    oCopy c3 of class TstOElf, c4
    C4 u =className= TstOElf
    C4 u =className= TstOEins
    C4.FEINS v ==> M.C3.FEINS
    C4.FZWEI v ==> M.C3.FZWEI
    C4 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C4 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    C4.FELF r ==> M.C3.FELF :CLASS.3
    C4 m =met-zwei--> call tstOut t, "  met Elf.zwei" m
    C4 m =met-drei--> call tstOut t, "  met Elf.drei" m
    tEinsDop <class TstOEins>
    oRun 7*3 21
    oRun 12*12 144
$/tstOEins/ */
    call classIni
    call tst t, 'tstOEins'
    tEins = classNew('n? TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
    call mAdd t.trans, tEins '<class TstOEins>'
    call tstOut t, 'class method calls of TstOEins'
    interpret oClaMet('TstOEins', 'eins')
    e = oNew('TstOEins')
    e2 = oNew('TstOEins')
    call mAdd t.trans, e '<obj e of TstOEins>'
    call mAdd t.trans, e2 '<obj e2 of TstOEins>'
    call tstOut t, 'FLDS of' e mCat(oFlds(e), ', ')
    call tstOut t, 'methodcalls of object e of TstOEins'
    call tstOmet e, 'eins'
    call tstOmet e2, 'zwei'
    call tstOmet e, 'nein'
    call tstOmet 'noObj', 'nein'
    tElf = classNew('n? TstOElf u TstOEins, f FELF r', 'm',
        , 'zwei call tstOut t, "  met Elf.zwei" m',
        , 'drei call tstOut t, "  met Elf.drei" m')
    call tstOut t, 'class method calls of TstOEins'
    interpret oClaMet('TstOElf', 'zwei')
    f = oNew('TstOElf')
    call mAdd t.trans, f '<obj f of TstOElf>'
    call tstOut t, 'FLDS of' f mCat(oFlds(f), ', ')
    call tstOut t, 'methodcalls of object f of TstOElf'
    call tstOmet f, 'eins'
    call tstOmet f, 'zwei'
    call tstOmet f, 'drei'
/*  call tstOut t, 'methodcalls of object f cast To TstOEins'
    call tstOmet oCast(f, 'TstOEins'), 'eins'
    call tstOmet oCast(f, 'TstOEins'), 'zwei'
    call tstOut t, 'FLDS of <cast(f, TstOEins)>',
        mCat(oFlds(oCast(f, 'TstOEins')), ', ')
 */
    call oMutatName c1, 'TstOEins'
    call tstOut t, 'oCopy c1 of class TstOEins, c2'
    call tstClassOut t, tEins, c1
    call oCopy c1, c2
    call tstClassOut t, tEins, c2
    call tstOut t, 'oCopy c3 of class TstOElf, c4'
    call oMutatName c3, 'TstOElf'
    call oCopy c3, c4
    call tstClassOut t, tElf, c4

/*    tEinsDop = classNew('n TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
*/ tEinsDop = tEins
    call tstOut t, 'tEinsDop' tEinsDop
    e3 = oNew('TstOEins')
    if e3 <<= e | e3 <<= e2 then
        call err 'doppelt reinitialised objects'
    rr = oRunner('return 7 * 3')
    call tstOut t, 'oRun 7*3' oRun(rr)
    r12 = oRunner('return 12 * 12')
    call tstOut t, 'oRun 12*12' oRun(r12)
    call tstEnd t
    return
endProcedure tstOEins

tstOmet: procedure expose m.
parse arg m, met
    interpret objMet(m, met)
    return
endProcedure tstOmet

tstJSay: procedure expose m.
/*
$=/tstJSay/
    ### start tst tstJSay #############################################
    *** err: bad option openArg in jOpen(<obj j of JRW>, openArg)
    *** err: jWrite(<obj j of JRW>, writeArg) but not opened w
    *** err: can only write JSay.jOpen(<obj s of JSay>, <)
    *** err: jWrite(<obj s of JSay>, write s vor open) but not opened+
    . w
    *** err: can only read JRWEof.jOpen(<obj e of JRWEof>, >)
    *** err: jRead(<obj e of JRWEof>, XX) but not opened r
    read e vor open 0 m.xx valueBefore
    read e nach open 0 m.xx M.XX
    out eins
    #jIn 1# tst in line 1 eins ,
    out zwei in 1 vv=readAdrVV
    #jIn 2# tst in line 2 zwei ;   .
    out drei in 1 vv=readAdrVV Schluss
$/tstJSay/ */

    call jIni
    call tst t, 'tstJSay'
    jrw = oNew('JRW')
    call mAdd t'.TRANS', jrw '<obj j of JRW>'
    call jOpen jrw, 'openArg'
    call jWrite jrw, 'writeArg'
    s = oNew('JSay')
    call mAdd t'.TRANS', s '<obj s of JSay>'
    call jOpen s, m.j.cRead
    s = oNew('JSay')
    call mAdd t'.TRANS', s '<obj s of JSay>'
    call jWrite s, 'write s vor open'
    call jOpen s, '>'
    call jWrite s, 'write s nach open'
    e = oNew('JRWEof')
    call mAdd t'.TRANS', e '<obj e of JRWEof>'
    call jOpen e, '>'
    e = oNew('JRWEof')
    call mAdd t'.TRANS', e '<obj e of JRWEof>'
    m.xx = 'valueBefore'
    call tstOut t, 'read e vor open' jRead(e, xx) 'm.xx' m.xx
    call jOpen e, m.j.cRead
    call tstOut t, 'read e nach open' jRead(e, xx) 'm.xx' m.xx
    call out 'out eins'
    vv = 'readAdrVV'
    m.vv = 'readAdrVVValueBefore'
    call out 'out zwei in' in(vv) 'vv='vv
    m.vv = 'readAdrVVValueBefore'
    call out 'out drei in' in(vv) 'vv='vv 'Schluss'
    call tstEnd t
    return
endProcedure tstJSay

tstJ: procedure expose m.
/*
$=/tstJ/
    ### start tst tstJ ################################################
    out eins
    #jIn 1# tst in line 1 eins ,
    1 in() tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    2 in() tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    3 in() tst in line 3 drei .schluss..
    #jIn eof 4#
    in() 3 reads vv VV
    line buf line one
    line buf line two
    line buf line three
    line buf line four
    *** err: jWrite(<buf b>, buf line five while reading) but not opene+
    d w
$/tstJ/ */

    call jIni
    call tst t, "tstJ"
    b = jOpen(jBuf(), '>')
    call mAdd t'.TRANS', b '<buf b>'
    call out 'out eins'
    do lx=1 by 1 while in(var)
        call out lx 'in()' m.var
        end
    call out 'in()' (lx-1) 'reads vv' vv
    call jOpen b, '>'
    call jWrite b, 'buf line one'
    call jClose b
    call mAdd m.b.stem, 'buf line two', 'buf line three'
    call jWrite b, 'buf line four'
    call jClose b
    call jOpen b, m.j.cRead
    do while (jRead(b, line))
        call out 'line' m.line
        end
    call jWrite b, 'buf line five while reading'
    call jClose b
    call tstEnd t
    return
endProcedure tstJ

tstJ2: procedure expose m.
/*
$=/tstJ2/
    ### start tst tstJ2 ###############################################
    class1 <Tst?1 class> <Tst?1 name>
    class2 <Tst?1 class> <Tst?1 name>
    class3 <Tst?1 class> <Tst?1 name>
    b read EINS feld eins, ZWEI feld zwei, DREI feld drei
    b read EINS feld eins, ZWEI feld zwei 2, DREI feld drei
    c read EINS feld eins, ZWEI feld zwei, DREI drei cat 1
    tstR: @tstWriteoV3 isA :<Tst?1 name>
    tstR:  .EINS = feld eins
    tstR:  .ZWEI = feld zwei
    tstR:  .DREI = drei cat 1
    c read EINS feld eins, ZWEI feld zwei 2, DREI drei cat 2
    tstR: @tstWriteoV4 isA :<Tst?1 name>
    tstR:  .EINS = feld eins
    tstR:  .ZWEI = feld zwei 2
    tstR:  .DREI = drei cat 2
$/tstJ2/ */

    call tst t, "tstJ2"
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call mAdd t'.TRANS', ty '<Tst?1 class>'
    call mAdd t'.TRANS', m.ty.name '<Tst?1 name>'
    call tstOut t, 'class1' ty m.ty.name
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call tstOut t, 'class2' ty m.ty.name
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call tstOut t, 'class3' ty m.ty.name
    call oMutate qq, ty
    m.qq.eins = 'feld eins'
    m.qq.zwei = 'feld zwei'
    m.qq.drei = 'feld drei'
    b = jBuf()
    call jOpen b, '>'
    call jWriteO b, oCopy(qq)
    m.qq.zwei = 'feld zwei 2'
    call jWriteO b, qq
    call jOpen jClose(b), m.j.cRead
    c = jOpen(jBuf(), '>')
    do xx=1 while assNN('res', jReadO(b))
        call out 'b read EINS' m.res.eins', ZWEI' m.res.zwei,
                                     || ', DREI' m.res.drei
        m.res.drei = 'drei cat' xx
        call jWriteO c, res
        end
    call jOpen jClose(c), m.j.cRead
    do while assNN('ccc', jReadO(c))
        call out 'c read EINS' m.ccc.eins', ZWEI' m.ccc.zwei,
                                     || ', DREI' m.ccc.drei
        call outO ccc
        end
    call tstEnd t
    return
endProcedure tstJ2

tstCat: procedure expose m.
/*
$=/tstCat/
    ### start tst tstCat ##############################################
    catRead 1 line 1
    catRead 2 line 2
    catRead 3 line 3
    appRead 1 line 1
    appRead 2 line 2
    appRead 3 line 3
    appRead 4 append 4
    appRead 5 append 5
$/tstCat/ */
    call catIni
    call tst t, "tstCat"
    i = cat(jBuf('line 1', 'line 2'), jBuf('line 3'))
    call jOpen i, m.j.cRead
    do lx=1 by 1 while jRead(i, v)
        call tstOut t, 'catRead' lx m.v
        end
    call jOpen jClose(i), m.j.cApp
    call jWrite i, 'append 4'
    call jWrite i, 'append 5'
    call jOpen jClose(i), m.j.cRead
    do lx=1 by 1 while jRead(i, v)
        call tstOut t, 'appRead' lx m.v
        end
    call tstEnd t
    return
endProcedure tstCat

tstEnv: procedure expose m.
    call pipeIni
/*
$=/tstEnv/
    ### start tst tstEnv ##############################################
    before pipeBeLa
    after pipeEnd
    *** err: jWrite(<jBuf c>, write nach pop) but not opened w
    #jIn 1# tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    before writeNow 2 c --> std
    before writeNow 1 b --> c
    b line eins
    b zwei |
    nach writeNow 1 b --> c
    add nach pop
    after push c only
    tst in line 1 eins ,
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    nach writeNow 2 c --> std
    *** err: jWrite(<jBuf c>, ) but not opened w
$/tstEnv/ */

    call tst t, "tstEnv"
    c = jBuf()
    call mAdd t'.TRANS', c '<jBuf c>'
    call out 'before pipeBeLa'
    b = jBuf("b line eins", "b zwei |")
    call pipe '+Ff', c, b
    call out 'before writeNow 1 b --> c'
    call pipeWriteNow
    call out 'nach writeNow 1 b --> c'
    call pipe '-'
    call out 'after pipeEnd'
    call jWrite c, 'write nach pop'
    call mAdd c'.BUF', 'add nach pop'
    call pipe '+A', c
    call out 'after push c only'
    call pipeWriteNow
    call pipe '-'
    call pipe '+f', , c
    call out 'before writeNow 2 c --> std'
    call pipeWriteNow
    call out 'nach writeNow 2 c --> std'
    call pipe '-'
    call jWrite c
    call tstEnd t
    return
endProcedure tstEnv

tstEnvCat: procedure expose m.
    call pipeIni
/*
$=/tstEnvCat/
    ### start tst tstEnvCat ###########################################
    c1 contents
    c1 line eins |
    before writeNow 1 b* --> c*
    b1 line eins|
    b2 line eins
    b2 zwei |
    c2 line eins |
    after writeNow 1 b* --> c*
    c2 contents
    c2 line eins |
$/tstEnvCat/ */

    call tst t, "tstEnvCat"

    b0= jBuf()
    b0= jBuf()
    b1= jBuf("b1 line eins|")
    b2 = jBuf("b2 line eins", "b2 zwei |")
    c1 = jBuf("c1 line eins |")
    c2 = jBuf("c2 line eins |")
    call pipe '+Af', c1, b0, b1, b2, c2
    call out 'before writeNow 1 b* --> c*'
    call pipeWriteNow
    call out 'after writeNow 1 b* --> c*'
    call pipe '-'
    call out 'c1 contents'
    call pipe '+f' , , c1
    call pipeWriteNow
    call pipe '-'
    call pipe '+f' , , c2
    call out 'c2 contents'
    call pipeWriteNow
    call pipe '-'
    call tstEnd t
    return
endProcedure tstEnvCat

tstPipe: procedure expose m.
    call pipeIni
/*
$=/tstPipe/
    ### start tst tstPipe #############################################
    .+0 vor pipeBegin
    #jIn 1# tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    .+7 nach pipeLast
    ¢7 +6 nach pipe 7!
    ¢7 +2 nach pipe 7!
    ¢7 +4 nach nested pipeLast 7!
    ¢7 (4 +3 nach nested pipeBegin 4) 7!
    ¢7 (4 (3 +1 nach pipeBegin 3) 4) 7!
    ¢7 (4 (3 tst in line 1 eins , 3) 4) 7!
    ¢7 (4 (3 tst in line 2 zwei ;    3) 4) 7!
    ¢7 (4 (3 tst in line 3 drei .schluss. 3) 4) 7!
    ¢7 (4 (3 +1 nach writeNow vor pipe 3) 4) 7!
    ¢7 (4 +3 nach preSuf vor nested pipeLast 4) 7!
    ¢7 +4 nach preSuf vor nested pipeEnd 7!
    ¢7 +5 nach nested pipeEnd vor pipe 7!
    ¢7 +6 nach writeNow vor pipeLast 7!
    .+7 nach writeNow vor pipeEnd
    .+8 nach pipeEnd
$/tstPipe/ */

    say 'x0' m.pipe.0
    call tst t, 'tstPipe'
    call out '+0 vor pipeBegin'
    say 'x1' m.pipe.0
    call pipe '+N'
    call out '+1 nach pipeBegin'
    call pipeWriteNow
    call out '+1 nach writeNow vor pipe'
    call pipe 'N|'
    call out '+2 nach pipe'
    call pipe '+N'
    call out '+3 nach nested pipeBegin'
    call pipePreSuf '(3 ', ' 3)'
    call out '+3 nach preSuf vor nested pipeLast'
    call pipe 'P|'
    call out '+4 nach nested pipeLast'
    call pipePreSuf '(4 ', ' 4)'
    call out '+4 nach preSuf vor nested pipeEnd'
    call pipe '-'
    call out '+5 nach nested pipeEnd vor pipe'
    call pipe 'N|'
    call out '+6 nach pipe'
    call pipeWriteNow
    say 'out +6 nach writeNow vor pipeLast'
    call out '+6 nach writeNow vor pipeLast'
    call pipe 'P|'
    call out '+7 nach pipeLast'
    call pipePreSuf '¢7 ', ' 7!'
    call out '+7 nach writeNow vor pipeEnd'
    call pipe '-'
    call out '+8 nach pipeEnd'
    say 'xx' m.pipe.0
    call tstEnd t
    return
endProcedure tstPipe

tstPipeS: procedure expose m.
/*
$=/tstPipeS/
    ### start tst tstPipeS ############################################
    eine einzige zeile
    nach all einzige Zeile
    select strip(creator) cr, strip(name) tb,
    (row_number()over())*(row_number()over()) rr
    from sysibm.sysTables
$/tstPipeS/
*/
    call tst t, "tstPipeS"
    call pipe '+s',, 'eine einzige zeile'
    call pipeWriteAll
    call out 'nach all einzige Zeile'
    call pipe 's',,
              , "select strip(creator) cr, strip(name) tb," ,
              ,      "(row_number()over())*(row_number()over()) rr" ,
              ,      "from sysibm.sysTables"
    call pipeWriteAll
    call pipe '-'
    call tstEnd t
    return
endProcedure tstPipeS

tstEnvVars: procedure expose m.
    call pipeIni
/*
$=/tstEnvVars/
    ### start tst tstEnvVars ##########################################
    put v1 value eins
    v1 hasKey 1 get value eins
    v2 hasKey 0
    one to theBur
    two to theBuf
    v1=value eins o=TST.ADR1
    v3=v3WieGehts? o=!v3WieGehts?
    v4=v4WieGehts? o=!v4WieGehts?
    o o0=<o0>
    *** err: no method o2String in class TstEnvVars0 of object <o0>
    *** err: o2String did not return
    s o0=0
    o o0=<o0>
    *** err: no method o2String in class TstEnvVars0 of object <o0>
    *** err: o2String did not return
    s o0=0
    o0&fSt0=rexx o0.fSt0 o=!rexx o0.fSt0
    o0&fRe0=rexx o0.fRe0 o=!rexx o0.fRe0
    o0&=rexx o0-value o=!rexx o0-value
        o o0=<o0>
    *** err: no method o2String in class TstEnvVars0 of object <o0>
    *** err: o2String did not return
    s o0=0
    o0&fSt0=put o0.fSt0 o=!put o0.fSt0
    o0&fRe0=putO o0.fRe0 o=!putO o0.fRe0
    o0&=put o0-value o=!put o0-value
$/tstEnvVars/
$=/tstEnvVars1/
    ### start tst tstEnvVars1 #########################################
    m.o1=put-o1-value m.o1.fStr=put-o1.fStr m.o1.fRef=<o0>
    *** err: no method o2String in class TstEnvVars1 of object <o1>
    *** err: o2String did not return
    o o1=<o1> s o1=0
    o1&fStr=put-o1.fStr o=!put-o1.fStr
    o1&=put-o1-value o=!put-o1-value
    *** err: no method o2String in class TstEnvVars0 of object <o0>
    *** err: o2String did not return
    o1&fRef=0 o=<o0>
    o1&fRef&fSt0=put o0.fSt0 o=!put o0.fSt0
    o1&fRef&fRe0=putO o0.fRe0 o=!putO o0.fRe0
    m.o1&fNest.fSt0= put-o1.fNest.fSt0 m.o1&fNest.fRe0= !put-o1&fNest.f+
    Re0
    o1&fNest.fSt0=put-o1.fNest.fSt0 o=!put-o1.fNest.fSt0
    o1&fNest&fRe0=put-o1&fNest.fRe0 o=!put-o1&fNest.fRe0
$/tstEnvVars1/
$=/tstEnvVars2/
    ### start tst tstEnvVars2 #########################################
    o2=<o2> getO(o2)=<o2> getO(o2&fRef)=<o1>
    o2&fRef&fStr=put-o1.fStr o=!put-o1.fStr
    o2&fRef&=put-o1-value o=!put-o1-value
    *** err: no method o2String in class TstEnvVars0 of object <o0>
    *** err: o2String did not return
    o2&fRef&fRef=0 o=<o0>
    o2&fRef&fRef&fSt0=put o0.fSt0 o=!put o0.fSt0
    o2&fRef&fRef&fRe0=putO o0.fRe0 o=!putO o0.fRe0
    o2&fRef&fNest.fSt0=put-o1.fNest.fSt0 o=!put-o1.fNest.fSt0
    o2&fRef&fNest&fRe0=put-o1&fNest.fRe0 o=!put-o1&fNest.fRe0
$/tstEnvVars2/
$=/tstEnvVarsS/
    ### start tst tstEnvVarsS #########################################
    oS=<oS> oS&fStS=<put oS.fStS>
    oS&fStV.0=1 oS&fStV.1=<put oS.fStV.1>
    m.oS.fStR.0=2 .2=!<put oS.fStR.2>
    oS&fStR.0=2 .1=<put oS.fStR.1> .2=<put oS.fStR.2>
    m.oS.0=9876 .1234=<put oS.1234>
    oS&0=9876 .12=M.<oS>.12 .1234=<put oS.1234>
$/tstEnvVarsS/
 */
    c0 = classNew('n? TstEnvVars0 u f fSt0 v, f = v, f fRe0 r')
    c1 = classNew('n? TstEnvVars1 u f fStr v,f fRef r' ,
              ', f fNest TstEnvVars0, f = v')
    o0 = oNew(c0)
    o1 = oNew(c1)
    o2 = oNew(c1)
    call tst t, "tstEnvVars"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'
    call vRemove 'v2'
    m.tst.adr1 = 'value eins'
    put1 = vPutO('v1', oMutate(tst'.'adr1, m.class.classV))
    call tstOut t, 'put v1' m.put1
    call tstOut t, 'v1 hasKey' vHasKey('v1') 'get' vGet('v1')
    call tstOut t, 'v2 hasKey' vHasKey('v2')
    if 0 then
        call tstOut t, 'v2 get'    vGet('v2')
    call vPutO 'theBuf', jBuf()
    call pipe '+F' , vGetO('theBuf')
    call out 'one to theBur'
    call out 'two to theBuf'
    call pipe '-'
    call pipe '+f',, vGetO('theBuf')
    call pipeWriteNow
    call pipe '-'
    call tstOut t, 'v1='vGet('v1') 'o='vGetO('v1')
    call vPut 'v3', 'v3WieGehts?'
    call tstOut t, 'v3='vGet('v3') 'o='vGetO('v3')
    call vPutO 'v4', s2o('v4WieGehts?')
    call tstOut t, 'v4='vGet('v4') 'o='vGetO('v4')

    call vPutO 'o0', o0
    call tstOut t, 'o o0='vGetO('o0')
    call tstOut t, 's o0='vGet('o0')
    fSt0 = 'fSt0'
    fRe0 = 'fRe0'
    m.o0 = 'rexx o0-value'
    m.o0.fSt0 = 'rexx o0.fSt0'
    m.o0.fRe0 = s2o('rexx o0.fRe0')
    call tstOut t, 'o o0='vGetO('o0')
    call tstOut t, 's o0='vGet('o0')
    call tstOut t, 'o0&fSt0='vGet('o0&fSt0') 'o='vGetO('o0&fSt0')
    call tstOut t, 'o0&fRe0='vGet('o0&fRe0') 'o='vGetO('o0&fRe0')
    call tstOut t, 'o0&='vGet('o0&') 'o='vGetO('o0&')

    call vPut 'o0&', 'put o0-value'
    call vPut 'o0&fSt0', 'put o0.fSt0'
    call vPutO 'o0&fRe0', s2o('putO o0.fRe0')
    call tstOut t, 'o o0='vGetO('o0')
    call tstOut t, 's o0='vGet('o0')
    call tstOut t, 'o0&fSt0='vGet('o0&fSt0') 'o='vGetO('o0&fSt0')
    call tstOut t, 'o0&fRe0='vGet('o0&fRe0') 'o='vGetO('o0&fRe0')
    call tstOut t, 'o0&='vGet('o0&') 'o='vGetO('o0&')

    call tstEnd t
    call tst t, "tstEnvVars1"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'

    call vPutO 'o1', o1
    call vPut 'o1&', 'put-o1-value'
    call vPut 'o1&fStr', 'put-o1.fStr'
    call vPutO 'o1&fRef', vGetO('o0')
    call tstOut t, 'm.o1='m.o1 'm.o1.fStr='mGet(o1'.fStr'),
         'm.o1.fRef='mGet(o1'.fRef')
    call tstOut t, 'o o1='vGetO('o1') 's o1='vGet('o1')
    call tstOut t, 'o1&fStr='vGet('o1&fStr') 'o='vGetO('o1&fStr')
    call tstOut t, 'o1&='vGet('o1&') 'o='vGetO('o1&')

    call tstOut t, 'o1&fRef='vGet('o1&fRef') 'o='vGetO('o1&fRef')
    call tstOut t, 'o1&fRef&fSt0='vGet('o1&fRef&fSt0') ,
        'o='vGetO('o1&fRef&fSt0')
    call tstOut t, 'o1&fRef&fRe0='vGet('o1&fRef&fRe0'),
         'o='vGetO('o1&fRef&fRe0')

    call vPut 'o1&fNest.fSt0', 'put-o1.fNest.fSt0'
    call vPutO 'o1&fNest.fRe0', s2o('put-o1&fNest.fRe0')
    call tstOut t, 'm.o1&fNest.fSt0=' mGet(o1'.fNest.fSt0') ,
            'm.o1&fNest.fRe0=' mGet(o1'.fNest.fRe0')
    call tstOut t, 'o1&fNest.fSt0='vGet('o1&fNest.fSt0'),
         'o='vGetO('o1&fNest.fSt0')
    call tstOut t, 'o1&fNest&fRe0='vGet('o1&fNest.fRe0'),
         'o='vGetO('o1&fNest.fRe0')
    call tstEnd t

    call tst t, "tstEnvVars2"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'
    call vPutO 'o2', o2
    call vPutO 'o2&fRef', vGetO('o1')
    call tstOut t, 'o2='o2 'getO(o2)='vGetO('o2'),
        'getO(o2&fRef)='vGetO('o2&fRef')

    call tstOut t, 'o2&fRef&fStr='vGet('o2&fRef&fStr'),
         'o='vGetO('o2&fRef&fStr')
    call tstOut t, 'o2&fRef&='vGet('o2&fRef&'),
     'o='vGetO('o2&fRef&')

    call tstOut t, 'o2&fRef&fRef='vGet('o2&fRef&fRef') ,
        'o='vGetO('o2&fRef&fRef')
    call tstOut t, 'o2&fRef&fRef&fSt0='vGet('o2&fRef&fRef&fSt0') ,
        'o='vGetO('o2&fRef&fRef&fSt0')
    call tstOut t, 'o2&fRef&fRef&fRe0='vGet('o2&fRef&fRef&fRe0'),
         'o='vGetO('o2&fRef&fRef&fRe0')
    call tstOut t, 'o2&fRef&fNest.fSt0='vGet('o2&fRef&fNest.fSt0'),
         'o='vGetO('o2&fRef&fNest.fSt0')
    call tstOut t, 'o2&fRef&fNest&fRe0='vGet('o2&fRef&fNest.fRe0'),
         'o='vGetO('o1&fNest.fRe0')
    call tstEnd t

    cS = classNew('n? TstEnvVarsS u f fStS v,f fStV s v, f fStR s r',
        ', f fNeS s TstEnvVars0, f = s v')
    oS = oNew(cS)
    call vPutO 'oS', oS
    oT = oNew(cS)
    call tst t, "tstEnvVarsS"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>',
        , oS '<oS>', oT '<oT>'
    call mPut oS'.fStS', '<put oS.fStS>'
    call tstOut t, 'oS='vGetO('oS') 'oS&fStS='vGet('oS&fStS')
    call mPut oS'.fStV.1', '<put oS.fStV.1>'
    call mPut oS'.fStV.0', 1
    call tstOut t, 'oS&fStV.0='vGet('oS&fStV.0'),
         'oS&fStV.1='vGet('oS&fStV.1')
    call mPut oS'.fStR.1', s2o('<put oS.fStR.1>')
    call mPut oS'.fStR.2', s2o('<put oS.fStR.2>')
    call mPut oS'.fStR.0', 2
    call tstOut t, 'm.oS.fStR.0='mGet(oS'.fStR.0'),
     '.2='mGet(oS'.fStR.2')
    call tstOut t, 'oS&fStR.0='vGet('oS&fStR.0'),
         '.1='vGet('oS&fStR.1') '.2='vGet('oS&fStR.2')
    call mPut oS'.1234', '<put oS.1234>'
    call mPut oS'.0', 9876
    call mPut oS'.fStR.0', 2
    call tstOut t, 'm.oS.0='mGet(oS'.0'),
     '.1234='mGet(oS'.1234')
    call tstOut t, 'oS&0='vGet('oS&0'),
         '.12='vGet('oS&12') '.1234='vGet('oS&1234')
    call tstEnd t
    return
endProcedure tstEnvVars

tstvWith: procedure expose m.
/*
$=/tstEW2/
    ### start tst tstEW2 ##############################################
    tstK1             TSTEW1
    tstK1&            get1 w
    tstK1&f1          get1.f1 v
    tstK1&f2          get1.f2 w
    tstK1&F3          get1.f3 v
    ttstK1&F3.FEINS   get1.f3.fEins v
    tstK1&F3.FZWEI    get1.f3.fZwei w
    tstK1&F3.FDREI o  !get1.f3.fDrei w
    tstK1&F3.FDREI    get1.f3.fDrei w
    tstK1&F3.1        get1.f3.1 w
    tstK1&F3.2        TSTEW1
    tstK1&F3.2&F1     get1.f1 v
    tstK1&F3.2&F3.2&F2 get1.f2 w
    *** err: missing key in mapGet(V, F1)
    *** err: no class found for object 0
    *** err: o2String did not return
    F1          0
    F1          get1.f1 v
    f2          get1.f2 w
    F3          get1.f3 v
    F3.FEINS    get1.f3.fEins v
    F3.FZWEI    get1.f3.fZwei w
    F3.FDREI o  !get1.f3.fDrei w
    F3.1        get1.f3.1 w
    pu1 F1      get1.f1 v
    pu2 F1      get2.f1 v
    po-2 F1     get1.f1 v
    *** err: missing key in mapGet(V, F1)
    *** err: no class found for object 0
    *** err: o2String did not return
    po-1 F1     0
$/tstEW2/  */
    call pipeIni
    c0 = classNew('n? TstEW0 u f FEINS v,f FZWEI w, f FDREI r,v,s r')
    call oClaMet c0, 'oFlds' /* new would do it, but we donot use it */
    cl = classNew('n? TstEW u r TstEW0, f F1 v, f F2 r, f F3 TstEW0')
    call oClaMet cl, 'oFlds' /* new would do it, but we donot use it */
    call oMutate tstEW1, cl
    m.tstEW1    = s2o('get1 w')
    m.tstEW1.f1 = 'get1.f1 v'
    m.tstEW1.f2 = s2o('get1.f2 w')
    m.tstEW1.f3 = 'get1.f3 v'
    m.tstEW1.f3.fEins = 'get1.f3.fEins v'
    m.tstEW1.f3.fZwei = s2o('get1.f3.fZwei w')
    m.tstEW1.f3.fDrei = s2o('get1.f3.fDrei w')
    m.tstEW1.f3.0 = 3
    m.tstEW1.f3.1 = s2o('get1.f3.1 w')
    m.tstEW1.f3.2 = tstEW1
    m.tstEW1.f3.3 = s2o('get1.f3.3 w')
    call oMutate tstEW2, cl
    m.tstEW2    = s2o('get2 w')
    m.tstEW2.f1 = 'get2.f1 v'
    m.tstEW2.f2 = s2o('get2.f2 w')
    call vPutO 'tstK1', tstEW1

    call tst t, 'tstEW2'
    call tstOut t, 'tstK1            ' vGetO('tstK1')
    call tstOut t, 'tstK1&           ' vGet('tstK1&')
    call tstOut t, 'tstK1&f1         ' vGet('tstK1&F1')
    call tstOut t, 'tstK1&f2         ' vGet('tstK1&F2')
    call tstOut t, 'tstK1&F3         ' vGet('tstK1&F3')
    call tstOut t, 'ttstK1&F3.FEINS  ' vGet('tstK1&F3.FEINS')
    call tstOut t, 'tstK1&F3.FZWEI   ' vGet('tstK1&F3.FZWEI')
    call tstOut t, 'tstK1&F3.FDREI o ' vGetO('tstK1&F3.FDREI')
    call tstOut t, 'tstK1&F3.FDREI   ' vGet('tstK1&F3.FDREI')
    call tstOut t, 'tstK1&F3.1       ' vGet('tstK1&F3.1')
    call tstOut t, 'tstK1&F3.2       ' vGetO('tstK1&F3.2')
    call tstOut t, 'tstK1&F3.2&F1    ' vGet('tstK1&F3.2&F1')
    call tstOut t, 'tstK1&F3.2&F3.2&F2' ,
                                vGet('tstK1&F3.2&F3.2&F2')
    call tstOut t, 'F1         ' vGet('F1')
    call vWith '+', tstEW1
    call tstOut t, 'F1         ' vGet('F1')
    call tstOut t, 'f2         ' vGet('F2')
    call tstOut t, 'F3         ' vGet('F3')
    call tstOut t, 'F3.FEINS   ' vGet('F3.FEINS')
    call tstOut t, 'F3.FZWEI   ' vGet('F3.FZWEI')
    call tstOut t, 'F3.FDREI o ' vGetO('F3.FDREI')
    call tstOut t, 'F3.1       ' vGet('F3.1')
    call tstOut t, 'pu1 F1     ' vGet('F1')
    call vWith '+', tstEW2
    call tstOut t, 'pu2 F1     ' vGet('F1')
    call vWith '-'
    call tstOut t, 'po-2 F1    ' vGet('F1')

    call vWith '-'
    call tstOut t, 'po-1 F1    ' vGet('F1')
    call tstEnd t
/*
$=/tstEW3/
    ### start tst tstEW3 ##############################################
    .          s c3&F1          = v(c3&f1)
    *** err: vGet flag f
    .          s c3&F1&FEINS    = 0
    *** err: vAdr null at c3&F3& in name c3&F3&FEINS
    *** err: vGet flag f
    .          s c3&F3&FEINS    = 0
    .          s c3&F3.FEINS    = val(c3&F3.FEINS)
    *** err: vGet flag f
    .          s c3&FEINS       = 0
    getO c3&
    aft Put   s c3&&FEINS      = v&&fEins
    Push c3   s F3.FEINS       = val(c3&F3.FEINS)
    aftPut=   s F3.FEINS       = pushPut(F3.FEINS)
    push c4   s F1             = v(c4&f1)
    put f2    s F2             = put(f2)
    put ..    s F3.FEINS       = put(f3.fEins)
    popW c4   s F1             = v(c3&f1)
    *** err: missing key in mapGet(V, F1)
    *** err: no class found for object 0
    *** err: o2String did not return
    popW c3   s F1             = 0
    .          s F222           = f222 pop stop
$/tstEW3/
*/
    call tst t, 'tstEW3'
    c3 = oNew('TstEW')
    call mAdd t.trans, c3 '<c3>'
    m.c3.f1 = 'v(c3&f1)'
    call vPutO 'c3', c3
    call tstEnvSG , 'c3&F1'
    call tstEnvSG , 'c3&F1&FEINS'
    call tstEnvSG , 'c3&F3&FEINS'
    call vPut 'c3&F3.FEINS', 'val(c3&F3.FEINS)'
    call tstEnvSG , 'c3&F3.FEINS'
    call tstEnvSG , 'c3&FEINS'
    call tstOut t,  'getO c3&', vGetO('c3&')
    call vPutO 'c3&', oNew('TstEW0')
    call vPut 'c3&&FEINS', 'v&&fEins'
    call tstEnvSG 'aft Put', 'c3&&FEINS'
    call vWith '+', c3
    call tstEnvSG 'Push c3', 'F3.FEINS'
    call vPut 'F3.FEINS', 'pushPut(F3.FEINS)'
    call tstEnvSG 'aftPut=', 'F3.FEINS'

    c4 = oNew('TstEW')
    call mAdd t.trans, c4 '<c4>'
    m.c4.f1 = 'v(c4&f1)'
    call vPut f222, 'f222 no stop'
    call vWith '+',  c4
    call tstEnvSG 'push c4', f1
    call vPut f2, 'put(f2)'
    call tstEnvSG 'put f2', f2
    call vPut f222, 'f222 stopped', 1
    call vPut 'F3.FEINS', 'put(f3.fEins)'
    call tstEnvSG 'put .. ', 'F3.FEINS'
    call vWith '-'
    call tstEnvSG 'popW c4', f1
    call vWith '-'
    call vPut f222, 'f222 pop stop'
    call tstEnvSG 'popW c3', f1
    call tstEnvSG          , f222
    call tstEnd t
    return
endProcedure tstvWith

tstEnvSG: procedure expose m. t
parse arg txt, nm
    call tstOut t, left(txt,10)'s' left(nm, 15)'=' vGet(nm)
    return

tstPipeLazy: procedure expose m.
    call pipeIni
/*
$=/tstPipeLazy/
    ### start tst tstPipeLazy #########################################
    a1 vor pipeBegin loop lazy 0 writeNow *** <class TstPipeLazyBuf>
    bufOpen <
    bufClose
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    a5 vor 2 writeNow in inIx 4
    a2 vor writeNow jBuf
    jBuf line 1
    jBuf line 2
    a3 vor writeNow in inIx 1
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    a4 vor barLast inIx 4
    a6 vor barEnd inIx 4
    a7 nach barEnd lazy 0 writeNow ***
    b1 vor barBegin lazy 0 writeNow *** <class TstPipeLazyRdr>
    RdrOpen <
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    rdrClose
    b4 vor writeNow
    b2 vor writeNow rdr inIx 2
    jRead lazyRdr
    tst in line 3 drei .schluss..
    jRead lazyRdr
    b3 vor barLast inIx 4
    b5 vor barEnd inIx 4
    b6 nach barEnd lazy 0 writeNow ***
    a1 vor pipeBegin loop lazy 1 writeAll *** +
        .<class TstPipeLazyBuf>
    a5 vor 2 writeAll in inIx 0
    a2 vor writeAll jBuf
    bufOpen <
    jBuf line 1
    jBuf line 2
    bufClose
    a3 vor writeAll in inIx 0
    #jIn 1# tst in line 1 eins ,
    tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    #jIn eof 4#
    a4 vor barLast inIx 0
    a6 vor barEnd inIx 4
    a7 nach barEnd lazy 1 writeAll ***
    b1 vor barBegin lazy 1 writeAll *** <class TstPipeLazyRdr>
    b4 vor writeAll
    b2 vor writeAll rdr inIx 1
    RdrOpen <
    jRead lazyRdr
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    jRead lazyRdr
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    jRead lazyRdr
    #jIn eof 4#
    rdrClose
    b3 vor barLast inIx 1
    b5 vor barEnd inIx 4
    b6 nach barEnd lazy 1 writeAll ***
$/tstPipeLazy/ */
    call tst t, "tstPipeLazy"
    do lz=0 to 1
        if lz then
            w = 'writeAll'
        else
            w = 'writeNow'
        m.t.inIx = 1-lz
        ty = class4Name('TstPipeLazyBuf', '')
        if ty == '' then do
            ty = classNew('n TstPipeLazyBuf u JRWDeleg', 'm',
            , 'jOpen call tstOut "T", "bufOpen" opt;',
              'call jOpen m.m.deleg, opt',
            , 'jClose call tstOut "T", "bufClose";',
              'call jClose m.m.deleg')
            end
        if \ lz then
             call mAdd t'.TRANS', ty '<class TstPipeLazyBuf>'
        call out 'a1 vor pipeBegin loop lazy' lz w '***' ty
        call pipe '+N'
        call out 'a2 vor' w 'jBuf'
        b = oNew('TstPipeLazyBuf', jBuf('jBuf line 1','jBuf line 2'))
        interpret 'call pipe'w 'b'
        call out 'a3 vor' w 'in inIx' m.t.inIx
        interpret 'call pipe'w
        call out 'a4 vor barLast inIx' m.t.inIx
        call pipe 'P|'
        call out 'a5 vor 2' w 'in inIx' m.t.inIx
        interpret 'call pipe'w
        call out 'a6 vor barEnd inIx' m.t.inIx
        call pipe '-'
        call out 'a7 nach barEnd lazy' lz w '***'

        ty = class4Name('TstPipeLazyRdr', '')
        if ty == '' then
            ty = classNew('n TstPipeLazyRdr u JRW', 'm',
            , 'jOpen call tstOut "T", "RdrOpen" opt',
            , 'jRead call out "jRead lazyRdr";' ,
                  'return jRead(m.m.rdr, var);',
            , 'jClose call tstOut "T",  "rdrClose";')
        if \ lz then
             call mAdd t'.TRANS', ty '<class TstPipeLazyRdr>'

        r = oNew('TstPipeLazyRdr')
            m.r.rdr = m.j.in
        if lz then
            call mAdd t'.TRANS', r '<lazyRdr>'
     m.t.inIx = 2-lz
     call out 'b1 vor barBegin lazy' lz w '***' ty
     call pipe '+N'
     call out 'b2 vor' w 'rdr inIx' m.t.inIx
     interpret 'call pipe'w 'r'
     call out 'b3 vor barLast inIx' m.t.inIx
     call pipe 'P|'
        call out 'b4 vor' w
        interpret 'call pipe'w
        call out 'b5 vor barEnd inIx' m.t.inIx
        call pipe '-'
     call out 'b6 nach barEnd lazy' lz w '***'
        end
    call tstEnd t
    return
endProcedure tstPipeLazy

tstEnvClass: procedure expose m.
    call pipeIni
/*
$=/tstEnvClass/
    ### start tst tstEnvClass #########################################
    a0 vor pipeBegin loop lazy 0 writeNow *** TY
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    a5 vor writeNow
    a1 vor jBuf()
    a2 vor writeNow b
    tstR: @<o20 of TstEnvClass10> isA :TstEnvClass10
    tstR:  .f11 = .
    tstR:  .F12 = value F12 of o1 <o20 of TstEnvClass10>
    tstR:  .f13 = .
    WriteO o2
    tstR: @<oc0 of TstEnvClass20> isA :TstEnvClass20 = valueO2Lazy0
    tstR:  .f24 = .
    tstR:  .F25 = value F25 of o20 <o20 of TstEnvClass20>
    a3 vor writeNow
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    a4 vor barLast inIx 4
    a6 vor barEnd
    a7 nach barEnd lazy 0 writeNow ***
    a0 vor pipeBegin loop lazy 1 writeAll *** TY
    a5 vor writeAll
    a1 vor jBuf()
    a2 vor writeAll b
    tstR: @<o21 of TstEnvClass10> isA :TstEnvClass10
    tstR:  .f11 = .
    tstR:  .F12 = value F12 of o1 <o21 of TstEnvClass10>
    tstR:  .f13 = .
    WriteO o2
    tstR: @<oc1 of TstEnvClass20> isA :TstEnvClass20 = valueO2Lazy1
    tstR:  .f24 = .
    tstR:  .F25 = value F25 of o21 <o21 of TstEnvClass20>
    a3 vor writeAll
    #jIn 1# tst in line 1 eins ,
    tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    #jIn eof 4#
    a4 vor barLast inIx 0
    a6 vor barEnd
    a7 nach barEnd lazy 1 writeAll ***
$/tstEnvClass/ */

    call tst t, "tstEnvClass"
    t10 = classNew('n? TstEnvClass10 u f f11 v, f F12 v, f f13 v')
    t20 = classNew('n? TstEnvClass20 u v, f f24 v, f F25 v')
    do lz=0 to 1
        if lz then
            w = 'writeAll'
        else
            w = 'writeNow'
        m.t.inIx = 1-lz
        call out 'a0 vor pipeBegin loop lazy' lz w '***' ty
        call pipe '+N'
        call out 'a1 vor jBuf()'
        b = jOpen(jBuf(), m.j.cWri)
        o1 = oNew('TstEnvClass10')
        m.o1.F12 = 'value F12 of o1' o1
        call mAdd t'.TRANS', o1 '<o2'lz 'of TstEnvClass10>'
        call jWriteO b, o1
        call jWrite b, 'WriteO o2'
        o2 = oNew('TstEnvClass20')
        m.o2 = 'valueO2Lazy'lz
        m.o2.F25 = 'value F25 of o2'lz o2
        oc = oCopy(oCopy(o2))
        call mAdd t'.TRANS', o2 '<o2'lz 'of TstEnvClass20>'
        call mAdd t'.TRANS', oc '<oc'lz 'of TstEnvClass20>'
        call jWriteO b, oc
        call out 'a2 vor' w 'b'
        interpret 'call pipe'w jClose(b)
        call out 'a3 vor' w
        interpret 'call pipe'w
        call out 'a4 vor barLast inIx' m.t.inIx
        call pipe 'P|'
        call out 'a5 vor' w
        interpret 'call pipe'w
        call out 'a6 vor barEnd'
        call pipe '-'
        call out 'a7 nach barEnd lazy' lz w '***'
        end
    call tstEnd t
    m.t.trans.0 = 0
    return
endProcedure tstEnvClass

tstFile: procedure expose m.
    call catIni
/*
$=/tstFile/
    ### start tst tstFile #############################################
    write read 0 last 10 vor anfang
    write read 1 last 80  links1 1   und rechts |  ..
    write read 2 last 80 liinks2 2   und rechts |  ..
    write read 5 last 80 links5 5 rechts5
    write read 99 last 80 links99 99 rechts
    write read 100 last 80 links100 100 rechts
    write read 101 last 80 links101 101 rechts
    write read 999 last 80 links999 999 rechts
    write read 1000 last 80 links1000 1000 rechts
    write read 1001 last 80 links1001 1001 rechts
    write read 2109 last 80 links2109 2109 rechts
    out > eins 1                                                       +
    .             .
    out > eins 2 schluss.                                              +
    .             .
    buf eins
    buf zwei
    buf drei
    out > zwei mit einer einzigen Zeile                                +
    .             .
    . links1 1   und rechts |  .                                       +
    .              .
$/tstFile/ */
    call tst t, "tstFile"
    pds = tstFilename('lib', 'r')
    call tstFileWr pds, 0, ' links0', '  und rechts |  .  '
    call tstFileWr pds, 1, ' links1', '  und rechts |  .  '
    call tstFileWr pds, 2, 'liinks2', '  und rechts |  .  '
    call tstFileWr pds, 5, 'links5', 'rechts5'
    call tstFileWr pds, 99, 'links99', 'rechts'
    call tstFileWr pds, 100, 'links100', 'rechts'
    call tstFileWr pds, 101, 'links101', 'rechts'
    call tstFileWr pds, 999, 'links999', 'rechts'
    call tstFileWr pds, 1000, 'links1000', 'rechts'
    call tstFileWr pds, 1001, 'links1001', 'rechts'
    call tstFileWr pds, 2109, 'links2109', 'rechts'
    pd2 = tstFilename('li2', 'r')
    call pipeIni
    call pipe '+F', s2o(tstPdsMbr(pd2, 'eins'))
    call out tstFB('out > eins 1') /* simulate fixBlock on linux */
    call out tstFB('out > eins 2 schluss.')
    call pipe '-'
    call pipe '+F', s2o(tstPdsMbr(pd2, 'zwei'))
    call out tstFB('out > zwei mit einer einzigen Zeile')
    call pipe '-'
    b = jBuf("buf eins", "buf zwei", "buf drei")
    call pipe '+f', , s2o(tstPdsMbr(pd2, 'eins')), b,
                    ,jBuf(),
                    ,s2o(tstPdsMbr(pd2, 'zwei')),
                    ,s2o(tstPdsMbr(pds, 'wr0')),
                    ,s2o(tstPdsMbr(pds, 'wr1'))
    call pipeWriteNow
    call pipe '-'
    call tstEnd t
    return
endProcedure tstFile

/*--- simulate fixBlock 80 on linux ---------------------------------*/
tstFB: procedure expose m.
parse arg line, recL
    if m.err.os \== 'LINUX' then
        return line
    else if recL == '' then
        return left(line, 80)
    else
        return left(line, recL)
endProcedure tstFB

tstPdsMbr: procedure expose m.
parse arg pds, mbr
    if m.err.os = 'TSO' then
        return pds'('mbr') ::F'
    if m.err.os = 'LINUX' then
        return pds'.'mbr
    call err 'tstPdsMbr implement os' m.err.os
endProcedure tstPdsMbr

tstFileWR: procedure expose m.
parse arg dsn, num, le, ri
    io = file(tstPdsMbr(dsn, 'wr'num))
    call jOpen io, m.j.cWri
    do x = 1 to num /* simulate fixBlock 80 on LINUX*/
        call jWrite io, tstFB(le x ri)
        end
    call jClose io
    if num > 100 then
        call jReset io, tstPdsMbr(dsn, 'wr'num)

    call jOpen io, m.j.cRead
    m.vv = 'vor anfang'
    do x = 1 to num
        if \ jRead(io, vv) then
            call err x 'not jRead'
        else if m.vv <> le x ri then
            call err x 'read mismatch' m.vv
        end
    if jRead(io, vv) then
        call err x 'jRead but should be eof 1'
    if jRead(io, vv) then
        call err x'+1 jjRead but should be eof 2'
    call jClose io
    call tstOut t, 'write read' num 'last' length(m.vv) strip(m.vv,'t')
    return
endProcedure tstFileRW

tstFileList: procedure expose m.
    call catIni
/*
$=/tstFileList/
    ### start tst tstFileList #########################################
    empty dir
    filled dir
    <<pref 2 List>>eins
    <<pref 2 List>>zwei
    <<pref 2 List>>drei
    <<pref 2 List>>vier
    filled dir recursive
    <<pref 2 List>>eins
    <<pref 2 List>>zwei
    <<pref 2 List>>drei
    <<pref 2 List>>vier
    <<pref 1 vier>>eins
    <<pref 1 vier>>zwei
    <<pref 1 vier>>drei
$/tstFileList/ */
/*
$=/tstFileListTSO/
    ### start tst tstFileListTSO ######################################
    empty dir
    filled dir
    <<pref 2 LIST>>DREI
    <<pref 2 LIST>>EINS
    <<pref 2 LIST>>ZWEI
    filled dir recursive
    <<pref 2 LIST>>DREI
    <<pref 2 LIST>>EINS
    <<pref 1 VIER>>DREI
    <<pref 1 VIER>>EINS
    <<pref 1 VIER>>ZWEI
    <<pref 2 LIST>>ZWEI
$/tstFileListTSO/ */
    if m.err.os = 'TSO' then
        call tst t, "tstFileListTSO"
    else
        call tst t, "tstFileList"
    fi = file(tstFileName('FileList', 'r'))
    call fileMkDir fi
    fl = fileList(fi)
    call tstOut t, 'empty dir'
    call jWriteNow t, fl
    call tstFileListMake t, fi, 2
    call tstOut t, 'filled dir'
    call jWriteNow t, fl
    call tstOut t, 'filled dir recursive'
    call jWriteNow t, fileList(fi, 'r')
    call tstEnd t
    return
endProcedure tstFileList

tstFileListMake: procedure expose m.
parse arg t, fi, lev
    if \ fileIsDir(fi) then
        call fileMkDir fi
    call mAdd t'.TRANS', filePath(fi) || m.file.sep ,
                '<<pref' lev right(filePath(fi),4)'>>'
    call jCat fileChild(fi, 'eins','::F'),jBuf('zeile eins','eins' lev)
    call jCat fileChild(fi, 'zwei','::F'),jBuf('zeile zwei','zwei' lev)
    call jCat fileChild(fi, 'drei','::F'),jBuf('zeile drei','drei' lev)
    if lev > 1 then
        call tstFileListMake t, fileChild(fi, 'vier'), lev-1
    return
endProcedure tstFileListMake

tstMail: procedure expose m.
do i=1 to 3
    call mailHead xy, 'mail from walter''s rexx' time() i, A540769
    call mAdd xy, 'und hier kommt der text' ,
                , 'und zeile zwei timestamp' i':' date('s') time() ,
                , left('und eine lange Zeile 159', 156, '+')159 ,
                , left('und eine lange Zeile 160', 157, '+')160 ,
                , left('und eine lange Zeile 161', 158, '+')161 ,
                , left('und eine lange Zeile ', 200, '+')203 'y ',
                , 'und kurz' ,
                , 'und Schluss'
    call mailSend xy
    call sleep 60
    end
    return
endprocedure tstMail

tstF: procedure expose m.
/*
$=/tstF/
    ### start tst tstF ################################################
    f(1 23%c345%c67%%8, eins,  zwei ) =1 23eins345 zwei 67%8;
    f(1\S23%c345%S67%%8, eins,  zwei ) =1\S23eins345zwei67%8;
    f(1 23%C345%C67%%8, eins,  zwei ) =1 23eins345 zwei 67%8;
    f(1 23%c345%S67%%8, eins,  zwei ) =1 23eins345zwei67%8;
    f(1%S2%c3@2%S4@%c5, eins,  zwei ) =1eins2 zwei 3zwei4 zwei 5;
    f(1%-2C2%3C3@2%3.2C4, eins,  zwei ) =1ei2ei 3zwe4;
    f(1@F1%c2@f2%c3@F3%c4, eins,  zwei ) =1fEins2fZwei3fDrei4;
    tstF2 _ %-9C @%5I @%8I @%+8I @%-8I -----
    _ 0             0        0       +0 0       .
    _ -1.2         -1       -1       -1 -1      .
    _ 2.34          2        2       +2 2       .
    _ -34.8765    -35      -35      -35 -35     .
    _ 567.91234   568      568     +568 568     .
    _ -8901     -8901    -8901    -8901 -8901   .
    _ 23456     23456    23456   +23456 23456   .
    _ -789012   *****  -789012  -789012 -789012 .
    _ 34e6      ***** 34000000 ******** 34000000
    _ -56e7     ***** ******** ******** ********
    _ 89e8      ***** ******** ******** ********
    _ txtli     txtli    txtli    txtli txtli   .
    _ undEinLan undEi undEinLa undEinLa undEinLa
    tstF2 _ %-9C @%5.2I @%12.2I @%+12.2I @%-12.2I -----
    _ 0          0.00         0.00        +0.00 0.00        .
    _ -1.2      -1.20        -1.20        -1.20 -1.20       .
    _ 2.34       2.34         2.34        +2.34 2.34        .
    _ -34.8765  *****       -34.88       -34.88 -34.88      .
    _ 567.91234 *****       567.91      +567.91 567.91      .
    _ -8901     *****     -8901.00     -8901.00 -8901.00    .
    _ 23456     *****     23456.00    +23456.00 23456.00    .
    _ -789012   *****   -789012.00   -789012.00 -789012.00  .
    _ 34e6      *****  34000000.00 +34000000.00 34000000.00 .
    _ -56e7     ***** ************ ************ ************
    _ 89e8      ***** ************ ************ ************
    _ txtli     txtli        txtli        txtli txtli       .
    _ undEinLan undEi undEinLanger undEinLanger undEinLanger
    tstF2 _ %-9C @%7e @% 8E @% 9.3e @% 11.4E -----
    _ 0         0.00e00  0.00E00  0.000e00  0.0000E000
    _ -1.2      -1.2e00 -1.20E00 -1.200e00 -1.2000E000
    _ 2.34      2.34e00  2.34E00  2.340e00  2.3400E000
    _ -34.8765  -3.5e01 -3.49E01 -3.488e01 -3.4877E001
    _ 567.91234 5.68e02  5.68E02  5.679e02  5.6791E002
    _ -8901     -8.9e03 -8.90E03 -8.901e03 -8.9010E003
    _ 23456     2.35e04  2.35E04  2.346e04  2.3456E004
    _ -789012   -7.9e05 -7.89E05 -7.890e05 -7.8901E005
    _ 34e6      3.40e07  3.40E07  3.400e07  3.4000E007
    _ -56e7     -5.6e08 -5.60E08 -5.600e08 -5.6000E008
    _ 89e8      8.90e09  8.90E09  8.900e09  8.9000E009
    _ txtli       txtli    txtli     txtli       txtli.
    _ undEinLan undEinL undEinLa undEinLan undEinLange
    _ 8.76e-07  8.76e-7  8.76E-7  8.760e-7  8.7600E-07
    _ 5.43e-11  5.4e-11  5.4E-11  5.43e-11  5.4300E-11
    _ -8.76e-07 -8.8e-7 -8.76E-7 -8.760e-7 -8.7600E-07
    _ -5.43e-11 -5e-011 -5.4E-11 -5.43e-11 -5.4300E-11
$/tstF/ */
    call tst t, 'tstF'
    call tstF1 '1 23%c345%c67%%8'
    call tstF1 '1\S23%c345%S67%%8'
    call tstF1 '1 23%C345%C67%%8'
    call tstF1 '1 23%c345%S67%%8'
    call tstF1 '1%S2%c3@2%S4@%c5'
    call tstF1 '1%-2C2%3C3@2%3.2C4'
    call tstF1 '1@F1%c2@f2%c3@F3%c4'
    nums = '0 -1.2 2.34 -34.8765 567.91234 -8901 23456' ,
                '-789012 34e6 -56e7 89e8 txtli undEinLangerText?'
    call tstF2 '_ %-9C @%5I @%8I @%+8I @%-8I', nums
    call tstF2 '_ %-9C @%5.2I @%12.2I @%+12.2I @%-12.2I', nums
    num2 = ' 8.76e-07  5.43e-11 -8.76e-07  -5.43e-11'
    call tstF2 '_ %-9C @%7e @% 8E @% 9.3e @% 11.4E', nums num2
    call tstEnd t
    return
endProcedure tstF

tstF1: procedure expose m.
parse arg fmt
    e='eins'
    z=' zwei '
    f2 = 'f2'
    m.e.f1 = 'fEins'
    m.e.f2 = 'fZwei'
    m.e.f3 = 'fDrei'
    call out "f("fmt"," e"," z") ="f(fmt, e, z)";"
    return
endProcedure tstF1

tstF2: procedure expose m.
parse arg fmt, vals
    call out 'tstF2' fmt '-----'
    do vx=1 to words(vals)
        call out f(fmt, word(vals, vx))
        end
    return
endProcedure tstF2

tstFe: procedure expose m.
/*
$=/tstFe/
  ### start tst tstFe ###############################################
  .                   1 < 1.00e00> <1.00e00>
  .                   0 < 0.00e00> <0.00e00>
  .                -2.1 <-2.10e00> <-2.1e00>
  .                  .3 < 3.00e-1> <3.00e-1>
  .             -.45678 <-4.57e-1> <-4.6e-1>
  .                 901 < 9.01e02> <9.01e02>
  .               -2345 <-2.35e03> <-2.3e03>
  .              678e90 < 6.78e92> <6.78e92>
  .              123e-4 < 1.23e-2> <1.23e-2>
  .             567e-89 < 5.7e-87> <5.7e-87>
  .              12e456 < 1.2e457> <1.2e457>
  .             78e-901 < 8e-0900> <8e-0900>
  .           2345e5789 < 2e05792> <2e05792>
  .           123e-4567 < 1e-4565> <1e-4565>
  .          8901e23456 < 9e23459> <9e23459>
  .          -123e-4567 <-1e-4565> <-0e-999>
  .          567e890123 <********> <*******>
  .       45678e-901234 < 0e-9999> <0e-9999>
  .                kurz <    kurz> <kurz   >
  .       undLangerText <undLange> <undLang>
$/tstFe/
*/
    call tst t, 'tstFe'
    vAll = '1 0 -2.1 .3 -.45678 901 -2345 678e90 123e-4' ,
            '567e-89 12e456 78e-901 2345e5789  123e-4567 8901e23456' ,
            '-123e-4567 567e890123 45678e-901234' ,
            'kurz undLangerText'
    do vx=1 to words(vAll)
        v = word(vAll, vx)
        call tstOut t, right(v, 20)  '<'fe(v, 8, 2, 'e', ' ')'>' ,
                                     '<'fe(v, 7, 1, 'e', '-')'>'
        end
    call tstEnd t
    return
endProcedure

tstFTst: procedure expose m.
/*
$=/tstFTstS/
    ### start tst tstFTstS ############################################
    1956-01-29-23.34.56.987654     SS => 1956-01-29-23.34.56.987654|
    1956-01-29-23.34.56.987654     Ss => 1956-01-29-23.34.56|
    1956-01-29-23.34.56.987654     S  => 1956-01-29-23.34.56|
    1956-01-29-23.34.56.987654     SD => 19560129|
    1956-01-29-23.34.56.987654     Sd => 560129|
    1956-01-29-23.34.56.987654     SE => 29.01.1956|
    1956-01-29-23.34.56.987654     Se => 29.01.56|
    1956-01-29-23.34.56.987654     St => 23.34.56|
    1956-01-29-23.34.56.987654     ST => 23:34:56|
    1956-01-29-23.34.56.987654     SY => GB29|
    1956-01-29-23.34.56.987654     SM => B2923345|
    1956-01-29-23.34.56.987654     SH => C33456|
$/tstFTstS/
$=/tstFTsts/
    ### start tst tstFTsts ############################################
    2014-12-23-16.57.38            sS => 2014-12-23-16.57.38.000000|
    2014-12-23-16.57.38            ss => 2014-12-23-16.57.38|
    2014-12-23-16.57.38            s  => 2014-12-23-16.57.38|
    2014-12-23-16.57.38            sD => 20141223|
    2014-12-23-16.57.38            sd => 141223|
    2014-12-23-16.57.38            sE => 23.12.2014|
    2014-12-23-16.57.38            se => 23.12.14|
    2014-12-23-16.57.38            st => 16.57.38|
    2014-12-23-16.57.38            sT => 16:57:38|
    2014-12-23-16.57.38            sY => EM23|
    2014-12-23-16.57.38            sM => M2316573|
    2014-12-23-16.57.38            sH => B65738|
$/tstFTsts/
$=/tstFTstD/
    ### start tst tstFTstD ############################################
    23450618                       DS => 2345-06-18-00.00.00.000000|
    23450618                       Ds => 2345-06-18-00.00.00|
    23450618                       D  => 2345-06-18-00.00.00|
    23450618                       DD => 23450618|
    23450618                       Dd => 450618|
    23450618                       DE => 18.06.2345|
    23450618                       De => 18.06.45|
    23450618                       Dt => 00.00.00|
    23450618                       DT => 00:00:00|
    23450618                       DY => PG18|
    23450618                       DM => G1800000|
    23450618                       DH => A00000|
$/tstFTstD/
0----+----1----+----2----+----3
abcdefghijklmnopqrstuvwxyz
$=/tstFTstd/
    ### start tst tstFTstd ############################################
    120724                         dS => 2012-07-24-00.00.00.000000|
    120724                         ds => 2012-07-24-00.00.00|
    120724                         d  => 2012-07-24-00.00.00|
    120724                         dD => 20120724|
    120724                         dd => 120724|
    120724                         dE => 24.07.2012|
    120724                         de => 24.07.12|
    120724                         dt => 00.00.00|
    120724                         dT => 00:00:00|
    120724                         dY => CH24|
    120724                         dM => H2400000|
    120724                         dH => A00000|
$/tstFTstd/
$=/tstFTstE/
    ### start tst tstFTstE ############################################
    09.12.1345                     ES => 1345-12-09-00.00.00.000000|
    09.12.1345                     Es => 1345-12-09-00.00.00|
    09.12.1345                     E  => 1345-12-09-00.00.00|
    09.12.1345                     ED => 13451209|
    09.12.1345                     Ed => 451209|
    09.12.1345                     EE => 09.12.1345|
    09.12.1345                     Ee => 09.12.45|
    09.12.1345                     Et => 00.00.00|
    09.12.1345                     ET => 00:00:00|
    09.12.1345                     EY => PM09|
    09.12.1345                     EM => M0900000|
    09.12.1345                     EH => A00000|
$/tstFTstE/
$=/tstFTste/
    ### start tst tstFTste ############################################
    31.05.2467                     eS => 2024-05-31-00.00.00.000000|
    31.05.2467                     es => 2024-05-31-00.00.00|
    31.05.2467                     e  => 2024-05-31-00.00.00|
    31.05.2467                     eD => 20240531|
    31.05.2467                     ed => 240531|
    31.05.2467                     eE => 31.05.2024|
    31.05.2467                     ee => 31.05.2467|
    31.05.2467                     et => 00.00.00|
    31.05.2467                     eT => 00:00:00|
    31.05.2467                     eY => OF31|
    31.05.2467                     eM => F3100000|
    31.05.2467                     eH => A00000|
$/tstFTste/
$=/tstFTstt/
### start tst tstFTstt ############################################
    12.34.56                       tS => 0000-01-01-12.34.56.000000|
    12.34.56                       ts => 0000-01-01-12.34.56|
    12.34.56                       t  => 0000-01-01-12.34.56|
    12.34.56                       tD => 00000101|
    12.34.56                       td => 000101|
    12.34.56                       tE => 01.01.0000|
    12.34.56                       te => 01.01.00|
    12.34.56                       tt => 12.34.56|
    12.34.56                       tT => 12:34:56|
    12.34.56                       tY => ??01|
    12.34.56                       tM => ?0112345|
    12.34.56                       tH => B23456|
$/tstFTstt/
$=/tstFTstT/
    ### start tst tstFTstT ############################################
    23.45.06                       TS => 0000-01-01-23.45.06.000000|
    23.45.06                       Ts => 0000-01-01-23.45.06|
    23.45.06                       T  => 0000-01-01-23.45.06|
    23.45.06                       TD => 00000101|
    23.45.06                       Td => 000101|
    23.45.06                       TE => 01.01.0000|
    23.45.06                       Te => 01.01.00|
    23.45.06                       Tt => 23.45.06|
    23.45.06                       TT => 23.45.06|
    23.45.06                       TY => ??01|
    23.45.06                       TM => ?0123450|
    23.45.06                       TH => C34506|
$/tstFTstT/
$=/tstFTstY/
    ### start tst tstFTstY ############################################
    FE25                           YS => 2015-04-25-00.00.00.000000|
    FE25                           Ys => 2015-04-25-00.00.00|
    FE25                           Y  => 2015-04-25-00.00.00|
    FE25                           YD => 20150425|
    FE25                           Yd => 150425|
    FE25                           YE => 25.04.2015|
    FE25                           Ye => 25.04.15|
    FE25                           Yt => 00.00.00|
    FE25                           YT => 00:00:00|
    FE25                           YY => FE25|
    FE25                           YM => E2500000|
    FE25                           YH => A00000|
$/tstFTstY/
$=/tstFTstM/
    ### start tst tstFTstM ############################################
    I2317495                       MS => 0000-08-23-17.49.50.000000|
    I2317495                       Ms => 0000-08-23-17.49.50|
    I2317495                       M  => 0000-08-23-17.49.50|
    I2317495                       MD => 00000823|
    I2317495                       Md => 000823|
    I2317495                       ME => 23.08.0000|
    I2317495                       Me => 23.08.00|
    I2317495                       Mt => 17.49.50|
    I2317495                       MT => 17:49:50|
    I2317495                       MY => ?I23|
    I2317495                       MM => I2317495|
    I2317495                       MH => B74950|
$/tstFTstM/
$=/tstFTstH/
    ### start tst tstFTstH ############################################
    B23456                         HS => 0000-01-01-12.34.56.000000|
    B23456                         Hs => 0000-01-01-12.34.56|
    B23456                         H  => 0000-01-01-12.34.56|
    B23456                         HD => 00000101|
    B23456                         Hd => 000101|
    B23456                         HE => 01.01.0000|
    B23456                         He => 01.01.00|
    B23456                         Ht => 12.34.56|
    B23456                         HT => 12:34:56|
    B23456                         HY => ??01|
    B23456                         HM => ?0112345|
    B23456                         HH => B23456|
$/tstFTstH/
*/
    say "f('%t  ')" f('%t  ')
    allOut = 'Ss DdEetTYMH'
    allIn  = 'S1956-01-29-23.34.56.987654' ,
             's2014-12-23-16.57.38' ,
             'D23450618' ,
             'd120724'   ,
             'E09.12.1345' ,
             'e31.05.2467' ,
             't12.34.56'  ,
             'T23.45.06' ,
             'YFE25' ,
             'MI2317495' ,
             'HB23456'
    do ix=1 to words(allIn)
        parse value word(allIn, ix) with iF 2 iV
        call tst t, "tstFTst"iF
        do ox=1 to length(allOut)
            ft = iF || substr(allOut, ox, 1)
            call tstOut t, left(iV, 30) ft  '=>' f('%t'ft, iV)'|'
            if 0 & iF = 'Y' then
                say '???' ft '>>>' mGet('F_GEN.%t'ft)
            end
        call tstEnd t
        end
    return
endProcedure tstFTst

tstFmt: procedure expose m.
    call pipeIni
/*
$=/tstFmt/
    ### start tst tstFmt ##############################################
    =   a2i b3b   d4                       fl5          ex6
    -11 -11 b3    d4-11+d4++++    -111.1000000 -1.11000e-12
    -1  -10 b     d4-10+d4+++            null1        null3
    -    -9 b3b-9 d4-9+d4+++       -11.0000000 -1.11900e-10
    -8+  -8 b3b-  d4-8+d4++        -18.0000000 -1.18000e010
    -7   -7 b3b   d4-7+d4+          -7.0000000 -1.70000e-07
    -    -6 b3    d4-6+d4           -0.1111160 -6.00000e006
    -5+  -5 b     d4-5+d                 null2        null2
    -4   -4 b3b-4 d4-4+         -11114.0000000 -1.11140e008
    -    -3 b3b-  d4-3              -0.1130000 -1.13000e-04
    -2+  -2 b3b   d4-               -0.1200000 -1.20000e001
    -1   -1 b3    d4                -0.1000000 -1.00000e-02
    0     0 b     d                      null1        null1
    1+    1 b3    d4                 0.1000000  1.00000e-02
    2++   2 b3b   d42                0.1200000  1.20000e001
    3     3 b3b3  d43+               0.1130000  1.13000e-04
    4+    4 b3b4+ d44+d          11114.0000000  1.11140e008
    5++   5 b     d45+d4                 null2        null2
    6     6 b3    d46+d4+            0.1111160  1.11116e005
    7+    7 b3b   d47+d4++           0.1111117  7.00000e-08
    8++   8 b3b8  d48+d4+++          8.0000000  1.80000e009
    9     9 b3b9+ d49+d4++++         0.9000000  1.19000e-08
    10   10 b     d410+d4++++            null1        null3
    11+  11 b3    d411+d4+++++       0.1110000  1.00000e-12
    1    12 b3b   d412+d4++++++  11112.0000000  2.00000e012
    13   13 b3b1  d               1111.3000000  1.13000e-12
    14+  14 b3b14 d4            111111.0000000  1.40000e013
    1    15 b     d41                    null2        null1
    16   16 b3    d416               6.0000000  1.16000e003
    17+  17 b3b   d417+              0.7000000  1.11170e-03
    1    18 b3b1  d418+d            11.0000000  1.11800e003
    19   19 b3b19 d419+d4            0.1190000  9.00000e-05
    20+  20 b     d420+d4+               null1        null2
    2    21 b3    d421+d4++         11.1210000  1.11000e-05
    22   22 b3b   d422+d4+++     11111.2000000  2.00000e007
    23+  23 b3b2  d423+d4++++        0.1111123  1.11230e-09
    c3L      a2i drei  d4                 fl5          ex6
    -11 -1.10E01 b3    d4-11+d   -111.1000000 -1.11000e-12
    -1  -1.00E01 b     d4-10+d          null1        null3
    -   -9.00E00 b3b-9 d4-9+d4    -11.0000000 -1.11900e-10
    -8+ -8.00E00 b3b-  d4-8+d4    -18.0000000 -1.18000e010
    -7  -7.00E00 b3b   d4-7+d4     -7.0000000 -1.70000e-07
    -   -6.00E00 b3    d4-6+d4     -0.1111160 -6.00000e006
    -5+ -5.00E00 b     d4-5+d           null2        null2
    -4  -4.00E00 b3b-4 d4-4+   -11114.0000000 -1.11140e008
    -   -3.00E00 b3b-  d4-3        -0.1130000 -1.13000e-04
    -2+ -2.00E00 b3b   d4-         -0.1200000 -1.20000e001
    -1  -1.00E00 b3    d4          -0.1000000 -1.00000e-02
    0    0.00E00 b     d                null1        null1
    1+   1.00E00 b3    d4           0.1000000  1.00000e-02
    2++  2.00E00 b3b   d42          0.1200000  1.20000e001
    3    3.00E00 b3b3  d43+         0.1130000  1.13000e-04
    4+   4.00E00 b3b4+ d44+d    11114.0000000  1.11140e008
    5++  5.00E00 b     d45+d4           null2        null2
    6    6.00E00 b3    d46+d4+      0.1111160  1.11116e005
    7+   7.00E00 b3b   d47+d4+      0.1111117  7.00000e-08
    8++  8.00E00 b3b8  d48+d4+      8.0000000  1.80000e009
    9    9.00E00 b3b9+ d49+d4+      0.9000000  1.19000e-08
    10   1.00E01 b     d410+d4          null1        null3
    11+  1.10E01 b3    d411+d4      0.1110000  1.00000e-12
    1    1.20E01 b3b   d412+d4  11112.0000000  2.00000e012
    13   1.30E01 b3b1  d         1111.3000000  1.13000e-12
    14+  1.40E01 b3b14 d4      111111.0000000  1.40000e013
    1    1.50E01 b     d41              null2        null1
    16   1.60E01 b3    d416         6.0000000  1.16000e003
    17+  1.70E01 b3b   d417+        0.7000000  1.11170e-03
    1    1.80E01 b3b1  d418+d      11.0000000  1.11800e003
    19   1.90E01 b3b19 d419+d4      0.1190000  9.00000e-05
    20+  2.00E01 b     d420+d4          null1        null2
    2    2.10E01 b3    d421+d4     11.1210000  1.11000e-05
    22   2.20E01 b3b   d422+d4  11111.2000000  2.00000e007
    23+  2.30E01 b3b2  d423+d4      0.1111123  1.11230e-09
$/tstFmt/ */

    call tst t, "tstFmt"
    b = jBuf()
    st = b'.BUF'
    call pipe '+F', b
    call tstDataClassOut '. c3 a2i i b3b c5 d4 c13 fl5 f8n2 ex6 e9n3',
        , -11, + 23
    call pipe '-'
    call fTabAuto fTabReset(abc, 1), b
    call fTabReset abc, 1
    call fTabAddDetect abc,      , st   , 'c3L'
    call fTabAdd       abc, 'a2i', ' % 8E'
    call fTabAddDetect abc, 'b3b', st   , 'drei'
    call fTabAdd       abc, 'd4', ' %-7C'
    call fTabAddDetect abc, 'fl5', st
    call fTabAddDetect abc, 'ex6', st
    call fTab abc, b
    call tstEnd t
    return
endProcedure tstFmt

tstFTab: procedure expose m.
    call pipeIni
/*
$=/tstFTab/
    ### start tst tstFTab #############################################
    testData begin
    ..---------a2i-b3b------------------d4------fl5-----ex6---
    -11       -11 b3           -11+d4++++ -111.100 -1e-012
    -1        -10 b            4-10+d4+++    null1   null3
    -          -9 b3b-9        d4-9+d4+++  -11.000 -1e-010
    -8+        -8 b3b-          d4-8+d4++  -18.000 -1.2e10
    -7         -7 b3b            d4-7+d4+   -7.000 -1.7e-7
    -          -6 b3              d4-6+d4   -0.111 -6.0e06
    -5+        -5 b                d4-5+d    null2   null2
    -4         -4 b3b-4             d4-4+ ******** -1.1e08
    -          -3 b3b-               d4-3   -0.113 -1.1e-4
    -2+        -2 b3b                 d4-   -0.120 -1.2e01
    -1         -1 b3                   d4   -0.100 -1.0e-2
    0           0 b                     d    null1   null1
    1+          1 b3                   d4    0.100 1.00e-2
    2++         2 b3b                 d42    0.120 1.20e01
    3           3 b3b3               d43+    0.113 1.13e-4
    4+          4 b3b4+             d44+d ******** 1.11e08
    5++         5 b                d45+d4    null2   null2
    6           6 b3              d46+d4+    0.111 1.11e05
    7+          7 b3b            d47+d4++    0.111 7.00e-8
    8++         8 b3b8          d48+d4+++    8.000 1.80e09
    9           9 b3b9+        d49+d4++++    0.900 1.19e-8
    10         10 b            410+d4++++    null1   null3
    11+        11 b3           11+d4+++++    0.111 1.0e-12
    1          12 b3b          2+d4++++++ ******** 2.00e12
    13         13 b3b1                  d 1111.300 1.1e-12
    14+        14 b3b14                d4 ******** 1.40e13
    1          15 b                   d41    null2   null1
    16         16 b3                 d416    6.000 1.16e03
    17+        17 b3b               d417+    0.700 1.11e-3
    1          18 b3b1             d418+d   11.000 1.12e03
    19         19 b3b19           d419+d4    0.119 9.00e-5
    20+        20 b              d420+d4+    null1   null2
    2          21 b3            d421+d4++   11.121 1.11e-5
    22         22 b3b          d422+d4+++ ******** 2.00e07
    23+        23 b3b2         423+d4++++    0.111 1.11e-9
    ..---------a2i-b3b------------------d4------fl5-----ex6---
    testData end
$/tstFTab/ */

    call tst t, "tstFTab"
    b = jBuf()
    st = b'.BUF'
    call pipe '+F', b
    call tstDataClassOut '. c3 a2i i b3b c5 d4 c13 fl5 f8n2 ex6 e9n3',
        , -11, + 23
    call pipe 'P|'
    call fTabReset ft, 2 1, 1 3, '-'
    call fTabAdd   ft, '='   , '%-6C' , '.', 'testData begin',
                                            , 'testData end'
    call fTabAdd   ft, 'a2i' , ' %6i'
    call fTabAdd   ft, 'b3b' , ' %-12C'
    call fTabAdd   ft, 'd4'  , ' %10C'
    call fTabAdd   ft, 'fl5' , ' %8.3I'
    call fTabAdd   ft, 'ex6' , ' %7e'
    call fTab ft
    call pipe '-'
    call tstEnd t
    return
endProcedure tstFTab


tstfUnits: procedure
/*
$=/tstfUnits/
    ### start tst tstfUnits ###########################################
    .            .3 ==>  0s30 ++>   0s30 -+> -0s30 -->  -0s30
    .            .8 ==>  0s80 ++>   0s80 -+> -0s80 -->  -0s80
    .             1 ==>  1s00 ++>   1s00 -+> -1s00 -->  -1s00
    .           1.2 ==>  1s20 ++>   1s20 -+> -1s20 -->  -1s20
    .            59 ==> 59s00 ++>  59s00 -+> -59s0 --> -59s00
    .         59.07 ==> 59s07 ++>  59s07 -+> -59s0 --> -59s07
    .        59.997 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .            60 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .          60.1 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .           611 ==> 10m11 ++>  10m11 -+> -10m1 --> -10m11
    .        3599.4 ==> 59m59 ++>  59m59 -+> -59m5 --> -59m59
    .        3599.5 ==>  1h00 ++>   1h00 -+> -1h00 -->  -1h00
    .          3661 ==>  1h01 ++>   1h01 -+> -1h01 -->  -1h01
    .         83400 ==> 23h10 ++>  23h10 -+> -23h1 --> -23h10
    .         84700 ==> 23h32 ++>  23h32 -+> -23h3 --> -23h32
    .         86400 ==>  1d00 ++>   1d00 -+> -1d00 -->  -1d00
    .         89900 ==>  1d01 ++>   1d01 -+> -1d01 -->  -1d01
    .       8467200 ==> 98d00 ++>  98d00 -+> -98d0 --> -98d00
    .    8595936.00 ==> 99d12 ++>  99d12 -+> -99d1 --> -99d12
    .    8638704.00 ==>  100d ++>   100d -+> -100d -->  -100d
    .       8640000 ==>  100d ++>   100d -+> -100d -->  -100d
    .     863913600 ==> 9999d ++>  9999d -+> ----d --> -9999d
    .     863965440 ==> ++++d ++> 10000d -+> ----d --> -----d
    .     8.6400E+9 ==> ++++d ++> +++++d -+> ----d --> -----d
    .            .3 ==>   0.300 ++>    0.300 -+>  -0.300 -->   -0.300
    .            .8 ==>   0.800 ++>    0.800 -+>  -0.800 -->   -0.800
    .             1 ==>   1.000 ++>    1.000 -+>  -1.000 -->   -1.000
    .           1.2 ==>   1.200 ++>    1.200 -+>  -1.200 -->   -1.200
    .            59 ==>  59.000 ++>   59.000 -+> -59.000 -->  -59.000
    .         59.07 ==>  59.070 ++>   59.070 -+> -59.070 -->  -59.070
    .        59.997 ==>  59.997 ++>   59.997 -+> -59.997 -->  -59.997
    .            60 ==>  60.000 ++>   60.000 -+> -60.000 -->  -60.000
    .          60.1 ==>  60.100 ++>   60.100 -+> -60.100 -->  -60.100
    .           611 ==> 611.000 ++>  611.000 -+> -611.00 --> -611.000
    .        3599.4 ==>   3k599 ++>    3k599 -+>  -3k599 -->   -3k599
    .        3599.5 ==>   3k600 ++>    3k600 -+>  -3k600 -->   -3k600
    .          3661 ==>   3k661 ++>    3k661 -+>  -3k661 -->   -3k661
    .         83400 ==>  83k400 ++>   83k400 -+> -83k400 -->  -83k400
    .     999999.44 ==> 999k999 ++>  999k999 -+> -999k99 --> -999k999
    .      999999.5 ==>   1M000 ++>    1M000 -+>  -1M000 -->   -1M000
    .    567.6543E6 ==> 567M654 ++>  567M654 -+> -567M65 --> -567M654
    .    .9999991E9 ==> 999M999 ++>  999M999 -+> -999M99 --> -999M999
    .    .9999996E9 ==>   1G000 ++>    1G000 -+>  -1G000 -->   -1G000
    .   .9999991E12 ==> 999G999 ++>  999G999 -+> -999G99 --> -999G999
    .   .9999996E12 ==>   1T000 ++>    1T000 -+>  -1T000 -->   -1T000
    .   567.6543E12 ==> 567T654 ++>  567T654 -+> -567T65 --> -567T654
    .   .9999991E15 ==> 999T999 ++>  999T999 -+> -999T99 --> -999T999
    .   .9999996E15 ==>   1P000 ++>    1P000 -+>  -1P000 -->   -1P000
    .   .9999991E18 ==> 999P999 ++>  999P999 -+> -999P99 --> -999P999
    .   .9999996E18 ==>   1E000 ++>    1E000 -+>  -1E000 -->   -1E000
    .   567.6543E18 ==> 567E654 ++>  567E654 -+> -567E65 --> -567E654
    .   .9999991E21 ==> 999E999 ++>  999E999 -+> -999E99 --> -999E999
    .   .9999996E21 ==>   1000E ++>    1000E -+>  -1000E -->   -1000E
    .   .9999992E24 ==> 999999E ++>  999999E -+> ------E --> -999999E
    .   .9999995E24 ==> ++++++E ++> 1000000E -+> ------E --> -------E
    .    10.6543E24 ==> ++++++E ++> +++++++E -+> ------E --> -------E
$/tstfUnits/ */
    call jIni
    call tst t, "tstfUnits"
    d = 86400
    lst = .3 .8 1 1.2 59 59.07 59.997 60 60.1 611 ,
          3599.4 3599.5 3661 d-3000 d-1700 d d+3500 ,
          d * 98 d * 99.49 d * 99.985 d*100 d * 9999 d * 9999.6 ,
          d * 1e5
    do wx=1 to words(lst)
        call tstOut t, right(word(lst, wx), 14) ,
                 '==>' fTime(     word(lst, wx)   ) ,
                 '++>' fTime(     word(lst, wx), 1),
                 '-+>' fTime('-'word(lst, wx),    ),
                 '-->' fTime('-'word(lst, wx), 1)
        end
    lst = subword(lst, 1, 14) 999999.44 999999.5,
        567.6543e6 .9999991e9 .9999996e9 .9999991e12 .9999996e12 ,
        567.6543e12 .9999991e15 .9999996e15 .9999991e18 .9999996e18 ,
        567.6543e18 .9999991e21 .9999996e21 .9999992e24 .9999995e24 ,
         10.6543e24
    do wx=1 to words(lst)
        call tstOut t, right(word(lst, wx), 14) ,
                 '==>' fDec(      word(lst, wx)   ) ,
                 '++>' fDec(      word(lst, wx), 1),
                 '-+>' fDec('-'word(lst, wx),     ),
                 '-->' fDec('-'word(lst, wx), 1)
        end
    call tstEnd t
    return
endProcedure tstfUnits

tstSb: procedure expose m.
/*
$=/tstSb/
    ### start tst tstSb ###############################################
    end        : 0
    char  3    : 1 abc
    lit   d?   : 0 .
    lit   de   : 1 de
    lit   de ? fg fgh: 1 fg
    while HIJ  : 0 .
    end        : 0
    while Jih  : 1 hi
    while ? klj: 1 jklkl ?
    end        : 1
    while ? klj: 0 .
    char  3    : 0 .
    lit        : 0 .
    until cba  : 0 .
    until ?qd  : 1 abc
    until ?qr  : 1 defdef .
    until ?qr  : 0 .
    strEnd ?   : 1 ?
    strEnd ?   : 0 .
    strEnd ?   : 1 ab??cd????gh?
    strEnd ") ": 1 ab) .
    strEnd ") ": 1 cd) ) gh) .
$/tstSb/ */
    call tst t, 'tstSb'
    call scanSBSrc s, 'abcdefghijklkl ?'
    call out 'end        :' scanSBEnd(s)
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit   d?   :' scanLit(s, 'd?') m.s.tok
    call out 'lit   de   :' scanLit(s, 'de') m.s.tok
    call out 'lit   de ? fg fgh:',
            scanLit(s, 'de', '?', 'fg', 'fgh') m.s.tok
    call out 'while HIJ  :' scanWhile(s, 'HIJ') m.s.tok
    call out 'end        :' scanSBEnd(s)
    call out 'while Jih  :' scanWhile(s, 'Jih') m.s.tok
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'end        :' scanSBEnd(s)
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit        :' scanLit(s) m.s.tok
    call scanSBSrc s, 'abcdefdef ?'
    call out 'until cba  :' scanUntil(s, 'cba') m.s.tok
    call out 'until ?qd  :' scanUntil(s, '?qd') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSBSrc s, 'ab??cd????gh?ijk'
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSBSrc s, 'ab) cd) ) gh) jk) )'
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call tstEnd t
    return
endProcedure tstSb

tstSb2: procedure expose m.
/*
$=/tstSb2/
    ### start tst tstSb2 ##############################################
    end        : 0
    char  3    : 1 abc
    lit   d?   : 0 .
    lit   de   : 1 de
    lit   de ? fg fgh: 1 fg
    while HIJ  : 0 .
    end        : 0
    while Jih  : 1 hi
    while ? klj: 1 jklkl ?
    end        : 1
    while ? klj: 0 .
    char  3    : 0 .
    lit        : 0 .
    until cba  : 0 .
    until ?qd  : 1 abc
    until ?qr  : 1 defdef .
    until ?qr  : 0 .
    strEnd ?   : 1 ?
    strEnd ?   : 0 .
    strEnd ?   : 1 ab??cd????gh?
    strEnd ") ": 1 ab) .
    strEnd ") ": 1 cd) ) gh) .
$/tstSb2/ */
    call tst t, 'tstSb2'
    call scanIni
    call scanSrc s, 'abcdefghijklkl ?'
    call out 'end        :' scanEnd(s)
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit   d?   :' scanLit(s, 'd?') m.s.tok
    call out 'lit   de   :' scanLit(s, 'de') m.s.tok
    call out 'lit   de ? fg fgh:',
            scanLit(s, 'de', '?', 'fg', 'fgh') m.s.tok
    call out 'while HIJ  :' scanWhile(s, 'HIJ') m.s.tok
    call out 'end        :' scanEnd(s)
    call out 'while Jih  :' scanWhile(s, 'Jih') m.s.tok
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'end        :' scanEnd(s)
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit        :' scanLit(s) m.s.tok
    call scanSrc s, 'abcdefdef ?'
    call out 'until cba  :' scanUntil(s, 'cba') m.s.tok
    call out 'until ?qd  :' scanUntil(s, '?qd') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab??cd????gh?ijk'
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab) cd) ) gh) jk) )'
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call tstEnd t
    return
endProcedure tstSb2

tstScan: procedure expose m.
/*
$=/tstScan.1/
    ### start tst tstScan.1 ###########################################
    scan src a034,Und hr123sdfER"st1" 'str2''mit''apo''s' .
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    scan n tok 3: Und key  val .
    scan v tok 1:   key  val .
    scan n tok 10: hr123sdfER key  val .
    scan q tok 5: "st1" key  val st1
    scan v tok 1:   key  val st1
    scan a tok 19: 'str2''mit''apo''s' key  val str2'mit'apo's
    scan v tok 1:   key  val str2'mit'apo's
$/tstScan.1/ */
    call scanIni
    call tst t, 'tstScan.1'
    call tstScan1 ,'l"litEins"l"litZwei"ndv" "aq1' ,
        ,"a034,Und hr123sdfER""st1"" 'str2''mit''apo''s' "
    call tstEnd t

/*
$=/tstScan.2/
    ### start tst tstScan.2 ###########################################
    scan src a034,Und hr123sdfER"st1" 'str2''mit''apo''s' .
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    scan n tok 3: Und key  val .
    scan b tok 1:   key  val .
    scan n tok 10: hr123sdfER key  val .
    scan " tok 5: "st1" key  val st1
    scan b tok 1:   key  val st1
    scan ' tok 19: 'str2''mit''apo''s' key  val str2'mit'apo's
    scan b tok 1:   key  val str2'mit'apo's
$/tstScan.2/ */
    call tst t, 'tstScan.2'
    call tstScan1 , 'ndsb1' ,
        ,"a034,Und hr123sdfER""st1"" 'str2''mit''apo''s' "
    call tstEnd t

/*
$=/tstScan.3/
    ### start tst tstScan.3 ###########################################
    scan src a034,'wie 789abc
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    *** err: scanErr ending Apostroph(') missing
    .    e 1: last token  scanPosition 'wie 789abc
    .    e 2: pos 6 in string a034,'wie 789abc
    scan ' tok 1: ' key  val .
    scan n tok 3: wie key  val .
    scan s tok 1:   key  val .
    *** err: scanErr illegal number end after 789
    .    e 1: last token 789 scanPosition abc
    .    e 2: pos 14 in string a034,'wie 789abc
    scan d tok 3: 789 key  val .
    scan n tok 3: abc key  val .
$/tstScan.3/ */
    call tst t, 'tstScan.3'
    call tstScan1 , 'nds1' ,
        ,"a034,'wie 789abc"
    call tstEnd t

/*
$=/tstScan.4/
    ### start tst tstScan.4 ###########################################
    scan src litEinsefr 23 sdfER'str1'litZwei "str2""mit quo"s .
    scan l tok 7: litEins key  val .
    scan n tok 3: efr key  val .
    scan b tok 1:   key  val .
    scan d tok 2: 23 key  val .
    scan b tok 1:   key  val .
    scan n tok 5: sdfER key  val .
    scan a tok 6: 'str1' key  val str1
    scan l tok 7: litZwei key  val str1
    scan b tok 1:   key  val str1
    scan q tok 15: "str2""mit quo" key  val str2"mit quo
    scan n tok 1: s key  val str2"mit quo
    scan b tok 1:   key  val str2"mit quo
$/tstScan.4/ */
    call tst t, 'tstScan.4'
    call tstScan1 , 'l"litEins"l"litZwei"ndbaq1' ,
           ,"litEinsefr 23 sdfER'str1'litZwei ""str2""""mit quo""s "
    call tstEnd t

/*
$=/tstScan.5/
    ### start tst tstScan.5 ###########################################
    scan src  aha;+-=f ab=cdEf eF='strIng' .
    scan b tok 1:   key  val .
    scan k tok 4:  no= key aha val def
    scan ; tok 1: ; key aha val def
    scan + tok 1: + key aha val def
    scan - tok 1: - key aha val def
    scan = tok 1: = key aha val def
    scan k tok 4:  no= key f val def
    scan k tok 4: cdEf key ab val cdEf
    scan b tok 1:   key ab val cdEf
    scan k tok 8: 'strIng' key eF val strIng
    scan b tok 1:   key eF val strIng
$/tstScan.5/ */
    call tst t, 'tstScan.5'
    call tstScan1 , 'bk1'," aha;+-=f ab=cdEf eF='strIng' "
    call tstEnd t
    return
endProcedure tstScan

tstScanRead: procedure expose m.
/*
$=/tstScanRead/
    ### start tst tstScanRead #########################################
    name erste
    space
    name Zeile
    space
    nextLine
    nextLine
    space
    name dritte
    space
    name Zeile
    space
    name schluss
    space
$/tstScanRead/ */
    call scanReadIni
    call tst t, 'tstScanRead'
    b = jBuf('erste Zeile  ',,'  dritte Zeile  schluss  ')
    s = jOpen(scanRead(b), m.j.cRead)
    do while \scanEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanVerify(s, ' ') then call tstOut t, 'space'
        else if scanReadNL(s) then      call tstOut t, 'nextLine'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        end
    call jClose s
    call tstEnd t

/*
$=/tstScanReadMitSpaceLn/
    ### start tst tstScanReadMitSpaceLn ###############################
    name erste
    spaceLn
    name Zeile
    spaceLn
    name dritte
    spaceLn
    name Zeile
    spaceLn
    name schluss
    spaceLn
$/tstScanReadMitSpaceLn/ */
    call tst t, 'tstScanReadMitSpaceLn'
    s = scanOpen(scanRead(b))
    do forever
        if scanName(s) then         call out 'name' m.s.tok
        else if scanSpace(s) then call out 'spaceLn'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        else                        leave
        end
    call scanClose s
    call tstEnd t

/*
$=/tstScanJRead/
    ### start tst tstScanJRead ########################################
    1 jRead n tok erste val .
    2 jRead s tok   val .
    3 jRead n tok Zeile val .
    4 jRead s tok   val .
    5 jRead n tok dritte val .
    6 jRead s tok   val .
    7 jRead n tok Zeile val .
    8 jRead s tok   val .
    9 jRead n tok schluss val .
    10 jRead s tok   val .
    11 jRead 0 tok 1 val 1
    12 jRead s tok   val 1
    13 jRead + tok + val 1
    14 jRead s tok   val 1
    15 jRead 0 tok 2. val 2..
    16 jRead s tok   val 2..
    17 jRead + tok + val 2..
    18 jRead . tok . val 2..
    19 jRead s tok   val 2..
    20 jRead 0 tok +.3 val +.3
    21 jRead 0 tok -45e-3 val -45E-3
    22 jRead s tok   val -45E-3
    23 jRead " tok "a""b" val a"b
    24 jRead s tok   val a"b
    25 jRead ' tok 'c''d' val c'd
    className 1: ScanRes 18: ScanRes
$/tstScanJRead/ */

    call tst t, 'tstScanJRead'
    call jWrite jOpen(b,'>>'), '1  + 2. +. +.3-45e-3 "a""b"' "'c''d'"
    s = jOpen(scanRead(jClose(b)), '<')
    do x=1 while ass('v', jReadO(s))  \== ''
        call out x 'jRead' m.v.type 'tok' m.v.tok 'val' m.v.val
        v.x = v
        end
    call jClose s
    call out 'className 1:' className(objClass(v.1)),
                    '18:' className(objClass(v.18))
    call tstEnd t
    return
endProcedure tstScanRead

tstScanUtilInto: procedure expose m.
/*
$=/tstScanUtilIntoL/
TEMPLATE P3
     DSN('DBAF.DA540769.A802A.P00003.BV5I3NRN.REC')
     DISP(OLD,KEEP,KEEP)
TEMPLATE P4
     DSN('DBAF.DA540769.A802A.P00004.BV5I3NTK.REC')
     DISP(OLD,KEEP,KEEP)
LOAD DATA        LOG NO RESUME NO REPLACE COPYDDN(TCOPYD)
           STATISTICS INDEX(ALL) REPORT NO UPDATE ALL
 EBCDIC  CCSID(00500,00000,00000)
 SORTKEYS
  -- ENFORCE NO
  SORTDEVT DISK
  SORTNUM 160
  WORKDDN(TSYUTD,TSOUTD)
  INTO TABLE OA1P.TWB981 PART 1 INDDN TREC134
 WORKDDN(TSYUTS,TSOUTS)
 INTO TABLE "A540769"
   ."TWK802A1"
 PART 00001 INDDN P0
 WHEN(00001:00002) = X'0041'
 ( "DE1"
  POSITION(  00003:00010) DECIMAL
 , "CH2"
  POSITION(  00011:00015) CHAR(00005)
 , "TS3"
  POSITION(  00016:00041) TIMESTAMP EXTERNAL
 , "TI4"
  POSITION(  00042:00049) TIME EXTERNAL
 , "DA5"
  POSITION(  00050:00059) DATE EXTERNAL
 , "IN6"
  POSITION(  00060:00063) INTEGER
 , "RE7"
  POSITION(  00064:00067) FLOAT(21)
 )
 INTO TABLE "A540769"."TWK802A1"
 PART 00002 INDDN P0
 WHEN(00001:00002) = X'0041'
 ( "DE1"
  POSITION(  00003:00010) DECIMAL
 , "CH2"
  POSITION(  00011:00015) CHAR(00005)
 )
 dobido
$/tstScanUtilIntoL/
$=/tstScanUtilInto/
  ### start tst tstScanUtilInto #####################################
  -- 1 scanUtilInto
  . ( "DE1"
  .  POSITION(  00003:00010) DECIMAL
  . , "CH2"
  .  POSITION(  00011:00015) CHAR(00005)
  . , "TS3"
  .  POSITION(  00016:00041) TIMESTAMP EXTERNAL
  . , "TI4"
  .  POSITION(  00042:00049) TIME EXTERNAL
  . , "DA5"
  .  POSITION(  00050:00059) DATE EXTERNAL
  . , "IN6"
  .  POSITION(  00060:00063) INTEGER
  . , "RE7"
  .  POSITION(  00064:00067) FLOAT(21)
  . ) .
  .  -- table OA1P.TWB981 part 00001
  -- 2 scanUtilInto
  . ( "DE1"
  .  POSITION(  00003:00010) DECIMAL
  . , "CH2"
  .  POSITION(  00011:00015) CHAR(00005)
  . ) .
  .  -- table A540769.TWK802A1 part 00002
  -- 3 scanUtilInto
$/tstScanUtilInto/ */

    call scanReadIni
    b = jBuf()
    call mAddst b'.BUF', mapInline('tstScanUtilIntoL')
    call tst t, 'tstScanUtilInto'
    s = jOpen(scanUtilReset(ScanRead(b)), '<')
    do ix=1
        call out '--' ix 'scanUtilInto'
        if \ scanUtilInto(s) then
            leave
        call out '  -- table' m.s.tb 'part' m.s.part
        end
    call tstEnd t
    return
endProcedure tstSCanUtilInto

tstScanWin: procedure expose m.
/*
$=/tstScanWin/
    ### start tst tstScanWin ##########################################
    info 0: last token  scanPosition erste     Zeile                 dr+
    itteZe\npos 1 in line 1: erste     Zeile
    name erste
    spaceNL
    name Zeile
    spaceNL
    name dritteZeeeile
    info 5: last token dritteZeeeile scanPosition    zeile4            +
    .    fuenfueberSechs\npos 1 in line 4:    zeile4
    spaceNL
    name zeile4
    spaceNL
    name fuenfueberSechsUnddSiebenUNDundUndUAcht
    spaceNL
    info 10: last token   scanPosition undZehnueberElfundNochWeiterZwoe+
    lfundim1\npos 9 in line 10:         undZehn
    name undZehnueberElfundNochWeiterZwoelfundim13
    spaceNL
    name Punkt
    infoE 14: last token Punkt scanPosition \natEnd after line 13: im13+
    .      Punkt
$/tstScanWin/ */
    call scanWinIni
    call tst t, 'tstScanWin'
    b = jBuf('?erste     Zeile?',,'?  dritteZeeeile?', '?   zeile4 ',
       ,'?          fuenf?', '?ueberSechsUnddS?', '?iebenUNDundUndU?',
       ,'?Acht           ?', '?               ?', '?        undZehn?',
       ,'?ueberElfundNoch?', '?WeiterZwoelfund?', '?im13      Punkt?')
    s = jOpen(scanWin(b, , , 2, 15), m.j.cRead)
    call tstOut t, 'info 0:' scanInfo(s)
    do sx=1 while \scanEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        if sx // 5 = 0 then
            call tstOut t, 'info' sx':' scanInfo(s)
        end
    call tstOut t, 'infoE' sx':' scanInfo(s)
    call tstEnd t

/*
$=/tstScanWinRead/
    ### start tst tstScanWinRead ######################################
    info 0: last token  scanPosition erste     Zeile                z3 +
    com Ze\npos 1 in line 1: erste     Zeile
    name erste
    spaceNL
    name Zeile
    spaceNL
    name z3
    info 5: last token z3 scanPosition  com Zeeeile z4 come4          f+
    uenf\npos 4 in line 3:  z3 com Zeeeile
    spaceNL
    name z4
    spaceNL
    name fuenf
    spaceNL
    info 10: last token   scanPosition com    Sechs  com  sieben   comA+
    cht  com\npos 15 in line 5:     fuenf     c
    name com
    spaceNL
$/tstScanWinRead/ */
    call tst t, 'tstScanWinRead'
    call mAdd t.cmp,
       ,  "name Sechs",
       ,  "spaceNL",
       ,  "name com",
       ,  "info 15: last token com scanPosition   sieben   comAcht  c",
       || "om com    com\npos 2 in line 7: m  sieben   com" ,
       ,  "spaceNL",
       ,  "name sieben",
       ,  "spaceNL",
       ,  "name Acht",
       ,  "spaceNL",
       ,  "info 20: last token   scanPosition ueberElfundNochWeit com ",
       || "elfundim13\npos 1 in line 11: ueberElfundNoch",
       ,  "name ueberElfundNochWeit",
       ,  "spaceNL",
       ,  "name im13",
       ,  "spaceNL",
       ,  "name Punkt",
       ,  "info 25: last token Punkt scanPosition \natEnd after line ",
       || "13: im13      Punkt",
       ,  "infoE 26: last token Punkt scanPosition \natEnd after line",
       || " 13: im13      Punkt"
    b = jBuf('?erste     Zeile?',,'? z3 com Zeeeile?', '? z4 come4 ',
       ,'?    fuenf     c?', '?om    Sechs  co?', '?m  sieben   com?',
       ,'?Acht  com com  ?', '?  com          ?', '?  com   undZehn?',
       ,'?ueberElfundNoch?', '?Weit com elfund?', '?im13      Punkt?')
    s = scanWin(b, , , 2, 15)
    call scanOpts s, , , 'com'
    call tstOut t, 'info 0:' scanInfo(jOpen(s, m.j.cRead))
    do sx=1 while \scanEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        if sx // 5 = 0 then
            call tstOut t, 'info' sx':' scanInfo(s)
        end
    call tstOut t, 'infoE' sx':' scanInfo(s)
    call tstEnd t
    return
endProcedure tstScanWin

tstjCatSql: procedure expose m.
/*
$=/tstJCatSql/
    ### start tst tstJCatSql ##########################################
    cmd1 select     current time                 stamp from s.1
    cmd2 update ";--""'/*"
    cmd3 delete '*/''"' / 3 - 1
    cmd4 .
$/tstJCatSql/ */
    call tst t, 'tstJCatSql'
    b = jBuf('select -- /* c1', ' /* c1 */ current/* c2 " '' ',
       ,'c3"', '  c4   */time', 'stamp-- c5', 'from s.1; /* c6 */ ;' ,
       ,';update ";--""''/*";;       del123',
       , 'ete ''*/''''"'' / 3 - 1  -- c7', '/*c8 */   ')
    call jCatSqlReset tstJCat, , jOpen(b, '<'), 30
    do sx=1 until nx = ''
        nx = jCatSqlNext(tstJCat, ';')
        call tstOut t, 'cmd'sx nx
        end
    call jClose b
    call tstEnd t
    return
endProcedure tstJCatSql

tstScanSql: procedure expose m.
    call scanWinIni
/*
$=/tstScanSqlId/
    ### start tst tstScanSqlId ########################################
    sqlId ABC
    spaceNL
    sqlId AB__345EF
    spaceNL
$/tstScanSqlId/ */
    call tst t, 'tstScanSqlId'
    b = jBuf('abc  --  kommentar', right('ab_', 72), '_345ef-- kom')
    s = jOpen(scanSql(b), m.j.cRead)
    do sx=1 while \scanEnd(s)
        if scanSqlID(s) then       call tstOut t, 'sqlId' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlDelimited/
    ### start tst tstScanSqlDelimited #################################
    sqlDeId ABC
    spaceNL
    sqlDeId AB_3F
    spaceNL
    sqlDeId abc
    spaceNL
    sqlDeId ab_Ef
    spaceNL
$/tstScanSqlDelimited/ */
    call tst t, 'tstScanSqlDelimited'
    b = jBuf('abc  --  kommentar',,'  -- ',,right('ab_', 72),'3F-- kom',
           , '"abc"  --  ko', ' -- ',,right('"ab_', 72),'Ef"-- kom')
    s = jOpen(scanSql(b), m.j.cRead)
    do sx=1 while \scanEnd(s)
        if scanSqlDeID(s) then       call tstOut t, 'sqlDeId' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlQualified/
    ### start tst tstScanSqlQualified #################################
    sqlQuId ABC 1 ABC
    sqlQuId AB_3F 1 AB_3F
    sqlQuId abc 1 abc
    sqlQuId ab_Ef 1 ab_Ef
    sqlQuId EINS.Zwei.DREI 3 EINS
    sqlQuId vi er.fu  enf 2 vi er
$/tstScanSqlQualified/ */
    call tst t, 'tstScanSqlQualified'
    b = jBuf('abc  --  kommentar',,'  -- ',,right('ab_', 72),'3F-- kom',
           , '"abc"  --  ko', ' -- ',right('"ab_', 72),'Ef"-- kom',
           , 'eins."Zwei', '" -- com', ' . -- com', '  -- com',
           , 'drei -- ko', '"vi er"."fu  enf   " -- co')
    s = jOpen(scanSql(b), m.j.cRead)
    do sx=1 while \scanEnd(s)
        if scanSqlQuID(s) then
            call tstOut t, 'sqlQuId' m.s.val m.s.val.0 m.s.val.1
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlNum/
    ### start tst tstScanSqlNum #######################################
    sqlNum 1
    spaceNL
    sqlNum 2..
    spaceNL
    sqlNum .3
    spaceNL
    sqlNum 4.5
    spaceNL
    sqlNum +6
    spaceNL
    sqlNum +7.03
    spaceNL
    sqlNum -8
    spaceNL
    sqlNum -.9
    spaceNL
    sqlNum 1E2
    spaceNL
    sqlNum -2.E-2
    spaceNL
    sqlNum +.3E+3
    spaceNL
$/tstScanSqlNum/ */
    call tst t, 'tstScanSqlNum'
    b = jBuf('1 2. .3 4.5 +6 + --kom', , ' -- com ', , ' 7.03 -8 -  .9',
             '1e2 - 2.e-2 + .3e+3')
    s = jOpen(scanSql(b), m.j.cRead)
    do sx=1 while \scanEnd(s)
        if scanSqlNum(s) then
            call tstOut t, 'sqlNum' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlNumUnit/
    ### start tst tstScanSqlNumUnit ###################################
    sqlNumUnit 1 KB
    spaceNL
    sqlNumUnit .3 MB
    sqlNumUnit .5
    sqlNumUnit +6.E-5 B
    spaceNL
    sqlNumUnit -7
    char *
    spaceNL
    sqlNumUnit -.8
    char T
    char B
    spaceNL
    *** err: scanErr scanSqlNumUnit after +9. bad unit TB
    .    e 1: last token Tb scanPosition .
    .    e 2: pos 41 in line 1: 1 kb .3mB.5 +   6.e-5B -7* -.8 TB + 9.T+
    b
    sqlNumUnit +9..
    spaceNL
$/tstScanSqlNumUnit/ */
    call tst t, 'tstScanSqlNumUnit'
    b = jBuf('1 kb .3mB.5 +   6.e-5B -7* -.8 TB + 9.Tb')
    s = jOpen(scanSql(b), m.j.cRead)
    do sx=1 while \scanEnd(s)
        if scanSqlNumUnit(s, 0, 'B KB MB') then
            call tstOut t, 'sqlNumUnit' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else if scanChar(s ,1) then call tstOut t, 'char' m.s.tok
        else call scanErr s, 'cannot scan'
        end
    call tstEnd t
    return
endProcedure tstScanSql

/*--- one single test scan with lines to scan in stem ln -------------*/
tstScan1:
parse arg sc, classs, ln
    if sc == '' then do
        call tstOut t, 'scan src' ln
        call scanSrc scanOpts(s), ln
        end
    else do
        call tstOut t, 'scan scanner' sc
        s = sc
        end
    m.s.key = ''
    m.s.val = ''
    do forever
        x = tstScanType(s, classs)
        if x == '' then
           leave
        call tstOut t, 'scan' x 'tok' length(m.s.tok)':' m.s.tok ,
                  'key' m.s.key 'val' m.s.val
        end
    return
endProcedure tstScan1

tstScanType: procedure expose m.
parse arg s, opt
    cx = 1
    a2 = ''
    res = 0
    do while cx <= length(opt)
        f = substr(opt, cx, 1)
        cx = cx + 1
        if pos(substr(opt, cx, 1), "'""") > 0 then do
            m.tstScanType.src = opt
            m.tstScanType.pos = cx
            call scanString tstScanType
            a2 = m.tstScanType.val
            cx = m.tstScanType.pos
            end
        if      f == 'a' then
            res = scanString(s, "'")
        else if f == 'b' then
            res = scanSpace(s)
        else if f == 'c' then
            res = scanChar(s, a2)
        else if f == 'd' then
            res = scanNat(s, a2)
        else if f == 'k' then
            res = scanKeyValue(s, 'def')
        else if f == 'l' then
            res = scanLit(s, a2)
        else if f == 'q' then
            res = scanString(s, '"')
        else if f == 'v' then
            res = scanVerify(s, a2)
        else if f == 'w' then
            res = scanWord(s)
        else if f == 'y' then
            res = scanVerify(s, a2, 'm')
        if res then
            return f
        end
    return scanType(s)
endProcedure tstScanType

/* copx tstBase end   *************************************************/

/* copx tst begin ****************************************************
      test infrastructure
***********************************************************************/
/*--- migrate all compares to new ones:
        tstCI input compare
        tstCO ouput migrated compares
        tstCIO input and output -------------------------------------*/
tstUtTime: procedure expose m.
    say 'begin' utTime()  sysvar('sysnode')
    do 3000000
       end
    call sleep 1
    say 'end  ' utTime()
return

tstUtc2d: procedure expose m.
/*
$=/tstUtc2d/
    ### start tst tstUtc2d ############################################
    .             ff            255
    .           ffff          65535
    .          10000          65536          65536 = 1 * 16 ** 4
    .          10001          65537
    .         ffffff       16777215
    .        1000000       16777216       16777216 = 1 * 16 ** 6
    .        1000001       16777217
    .        20000FF       33554687
    .      100000000     4294967296     4294967296 = 1 * 16 ** 8
    .      300000000    12884901888    12884901888 = 3 * 16 ** 8
    .      3020000EF    12918456559
$/tstUtc2d/
*/
    numeric digits 33
    call tst t, 'tstUtc2d'
    all = 'ff ffff 10000 10001 ffffff 1000000 1000001 20000FF' ,
           '100000000 300000000 3020000EF'
    do ax = 1 to words(all)
        a = word(all, ax)
        if substr(a, 2) = 0 then
            b = right(left(a, 1) * 16 ** (length(a)-1), 15) ,
              '=' left(a, 1) '* 16 **' (length(a)-1)
        else
            b = ''
        call out right(a, 15)right(utc2d(x2c(a)), 15)b
        end
    call tstEnd t
    return
endProcedure tstUtc2d

tstCI: procedure expose m.
parse arg m, nm
    m.m.CIO = 0
    signal tstCIwork
tstCIO: procedure expose m.
parse arg m, nm
    m.m.CIO = 1
tstCIwork:
    m.m.name = nm
    m.m.cmp.1 = left('### start tst' nm '', 67, '#')

    do ix=2 to arg()-1
        m.m.cmp.ix = arg(ix+1)
        end
    m.m.cmp.0 = ix-1
    if m.m.CIO then
        call tstCO m
    return

tstCO: procedure expose m.
parse arg m
    call tst2dpSay m.m.name, m'.CMP', 68
    return
/*--- initialise m as tester with name nm
        use inline input nm as compare lines -----------------------*/
tstReset: procedure expose m.
parse arg m, nm
    call tstIni
    m.m.name = nm
    m.m.inIx  = 0
    m.m.out.0 = 0
    m.m.err   = 0
    m.err.count = 0
    m.tst.act = m
    if \ datatype(m.m.trans.0, 'n') then
        m.m.trans.0 = 0
    m.m.trans.old = m.m.trans.0
    return
endProcedure tstReset

tst: procedure expose m.
parse arg m, nm, cmpSt
    call tstReset m, nm
    m.tst.tests = m.tst.tests+1
    if cmpSt == '' then do
        cmpSt = mCut(t'.CMP', 0)
        call tst4dp cmpSt, mapInline(nm)
        end
    m.m.cmp = cmpSt
    m.m.moreOutOk = 0
    call mAdd mCut(m'.IN', 0), 'tst in line 1 eins ,'    ,
                     , 'tst in line 2 zwei ;   ' ,
                     , 'tst in line 3 drei .schluss.'
    call tstOut m, left('### start tst' nm '', 67, '#')
    call errReset 'h', 'return tstErrHandler(ggTxt)'
    m.m.errCleanup = m.err.cleanup
    if m.tst.ini.j \== 1 then do
/*      call err implement outDest 'i', 'call tstOut' quote(m)', msg'
*/      end
    else do
        call oMutatName m, 'Tst'
        call oMutatName m'.IN', 'Tst'
        m.m.jReading = 1
        m.m.jWriting = 1
        m.m.jUsers = 0
        m.m.in.jReading = 1
        m.m.in.jWriting = 1
        m.m.in.jUsers = 0
        if m.tst.ini.e \== 1 then do
            m.m.oldJin = m.j.in
            m.m.oldOut = m.j.out
            m.j.in = m
            m.j.out = m
            end
        else do
            if m.pipe.0 <> 2 then
                call tstErr m, 'm.pipe.0' m.pipe.0 '<> 2'
            call pipe '+Ff', m , m'.IN'
            end
        end
    return m
endProcedure tst

tstEnd: procedure expose m.
parse arg m, opt opt2
    cmp = m.m.cmp
    m.m.jReading = 0
    m.m.jWriting = 0
    if m.tst.ini.j == 1 then do
        m.m.jReading = 0
        m.m.jWriting = 0
           if m.tst.ini.e \== 1 then do
            m.j.in = m.m.oldJin
            m.j.out = m.m.oldOut
            end
        else do
            if m.j.in \== m'.IN' | m.j.out \== m then
                call tstErr m, m.j.in '\==' m '|' m.j.out '\==' m
            call pipe '-'
            if m.pipe.0 <> 2 then
                call tstErr m, 'm.pipe.0' m.pipe.0 '<> 2'
            end
        end
    if m.m.err = 0 then
        if m.m.errCleanup \= m.err.cleanup then
            call tstErr m, 'err.Cleanup' m.err.cleanup '<> old',
                        m.m.errCleanup
    if m.m.out.0 \= m.cmp.0 & \ (m.m.moreOutOk,
            &  m.m.out.0 > m.cmp.0) then do
        call tstErr m, 'old' m.cmp.0 'lines \= new' m.m.out.0
        do nx = m.m.out.0 + 1 to min(m.m.out.0+10, m.cmp.0)
            say 'old -  ' m.cmp.nx
            end
        end
    call errReset 'h'
    m.tst.act = ''
    soll = 0
    if opt = 'err' then do
        soll = opt2
        if m.m.err \= soll then
            call err soll 'errors expected, but got' m.m.err
        end
    if m.m.err \= soll then do
        say 'new lines:' (m.m.out.0 - 1)
           call tst2dpSay m.m.name, m'.OUT', 68
        end
    say left('###' m.m.name 'end with' m.m.err 'errors ', 67, '#')

    if 1 & m.m.err \= soll then
        call err 'dying because of' m.m.err 'errors'
    m.m.trans.0 = m.m.trans.old
    return
endProcedure tstEnd

tst2dp: procedure expose m.
parse arg st, dp, ml
    dx = m.dp.0
    do sx=1 to m.st.0
        li = m.st.sx
        cx = 1
        do until cx > length(li)
            c = substr(li, cx, 1)
            ou = left('.', strip(c) == '' | pos(c, '.+') > 0)
            ex = min(length(li),cx+ml-length(ou)-2)
            ou = ou || substr(li, cx, ex+1-cx)
            dx = dx + 1
            c = right(ou, 1)
            if ex < length(li) then
                m.dp.dx = ou || '+'
            else if strip(c) == '' | pos(c, '.+') > 0 then
                m.dp.dx = ou || '.'
            else
                m.dp.dx = ou
            cx = ex +1
            end
        end
    m.dp.0 = dx
    return
endProcedure tst2dp

tst2dpSay: procedure expose m.
parse arg name, st, ml
    say '$=/'name'/'
    call tst2dp st, mCut('TST.TMP', 0), 68
    do nx=1 to m.tst.tmp.0
           say '   ' m.tst.tmp.nx
           end
     say '$/'name'/'
return tst2dpSay

tst4dp: procedure expose m.
parse arg st, dp
    sx = m.st.0
    inData = 0
    data = ''
    do dx=1 to m.dp.0
        li = strip(m.dp.dx)
        if pos(left(li, 1), '.+') > 0 then
         li = substr(li, 2)
        if right(li, 1) == '+' then do
            inData = 1
            data = data || left(li, length(li)-1)
            iterate
            end
        if right(li, 1) == '.' then
            li = left(li, length(li)-1)
        sx = sx + 1
        m.st.sx = repAll(data || li, '$ä', '/*', '$ö', '*/')
        inData = 0
        data = ''
        end
    m.st.0 = sx
    if inData then
        call err 'end inData'
    return
endProcedure tst4dp

/*--- write to test: say lines and compare them ----------------------*/
tstWrite: procedure expose m.
parse arg m, arg
    call tstOut m, 'out:' arg
    return
endProcedure tstWrite

tstOut: procedure expose m.
parse arg m, arg
    do tx=m.m.trans.0 by -1 to 1
        arg = repAll(arg, word(m.m.trans.tx, 1),
            , subword(m.m.trans.tx, 2))
        end
    call mAdd m'.OUT', arg
    nx = m.m.out.0
    cmp = m.m.cmp
    c = m.cmp.nx
    if nx > m.cmp.0 then do
        if nx = m.cmp.0+1 & \ m.m.moreOutOK then
            call tstErr m, 'more new Lines' nx
        end
    else if c \== arg then do
        do cx=1 to min(length(c), length(arg)) ,
             while substr(c, cx, 1) == substr(arg, cx, 1)
             end
         msg = 'old line' nx '<> new overnext, firstDiff' cx',',
                 'len old' length(c)', new' length(arg)

        if cx > 10 then
            msg = overlay('|', msg, cx-10)
        call tstErr m, msg
        say c
        end
    say arg
    return 0
endProcedure tstOut

tstWriteO: procedure expose m.
parse arg m, var
    cl = objClass(var, '')
    if cl == '' then do
        if var == '' then
            call tstOut t, 'tstR: @ obj null'
        else
            call tstOut t, 'no class for' var 'in tstWriteO|'
        end
    else if abbrev(var, m.o.escW) then do
        call tstOut t, o2String(var)
        end
    else if cl == m.class.classV then do
        call tstOut t, m.var
        end
    else if oKindOf(var, 'JRW') then do
        call tstOut t, 'tstWriteO kindOf JRW jWriteNow begin <<<'
        call jWriteNow m, var
        call tstOut t, 'tstWriteO kindOf JRW jWriteNow end   >>>'
        end
    else if oKindOf(var, 'ORun') then do
        call tstOut t, 'tstWriteO kindOf ORun oRun begin <<<'
        call oRun var
        call tstOut t, 'tstWriteO kindOf ORun oRun end   >>>'
        end
    else do
        do tx=m.m.trans.0 by -1 to 1 ,
                while word(m.m.trans.tx, 1) \== var
            end
        if tx < 1 then
            call mAdd M'.TRANS', var 'tstWriteoV' || (m.m.trans.0+1)
        call classOut , var, 'tstR: '
        end
    return
endProcedure tstWriteO

tstReadO: procedure expose m.
parse arg m, arg
    if right(m, 3) == '.IN' then
       m = left(m, length(m)-3)
    else
        call err 'tstReadO bad m' m
    ix = m.m.inIx + 1
    m.m.inIx = ix
    if ix <= m.m.in.0 then do
        call tstOut m, '#jIn' ix'#' m.m.in.ix
        return s2o(m.m.in.ix)
        end
    call tstOut m, '#jIn eof' ix'#'
    return ''
endProcedure tstReadO

tstFilename: procedure expose m.
parse arg suf, opt
    if m.err.os == 'TSO' then do
        dsn = dsn2jcl('~tmp.tst.'suf)
        if opt = 'r' then do
            if sysDsn("'"dsn"'") \== 'DATASET NOT FOUND' then
                call adrTso "delete '"dsn"'"
            call csiOpen 'TST.CSI', dsn'.**'
            do while csiNext('TST.CSI', 'TST.FINA')
                say 'deleting csiNext' m.tst.fina
                call adrTso "delete '"m.tst.fina"'"
                end
            end
        return dsn
        end
    else if m.err.os == 'LINUX' then do
        if abbrev(suf, '/') then
            fn = suf
        else
            fn = .Stream%%new('~/tmp/tst/'suf)%%qualify /* full path */
        cx = lastPos('/', fn)
        if cx > 0 then do
            dir = left(fn, cx-1)
            if \sysIsFileDirectory(dir) then
                call adrSh "mkdir -p" dir
            if \sysIsFileDirectory(dir) then
                call err 'tstFileName could not create dir' dir
            end
        if opt \= 'r' then
            nop
        else if sysIsFile(fn) then
            call sysFileDelete fn
        else if sysIsFileDirectory(fn) then
            call adrSh 'rm -r' fn
        return fn
        end
    else
        call err 'tstFilename does not implement os' m.err.os
endProcedure tstFilename

/*--- say total errors and fail if not zero --------------------------*/
tstTotal: procedure expose m.
    say '######'
    say '######'
    say '######' m.tst.tests 'tests with' ,
                 m.tst.err 'errors in' m.tst.errNames
    say '######'
    say '######'
    if m.tst.err \== 0 then
        call err m.tst.err 'errors total'
    return
endProcedure tstTotal

/*--- test err: message, count it and continue -----------------------*/
tstErr: procedure expose m.
parse arg m, msg
    say '### error' msg
    m.m.err = m.m.err + 1
    m.tst.err = m.tst.err + 1
    nm = m.m.name
    if wordPos(nm, m.tst.errNames) < 1 then
        m.tst.errNames = m.tst.errNames nm
    return 0
endProcedure tstErr

/*--- tstErrHandler: intercept errors --------------------------------*/
tstErrHandler: procedure expose m.
parse arg ggTxt
    m = m.tst.act
    if m == '' then
        call err ggTxt
    m.err.count = m.err.count + 1
    call errMsg ' }'ggTxt
    call tstOut m.tst.act, '*** err:' m.err.1
        do x=2 to m.err.0
            call tstOut m, '    e' (x-1)':' m.err.x
            end
    return 0
endSubroutine tstErrHandler

tstTrc: procedure expose m.
parse arg msg
    m.tst.trc = m.tst.trc + 1
    say 'tstTrc' m.tst.trc msg
    return m.tst.trc
endProcedure tstTrc

/*--- tstIni: global initialization ----------------------------------*/
tstIni: procedure expose m.
    if m.tst.ini \== 1 then do
        m.tst.ini = 1
        call mapIni
        m.tst.err = 0
        m.tst.trc = 0
        m.tst.errNames = ''
        m.tst.tests = 0
        m.tst.act = ''
        end
    if m.tst.ini.j \== 1 & m.j.ini == 1 then do
        m.tst.ini.j = 1
          call classNew 'n Tst u JRWO', 'm',
             , "jReadO return tstReadO(m)",
             , "jWrite call tstOut m, line",
             , "jWriteO call tstWriteO m, var"
        end
    if m.tst.ini.e \== 1 & m.pipe.ini == 1 then do
        m.tst.ini.e = 1
        end
    return
endProcedure tstIni
/* copx tst    end   **************************************************/
/* copx tstData begin *************************************************/
tstData: procedure expose m.
parse arg ty 2 le 'n' nu, l, r, num
    abs = abs(num)
    if nu \== '' then do
        if abs // 5 = 0 then
            return 'null' || (abs % 5 // nu + 1)
        end
    if ty = 'c' then do
        if le = '' then
            le = 8
        le = abs // le + 1
        if r = '' then
            r = '+'
        return left(l || num || r, le, right(r, 1))
        end
    if pos(ty, 'ief') < 1 then
        call err 'bad type' ty
    nn = abs
    if abbrev(num, '-') | abbrev(num, '+') then
        parse var num si 2 nn
    else
        si = ''
    if ty == 'e' then
        ex = 'e' || left('-', abs // 2) || (abs // 15)
    else
        ex = ''
    if le \== '' then
        nn = right(nn, abs // max(1, le - length(si||ex)) + 1, 1)
    if ty \== 'i' & (abs // 4) \= 0 & length(nn) > 1 then
            nn = overlay('.', nn, length(nn) - abs // length(nn))
     return si || nn || ex
endProcedure tstData

tstDataClassFo: procedure expose m.
parse arg flds
    ty = ''
    do fx=1 by 2 to words(flds)
        if word(flds, fx) = '.' then
            ty = ty', v'
        else
            ty = ty', f' word(flds, fx) 'v'
        end
    t = classNew('n* tstData u' substr(ty, 2))
    fo = oNew(m.t.name)
    fs = oFlds(fo)
    do fx=1 to m.fs.0
        f = fo || m.fs.fx
        m.f = word(flds, 2*fx)
        end
    return fo
endProcedure tstDataClassFo


tstDataClassOut: procedure expose m.
parse arg flds, f, t
    fo = tstDataClassFo(flds)
    fs = oFlds(fo)
    do x=f to t
        o = oCopy(fo)
        do fx=1 to m.fs.0
            na = substr(m.fs.fx, 2)
            f = o || m.fs.fx
            m.f = tstData(m.f, na, '+'na'+', x)
            end
        call outO o
        end
    return
endProcedure tstDataClassOut
/* copx tstData end ***************************************************/
/* copy tstAll end   **************************************************/
/* copy time begin -----------------------------------------------------
 11.05.23 wk: numeric digits transparent: in jeder Procedure drin
              time2jul, tst externalisiert
----------------------------------------------------------------------*/
/*--- read timeZoneOffset and leapSeconds registers
        and set variables for uniq ----------------------------------*/
timeReadCvt: procedure expose m.
parse arg debug
    numeric digits 15
    /* offsets documented in z/OS Data Areas  Vol.1 */
    cvtOH      = '00000010'          /* cvt control block Address */
    cvtext2O   = x2d('00000560') /* offset  to extension 2    */
    cvtldtoO   = x2d('00000038') /* offset to timezone    */
    cvtlsoO    = x2d('00000050') /* offset to leapSeconds */

    /* CVT CB        address               + extention2   */
    cvtExt2A       = C2D(STORAGE(cvtOH,4)) + cvtext2O
    /* cvtLdto timeZone              address +offset      */
    m.timeZone     = c2d(STORAGE(d2x(cvtext2A+cvtldtoO, 8), 8))
    m.timeStckUnit = 1e-6 / 256 / 16
    /* cvtLso LeapSecs               address +offset      */
    m.timeLeap     = C2D(STORAGE(d2x(cvtext2A+cvtlsoO, 8), 8))
    m.timeUQDigits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345678'
                         /* find lrsn of day 0 */
    m.timeUQZero = timeGmt2Lrsn('2004-12-31-00.00.22.000000')
                         /* 0 out last 6 bits  */
    m.timeUQZero = b2x(overlay('000000', x2b(m.timeUQZero), 43))
    if debug == 1 then do
      say 'stckUnit          =' m.timeStckUnit
      say 'timeLeap          =' d2x(m.timeLeap,16) '=' m.timeLeap ,
                   '=' format(m.timeLeap * m.timeStckUnit, 9,3) 'secs'
      say 'timeZone          =' d2x(m.timeZone,16) '=' m.timeZone,
                   '=' format(m.timeZone  * m.timeStckUnit, 6,3) 'secs'
      say "cvtext2_adr       =" d2x(cvtExt2A, 8)
      say 'timeUQZero        =' m.timeUQZero
      say 'timeUQDigis       =' ,
                    length(m.timeUQDigits) 'digits' m.timeUQDigits
    end
    m.timeReadCvt = 1
    return
endSubroutine timeReadCvt

timestampParse:
    parse arg yyyy'-'mo'-'dd'-'hh'.'mm'.'ss'.'ffffff
    return

/*--- GMT timestamp yyyy-mm.... -> stck value char(8)
         BLSUXTID is described in z/OS V1R7.0 MVS IPCS Customization
         BLSUXTID format : mo/dd/yyyy hh:mm:ss.ffffff ---------------*/
timeGmt2Stck: procedure expose m.
    parse arg tst
    call timestampParse tst
    tDate = mo'/'dd'/'yyyy' 'hh':'mm':'ss'.'ffffff
    ACC=left('', 8, '00'x)
    ADDRESS LINKPGM "BLSUXTID TDATE ACC"
    RETURN acc
endProcedure timeGmt2Stck

/*--- GMT timestamp yyyy-mm.... -> stck value in hex(16) ------------*/
timeGmt2LRSN: procedure expose m.
    return c2x(left(timeGmt2Stck(arg(1)), 6))
endProcedure timeGmt2LRSN

/*--- LZT (Locale Zurich Tst -> stck value in hex(16) ---------------*/
timeLZT2LRSN: procedure expose m.
parse arg tst
    numeric digits 23
    if m.timeReadCvt \== 1 then
        call timeReadCvt
    return left(d2x(c2d(timeGmt2Stck(tst)) ,
                     - m.timeZone + m.timeLeap, 16), 12)
endProcedure timeLZT2LRSN
/*--- conversion from Stck Clock Value to GMT Timestamp
        BLSUXTOD is described in z/OS V1R7.0 MVS IPCS Customization
        input -> + leapseconds -> output ----------------------------*/
timeStck2Gmt: PROCEDURE expose m.
parse arg stck
  stck = left(stck, 8, '00'x)
  TDATE = COPIES('0' , 26)
  ADDRESS LINKPGM "BLSUXTOD stck TDATE"
  /* return format   : mo/dd/yyyy hh:mm:ss.ffffff */
  /* Timestamp format: yyyy-mm-dd-hh.mm.ss.ffffff */
  parse var TDATE mo '/' dd '/' yyyy hh ':' mm ':' ss '.' ffffff
RETURN yyyy'-'mo'-'dd'-'hh'.'mm'.'ss'.'ffffff
endProcedure timeStck2Gmt

/*--- conversion from Lrsn Clock Value to GMT Timestamp -------------*/
timeLrsn2Gmt:
    return timeStck2Gmt(x2c(arg(1)))
endProcedure timeLrsn2Gmt

/*--- conversion from Lrsn Clock Value to LZT Local Zurich Time -----*/
timeLrsn2LZT: procedure expose m.
parse arg lrsn
    numeric digits 23
    if m.timeReadCvt \== 1 then
        call timeReadCvt
    return timeStck2Gmt(d2c(x2d(left(lrsn, 16, 0)) ,
                           + m.timeZone-m.timeLeap))
endProcedure timeLrsn2LZT

/*--- timestamp to julian --------------------------------------------*/
time2jul: procedure expose m.
parse arg yyyy '-' mm '-' dd '-'
        /* date function cannot convert to julian, only from julian
            ==> guess a julian <= the correct and
                try the next values
        */
    j = trunc((mm-1) * 29.5) + dd
    yy = right(yyyy, 2)
    do j=j by 1
        j = right(j, 3, 0)

        d = date('s', yy || j, 'j')
        if substr(d, 3) = yy || mm || dd then
            return yy || j
        end
    return
endProcedure time2jul
/* convert a uniq variable to lrsn ************************************/
timeUniq2lrsn: procedure expose m.
parse arg uniq
    numeric digits 15
    if m.timeReadCvt \== 1 then
        call timeReadCvt
    uniq = left(uniq, 8, 'A')
    d42 = d2x(q2i(uniq, m.timeUQDigits))
    d48 = b2x('00'x2b(d42)'000000')
    lrsn = right(d2x(x2d(d48) + x2d(m.timeUQZero)), 12, 0)
    return lrsn
endProcedure uniq2lrsn
/*--- translate a number in q-system to decimal
       arg digits givs the digits corresponding to 012.. in the q sysem
       q = length(digits) --------------------------------------------*/
q2i: procedure expose m.
parse arg v, digits
    b = length(digits)
    i = 0
    do x = 1 to length(v)
        q = substr(v, x, 1)
        r = pos(q, digits)
        if r < 1 then
            call err 'bad digit' q 'in' v 'valid digits' digits
        i = i * b + r - 1
        end
    return i
endProcedure q2i
/*--- translate a decimal number to q-system - inverse of q2i --------*/
i2q: procedure expose m.
parse arg i, digits
    if i = 0 then
        return left(digits, 1)
    b = length(digits)
    v = ''
    do while i > 0
        v = substr(digits, 1 + (i // b), 1) || v
        i = i % b
        end
    return v
endProcedure i2q
/* copy time end -----------------------------------------------------*/
/* copy sort begin ****************************************************/
sort: procedure expose m.
parse arg i, o, cmp
    if cmp == '' then
        m.sort.comparator = "cmp = m.l.l0 <<= m.r.r0"
    else if length(cmp) < 6 then
        m.sort.comparator = "cmp = m.l.l0" cmp "m.r.r0"
    else if pos(';', cmp) < 1 then
        m.sort.comparator = "aLe = l'.'l0; aRi = r'.'r0; cmp =" cmp
    else
        m.sort.comparator = "aLe = l'.'l0; aRi = r'.'r0;" cmp
    call sort1 i, 1, m.i.0, o, 1, sort.work, 1
    m.o.0 = m.i.0
    return
endProcedure sort

sort1: procedure expose m.
parse arg i, i0, le, o, o0, w, w0
    if le <= 3 then do
        if le = 3 then do
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, w, w0
            call sortMerge i, i0+2, i0+3, w, w0, w0+2, o, o0
            end
        else if le = 2 then
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, o, o0
        else if le = 1 then
            m.o.o0 = m.i.i0
        return
        end
    h = (le + 1) % 2
    call sort1 i, i0,   h,    o, o0+le-h, w, w0
    call sort1 i, i0+h, le-h, w, w0,      o, o0
    call sortMerge o, o0+le-h, o0+le, w, w0, w0+le-h, o, o0
    return
endProcedure sort1

sortMerge: procedure expose m.
parse arg l, l0, le, r, r0, re, o, o0
    do while l0 < le & r0 < re
        interpret m.sort.comparator
        if cmp then do
            m.o.o0 = m.l.l0
            l0 = l0 + 1
            end
        else do
            m.o.o0 = m.r.r0
            r0 = r0 + 1
            end
        o0 = o0 + 1
        end
    do while l0 < le
        m.o.o0 = m.l.l0
        l0 = l0 + 1
        o0 = o0 + 1
        end
    do while r0 < re
        m.o.o0 = m.r.r0
        r0 = r0 + 1
        o0 = o0 + 1
        end
    return
endProcedure sortMerge
/* copy sort end   ****************************************************/
/* copy match begin ***************************************************/
/*--- 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 comp begin *****************************************************
    the shell compiler
         syntax and semantics of the shell language see javaDoc
***********************************************************************/
/***** initialisation *************************************************/
/*--- module initialisation ------------------------------------------*/
compIni: procedure expose m.
    if m.compIni = 1 then
        return
    m.compIni = 1
    call pipeIni
    call scanReadIni
    cc = classNew('n Compiler u')
    call mNewArea 'COMP.AST', '='

    m.comp.chOp   = '.-<@|?%^'
    m.comp.chKind = '.-=#@:'
    m.comp.chKiNO = '=#:'
    call mPut 'COMP.INFO..', "object"
    call mPut 'COMP.INFO.-', "string"
    call mPut 'COMP.INFO.=', "skeleton"
    call mPut 'COMP.INFO.#', "text"
    call mPut 'COMP.INFO.@', "rexxShell"
    call mPut 'COMP.INFO.:', "pureShell"

    m.comp.chDol = '$'
    m.comp.chSpa = ' 'x2c('09')
    call mPut 'COMP.EXTYPE.b', m.comp.chDol'{}'       /* braces */
    call mPut 'COMP.EXTYPE.d', m.comp.chDol            /* data */
    call mPut 'COMP.EXTYPE.s', m.comp.chDol            /* strip */
    call mPut 'COMP.EXTYPE.w', m.comp.chDol||m.comp.chSpa /* word */

    m.comp.idChars = m.ut_alfNum'@_'
    m.comp.wCatC = 'compile'
    m.comp.wCatO = 'out'
    m.comp.wCatS = 'do withNew with for forWith ct proc table'
    return
endProcedure compIni

compReset: procedure expose m.
parse arg m
    m.m.scan = scanRead(,,'0123456789')
    return m
endProcedure compReset

/*--- constructor of Compiler ----------------------------------------*/
comp: procedure expose m.
parse arg src
    nn = oNew('Compiler')
    m.nn.cmpRdr = j2rdr(src)
    return nn
endProcedure comp

/**** user interface **************************************************/
/*--- compile and run ------------------------------------------------*/
compRun: procedure expose m.
parse arg spec, inO, ouO, infoA
    cmp = comp(inO)
    r = compile(cmp, spec)
    if infoA \== '' then
        m.infoA = 'run'
    if ouO \== '' then
        call pipe '+F', ouO
    call oRun r
    if ouO \== '' then
        call pipe '-'
    return 0
endProcedure compRun

/*--- compile the source and return an oRunner with the code ---------*/
compile: procedure expose m.
parse arg m, spec
    call compReset m
    s = m.m.scan
    if spec = '' then
        spec = '@'
    else
        spec = strip(spec, 'l')
    if m.m.cmpRdr == '' then do
        call scanSrc s, spec
        end
    else do
        call scanReadReset s, m.m.cmpRdr
        call jOpen s, m.j.cRead, spec
        end
    m.m.out = ''
    m.m.end = ''
    res = compAst(m, '¢')
    eOld = m.err.count
    do forever
        one = ''
        if pos(scanLook(s, 1), m.comp.chKind'*') > 0 then do
            call scanChar s, 1
            ki = m.s.tok
            one = compileOne(m, ki)
            end
        else if \ scanName(s) then do
            call scanErr s, 'kind or hook expected after $#'
            end
        else if m.s.tok == 'out' then do
            m.m.out = scanPos(s)
            leave
            end
        else if m.s.tok == 'end' then do
            if m.m.end = '' then
                m.m.end = scanPos(s)
            one = compileOne(m)
            end
        else if m.s.tok == 'version' then do
            call scanSpace s
            if \ scanWord(s) | m.s.tok \== '2.0' then
                call scanErr s, 'only version 2.0 is supported'
            call scanNl s, 1
            end
        else do
            one = compileOne(m, , m.s.tok)
            end
        if one \== '' then
            call mAdd res, one
        if scanEnd(s) then
            leave
        if \ scanLit(s, '$#') then
            return scanErr(s, m.comp.info.ki "expected: compile",
                 ki "stopped before end of input")
        end
    if m.m.cmpRdr \== '' then
        call jClose s
    if m.err.count <> eOld then
        return ''
    if 0 then
        call compAstSay res, 0
    return oRunner(compAst2Rx(m, ';', res))
endProcedure compile

/*--- compile or use hook for one part from spec or input -----------*/
compileOne: procedure expose m.
parse arg m, ki, hook
    s = m.m.scan
    call compSpComment m
    if ki == '*' | m.m.end \== '' then do
        do until scanLook(s, 2) == '$#' | scanEnd(s)
            call scanNl s, 1
            end
        return ''
        end
    if ki \== '' then do
        call scanNl s
        return compUnit(m, ki, '$#')
        end
    say 'interpreting hook' hook':' strip(scanLook(s))
    interpret return 'wshHook_'hook'(m)'
endProcedure compileOne

/*--- parse the whole syntax of a unit ------------------------------*/
compUnit: procedure expose m.
parse arg m, ki, stopper
    s = m.m.scan
    if pos(ki, m.comp.chKind) < 1 then
        return scanErr(s, 'bad kind' ki 'in compUnit(...'stopper')')
    else if pos(ki, '.-=@:') > 0 then do
     a = compAst(m, '¢')
        do forever
         one = compPipe(m, ki)
         if one \== '' then
          call mAdd a, one
         if \ scanLit(m.m.scan, '$;') then
                return a
         end
        end
    else if ki == '#' then do
        res = compAST(m, '¢')
        call scanChar s
        if verify(m.s.tok, m.comp.chSpa) > 0 then
            call mAdd res, compAstAddOp(m,
                , compAst(m, '=', strip(m.s.tok, 't')), '$')
        do while scanNL(s, 1) & \ abbrev(m.s.src, stopper)
            call mAdd res, compAstAddOp(m,
                , compAst(m, '=', strip(m.s.src, 't')), '$')
            end
        return res
        end
    call err 'bad kind' ki 'in compUnit'
endProcedure compUnit

/*--- compile an expression, type d=data, b=block w=word, s=strip ---*/
compExpr: procedure expose m.
parse arg m, type, ki
    if symbol('m.comp.exType.type') \== 'VAR' then
        call err s, 'bad type' type 'in compExpr'
    s = m.m.scan
    if length(ki) \== 1 | pos(ki, '.-=@') < 1 then
        return scanErr(s, 'bad kind' ki 'in compExpr')

    res = compAST(m, translate(ki, '-', '='))
    astKi = translate(ki, 'os=c', '.-=@')
    laPrim = 0
    gotTxt = 0
    m.res.containsC = 0
    if pos(type, 'sb') > 0 then
        m.res.containsC = compSpComment(m) >= 2
    do forever
        if scanVerify(s, m.comp.exType.type, 'm') then do
            one = compAST(m, astKi, m.s.tok)
            if verify(m.s.tok, m.comp.chSpa) > 0 then
                gotTxt = 1
            end
        else do
            old = scanPos(s)
            if \ scanLit(s, m.comp.chDol) then
                leave
            one = compPrimary(m, ki)
            if one = '' then do
                call scanSetPos s, old
                leave
                end
            laPrim = m.res.0 + 1
            end
        call mAdd res, one
        if compComment(m) then
            m.res.containsC = 1
        end
    if pos(type, 'bs') > 0 then do
        do rx=m.res.0 by -1 to laPrim+1
            one = m.res.rx
            m.one.text = strip(m.one.text, 't')
            if length(m.one.text) <> 0 then
                leave
            call mFree one
            end
        m.res.0 = rx
        end
    m.res.containsD = laPrim > 0 | gotTxt
    return compAstFree0(res, '')
endProcedure compExpr

/*--- compile a primary and return code ------------------------------*/
compPrimary: procedure expose m.
parse arg m, ki, compOk
    s = m.m.scan
    ops = ''
    if scanLit(s, '.', '-') then
        ops = m.s.tok || compOpKi(m, m.s.tok)
    getOps = ops
    if getOps == '' then
        getOps = substr('-.', 1+(ki == '.'), 1)
    if scanString(s) then
        return compASTAddOp(m, compAST(m, '=', m.s.val), getOps)
    r = compVar(m, 'c')
    if r \== '' then do
        if m.r.var \== 'c' then
             return compASTAddOp(m, compAst(m,'&',m.r.var, r), getOps)
        o2 = ops
        do while pos(right(o2, 1) ,'.@') > 0
         o2 = left(o2, length(o2)-1)
            end
        if right(o2, 1) == '^' then
         o2 = left(o2, length(o2)-1)
        else if compOk \== 1 then
            call scanErr s, 'compile can only be used by ^'
           return compASTAddOp(m, compAst(m, 'M'), o2)
        end
    if ops == '' then
     return ''
     one = compBlock(m, ops)
    if one \== '' then
        return one
    return scanErr(s, 'primary not finished')
endProcedure compPrimary

/*--- scan an operator chain and a kind ------------------------------*/
compOpKi: procedure expose m.
parse arg m, defKi
    s = m.m.scan
    call scanVerify s, m.comp.chOp || m.comp.chKiNO
    op = m.s.tok
    kx = verify(op, m.comp.chKiNO, 'm')
    if kx = length(op) & op \== '' then
        return op
    else if kx \== 0 then
        call scanErr s, 'kind' substr(op, kx, 1) 'not after ops'
    else if pos(right(op, 1), m.comp.chKind'^%') > 0 then
        return op
    else if defKi == '<' | right(op, 1) == '<' then
        return op'='
    else if defKi \== '' then
        return op || defKi
    else
     call scanErr s, 'no kind after ops' op
endProcedure compOpKi

/*--- block or expression --------------------------------------------*/
compExprBlock: procedure expose m.
parse arg m, ki
    s = m.m.scan
    old = scanPos(s)
    pk = compOpKi(m, ki)
    kiFi = left('<', ki =='<')
    res = compBlock(m, kiFi || pk)
    if res \== '' then do
        if length(pk) \== 1 | ki == '<' then
            return res
        else
            return compAstAddOp(m, res, translate(pk, '--', '=#'))
        end
    if pk \== '' then
        lk = right(pk, 1)
    else
        lk = translate(ki, '.', '@')
    res = compExpr(m, 's', lk)
    if res \== '' then
        return compASTAddOp(m, res, kiFi || left(pk, length(pk)-1))
    call scanSetPos s, old
    return res
endProcedure compExprBlock

/*--- compile a pipe and return code ---------------------------------*/
compPipe: procedure expose m.
parse arg m, ki
    s = m.m.scan
    res = compAST(m, 'P', ' ', '', '')
    do forever
        one = compExprStmts(m, ki)
        if one \== '' then do
            if m.res.0 > 2 then  do;call compAstSay res, 0
                call scanErr s, '$| before statements needed' ; end
            call mAdd res, one
            end
        pre = left(m.comp.chDol, scanLit(s, m.comp.chDol))
        if scanLit(s, '<') then do
            if m.res.2 == '' then
                m.res.2 = compAst(m, '.')
            else
                call mAdd m.res.2, compAst(m, 'o', ', ')
            call mAdd m.res.2, compExprBlock(m, '<')
            m.res.text = m.res.text'f'
            end
        else if scanLit(s, '>>', '>') then do
            if m.res.1 <> '' then
                call scanErr s, 'duplicate output'
            m.res.text = if(m.s.tok == '>', 'F', 'A') ,
                ||substr(m.res.text, 2)
            m.res.1 = compExprBlock(m, '<')
            end
        else if scanLit(s, '|') then do
            if m.res.0 < 3 then
                call scanErr s, 'stmts expected before |'
            call compSpNlComment m
            call mAdd res, compCheckNE(m, compExprStmts(m, ki),
                , 'stmts or expressions after | expected')
            end
        else do
            call scanBack s, pre
            if m.res.0 > 3 | m.res.1 \== '' | m.res.2 \== '' then
                return res
            one = if(m.res.0 = 3, m.res.3)
            call mFree res
            return one
            end
        end
endProcedure compPipe

/*--- compile expressions and stmts ---------------------------------*/
compExprStmts: procedure expose m.
parse arg m, ki
    s = m.m.scan
    res = compAst(m, '¢')
       nlLe = 0 /* sophisticated logic using left and right NLs*/
    tb = ''
    do forever
        if tb \== '' then do
            call compSpComment m
            px = m.s.pos
            e1 = compExpr(m, 'w', '=')
            if e1 \== '' then do
                aa = compAst(m, '¢')
                fx = 0
                do until e1 == ''
                    do fx=fx+1 to m.tb.0 until px < m.ff.end
                        ff = m.tb.fx
                        end
                    if fx > m.tb.0 then
                        call scanErr s, 'right of all table fields'
                    if m.s.pos <= m.ff.pos then
                        call scanErr s, 'before table field' m.ff.name
                    call mAdd aa, compAst(m, 'A', ,
                        , compAst(m, '=', m.ff.name), e1)
                    call compSpComment m
                    px = m.s.pos
                    e1 = compExpr(m, 'w', '=')
                    end
                call mAdd res, compAst(m, 'F', 'with',
                    , compAst(m, 'o', "oNew('"m.tb.class"')"),
                    , aa, compAst(m, '*', '$.'))
                end
            nlRi = scanNL(s)
            end
        else if ki == ':' then do
            call compSpNlComment m, '*'
            nlRi = 0
            end
        else if ki == '@' then do
            call compSpNlComment m
            one = compExpr(m, 's', ki)
            if one == '' then
                nlRi = 0
            else if m.one.0 < 1 then
                call scanErr s, 'assert not empty' m.one.0
            else do
                do forever /* scan all continued rexx lines */
                    nlRi = 1
                    la = m.one.0
                    la = m.one.la
                    if m.la.kind \== 'c' then
                        leave
                    m.la.text = strip(m.la.text, 't')
                    if right(m.la.text, 1) \== ',' then
                        leave
                    m.la.text = strip(left(m.la.text,
                            , length(m.la.text)-1), 't')' '
                    call compSpNlComment m
                    cont = compExpr(m, 's', '@')
                    if cont == '' | m.cont.kind \== m.one.kind then
                        call scanErr s, 'bad rexx continuation'
                    call mAddSt one, cont
                    call mFree cont
                    end
                call mAdd res, one
                end
            end
        else do
            do cc=0 while compComment(m)
                end
            one = compExpr(m, 'd', ki)
            nlRi = scanNL(s)
            if one == '' then do
                if nlLe & nlRi & cc < 1 then
                    call mAdd res,compAstAddOp(m,compAst(m,'='),'$')
                end
            else if m.one.containsD | (nlLe & nlRi,
                      & \ (cc > 0 | m.one.containsC)) then do
                call mAdd res, compAstAddOp(m, one, '$')
                end
            else do
                call mFree one
                end
            end
        nlLe = nlRi
        if \ nlRi then do
            one = compStmt(m)
            if one = '' then
                return compAstFree0(res)
            if m.one.kind == 'T' then
                tb = one
            else
                call mAdd res, one
            end
        end
endProcedure compExprStmts

/*--- compile a single statement -------------------------------------*/
compStmt: procedure expose m.
parse arg m
    s = m.m.scan
    res = compAss(m)
    if res \== '' then
        return res
    pre = ''
    old = scanPos(s)
    if scanLit(s,m.comp.chDol'$',m.comp.chDol'@',m.comp.chDol,'@') then
        pre = m.s.tok
    if pre == m.comp.chDol'$' then
        return  compAstAddOp(m, compCheckNN(m, compExprBlock(m,'='),
            , 'block or expression expected after $$'), '$')
    wCat = compName(m, 'sv')
    fu = m.s.tok

    if right(pre, 1) == '@' & wCat \== 's' then do
        if wCat == 'v' then
         return compAstAddOp(m, compAst(m, '=', fu), '%.&')
        if compSpNlComment(m) then
                return compAstAddOp(m,
                , compCheckNE(m, compExpr(m, 's','@'),
                , 'block/primary/expr after $@'), ';')

        ops = compOpKi(m, '@')
        one = compBlock(m, ops)
        if one \== '' then
            return one
        one = compPrimary(m, right(ops, 1))
        if one \== '' then
            return compAstAddOp(m, one, ops)
        call scanSetPos s, old
        return ''
        end

    if wordPos(fu, 'for forWith with withNew') > 0 then do
        res = compAst(m, 'F', fu)
        call compSpComment m
        if fu \== 'with' then do
            b = compVar(m, left('o', fu == 'withNew'))
            end
        else do
            b = compAss(m, '.', 'o')
            if b == '' then
                b = compCheckNE(m, compExpr(m, 's', '.'),
                , "assignment or expression after with")
            end
        call compSpComment m
        st = compCheckNN(m, compStmt(m), "var? statement after" fu)
        if b = '' then do
            b = compBlockName(m, st)
            if b == '' then
                call scanErr s, "variable or named block after" fu
            b = compAst(m, '=', b)
            end
        call mAdd res, b, st
        if fu == 'withNew' then do
            vars = substr(compAstAssignedVars(m.res.2), 3)
            m.res.class = classNew('n* CompTable u' vars)
            m.res.1 = compAst(m, 'A', ,m.res.1, compAst(m, 'o',
                , "oNew('"m.res.class"')"))
            end
        if abbrev(fu, 'with') then do
            r1 = m.res.1
            if m.r1.kind == 'A' then do
                a1 = m.r1.1
                if m.a1.var == 'o' then do
                    call mAdd res, compAstAddOp(m, m.r1.2, '$.')
                    m.res.1 = m.r1.2
                    call mFree a1
                    end
                end
            end
        return res
        end

    if fu == 'do' then do
        call compSpComment m
        pre = compExpr(m, 's', '@')
        res = compAst(m, 'D', , pre)
        p1 = m.pre.1
        if pre \== '' then do
            txt = ''
            do px=1 to m.pre.0
                pC = m.pre.px
                if m.pC.kind \== 'c' then
                    leave
                txt = txt m.pC.text
                cx = pos('=', txt)
                if cx > 0 then do
                    m.res.text = strip(left(txt, cx-1))
                    leave
                    end
                end
            end
        call compSpComment m
        call mAdd res, compCheckNN(m, compStmt(m), 'stmt after do')
        return res
        end

    if fu == 'ct' then do
        call compSpComment m
        return compAst(m, 'C', , compCheckNN(m, compStmt(m),
            , 'ct statement'));
        end
    if fu == 'proc' then do
           call compSpComment m
        nm = ''
        if compName(m, 'v') == 'v' then do
            nm = m.s.tok
            call compSpComment m
            end
        st = compCheckNN(m, compStmt(m), 'proc statement')
        if nm == '' then do
            nm = compBlockName(m, st)
            if nm == '' then
                call scanErr s, 'var or namedBlock expected after proc'
            end
        return compAst(m, 'B', '', compAst(m, '=', nm), st)
        end
    if fu == 'table' then do
        call compSpComment m
        if scanNl(s) then
            call compSpComment m
        res = compAst(m, 'T', 'c')
        flds = ''
        do while compName(m, 'v') == 'v'
            f1 = compAst(m, 'T')
            m.f1.end = m.s.pos
            m.f1.pos = m.s.pos - length(m.s.tok)
            m.f1.name = m.s.tok
            m.f1.text = 'f' m.f1.name m.f1.pos m.f1.end
            call mAdd res, f1
            flds = flds', f' m.s.tok 'v'
            call compSpComment m
            end
        if \ scanNl(s) then
            call scanErr s, 'name or nl after table expected'
        if m.res.0 < 1 then
            call scanErr s, 'no names in table'
        m.res.class = classNew('n* CompTable u' substr(flds, 3))
        m.res.text = 'c' cl
        return res
        end
    call scanBack s, pre || fu
    return ''
endProcedure compStmt

compBlockName: procedure expose m.
parse arg m, a
    a1 = m.a.1
    if m.a.kind == '¢' then
         return m.a.text
    else if m.a.kind == '*' & m.a1.kind == '¢' then
        return m.a1.text
    return ''
endProcedure compBlockName

compVar: procedure expose m.
parse arg m, vk
    s = m.m.scan
    ty = compName(m, 'v' || vk)
    if ty \== '' then do
        r = compAst(m, '=', m.s.tok)
        m.r.var = ty
        return r
        end
    if \ scanLit(s, '{') then
        return ''
    call scanLit s, '?', '>'
    f = m.s.tok
    r = compCheckNE(m, compExpr(m, 'b', '='), 'var name')
    if \scanLit(s, '}') then
        call scanErr s, 'closing } missing after {'
    m.r.var = f
    return r
endProcedure compVar

compAss: procedure expose m.
parse arg m, ki, vk
    s = m.m.scan
    call scanLit s, m.comp.chDol'=', '='
    pr = m.s.tok
    if pr \== '' then
        call compSpComment m
    v = compVar(m, vk)
    if v \== '' then do
        call compSpComment m
        if \ scanLit(s, '=') then
            call scanErr s, '= expected in assignment after' pr 'var'
        end
    else if pr == '' then
        return ''
    else
        oldInfo = scanInfo(s)
    eb = compCheckNE(m, compExprBlock(m, if(ki=='', '=', ki)),
        , 'block or expression in assignment after' pr)
    if v == '' then do
        v = compBlockName(m, eb)
        if v == '' then
            call scanErr s, 'var or namedBlock expected',
                    'in assignment after' pr, oldInfo
        else
            v = compAst(m, '=', v)
        end
    return compAst(m, 'A', , v, eb)
endProcedure compAss

/*--- block deals with the correct kind and operators
      the content is parsed by compUnit ------------------------------*/
compBlock: procedure expose m.
parse arg m, ops
orOps = ops
    s = m.m.scan
    if \ scanLit(s, '¢', '/') then
        return ''
    start = m.s.tok
    if pos(right(ops, 1), '%^') > 0 then
        ops = ops'='
    if ops == '' | pos(right(ops, 1), m.comp.chKind) < 1 then
        return scanErr(s, 'bad kind' ops 'for block') /0
    ki = right(ops, 1)
    ops = left(ops, length(ops)-1)
    isCall = pos(right(ops, 1), '%^') > 0
    starter = start
    if start == '¢' then
        stopper = m.comp.chDol'!'
    else do
        call scanVerify s, '/', 'm'
        starter = '/'m.s.tok'/'
        stopper = m.comp.chDol || starter
        if \scanLit(s, '/') then
            call scanErr s, 'ending / after stopper' stopper 'expected'
        end
    if \ isCall then do
        res = compUnit(m, ki, stopper)
        end
    else do
        call compSpComment m
        res = compPrimary(m, '.', right(ops, 1) == '^')
        if m.res.kind == 'M' then
            ops = left(ops, length(ops)-1)
        else
            res = compAst(m, right(ops, 1), , res)
        args = compExpr(m, 's', ki)
        if args \== '' then
            call mAdd res, args
        end
    if \ scanLit(s, stopper, substr(stopper, 2)) then
           call scanErr s, 'ending' stopper 'expected after' starter
    if abbrev(starter, '/') then
        m.res.text = substr(starter, 2, length(starter)-2)
    return compAstAddOp(m, res, ops)
endProcedure compBlock

/**** lexicals ********************************************************/
/*--- skip a comment. return 0 if there is none ----------------------*/
compComment: procedure expose m.
parse arg m
    s = m.m.scan
    got = 0
    do forever
        if scanLit(s, m.comp.chDol'**') then
            m.s.pos = 1 + length(m.s.src) /* before next nl */
        else if scanLit(s, m.comp.chDol'*+') then
            call scanNL s, 1
        else if scanLit(s, m.comp.chDol'*(') then do
            do forever
                if scanVerify(s, m.comp.chDol, 'm') then iterate
                if scanNL(s) then iterate
                if compComment(m) then iterate
                if \ scanLit(s, m.comp.chDol) then
                    call scanErr s, 'source end in comment'
                if scanLit(s, '*)') then
                    return 1
                if scanLit(s, m.comp.chDol) then iterate
                if scanString(s) then iterate
                end
            end
        else
            return got
        got = 1
        end
endProcedure compComment

/*--- skip spaces and comments ---------------------------------------*/
compSpComment: procedure expose m.
parse arg m, xtra
    s = m.m.scan
    got = 0
    do forever
        if scanVerify(s, m.comp.chSpa) then
            got = bitOr(got, 1)
        else if compComment(m) then
            got = bitOr(got, 2)
        else if xtra == '' then
            return got
        else if \ scanLit(s, xtra) then
            return got
        else do
            got = bitOr(got, 4)
            m.s.pos = 1+length(m.s.src)
            end
        end
endProcedure compSpComment

/*--- skip spaces, NLs and comments ----------------------------------*/
compSpNlComment: procedure expose m.
parse arg m, xtra
    found = 0
    do forever
        if compSpComment(m, xtra) < 1 then
            if \ scanNL(m.m.scan) then
             return found
        found = 1
        end
endProcedure compSpNlComment
/*--- scan a name in one of the categories
        v=var, c=compile, s=stmt ----------------------------------*/
compName: procedure expose m.
parse arg m, cats
    s = m.m.scan
    if \ scanName(s) then
        return ''
    if wordPos(m.s.tok, m.comp.wCatS) > 0 then do
        if pos('s', cats) > 0 then
            return 's'
        end
    else if wordPos(m.s.tok, m.comp.wCatC) > 0 then do
        if pos('c', cats) > 0 then
            return 'c'
        end
    else if wordPos(m.s.tok, m.comp.wCatO) > 0 then do
        if pos('o', cats) > 0 then
            return 'o'
        end
    else if pos('v', cats) > 0 then do
        return 'v'
        end
    call scanBack s, m.s.tok
    return ''
endProcedure compName


compSpNlComment: procedure expose m.
/**** small helper routines ******************************************/
compInter: procedure expose m.
    interpret arg(1)
    return
endProcedure compInter

/*--- if va == null then issue an error with msg --------------------*/
compCheckNN: procedure expose m.
parse arg m, va, msg
    if va == '' then
        call scanErr m.m.scan, msg 'expected'
    return va
endProcedure compCheckNN

/*--- return true iff expression is empty ---------------------------*/
compIsEmpty: procedure expose m.
parse arg m, a
    if a == '' then
        return 1
    do while a \== '' & m.a.kind == '*'
        a = m.a.1
        end
    return a == '' | (m.a.kind \== '¢' & m.a.0 < 1)
endProcedure compIsEmpty

/*--- if va == null or empty then issue an error with msg -----------*/
compCheckNE: procedure expose m.
parse arg m, ex, msg
    e1 = left(ex, 1)
    if compIsEmpty(m, ex) then
        call scanErr m.m.scan, msg 'expected'
    return ex
endProcedure compCheckNE

/**** AST = Astract Syntax Graph ***************************************
          goal is to migrate to migrate to old codeGenerator to AST

------- atoms, no children
  =  string constant
----------- rexx fragments
  c  rexxStatement
  s  string
  o  object
  r  run
  f  file

------- containers
  ¢  block          ==>* c-.
  @  code = 1 stmt  ==>*
  -  string
  .  object
  *  operand chain  ==>1

------- molecules
  &  variable access ==>1 =-
  A  assignment     ==> 2
  B  proc           ==> 2
  C  ct             ==> 1
  D  do                ==> 2
  F  for + with     ==> 2
  P  Pipe           ==>* 1=input 2=output
  T  Table
  M  compile
  %  RunOut         ==> 1,2 (Run, arguments)
  ^  RunRet         ==> 1,2 (Run, arguments)
  I??  Input          ==>* .

***********************************************************************/
/*--- create a new AST -----------------------------------------------*/
compAST: procedure expose m.
parse arg m, ki, txt
    n = mNew('COMP.AST')
    if length(ki) <> 1 then
        call err 'compAST bad kind' ki / 0
    m.n.kind = ki
    m.n.text = txt
    if pos(ki, '¢@-.*&ABCDFPTM%^') > 0 then do
        do cx=1 to arg()-3
            m.n.cx = arg(cx+3)
            end
        m.n.0 = cx-1
        if txt \== '' & pos(ki, '&*FPT') < 1 then
            return err('kind' ki 'text='txt'|')/0
        end
    else if pos(ki, '=csorf') > 0  then do
        m.n.0 = 'kind'ki
        end
    else do
        call err "compAst kind '"ki"' not supported"
        1/0
        end
    return n
endProcedure compAST

/*--- free AST if empty ----------------------------------------------*/
compASTFree0: procedure expose m.
parse arg a, ret
    if m.a.0 > 0 then
        return a
    call mFree a
    return ret
endProcedure compAstFree0

/*--- add operandands to an AST -------------------------------------*/
compASTAddOp: procedure expose m.
parse arg m, a, ops
    do while right(ops, 1) == m.a.kind
        ops = left(ops, length(ops)-1)
        end
    if ops == '' then
        return a
    ops = translate(ops, '-', '=')
    if m.a.kind \== '*' then
        return compAst(m, '*', ops, a)
    m.a.text = ops || m.a.text
    return a
endProcedure compAstAddOp

/*--- return the kind of an AST --------------------------------------*/
compASTKind: procedure expose m.
parse arg m, a
    m.m.astOps = ''
    if \ abbrev(a, 'COMP.AST.') then
        return left(a, 1)
    c = a
    do while m.c.kind == 'ops'
        if m.c.text \== '' then
            return left(m.c.text, 1)
        c = m.c.1
        end
    if a == c then
        return '?'
    return compAstKind(m, c)
endProcedure compASTKind

compAstAssignedVars: procedure expose m.
parse arg a
    res = ''
    if m.a.kind == 'F' then
        return ''
    if m.a.kind == 'A' then do
        a1 = m.a.1
        if m.a1.kind == '=' & m.a1.var == 'v' then do
            if words(m.a1.text) \= 1 then
                call astErr a1 'bad var'
            a2 = m.a.2
            say m.a2.kind m.a2.text
            if pos(m.a2.kind, '-=s') > 0 ,
                | (m.a2.kind == '*' & abbrev(m.a2.text, '-')) then
                res = ', f' m.a1.text 'v'
            else if pos(m.a2.kind, '.<@o') > 0 ,
                | (m.a2.kind == '*' & abbrev(m.a2.text, '.<@')) then
                res = ', f' m.a1.text 'r'
            else
                call astErr a2, 'string or object????'
            end
        end
    if datatype(m.a.0, 'n') then
        do ax=1 to m.a.0
            res = res || compAstAssignedVars(m.a.ax)
            end
    return res
endProcedure compAstAssignedVars

compAstSay: procedure expose m.
parse arg a, lv
    if \ abbrev(a, 'COMP.AST.') then do
        if a \== '' then
            return err('bad ast' a)
        say left('', 19)': * empty ast'
        return
        end
    say lefPad(left('', lv) m.a.kind, 10) ,
        || rigPad(if(dataType(m.a.0, 'n'), m.a.0), 3),
        '@'rigPad(substr(a, 10), 4)':' m.a.text'|'
    if dataType(m.a.0, 'n') then do cx=1 to m.a.0
        call compAstSay m.a.cx, lv+1
        end
    return
endProcedure compAstSay

compAstErr: procedure expose m.
parse arg a, txt
    call errSay txt
    call compAstSay a, 0
    return err(txt)
endProcedure compAstErr

compAstOnlyOut: procedure expose m.
parse arg a, rec
    do ax=1 to m.a.0
        a1 = m.a.ax
        if m.a1.kind == '*' then
            if abbrev(m.a1.text, '$') then
                iterate
        if m.a1.kind = '¢' & rec \== 0 then
            if compAstOnlyOut(a1) then
                iterate
        return 0
        end
    return 1
endProcedure compAstOnlyOut

/*--- return the code for an AST with operand chain trg --------------*/
compCode2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if pos(o1, ';') > 0 then
        return compCode2rx(m, oR, strip(f))
    if pos(o1, '-.<|?@') > 0 then
        return compRun2rx(m, ops, quote(oRunner(f)))

    call err 'compCode2rx bad ops' ops
endProcedure compCode2rx

compCon2rx: procedure expose m.
parse arg m, ops, f, a
    do ox=length(ops) by -1 to 1 while pos(substr(ops,ox,1), '.-')>0
        end
    if substr(ops, ox+1, 1) == '.' then
        f = s2o(f)
    if length(f) < 20 then
        v = quote(f, "'")
    else if a \== '' & m.a.text == f then
        v = 'm.'a'.text'
    else
        v =    'm.'compAst(m, '=', f)'.text'
    if substr(ops, ox+1, 1) == '.' then
        return compObj2rx(m, left(ops, ox), v)
    else
        return compString2rx(m, left(ops, ox), v)
endProcedure compCon2rx

compString2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '-' then
        return compString2rx(m, oR, f)
    if o1 == '.' then
        return compObj2rx(m, oR, 's2o('f')')
    if pos(o1, '$') > 0 tOnlyOut then
        return compCode2rx(m, oR, 'call out' f';')
    if o1 == '&' then do
        o2 = substr('1'ops, length(ops), 1)
        if o2 \== '.' & o2 \== '<' then
            return compString2rx(m, oR, 'vGet('f')')
        else
            return compObj2rx(m, oR, 'vGetO('f')')
        end
    if o1 == '<' then
        return compFile2rx(m, oR, 'file('f')')
    call err 'compString2rx bad ops' ops
endProcedure compString2rx

compObj2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '.' then
        return compObj2rx(m, oR, f)
    if o1 == '-' then
        return compString2rx(m, oR, 'o2string('f')')
    if o1 == '$' then
        return compCode2rx(m, oR, 'call outO' f';')
    if o1 == '<' then
        return compFile2rx(m, oR, 'o2file('f')')
    if o1 == '@' then
        return compRun2Rx(m, oR, f)
    if pos(o1, ';%^') > 0 then
        return compRun2rx(m, ops, f)
    call err 'compObj2rx bad ops' ops
endProcedure compObj2rx

compRun2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '@' then
        return compRun2Rx(m, oR, f)
    if o1 == ';' | o1 == '%' then /*??? immer % verwenden ???*/
        return compCode2Rx(m, oR, 'call oRun' f';')
    return compObj2rx(m, ops, f)
endProcedure compObj2rx

compFile2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if pos(o1, '<.@') > 0 then
        return compFile2rx(m, oR, f)
    if o1 == '|' | o1 == '?' then
        return compObj2Rx(m, oR, 'jSingle('f ||if(o1=='?', ", ''")')')
    return compRun2rx(m, ops, f)
endProcedure compFile2rx

compAst2rx: procedure expose m.
parse arg m, ops, a
    ki = m.a.kind

    if ki == 'c' then
        return compCode2Rx(m, ops, m.a.text)
    if ki == 's' then
        return compString2rx(m, ops, m.a.text)
    if ki == 'o' then
        return compObj2Rx(m, ops, m.a.text)
    if ki == '=' then
        return compCon2Rx(m, ops, m.a.text, a)
    if ki == '*' then
        return compAst2Rx(m, ops || m.a.text, m.a.1)
    o1 = right(ops, 1)
    oR = left(ops, max(0, length(ops)-1))
    if ki == '-' then
        if m.a.0 = 1 then
            return compAst2rx(m, ops, m.a.1)
        else
            return compString2rx(m, ops, compCatRexxAll(m, a,,, ' || '))
    if ki == '.' then
        return compObj2Rx(m, ops, compCatRexxAll(m, a,,, ' || '))
    if ki == '@' then
        return compCode2Rx(m, ops, compCatRexxAll(m, a,,,' || ')';')
    if ki == '¢' then do
        a1 = m.a.1
        if m.a.0 == 1 & m.a1.kind == '¢' then
            return compAst2Rx(m, ops, a1)
        if o1 == '-' then do
            if \ compAstOnlyOut(a) then
                return compAst2Rx(m, ops';', a)
            res = ''
            do ax=1 to m.a.0
                a1 = m.a.ax
                if m.a1.kind == '*' & abbrev(m.a1.text, '$') then
                    b = compAst2rx(m, overlay('-',m.a1.text,1), m.a1.1)
                else if m.a1.kind == '¢' then
                    b = compAst2rx(m, '-', a1)
                else
                    call compAstErr a, 'onlyOut but' ax'='a1 ,
                        'kind='m.a1.kind 'text='m.a1.text
                res = compCatRexx(res, b, ' ')
                end
                    /* ???? only necessary if part of expression ????*/
            return compString2rx(m, oR, '('strip(res)')')
            end
        if o1 == '<' then
         return compAst2Rx(m, ops'@', a)
        if o1 == '.' then
            return compAst2Rx(m, oR'|', a)
        if o1 == '|' | o1 == '?' then
            if m.a.0 = 1 & compAstOnlyOut(a, 0) then do
                a1 = m.a.1
                if m.a1.kind \== '*' | \ abbrev(m.a1.text, '$') then
                    call compAstErr a, 'onlyOut but',
                        '1='a1 'kind='m.a1.kind 'text='m.a1.text
                return compAst2Rx(m, oR'.'substr(m.a1.text, 2), m.a1.1)
                end
            else
                return compFile2Rx(m, ops, compAst2rx(m, '<;', a))
        if pos(o1, '@;') > 0 then do
            if m.a.0 = 1 then  /*???pipe und code können multi stmts */
               return compCode2Rx(m, ops, 'do;' ,
                    compAst2rx(m,';',m.a.1) 'end;')
            res = ''
            do ax=1 to m.a.0
                res = res compAst2rx(m, ';', m.a.ax)
                end
            if res = '' then
                return compCode2Rx(m, ops, 'nop;')
            return compCode2Rx(m, ops, 'do;'res 'end;')
            end
        end
    if ki == '&' then do
        nm = compAst2Rx(m, '-', m.a.1)
        if m.a.text=='' | m.a.text=='v' then
            return compString2rx(m, ops'&', nm)
        else if m.a.text == '?' then
         return compString2rx(m, ops, 'vIsDefined('nm')')
        else if m.a.text == '>' then
         return compString2rx(m, ops, 'vReadO('nm')')
        else
         call compAstErr a, 'bad text' m.a.text 'in ast &'
        end
    if ki == '%' | ki == '^' then do
        c1 = compAst2Rx(m, '.', m.a.1)
        if m.a.0 = 1 then
            return compRun2Rx(m, ops, c1)
        args = compAst2Rx(m, '', m.a.2)
        if ki == '%' then
            return compCode2Rx(m, ops, 'call oRun' c1',' args';')
        call err 'implement'
        end
    if ki == 'A' then do /* assignment */
        nm = compAst2Rx(m, '-', m.a.1)
        vl = m.a.2
     if m.vl.kind == '=' | m.vl.kind == '-' ,
            | (m.vl.kind == '*' & right(m.vl.text, 1) == '-') then
         return compCode2Rx(m, ops,
                , 'call vPut' nm',' compAst2Rx(m, '-', vl)';')
        else
         return compCode2Rx(m, ops,
                , 'call vPutO' nm',' compAst2Rx(m, '.', vl)';')
        end
    if ki == 'B' then do /* proc */
        call vPutO compInter('return' compAst2Rx(m, '-', m.a.1)),
            , oRunner(compAst2Rx(m ,';', m.a.2))
        return ''
        end
    if ki == 'C' then do /* ct */
     call compInter compAst2Rx(m, ';', m.a.1)
        return ''
        end
    if ki == 'D' then do /* do */
        res = 'do' compAst2rx(m, '', m.a.1)';'
        if m.a.text \== '' then
            res = res "call vPut '"m.a.text"'," m.a.text";"
        return compCode2Rx(m, ops, res compAst2Rx(m, ';', m.a.2),
             "end;")
        end
    if ki == 'F' then do /* for... */
        a1 = m.a.1
         st = compAst2Rx(m, ';', m.a.2)
        if abbrev(m.a.text, 'with') then do
            if m.a1.kind \== 'A' then do
                v = compAst2Rx(m, '.', a1)
                end
             else do
                v = compAst2Rx(m, ,a1)
                if \ abbrev(v, 'call vPutO ') | right(v, 1) \==';' then
                    call scanErr s, 'bad vPutO' v
                v = 'vPutO('substr(v, 12, length(v)-12)')'
                end
            if m.a.0 <= 2 then
                return 'call vWith "+",' v';' st 'call vWith "-";'
            a3 = m.a.3
            if m.a3.kind == '*' then
                return 'call vWith "+",' v';' st,
                    compObj2Rx(m, m.a3.text, "vWith('-')")
            end
        v = compAst2Rx(m, '-', m.a.1)
        if m.a.text == 'forWith' then
            st = 'call vWith "=", vGetO('v');' st
        if abbrev(m.a.text, 'for') then
            st = 'do while vReadO('v');' st 'end;'
        if m.a.text == 'forWith' then
            st = 'call vWith "+";' st 'call vWith "-";'
        return compCode2Rx(m, ops, st)
        end
    if ki == 'P' then do /* pipe */
        if ((left(m.a.text, 1) == ' ') \== (m.a.1 == '')) ,
         | ((substr(m.a.text, 2) == '') \== (m.a.2 == '')) ,
         | (m.a.0 <= 3 & m.a.text == '') then
            call compAstErr a, 'bad/trivial astPipe'
        res = ''
        do ax=3 to m.a.0
            a1 = ''
            if ax < m.a.0 then /* handle output */
                t1 = 'N'
            else if m.a.1 == '' then
                t1 = 'P'
            else do
                t1 = left(m.a.text, 1)
                a1 = compAst2Rx(m, '.', m.a.1)
                end
            if ax == 3 then do /* handle input */
                t1 = '+'t1 || substr(m.a.text, 2)
                if m.a.2 \== '' then
                    a1 = a1',' compAst2Rx(m, '.', m.a.2)
                end
            else
                t1 = t1'|'
            res = res "call pipe '"t1"'," a1";" ,
                   compAst2Rx(m, ';', m.a.ax)
            end
        return compCode2Rx(m, ops, res "call pipe '-';")
        end
    if ki == 'R' then do /* Run with args */
        res = 'call oRun' compAst2Rx(m, , m.a.1)
        if m.a.0 > 1 then
            res = res',' compAst2Rx(m, , m.a.2)
        return compRun2rx(m, ops, res';')
        end
    if ki == 'M' then do
        if m.a.0 = 0 then
            args = ''
        else
            args = ',' compAst2Rx(m, , m.a.1)
        return compRun2rx(m, ops, 'compile(comp(j2Buf())' args')')
        end
    call compAstErr a, 'comAst2rx bad ops' ops 'kind' ki
endProcedure compAst2rx

compCatRexxAll: procedure expose m.
parse arg m, a, ops, mi, sep
    res = ''
    do ax=1 to m.a.0
        res = compCatRexx(res, compAst2rx(m, ops, m.a.ax), mi , sep)
        end
    return strip(res)
endProcedure compCatRexxAll

/*--- cat two rexx parts, avoid strange effects--------------------*/
compCatRexx: procedure expose m.
parse arg le, ri, mi, sep
    if mi \== '' then
        return le || mi || ri
    lr = right(le, 1)
    rl = left(ri, 1)
    if (lr == "'" | lr == '"') then do
        if rl == lr then                /* "a","b" -> "ab" */
            return left(le, length(le)-1) || substr(ri, 2)
        else if  rl == '(' then            /* "a",( -> "a" || ( */
            return le||sep||ri            /* avoid function call    */
        end
    else if pos(lr, m.comp.idChars) > 0 then
        if pos(rl, m.comp.idChars'(') > 0 then
            return le || sep || ri        /* a,b -> a || b */
    return le || ri
endProcedure compCatRexx
/* copy comp end ******************************************************/
/* copy scanSB begin ***************************************************
     Achtung: inc generiert SB aus scanSB, Aenderungen nur in scanSB|
ScanSB: basic scan
    scanLook(m,len) : returns next len chars, pos is not moved
    scanChar(m,len) : scans next len chars
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanWord(m,u)  : scan a space delimited word or a string,
                          if u=1 then uppercase non-strings
variable interface
    scanSrc(m, source) starts scanning a single line
    scanEnd(m)     : returns whether we reached end of input
    scanErr(m, txt): error with current scan location

    m is an address, to store our state
    if a scan function succeeds, the scan posititon is moved

returns: true if scanned, false otherwise
         m.m.tok  ==> last token
         m.m.val  ==> last value for scanString/Word
         m.m.pos ==> scan position
         m.m.src ==> scan source
***********************************************************************/
/*--- return the next len characters until end of src ----------------*/
scanLook: procedure expose m.
parse arg m, len
    if len == '' then
        return substr(m.m.src, m.m.pos)
    else
        return substr(m.m.src, m.m.pos,
                     , min(len, 1 + length(m.m.src) - m.m.pos))
endProcedure scanLook

/*--- scan len chararcters, atmost to end of src ---------------------*/
scanChar: procedure expose m.
parse arg m, len
    m.m.tok = scanLook(m, len)
    m.m.pos = m.m.pos + length(m.m.tok)
    return m.m.tok \== ''
endProcedure scanChar

/*--- scan the literal lit ------------------------------------------*/
scanLit: procedure expose m.
parse arg m
    do ax=2 to arg()
        if abbrev(substr(m.m.src, m.m.pos), arg(ax)) then do
            m.m.tok = arg(ax)
            m.m.pos = m.m.pos + length(arg(ax))
            return 1
            end
        end
    m.m.tok = ''
    return 0
endProcedure scanLit

/*--- scan with verify, vOpt is passed to verify ---------------------*/
scanVerify: procedure expose m.
parse arg m, alpha, vOpt, onlyIfMatch
    if arg() > 3 then
        call err 'deimplement onlyIfMatch???'
    if vOpt == '' then   /* empty string does not take default */
        nx = verify(m.m.src, alpha, , m.m.pos)
    else
        nx = verify(m.m.src, alpha, vOpt, m.m.pos)
    if nx = 0 then
        if onlyIfMatch == 1 then
            nx = m.m.pos
        else
            nx = length(m.m.src) + 1
    m.m.tok = substr(m.m.src, m.m.pos, nx - m.m.pos)
    m.m.pos = nx
    return m.m.tok \== ''
endProcedure scanVerify

/*--- scan while in charset ------------------------------------------*/
scanWhile: procedure expose m.
parse arg m, chSet
    return scanVerify(m, chSet, 'n')

/*--- scan until in charset ------------------------------------------*/
scanUntil: procedure expose m.
parse arg m, chSet
    return scanVerify(m, chSet, 'm')

/*--- scan until (and over) string End -------------------------------*/
scanStrEnd: procedure expose m.
parse arg m, sep
    px = m.m.pos
    do forever
        px = pos(sep, m.m.src, px)
        if px = 0 then do
            m.m.tok = ''
            return 0
            end
        px = px + length(sep)
        if \ abbrev(substr(m.m.src, px), sep) then do
            m.m.tok = substr(m.m.src, m.m.pos, px-m.m.pos)
            m.m.pos = px
            return 1
            end
        px = px + length(sep)
        end
endProcedure scanStrEnd

/*--- scan a string with quote char qu -------------------------------*/
scanString: procedure expose m.
parse arg m, prefs
    if prefs = '' then do
        call scanLit m, "'", '"'
        end
    else do
        do px=1 to words(prefs) until scanLit(m, word(prefs, px))
            end
        end
    if m.m.tok == '' then
        return 0
    t1 = m.m.tok
    qu = right(t1, 1)
    if \ scanStrEnd(m, qu) then do
        m.m.pos = m.m.pos - length(t1)
        return scanErr(m, 'ending Apostroph('qu') missing')
        end
    m.m.val = repAll(left(m.m.tok, length(m.m.tok)-1), qu||qu, qu)
    m.m.tok = t1 || m.m.tok
    return 1
endProcedure scanString

/*--- scan a word and put value into *.val
           a word is either delimited by space or stopper
                     or a string (with single or double quotes -------*/
scanWord: procedure expose m.
parse arg m, stopper, ucWord
    if scanString(m) then
        return 1
    if stopper == '' then
        stopper = ' '
    if \scanUntil(m, stopper) then
        return 0
    m.m.val = m.m.tok
    if ucWord == 1 then
        m.m.val = translate(m.m.val)
    return 1
endProcedure scanWord

/*--- skip, scan and return next word --------------------------------*/
scanSkWord: procedure expose m.
parse arg m, stopper, ucWord, eMsg
    if scanWord(scanSkip(m), stopper, ucWord) then
        return m.m.val
    else if eMsg == '' then
        return ''
    else
        call scanErr m, eMsg 'expected'
endProcedure scanSkWord

/*--- go back the current token --------------------------------------*/
scanBack: procedure expose m.
parse arg m, tok
    if m.m.pos <= length(tok) then
        call scanErr sc, 'cannot back "'tok'" length'/0
    cx = m.m.pos - length(tok)
    if substr(m.m.src, cx, length(tok)) \== tok then
        return scanErr(m, 'cannot back "'tok'" value')/0
    m.m.pos = cx
    return
endProcedure scanBack

/*--- set new src - allow scanning without open ----------------------*/
scanSBSrc: procedure expose m.
parse arg m, m.m.src
    return scanSBOpen(m)
endProcedure scanSBSrc

/*--- start scanning with a new single src ---------------------------*/
scanSBOpen: procedure expose m.
parse arg m
    m.m.pos = 1
    m.m.tok = ''
    return m
endProcedure scanSBOpen

/*--- start scanning with a new single src ---------------------------*/
scanSBClose: procedure expose m.
parse arg m
    m.m.pos = length(m.m.src) + 1
    m.m.tok = '--- closed ---'
    return m
endProcedure scanSBClose

scanSBSpace: procedure expose m.
parse arg m
    nx = verify(m.m.src, ' ', , m.m.pos)
    if nx = 0 then
        nx = length(m.m.src) + 1
    res = nx <> m.m.pos
    m.m.tok = left(' ', res)
    m.m.pos = nx
    return res
endProcedure scanSBSpace

/*--- skip over space, nl and comments and return m -----------------*/
scanSkip: procedure expose m.
parse arg m
    call scanSpace m
    return m
endProcedure scanSkip

scanErr: procedure expose m.
parse arg m, txt
    if arg() < 3 then
        return err('s}'txt'\n'scanInfo(m))
    else
        return err('scanErr' txt'\n'arg(3))
endProcedure scanErr

scanSBInfo: procedure expose m.
parse arg m
    return 'last token' m.m.tok 'scanPosition' ,
        strip(substr(m.m.src, m.m.pos, 40), 't') ,
        || '\npos' m.m.Pos 'in string' strip(m.m.src, 't')
endProcedure scanSBInfo

/*--- return position in simple format -------------------------------*/
scanSBPos: procedure expose m.
parse arg m
    return 'singleSrc' m.m.pos
    return if(m.m.pos > length(m.m.src), 'E', 'singleSrc' m.m.pos)

/*--- set position to position in arg to------------------------------*/
scanSetPos: procedure expose m.
parse arg m, to
    cur = scanPos(m)
    wc = words(cur)
    if wc <> words(to) ,
        | subWord(cur, 1, wc-1) <> subWord(to, 1, wc-1) then
        call scanErr m 'cannot back from' cur 'to' to
    m.m.pos = word(to, wc)
    return

/*--- return true if at end of src -----------------------------------*/
scanSBEnd: procedure expose m.
parse arg m
    return m.m.pos > length(m.m.src)
/* copy scanSB end ****************************************************/
/* copy scan begin ****************************************************
Scan: scan an input: with multiple lines
    ==> all of scanSB
    scanEnd(m)     : returns whether we reached end of input
    scanLit(m,lit) : scan Literal lit if present or return 0
    scanChar(m,n)  : scan next n characters
    scanName(m)    : scan a name
    ScanNat(m)     : scan a natural number (without sign)
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanKeyValue(m): scan a key = value clause (with spaces)
    scanWord(m,u)  : scan a space delimited word or a string,
                          if u=1 then uppercase non-strings
    scanErr(m, txt): error with current scan location

    m is an address, to store our state
    if a scan function succeeds, the scan posititon is moved

returns: true if scanned, false otherwise
         m.m.tok  ==> last token
         m.m.val  ==> last value for scanString/Word/KeyValue
         m.key    ==> key for scanKeyValue
         m.m.pos ==> scan position
         m.m.src ==> scan source
***********************************************************************/
scanIni: procedure expose m.
    if m.scan.ini == 1 then
        return
    m.scan.ini = 1
    call jIni
    ts = classNew('n ScanRes u f TOK v, f VAL v, f KEY v, f TYPE v')
    call classNew 'n ScanSB u JRWO', 'm',
        , 'scanEnd  return 1',
        , 'scanNL   m.m.tok = ""; return 0',
        , 'scanCom  m.m.tok = ""; return 0',
        , 'scanInfo return scanSBInfo(m)' ,
        , 'jReset call scanSbSrc m, arg;' ,
                  'call scanOpts m, arg2, arg3, arg(4)',
        , "jOpen call scanSBOpen scanOC(m, opt, 'ScanSBR')" ,
        , "jClose call scanSBClose scanOC(m, , 'ScanSB')",
        , 'scanPos return scanSBPos(m)'
    call classNew 'n ScanSBR u ScanSB', 'm',
        , "jReadO if scanType(m) == '' then return '';" ,
                    " else return oClaCopy('"ts"', m, '')"
    return
endProcedure scanIni
/*--- check open opt is read and mutate ------------------------------*/
scanOC: procedure expose m.
parse arg m, opt, cla
    if \ abbrev(m.j.cRead, opt) then
        call err 'scanOpen opt must be' m.j.cRead 'not' opt
    return oMutatName(m, cla)
endProcedure scanOC
/*--- start scanning with a new single src ---------------------------*/
scanSrc: procedure expose m.
parse arg m, src
    return scanSbSrc(oMutatName(m, 'ScanSB'), src)

scanOpen: procedure expose m.
parse arg m
    opt = ''
    interpret objMet(m, 'jOpen')
    return m
endProcedure scanOpen

scanClose: procedure expose m.
parse arg m
    interpret objMet(m, 'jClose')
    return m
endProcedure scanOpen

scanInfo: procedure expose m.
parse arg m
    interpret objMet(m, 'scanInfo')

/*--- return true if at end of src -----------------------------------*/
scanEnd: procedure expose m.
parse arg m
    if m.m.pos <= length(m.m.src) then
        return 0
    interpret objMet(m, 'scanEnd')

/*--- scan over white space, nl, comments ...-------------------------*/
scanSpace: procedure expose m.
parse arg m
    fnd = 0
    do while scanSBSpace(m) | scanCom(m) | scanNl(m)
        fnd = 1
        end
    m.m.tok = left(' ', fnd)
    return fnd
endProcedure scanSpace

/*--- scan next line -------------------------------------------------*/
scanNL: procedure expose m.
parse arg m, unCond
    interpret objMet(m, 'scanNL')

/*--- scan one comment -----------------------------------------------*/
scanCom: procedure expose m.
parse arg m
    interpret objMet(m, 'scanCom')

scanPos: procedure expose m.
parse arg m
    interpret objMet(m, 'scanPos')
endProcedure scanPos

scanOpts: procedure expose m.
parse arg m, m.m.scanName1, namePlus, m.m.scanComment
    if m.m.scanName1 == '' then
        m.m.scanName1 = m.ut_alfa
    if namePlus == '' then
        m.m.scanNameR = m.m.scanName1 || '0123456789'
    else
        m.m.scanNameR = m.m.scanName1 || namePlus
    return m
endProcedure scanOpts

/*--- begin scanning a single line -----------------------------------*/
scanSrc: procedure expose m.
parse arg m, m.m.src
    return scanOpen(m)
endProcedure scanSrc

/*--- scan a Name, first char in *.scanName1, rest in *.scanNameR ----*/
scanName: procedure expose m.
parse arg m
    if pos(substr(m.m.src, m.m.pos, 1),
                 , m.m.scanName1) <= 0 then do
        m.m.tok = ''
        return 0
        end
    return scanVerify(m, m.m.scanNameR)
endProcedure scanName

/*--- scan a natural number (no sign, decpoint ...) ------------------*/
scanNat: procedure expose m.
parse arg m, chEn
    if \ scanVerify(m, '0123456789') then
        return 0
    if chEn \== 0 then
        if pos(scanLook(m , 1), m.m.scanNameR) > 0 then
            call scanErr m, 'illegal number end after' m.m.tok
    return 1
endProcedure ScanNat

/*--- scan an integer (optional sign, no decpoint ...) ---------------*/
scanInt: procedure expose m.
parse arg m, chEn
    call scanLit m, '+', '-'
    si = m.m.tok
    if \ scanNat(m, chEn) then do
        m.m.pos = m.m.pos - si
        return 0
        end
    m.m.tok = si || m.m.tok
    return 1
endProcedure scanInt

/*--- scan a number (optional sign, decpoint, exponent) ------------*/
scanNum: procedure expose m.
parse arg m, chEn
    sx = m.m.pos
    call scanLit m, '+', '-'
    po = scanLit(m, '.')
    if \ scanNat(m, 0) then do
        m.m.pos = sx
        return 0
        end
    if \ po then
        if scanLit(m, '.') then
            call scanNat m, 0
        if scanLit(m, 'e', 'E') then
            if \ scanInt(m, 0) then
                call scanErr m, 'exponent expected after' ,
                             substr(m.m.src, sx, m.m.pos-sx)
    m.m.tok = substr(m.m.src, sx, m.m.pos-sx)
    m.m.val = translate(m.m.tok)
    if chEn \== 0 then
        if pos(scanLook(m , 1), m.m.scanNameR) > 0 then
            call scanErr m, 'illegal number end after' m.m.tok
    return 1
endProcedure scanNum

scanType: procedure expose m.
parse arg m, opt
    m.m.tok = ''
    if scanName(m) then
        m.m.type = 'n'
    else if scanNum(m) then
        m.m.type = 0
    else if scanString(m) then
        m.m.type = left(m.m.tok, 1)
    else if scanSpace(m) then
        m.m.type = 's'
    else do
        call scanChar m, 1
        m.m.type = m.m.tok
        end
    return m.m.type
endProcedure scanType

/*--- scan a key = word phrase
          put key into m.key and word into m.m.val -------*/
scanKeyValue: procedure expose m.
parse arg m, def
    if \ scanName(m) then
        return 0
    m.m.key = m.m.tok
    if \ scanLit(scanSkip(m), '=') then do
        m.m.val = def
        m.m.tok = ' no='
        end
    else if \scanWord(scanSkip(m)) then
        return scanErr(m, 'word expected after' m.m.key '=')
    return 1
endProcedure scanKeyValue

/*--- emit an error with current scan pos ----------------------------*/
/* copy scan end   ****************************************************/
/* copy scanRead begin ************************************************/
scanReadIni: procedure expose m.
    if m.scanRead.ini = 1 then
        return
    m.scanRead.ini = 1
    call scanIni
 /* ts = classNew('n Scan u f TOK v, f VAL v, f KEY v, f TYPE v') */
    call classNew 'n ScanRead u ScanSB', 'm',
        , 'scanEnd return m.m.atEnd' ,
        , 'scanNL return scanReadNL(m, unCond)',
        , 'scanCom return scanReadCom(m)',
        , 'scanInfo return scanReadInfo(m)',
        , 'scanPos return scanReadPos(m)',
        , "jOpen   call scanReadOpen scanOC(m, opt, 'ScanReadR')",
                    ", ,arg()>=3, arg(3)" ,
        , "jClose  call scanReadClose scanOc(m, , 'ScanRead')"
    call classNew 'n ScanReadR u ScanRead', 'm',
        , 'jReadO' oClaMet(class4Name('ScanSBR'), 'jReadO')
    call classNew "n EditRead u JRW", "m",
        , "jRead  return editRead(m, var)",
        , "jOpen" ,
        , "jReset m.m.linex = arg - 1"
    return
endProcedure scanReadIni

/*--- begin scanning the lines of a reader ---------------------------*/
scanRead: procedure expose m.
parse arg rdr, n1, np, co
    return scanReadReset(oNew('ScanRead'), rdr, n1, np, co)

scanReadReset: procedure expose m.
parse arg m, m.m.rdr, n1, np, co
    call oMutatName m, 'ScanRead'
    call scanOpts m, n1, np, co
    return m
endProcedure scanReadReset

scanReadOpen: procedure expose m.
parse arg m, o, hasLine0, line0
    m.m.atEnd = 0
    m.m.lineX = 0
    m.m.val = ''
    m.m.key = ''
    call jOpen m.m.rdr, '<'
    if hasLine0 then do
        m.m.pos = 1
        m.m.src = line0
        end
    else
        call scanReadNL m, 1
    return m
endProcedure scanReadOpen

scanReadClose: procedure expose m.
parse arg m
    call jClose m.m.rdr
    m.m.atEnd = 'closed'
    return scanSBClose(m)
endProcedure scanReadClose

/*--- scan over next newLine
        if unCond \== 1 only if we are already at endOfLine
         return true if we scanned a NL ------------------------------*/
scanReadNL: procedure expose m.
parse arg m, unCond
    if unCond \== 1 then
        if m.m.pos <= length(m.m.src) then
            return 0
    if m.m.atEnd then
        return 0
    m.m.atEnd = \ jRead(m.m.rdr, m'.SRC')
    if m.m.atEnd then do
        m.m.pos = 1 + length(m.m.src)
        return 0
        end
    m.m.pos = 1
    m.m.lineX = m.m.lineX + 1
    return 1
endProcedure scanReadNl

scanReadCom: procedure expose m.
parse arg m
    m.m.tok = ''
    if m.m.scanComment == '' then
        return 0
    if \ abbrev(substr(m.m.src, m.m.pos), m.m.scanComment) then
        return 0
    m.m.pos = 1 + length(m.m.src)
    m.m.tok = ' '
    return 1
endProcedure scanReadCom

scanReadPos: procedure expose m.
parse arg m, msg
    return m.m.lineX m.m.pos
endProcedure scanReadPos

scanReadInfo: procedure expose m.
parse arg m
    msg = 'last token' m.m.tok 'scanPosition' ,
        strip(substr(m.m.src, m.m.pos, 40), 't')
    if scanEnd(m) then
        msg = msg'\natEnd after'
    else
        msg = msg'\npos' m.m.pos 'in'
    return msg 'line' m.m.lineX':' strip(m.m.src, 't')
endProcedure scanReadInfo

/*--- use scan sqlEdit macro --> temporarily here --------------------*/
/*--- read next line from edit data ----------------------------------*/
editRead: procedure expose m.
parse arg m, var
    m.m.lineX = m.m.lineX + 1
    if adrEdit('(ll) = line' m.m.lineX, 12) \= 0 then
        return 0
    m.var = ll
    return 1
endProcedure editRead
/*--- search loop in edit macro --------------------------------------*/
scanSqlSeekId: procedure expose m.
parse arg m, lx, cmd, opts
    if opts = '' then
        opts = word
                     /* line 1 col 0, otherwise first word is skipped*/
    if adrEdit("cursor =" max(trunc(lx), 1) 0, 12) = 12 then
        return -1
    do while adrEdit("seek" cmd opts, 4) = 0 /* find each command*/
        call adrEdit "(fx) = cursor"
        if m.debug then do
            call adrEdit "(LI) = LINE" fx
            call debug 'scanSqlSeekId after' lx 'found' cmd 'at' fx li
            end
        call jReset m.m.rdr, fx
        call jOpen m, '<'
        m.m.lineX = fx
        do while word(scanPos(m), 1) <= fx & scanSqlClass(m)
            if m.m.sqlClass = 'i' & m.m.val == cmd then
                return fx
            end
        call jClose m
        end
    return -1
endProcedure scanSqlSeekId
/* copy scanRead end **************************************************/
/* copy scanWin begin *************************************************
     scan the the concatenation of the lines of a reader
         any token my be split over several line
         except the end-of-line-comment-token
***********************************************************************/
scanWinIni: procedure expose m.
    if m.scanWin.ini = 1 then
        return
    m.scanWin.ini = 1
    call scanReadIni
    call classNew 'n ScanWin u ScanSB', 'm',
        , 'jReset call scanWinReset m, arg, arg2, arg3',
        , "jOpen call scanWinOpen scanOC(m, opt, 'ScanWinR'), arg(3)",
        , "jClose call scanReadClose scanOC(m, , 'ScanWin')",
        , 'scanNL return scanWinNl(m, unCond)',
        , 'scanCom return scanWinCom(m)',
        , 'scanInfo return scanWinInfo(m)',
        , 'scanPos  return scanWinPos(m)'
    call classNew 'n ScanWinR u ScanWin', 'm',
        , 'jReadO' oClaMet(class4Name('ScanSBR'), 'jReadO')
    return
endProcedure scanWinIni

/*--- instanciate a new window scanner, open rdr ---------------------*/
scanWin: procedure expose m.
parse arg rdr, wiSz, wiBa, cuPo, cuLe
    return scanWinOpts(oNew('ScanWin', rdr), wiSz, wiBa, cuPo, cuLe)

/*--- set the reader and attributes of window scanner m -------------*/
scanWinReset: procedure expose m.
parse arg m, m.m.rdr, wiSz, wiGa, cuPo, cuLe
    m.m.atEnd = 'closed after reset'
    return scanWinOpts(scanOpts(m), wiSz, wiGa, cuPo, cuLe)
endProcedure scanWinReset

/*--- set the attributes of window scanner m ------------------------*/
scanWinOpts: procedure expose m.
parse arg m, wiSz, wiGa, cuPo, cuLe
    wiSz = word(wiSz 5, 1)
    wiGa = word(wiGa 1, 1)
    m.m.cutPos = word(cuPo 1, 1)
    m.m.cutLen = word(cuLe 72, 1)
    m.m.winTot = (wiSz * 2 + wiGa) * m.m.cutLen
    m.m.posLim = (wiSz     + wiGa) * m.m.cutLen
    m.m.posOff =  wiGa * m.m.cutLen
    return m
endProcedure scanWinOpts

/*--- open reader and start scanning --------------------------------*/
scanWinOpen: procedure expose m.
parse arg m, lx
    m.m.atEnd = 0
    if lx = '' then
        m.m.lineX = 1
    else
        m.m.lineX = lx
    call scanSBOpen m
    m.m.val = ''
    m.m.key = ''
    m.m.pos = 1
    m.m.src = ''
    call jOpen m.m.rdr, m.j.cRead
    call scanWinRead m
    return m
endProcedure scanWinOpen

/*--- move the source window: cut left side and append at right side
      return number of characters cut at left ------------------------*/
scanWinRead: procedure expose m.
parse arg m
    dlt = 0
    if m.m.atEnd then
        return 0
    if m.m.pos >= m.m.posLim then do     /*  cut left side */
        dlt = m.m.pos - (m.m.pos // m.m.cutLen + m.m.posOff)
        m.m.src = substr(m.m.src, dlt+1)
        m.m.pos = m.m.pos - dlt
        m.m.lineX = m.m.lineX + dlt % m.m.cutLen
        end
    do while length(m.m.src) < m.m.winTot /* read and fill to len */
        if \ jRead(m.m.rdr, m'.'one) then do
            m.m.atEnd = 1
            return dlt
            end
        m.m.src = m.m.src || substr(m.m.one, m.m.cutPos, m.m.cutLen)
        end
    call assert 'length(m.m.src) = m.m.winTot', 'm.m.winTot m.m.src'
    return dlt
endProcedure scanWinRead

/*--- return position of next line start -----------------------------*/
scanWinNLPos: procedure expose m.
parse arg m
    return m.m.pos + m.m.cutLen - ((m.m.pos - 1) // m.m.cutLen)

/*--- scan comment ---------------------------------------------------*/
scanWinCom: procedure expose m.
parse arg m
    call scanWinRead m
    if scanLit(m, '/*') then do
        ex = pos('*/', m.m.src, m.m.pos+2)
        if ex <= m.m.pos then
            return scanErr(m, '*/ missing after /*')
        m.m.pos = ex+2
        call scanWinRead m
        end
    else do
        cl = length(m.m.scanComment)
        np = scanWinNlPos(m)
        if \ ( cl>0 & m.m.pos+cl <= np & m.m.scanComment ,
                == substr(m.m.src, m.m.pos, cl)) then do
            m.m.tok = ''
            return 0
            end
        m.m.pos = np
        end
    m.m.tok = ' '
    return 1
endProcedure scanWinCom

/*--- scan nl --------------------------------------------------------*/
scanWinNL: procedure expose m.
parse arg m, unCond
    call scanWinRead m
    if unCond \== 1 then
       return 0
    np = scanWinNLPos(m)
    if np \= m.m.pos then
        return 0
    m.m.pos = np
    return 1
endProcedure scanWinNl

/*--- return current position in input ------------------------------*/
scanWinPos: procedure expose m.
parse arg m
    if scanEnd(m) then
        return 'E'
    ps = m.m.pos - 1
    return (m.m.lineX + (ps % m.m.cutLen)) (ps // m.m.cutLen + 1)
endProcedure scanWinPos

/*--- return a description of the current scan position --------------*/
scanWinInfo: procedure expose m.
parse arg m
    p = scanWinPos(m)
    if p == 'E' then do
        res = 'atEnd after'
        p = m.m.lineX - 1 + length(m.m.src) % m.m.cutLen
        end
    else do
        res = 'pos' word(p, 2) 'in'
        p = word(p, 1)
        end
    return 'last token' m.m.tok 'scanPosition' ,
          strip(left(substr(m.m.src, m.m.pos), 40), 't') ,
        || '\n'res 'line' p':' strip(substr(m.m.src,
          , 1 + (p - m.m.lineX) * m.m.cutLen, m.m.cutLen), 't')
endProcedure scanWinInfo
/* copy scanWin end   *************************************************/
/* copy scanSql begin *************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanSql: procedure expose m.
parse arg inRdr
    return scanSqlReset(scanWin(inRdr), inRdr)

scanSqlReset: procedure expose m.
parse arg m, r, scanWin
    if scanWin \== 0 then
        call scanWinOpts m, 5, 2, 1, 72
    m.m.rdr = r
    return scanOpts(m, , '0123456789_' , '--')
endProcedure scanSqlReset

/*--- scan a sql token put class in m.sqlclass:
      'i': ordinary identifier   e.g. Name
      'd': delimited identifier  e.g. "Delimited"
      'q': qualified identifier  e.g. abc."efg"
      'u': integer units         e.g. 8G
      'n': number                e.g. -234 or .2e3
      's': string                e.g. 'abc''ef'
      '' : at end
         : any other character   e.g. ;
      ----------------------------------------------------------------*/
scanSqlClass: procedure expose m.
parse arg m, retSpace
    m.m.val = ''
    if scanSpace(m) & retSpace = 1 then do
        m.m.sqlClass = 'b'
        return 1
        end
    c2 = scanLook(m ,2)
    if scanString(m, "' x' X'") then do
        m.m.sqlClass = 's'
        if \abbrev(m.m.tok, "'") then
            m.m.val = x2c(m.m.val)
        end
    else if scanSqlQuId(m) then do
        if m.m.val.0 > 1 then
            m.m.sqlClass = 'q'
        else if abbrev(m.m.tok, '"') then
            m.m.sqlClass = 'd'
        else
            m.m.sqlClass = 'i'
        end
    else if scanSqlNum(m, 0)  then
        m.m.sqlClass = 'n'
    else if scanChar(m, 1) then
        m.m.sqlClass = m.m.tok
    else if scanEnd(m) then do
        m.m.sqlClass = ''
        return 0
        end
    else
        call scanErr m, 'cannot scan sql'
    return 1
endProcedure scanSqlClass

scanSqlSkipBrackets: procedure expose m.
parse arg m, br
    if br \== '' then
        nop
    else if scanLit(m, '(') then
        br = 1
    else
        return 0
    do while scanSqlClass(m) & m.m.sqlClass \== ';'
        if m.m.sqlClass = '('        then br = br + 1
        else if m.m.sqlClass \== ')' then iterate
        else if br > 1              then br = br - 1
        else                             return 1
        end
    call scanErr m, '; or eof, but' br 'closing ) expected'
endProcedure skipBrackets
/*--- scan an ordinary sql identifier e.g. abc, ef_12 ----------------*/
scanSqlId: procedure expose m.
parse arg m
    if \ scanName(m) then
        return 0
    m.m.val = translate(m.m.tok)
    return 1
endProcedure scanSqlId

/*--- scan a delimited or ordinay sql identifier ---------------------*/
scanSqlDeId: procedure expose m.
parse arg m
    if scanSqlId(m) then
        return 1
    if \ scanString(m, '"') then
        return 0
    m.m.val = strip(m.m.val, 't')
    return 1
endProcedure scanSqlDeId

/*--- scan a qualified sql identifier --------------------------------*/
scanSqlQuId: procedure expose m.
parse arg m
    res = ''
    rto = ''
    do qx=1
        if \ scanSqlDeId(m) then do
            if qx <> 1 then
                call scanErr m, 'id expected after .'
            return 0
            end
        m.m.val.qx = m.m.val
        res = res'.'m.m.val
        rto = rto'.'m.m.tok
        if \ scanLit(scanSkip(m), '.') then
            leave
        call scanSpace m
        end
    m.m.val.0 = qx
    m.m.val = substr(res, 2)
    m.m.tok = substr(rto, 2)
    return 1
endProcedure scanSqlQuId

/*--- scan a sql number ----------------------------------------------*/
scanSqlNum: procedure expose m.
parse arg m, checkEnd, noSp
    si = ''
    if noSp == 1 then
        call err 'deimplement noSp, use scanNum instead'
    if scanLit(m, '+', '-') then do
        si = m.m.tok
        call scanSpace m
        ch = scanLook(m, 2)
        if left(ch, 1) == '.' then
            ch = substr(ch, 2)
        if pos(left(ch, 1), '0123456789') < 1 then do
            call scanBack m, si
            m.m.val = ''
            return 0
            end
        end
    res = scanNum(m, checkEnd)
    m.m.val = si || m.m.val
    return res

endProcedure scanSqlNum

/*--- scan a sql number with a unit which may follow without space ---*/
scanSqlNumUnit: procedure expose m.
parse arg m, both, units
    if \ scanSqlNum(m, 0) then
        return 0
    nu = m.m.val
    sp = scanSpace(m)
    if scanSqlId(m) then do
        if units == '' | wordpos(m.m.val, units) > 0 then
            nu = nu m.m.val
        else if both | \ sp then
            call scanErr m, 'scanSqlNumUnit after' nu 'bad unit' m.m.val
        else
            call scanBack m, m.m.tok
        end
    else if both then
        call scanErr m, 'scanSqlNumUnit no unit after' nu
    else if \sp & pos(scanLook(m, 1), m.m.scanNameR) > 0 then
        call scanErr m, 'scanSqlNumUnit bad number end after' nu
    m.m.val = nu
    return 1
endProcedure scanSqlNumUnit

scan2Trgs: procedure expose m.
parse arg m, t1, t2
    cx = m.m.pos - 1
    do forever
        cx = verify(m.m.src, t1 || t2, 'm', cx + 1)
        if cx = 0 then do
            m.m.pos = length(m.m.src) + 1
            return ''
            end
        if pos(substr(m.m.src, cx, 1), t1) > 0 then do
            m.m.pos = cx
            return substr(m.m.src, cx, 1)
            end
        do ax=4 to arg()
            if arg(ax) == substr(m.m.src, cx, length(arg(ax))) then do
                m.m.pos = cx
                return arg(ax)
                end
            end
        end
endProcedure scan2Trgs

scanSql2Stop: procedure expose m.
parse arg m, sta, stop
    sta = substr(sta, 2)
    c1 = left(sta, 1)
    if c1 == 't' then do
        bx = m.m.pos
        c1 = scan2Trgs(m, '"'''stop, '-/', '--', '/*')
        if bx < m.m.pos then
            return 't'sta
        m.m.pos = m.m.pos + length(c1)
        c1 = left(c1, 1)
        sta = c1 || sta
        end
    if c1 == '/' then do
        bx = m.m.pos
        c1 = scan2Trgs(m, '"''', '-*', '--', '*/')
        if bx < m.m.pos then
            return '+'sta
        m.m.pos = m.m.pos + length(c1)
        if c1 == '*/' then
            return sta
        c1 = left(c1, 1)
        sta = c1 || sta
        end
    if abbrev(sta, "'") | abbrev(sta, '"') then do
         if scanStrEnd(m, c1) then
             return sta
         m.m.pos = 1 + length(m.m.src)
         return '+'sta
         end
    if pos(c1,  '-'stop) > 0 then do
        if c1 == '-' then
            m.m.pos = length(m.m.src) + 1
        return sta
        end
    if \ abbrev(sta, '/') then
        call err 'bad sta2' sta 'for scanSql2Stop'
    call err implement
        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
endProcedure scanSql2Stop

/* copy scanSql end   *************************************************/
/* copy scanUtil begin *************************************************
    scan db2 utility input statements using scan and a reader
**********************************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanUtilReader: procedure expose m.
parse arg m, inRdr
    call scanReader m, inRdr
    call scanOptions sc, , , '--'
    call scanUtilReset m
    return m
endProcedure scanUtilReader

scanUtilSql: procedure expose m.
parse arg inRdr
    m = scanSql(inRdr)
    call scanUtilReset m
    return m
endProcedure scanUtilReader

scanUtilReset: procedure expose m.
parse arg m
    m.m.utilBrackets = 0
    m.scanUtil =  'BACKUP CATENFM CATMAINT CHECK' ,
                  'COPY COPYTOCOPY DIAGNOSE EXEC LISTDEF LOAD' ,
                  'MERGECOPY MODIFY OPTIONS QUIESCE REBUILD' ,
                  'RECOVER REORG REPAIR REPORT RESTORE' ,
                  'RUNSTATS STOSPACE TEMPLATE UNLOAD'
    return m
endProcedure scanUtilReset
/*--- scan next token and put its class in m.sc.utilClass:
      'u' a utility name
      'n' a name
      '"' a quoted name
      "'" an apostroph'd string
      '.' a .
      ',' a ,
      'v' a value
      ''  at end
      ---------------------------------------------------------------*/
scanUtil: procedure expose m.
parse arg sc
    m.sc.utilSpace = scanSpace(sc)
    ty = '?'
    if scanLit(sc, '(') then do
        m.sc.utilBrackets = m.sc.utilBrackets + 1
        end
    else if scanLIT(sc, ')') then do
        m.sc.utilBrackets = m.sc.utilBrackets - 1
        if m.sc.utilBrackets < 0 then
           call scanErr sc, 'unmatched closing bracket )'
        end
    else if scanLit(sc, ',') then do
        end
    else if scanLit(sc, '.') then do
        end
    else if scanString(sc, "'") then do
        end
    else if scanString(sc, '"') then do
        end
    else if scanName(sc) then do
        m.sc.val = translate(m.sc.tok)
        if m.sc.utilBrackets > 0 then
            ty = 'n'
        else if 0 < wordPos(m.sc.val, m.scanUtil) then
            ty = 'u'
        else
            ty = 'n'
        end
    else if scanVerify(sc, ' (),''"', 'm') then do
        ty = 'v'
        m.sc.val = translate(m.sc.tok)
        end
    else if \scanEnd(sc) then do
            call scanErr sc, 'scanUtil stopped before end'
        end
    else do
        ty = ''
        m.sc.val = ''
        end
    if ty == '?' then
        m.sc.utilClass = left(m.sc.tok, 1)
    else
        m.sc.utilClass = ty
    return m.sc.utilClass
endProcedure scanUtil

/*--- scan a value or a bracketed list of values ---------------------*/
scanUtilValue: procedure expose m.
parse arg sc, remApo, nl
    if remApo = '' | rempApo = 0 then
        remApo = "nv"
    else if rempApo = 1 then
        remApo = "nv'"
    if '(' \== scanUtil(sc) then
         return scanUtilValueOne(sc, remApo)
    v = ''
    brx = m.sc.utilBrackets
    oLine = word(scanPos(sc), 1)
    do forever
        call scanUtil sc
        one = scanUtilValueOne(sc, remApo)
        if one == '' then
           call scanErr sc, 'eof in brackets'
        else if brx > m.sc.utilBrackets then
           return v
        nLine = word(scanPos(sc), 1)
        if \ m.sc.utilSpace then
            v = v || one
        else if nl \== '' & oLine <> nLine then
            v = v || nl || one
        else
            v = v' 'one
        oLine = nLine
        end
endProcedure scanUtilValue

scanUtilValueOne: procedure expose m.
parse arg sc, valTy
    if m.sc.utilClass == '' then
        return ''
    else if m.sc.utilClass == 'u' then
        call scanErr sc, 'util in scanUtilValueOne'
    if pos(m.sc.utilClass, valTy) > 0 then
        return m.sc.val
    else
        return m.sc.tok
endProcedure scanUtilValueOne

/*--- skip over nested brackets --------------------------------------*/
scanUtilSkipBrackets: procedure expose m.
parse arg m, br, doCat
    if br \== '' then
        lim = m.m.utilBrackets - br
    else if scanLit(m, '(') then do
        lim = m.m.utilBrackets
        m.m.utilBrackets = lim + 1
        end
    else
        return 0
    doCat = doCat == 1
    res = ''
    do while scanUtil(m) \== ''
        if m.m.utilBrackets <= lim then do
            if doCat then
                m.m.val = res
            return 1
            end
        if doCat then
            res = res m.m.tok
        end
    return scanErr(m, 'eof with' m.m.utilBrackets 'open (')
endProcedure skipBrackets

/*--- analyze a punch file write intoField to stdOut -----------------*/
scanUtilInto: procedure expose m.
parse arg m
    if m.m.utilBrackets \== 0 then
        call scanErr m, 'scanUtilInto with brackets' m.m.utilBrackets
  /*sc = scanUtilReader(m.j.in)
    call jOpen sc, 'r'
 */ do forever
        cl = scanUtil(m)
        if cl == '' then
            return 0
        if cl = 'n' & m.m.tok == 'INTO' then
            leave
        end
    if scanUtil(m) \== 'n' | m.m.tok \== 'TABLE' then
        call scanErr m, 'bad into table '
    if \ scanSqlQuId(scanSkip(m)) then
        call scanErr m, 'table name expected'
    if m.m.utilBrackets \== 0 then
        call scanErr m, 'into table in brackets' m.m.utilBrackets
    m.m.tb = m.m.val
    m.m.part = ''
    m.m.when = ''
    do forever
        cl = scanUtil(m)
        if cl == '' then
            call scanErr m, 'eof after into'
        if cl == 'n' & m.m.tok == 'PART' then do
            if scanUtil(m) == 'v' then
                m.m.part = m.m.val
            else
                call scanErr m, 'bad part'
            end
        else if cl == 'n' & wordPos(m.m.val, 'WHEN WORKDDN') > 0 then do
            call scanUtilSkipBrackets m
            end
        else if cl == '(' then do
           leave
           end
        end
    oX =  m.m.lineX
    oL =  overlay('', m.m.src, 1, m.m.pos-2)
    do while m.m.utilBrackets > 0
        call scanUtil m
        if oX \== m.m.lineX then do
            call out strip(oL, 't')
            oX =  m.m.lineX
            oL =  m.m.src
            end
        end
    call out left(oL, m.m.pos)
 /* call jClose sc
 */ return 1
endProcedure scanUtilInto
/* copy scanUtil end **************************************************/
/* copy pipe begin *****************************************************
***********************************************************************/
pipeIni: procedure expose m.
    if m.pipe.ini == 1 then
        return
    m.pipe.ini = 1
    call catIni
    call mapReset v
    m.v_with.0 = 0
    m.v_withMap = ''
    m.v_with.0.map = ''
    m.pipe.0 = 1
    m.pipe.1.in  = m.j.in
    m.pipe.1.out = m.j.out
    call pipe '+'
    return
endProcedure pipeIni

/*-------------------------------
  +-       push pop frame
  PYNFA    ouput: Parent saY Newcat File, Appendtofile
  psf|     input: parent string file oldOut
  old          --> new
  pipeBegin    --> pipe '+N'
  pipeBeLa f   --> pipe '+F'
  pipeLast     --> pipe 'P|'
  pipeLast f   --> pipe 'F|', f
  pipeEnd      --> pipe '-'
--------------------------------*/
pipe: procedure expose m.
parse arg opts, aO, aI
    ox = 1; oc = substr(opts, ox, 1)
    ax = m.pipe.0
    px = ax -1
    if oc == '-' then do
        if px < 2 then
            call err 'pipe pop empty'
        call jClose m.pipe.ax.out
        call jClose m.pipe.ax.in
        ax = px
        m.pipe.0 = ax
        px = ax-1
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if oc == '+' then do
        px = ax
        ax = ax+ 1
        m.pipe.0 = ax
        m.pipe.ax.in  = jOpen(m.pipe.px.in, '<')
        m.pipe.ax.out = jOpen(m.pipe.px.out, '>')
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    oOut = m.pipe.ax.out
    if pos(oc, 'NYPFA') > 0 then do
        call jClose oOut
        if oc == 'Y' then
            m.pipe.ax.out = jOpen(m.pipe.1.out, '>')
        else if oc == 'P' then
            m.pipe.ax.out = jOpen(m.pipe.px.out, '>')
        else if oc == 'N' then
            m.pipe.ax.out = jOpen(Cat(), '>')
        else if oc == 'F' then
            m.pipe.ax.out = jOpen(o2file(aO), '>')
        else if oc == 'A' then
            m.pipe.ax.out = jOpen(o2file(aO), '>>')
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if pos(oc, 's|fp') > 0 then do
        call jClose m.pipe.ax.in
        if oc == 'p' then
            m.pipe.ax.in = jOpen(m.pipe.px.in, '<')
        else if oc == '|' then
            m.pipe.ax.in = jOpen(oOut, '<')
        else if oc == 'f' then do
            if arg() <= 3 then
                m.pipe.ax.in = jOpen(o2file(aI), '<')
            else do
                ct = jOpen(Cat(), '>')
                do lx = 3 to arg()
                    call jWriteAll ct, arg(lx)
                    end
                m.pipe.ax.in = jOpen(jclose(ct), '<')
                end
            end
        else if arg() <= 3 then
            m.pipe.ax.in = jOpen(jBuf(aI), '<')
        else do
            bu = jOpen(jBuf(), '>')
            do lx = 3 to arg()
                call jWrite bu, arg(lx)
                end
            m.pipe.ax.in = jOpen(jclose(bu), '<')
            end
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if oc \== ' ' then
        call err 'implement' substr(opts, ox) 'in pipe' opts
    m.j.in  = m.pipe.ax.in
    m.j.out = m.pipe.ax.out
    return
endProcedure pipe

/*--- write all from rdr (rsp in) to out, not lazy ----------------*/
pipeWriteNow: procedure expose m.
    parse arg rdr
    call jWriteNow m.j.out, if(rdr == '', m.j.in, rdr)
    return
endProcedure pipeWriteNow

/*--- write all from rdr (rsp in) to out, possibly lazy -----------*/
pipeWriteAll: procedure expose m.
parse arg rdr
    call jWriteAll m.j.out, if(rdr == '', m.j.in, rdr)
    return
endProcedure pipeWriteAll

pipePreSuf: procedure expose m.
parse arg le, ri
    do while in(v)
        call out le || m.v || ri
        end
    return
endProcedure pipePreSuf

vIsDefined: procedure expose m.
parse arg na
    return   '' \== mapValAdr(v, na)
endProcedure vIsDefined

vWith: procedure expose m.
parse arg fun, o
    if fun == '-' then do
        tBe = m.v_with.0
        tos = tBe - 1
        if tos < 0 then
            call err 'pop empty withStack'
        m.v_with.0 = tos
        m.v_withMap = m.v_with.tos.map
        return m.v_with.tBe.obj
        end
    tos = m.v_with.0
    if fun == '=' then do
        if o == '' & m.v_with.tos.obj == '' then
            return
        if o \== '' & m.v_with.tos.obj \== '' then
            if objClass(o) == objClass(m.v_with.tos.obj) then do
                m.v_with.tos.obj = o
                return
                end
        par = tos - 1
        end
    else if fun == '+' then do
        par = tos
        tos = tos + 1
        m.v_with.0 = tos
        end
    m.v_with.tos.obj = o
    if par > 0 then
        key = m.v_with.par.classes
    else
        key = ''
    if o \== '' then
        key = strip(key objClass(o))
    m.v_with.tos.classes = key
    if symbol('m.v_withManager.key') == 'VAR' then do
        m.v_with.tos.map = m.v_withManager.key
        m.v_withMap = m.v_withManager.key
        return
        end
    m = mapNew()
    m.v_with.tos.map = m
    m.v_withMap = m
    m.v_withManager.key = m
    do kx=1 to words(key)
        c1 = word(key, 1)
        ff = oClaMet(c1, 'oFlds')
        do fx=1 to m.ff.0
            mNm = m || m.ff.fx
            f2cNm = 'F2C'm.ff.fx
            m.mNm = kx m.c1.f2cNm
            end
        do fx=1 to m.c1.stms.0
            mNm = m || c1.stms.fx
            m.mNm = kx m.class.classV
            end
        do fx=1 to m.c1.StFl.0
            f2cNm = 'F2C'm.c1.StFl.fx
            mNm = m || m.c1.StFl.fx
            m.mNm = kx m.c1.f2cNm
            end
        end
    return
endProcedure vWith

vGet: procedure expose m.
parse arg na
    parse value vAdr(na) with flag 2 adr
    if flag == 'm' then
        return o2string(mapGet(v, adr))
    else if flag == 's' then
        return m.adr
    else if flag == 'o' then
        return o2String(m.adr)
    return err('vGet flag' flag)
endProcedure vGet

vGetO: procedure expose m.
parse arg na
    parse value vAdr(na) with flag 2 adr
    if flag == 'm' then
        return mapGet(v, adr)
    else if flag == 's' then
        return s2o(m.adr)
    else if flag == 'o' then
        return m.adr
    return err('vGetO flag' flag)
endProcedure vGetO

vPut: procedure expose m.
parse arg na, val
    parse value vAdr(na) with flag 2 adr
    if flag == 'm' then
        call mapPut v, adr, s2o(val)
    else if flag == 's' then
        m.adr = val
    else if flag == 'o' then
        m.adr = s2o(val)
    else
        return err('vPut flag' flag)
    return val
endProcedure vPut

vPutO: procedure expose m.
parse arg na, val
    parse value vAdr(na) with flag 2 adr
    if flag == 'm' then
        call mapPut v, adr, val
    else if flag == 's' then
        m.adr = o2String(val)
    else if flag == 'o' then
        m.adr = val
    else
        return err('vPutO flag' flag)
    return val
endProcedure vPut

vAdr: procedure expose m.
parse arg na
    cx = pos('&', na)
    if cx = 1 | na = '' then
        call err 'vAdr bad name' na
    if cx < 1 then
        oNm = na
    else
        oNm = left(na, cx-1)
    if m.v_withMap \== '' then do
        mp = m.v_withMap
        if symbol('m.mp.oNm') == 'VAR' then do
            parse var m.mp.oNm wx cl
            end
        else do
            sNm = vStemPath(oNm)
            if symbol('m.mp.sNm') == 'VAR' then
                parse var m.mp.sNm wx cl
            end
        if symbol('wx') == 'VAR' then do
            o = m.v_with.wx.obj'.'oNm
            if cx < 1 then
                if cl == m.class.classV then
                    return 's'o
                else
                    return 'o'o
            end
        end
    if cx < 1 then
        return 'm'na
    if symbol('wx') \== 'VAR' then
        o = mapGet(v, oNm)
    cx = cx + 1
    do forever
        if o = '' then
            call err 'vAdr null at' left(na, cx-1) 'in name' na
        cy = pos('&', na, cx)
        if cy < cx then
            leave
        if cy = cx then
            a = o
        else
            a = o'.'substr(na, cx, cy-cx)
        if symbol('m.a') \== 'VAR' then
            call err 'vAdr undefined' a 'at' left(na, cy-1) 'in name' na
        o = m.a
        cx = cy+1
        end
    f = substr(na, cx)
    if f \== '' then
        f = '.'f
    a = o || f
    cl = objClass(o, '')
    if cl == '' then
        return = 'c'a
    f2cF = 'F2C'f
    if symbol('m.cl.f2cF') \== 'VAR' then do
        f2cF = 'F2C'vStemPath(f)
        if symbol('m.cl.f2cF') \== 'VAR' then
            return 'f'a
        end
    if m.cl.f2cF == m.class.classV then
        return 's'a
    else
        return 'o'a
endProcedure vAdr

/*--- resolve stem indexes within variable path ---------------------*/
vStemPath: procedure expose m.
parse arg f
    cx = 0
    do until cx < 1
     cy = verify(f, '0123456789', 'n', cx+1)
        if cy < 1 then do
            if substr(f, cx+1) \== 0 then
                f = left(f, cx)'*'
            return f
            end
        if substr(f, cy, 1) \== '.' | cy=cx+1 then do
            cx = pos('.', f, cy)
            end
        else if substr(f, cx+1, cy-cx) == 0 then do
            call err '0 within name' f
            end
        else do
            f = left(f,cx)'*'substr(f,cy)
            cx = pos('.', f, cx+2)
            end
        end
    return f
endProcedure vStemPath

vRead: procedure expose m.
parse arg na
    return in("V."na)

vReadO: procedure expose m.
parse arg na
    res = inO()
    if res == '' then
        return 0
    call vPutO na, res
    return 1
endProcedure vReadO

vHasKey: procedure expose m.
parse arg na
    return mapHasKey(v, na)

vRemove: procedure expose m.
parse arg na
    return mapRemove(v, na)
/* copy pipe end ******************************************************/
/* copy cat  begin ****************************************************
***********************************************************************/
/*--- create a new cat -----------------------------------------------*/
cat: procedure expose m.
    m = oNew('Cat') /* calls catReset */
    do ax=1 to arg()
        call catWriteAll m, arg(ax)
        end
    return m
endProcedure cat

catReset: procedure expose m.
parse arg m
    m.m.RWs.0 = 0
    m.m.catWr = ''
    m.m.catRd = ''
    m.m.catIx = -9e9
    m.m.catKeepOpen = ''
    return m
endProcedure catReset

catClose: procedure expose m.
parse arg m
    if m.m.catWr \== '' then do
        call jClose m.m.catWr
        call mAdd m'.RWS', m.m.catWr
        m.m.catWr = ''
        end
    if m.m.catRd \== '' then do
        call jClose m.m.catRd
        m.m.catRd = ''
        end
    m.m.catIx = -9e9
    return m
endProcedure catClose

catOpen: procedure expose m.
parse arg m, oo
    if oo == m.j.cRead then do
        m.m.catIx = 0
        call catNextRdr m
        m.m.jReading = 1
        end
    else if oo == m.j.cWri | oo == m.j.cApp then do
        if oo == m.j.cWri then
            m.m.RWs.0 = 0
        m.m.catIx = -9e9
        m.m.jWriting = 1
        end
    else do
        call err 'catOpen('m',' oo') bad opt'
        end
    return m
endProcedure catOpen

/*--- return and open next reader ------------------------------------*/
catNextRdr: procedure expose m.
parse arg m
    if m.m.catRd \== '' then
        call jClose m.m.catRd
    cx = m.m.catIx + 1
    m.m.catIx = cx
    if cx > m.m.RWs.0 then do
        m.m.catRd = ''
        return 0
        end
    m.m.catRd = m.m.RWs.cx
    if cx = word(m.m.catKeepOpen, 1) then
        m.m.catKeepOpen = subWord(m.catKeepOpen, 2)
    else
        call jOpen m.m.catRd , m.j.cRead
    return 1
endProcedure catNextRdr

catReadO: procedure expose m.
parse arg m
    do while m.m.catRd \== ''
        res = jReadO(m.m.catRd)
        if res \== '' then
            return res
        call catNextRdr m
        end
    return ''
endProcedure catReadO

catWrite: procedure expose m.
parse arg m, line
    if m.m.catWr == '' then
        m.m.catWr = jOpen(jBuf(), m.j.cWri)
    call jWrite m.m.catWr, line
    return
endProcedure catWrite

catWriteO: procedure expose m.
parse arg m, var
    if m.m.catWr == '' then
        m.m.catWr = jOpen(jBuf(), m.j.cWri)
    call jWriteO m.m.catWr, var
    return
endProcedure catWriteO

/*--- write contents of a reader to cat
          or keep it for later reading -------------------------------*/
catWriteAll: procedure expose m.
parse arg m
    if m.m.catWr \== '' then do
        call mAdd m'.RWS', jClose(m.m.catWr)
        m.m.catWr = ''
        end
    do ax=2 by 1 to arg()
        r = o2File(arg(ax))
        call mAdd m'.RWS', r
        if m.r.jReading then do
            m.m.catKeepOpen = m.m.rws.0 m.m.catKeepOpen
            call jOpen r, m.j.cRead
            end
        end
    return
endProcedure catWriteAll

/*--- create a reader/WriteO for an external file --------------------*/
file: procedure expose m.
parse arg str
    return oNew('File', str)
endProcedure file

fileChild: procedure expose m.
parse arg m, name, opt
    interpret objMet(m, 'fileChild')
endProcedure fileChild

fileRm: procedure expose m.
parse arg m
    interpret objMet(m, 'fileRm')
    return
endProcedure fileRm

filePath: procedure expose m.
parse arg m
    interpret objMet(m, 'filePath')
endProcedure filePath

fileIsFile: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsFile')
endProcedure fileIsFile

fileIsDir: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsDir')
endProcedure fileIsDir

fileMkDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileMkDir')
    return
endProcedure fileRm

fileRmDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileRmDir')
    return
endProcedure fileRm

/*--- create a reader/WriteO for the filelist of a directory----------*/
fileList: procedure expose m.
parse arg m, opt
    str = oIfStr(m, '')
    if str == '' then
        return oNew('FileList', filePath(m),  opt)
    else
        return oNew('FileList', dsn2Jcl(str),  opt)
endProcedure fileList

catIni: procedure expose m.
    if m.cat.ini == 1 then
        return
    m.cat.ini = 1
    call errIni
    call jIni
    call classNew "n Cat u JRWO", "m",
        , "jOpen  call catOpen m, opt",
        , "jReset call catReset m, arg",
        , "jClose call catClose m",
        , "jReadO return catReadO(m)",
        , "jWrite call catWrite m, line; return",
        , "jWriteO call catWriteO m, var; return",
        , "jWriteAll call catWriteAll m, rdr; return"

    call classAddMet m.class.classV, 'o2File return file(m.m)'
    call classAddMet m.class.classW, 'o2File return file(substr(m,2))'
    if m.err.os == 'TSO' then
        call fileTsoIni
    else if m.err.os == 'LINUX' then
        call fileLinuxIni
    else
        call err 'file not implemented for os' m.err.os
    return
endProcedure catIni
/* copy cat  end   ****************************************************/
/* copy mail begin ***************************************************/
mailHead: procedure expose m.
parse arg m, subj, rec, snd
    m.m.1 = 'SUBJECT   ' subj
    m.m.2 = 'RECPLIST  ' rec
    m.m.3 = 'SENDERID  ' if(snd=='', userid(), snd)
    m.m.4 = 'SENDERIDCC N'
    m.m.5 = 'DOCTEXT'
    m.m.0 = 5
    return m
endProcedure mailHead

mailSend: procedure expose m.
parse arg m, dsn
    call mAdd m, 'DOCTEXTEND'
    if dsn == '' then
        call dsnAlloc 'dd(iemapi) new  ::v255'
    else
        call dsnAlloc 'dd(iemapi) shr' dsn
    call writeDD iemapi, 'M.'m'.'
    call tsoClose iemapi
    call dsnAlloc 'dd(iemlog) sysout(*)'
    call adrTso "call *(os3550)", '*'
    if rc \== 0 then
        say 'error os3550 rc='rc 'mail not sent|||||'
    call tsoFree iemlog iemapi
    return 0
endProcedure mailSend
/* copy mail end *****************************************************/
/* copy fiLinux begin *************************************************/
/*--- send ggShCmd to shell bash,
        fail if rc <> 0 or not listed in ggRet -----------------------*/
adrSh: procedure expose m.  /* really no need for variables???? */
    parse arg ggShCmd, ggRet
    address 'bash' ggShCmd
    if rc == 0                     then return 0
    else if ggRet == '*'           then return rc
    else if wordPos(rc, ggRet) > 0 then return rc
    else
        call err 'adrSh rc' rc 'for' ggShCmd
endProcedure adrSh

fileLinuxReset: procedure expose m.
parse arg m, nm
    m.m.spec = nm
    if abbrev(nm, '&') then do
        if nm == '&in' then do
            m.m.stream = .input
            m.m.jReading = 1
            end
        else if nm == '&out' then do
            m.m.stream = .output
            m.m.jWriting = 1
            end
        else do
            call err 'bad spec' nm
            end
        end
    else do
        m.m.stream = .Stream%%new(nm)
        m.m.stream%%init(m.m.stream%%qualify)
        end
    return m
endProcedure fileLinuxReset

fileLinuxOpen: procedure expose m.
parse arg m, opt
    if opt == m.j.cRead then do
        res = m.m.stream%%open(read shareread)
        m.m.jReading = 1
        end
    else do
        if opt == m.j.cApp then
            res = m.m.stream%%open(write append)
        else if opt == m.j.cWri then
            res = m.m.stream%%open(write replace)
        else
            call err 'fileLinuxOpen('m',' opt') with bad opt'
        m.m.jWriting = 1
        end
    if res \== 'READY:' then
        call err 'fileLinuxOpen fails' res':' opt ,
        "'"m.m.stream%%qualify"'"
    return m
endProcedure fileLinuxOpen

fileLinuxClose:
parse arg m
    res = m.m.stream%%close
    if res \== 'READY:' then
        call err 'fileLinuxClose' res':' m.m.stream%%qualify
    return m
endProcedure fileLinuxClose

fileLinuxRead: procedure expose m.
parse arg m, var
    res = m.m.stream%%lineIn
    if res == '' then
        if m.m.stream%%state \== 'READY' then
            return 0
    m.var = res
    m.o.o2c.var = m.class.classV
    return 1
endProcedure fileLinuxRead

fileLinuxWrite: procedure expose m.
parse arg m, line
    if m.m.stream%%lineOut(line) then
        call err 'fileLinuxWrite'
    return
endProcedure fileLinuxWrite

fileLinuxRmDir: procedure expose m.
parse arg m, opt
    if opt == '' then
        return adrSh('rmdir' m.m.spec)
    else if opt == '-r' then
        return adrSh('rm -r' m.m.spec)
    else
        call err 'bad opt' opt 'in fileLinuxRmDir'
endProcedure fileLInuxRmDir

fileLinuxListReset: procedure expose m.
parse arg m, m.m.spec, o
    if o == 'r' then
        m.m.opt = 'S'
    else if o == '' then
        m.m.opt = ''
    else
        call err 'bad opt' o 'in fileLinuxListReset'
    m.m.rx = 'closed'
    return m
endProcedure fileLinuxListReset

fileLinuxListOpen: procedure expose m.
parse arg m
    if m \== translate(m) then
        call err 'bad m for fileLinuxList:' m
    if m.m.opt == '' then
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST')
    else
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST', m.m.opt)
    if rc \== 0 then
        call err 'rc' rc 'in sysFileTree('m.m.spec', m.'m'.LIST)'
    m.m.rx = 0
    m.m.jReading = 1
    return m
endProcedure fileLinuxListOpen

fileLinuxListRead: procedure expose m.
parse arg m, var
    x = m.m.rx + 1
    if x > m.m.list.0 then
        return 0
    m.var = substr(m.m.list.x, 43)
    m.m.rx = x
    call oMutate var, m.class.classV
    return 1
endProcedure fileLinuxListRead

fileLinuxIni: procedure expose m.
    if m.fileLinux.ini == 1 then
        return
    m.fileLinux.ini = 1
    m.file.sep = '/'
    call jIni
    call classNew "n File u JRW", "m",
        , "jReset call fileLinuxReset m, arg",
        , "jOpen  call fileLinuxOpen m, opt",
        , "jClose call fileLinuxClose m",
        , "jRead return fileLinuxRead(m, var)",
        , "jWrite call fileLinuxWrite m, line",
        , "jWriteO call jWrite m, o2String(var)",
        , "filePath return m.m.stream%%qualify",
        , "fileIsFile return sysIsFile(m.m.stream%%qualify)" ,
        , "fileIsDir return sysIsFileDirectory(m.m.stream%%qualify)" ,
        , "fileChild return file(m.m.stream%%qualify'/'name)",
        , "fileRm return adrSh(m.m.spec)",
        , "fileMkDir return adrSh('mkdir' m.m.stream%%qualify)" ,
        , "fileRmDir return fileLinuxRmDir(m, opt)"
    call classNew "n FileList u JRW", "m",
        , "jReset call fileLinuxListReset m, arg, arg2",
        , "jOpen  call fileLinuxListOpen m, opt",
        , "jClose m.m.rx = 'closed'",
        , "jRead return fileLinuxListRead(m, var)"
    return
endProcedure fileLinuxIni
/* copy fiLinux end   *************************************************/
/* copy fileTso begin *************************************************/
fileTsoReset: procedure expose m.
parse arg m, sp
    m.m.readIx = 'c'
    if symbol('m.m.defDD') \== 'VAR' then do
        m.m.defDD = 'CAT*'
        m.fileTso.buf = m.fileTso.buf + 1
        m.m.buf = 'FILETSO.BUF'm.fileTso.buf
        m.m.spec = sp
        end
    if sp \== '' then do
        m.m.spec = dsnSpec(sp)
        rr = translate(subword(m.m.spec, 4))
        m.m.stripT = \ (pos(':V', rr) > 0 | pos('RECFM(V', rr) > 0)
        end
    return m
endProcedure fileTsoReset

fileTsoOpen: procedure expose m.
parse arg m, opt
    buf = m.m.buf
    if opt == m.j.cRead then do
        aa = dsnAlloc(m.m.spec, 'SHR', m.m.defDD)
        aDD = word(aa, 1)
        aDsn = m.tso_dd.aDD
        if pos('(', aDsn) > 0 & pos('/', aDsn) < 1 then
            /* avoid messy tso message when read nonExistant member */
            if sysDsn("'"m.tso_dd.aDD"'") <> 'OK' then
                call err 'cannot read' m.tso_dd.aDD':',
                               sysDsn("'"m.tso_dd.aDD"'")
        call tsoOpen word(aa, 1), 'R'
        m.m.jReading = 1
        m.buf.0 = -1
        m.m.readIx = 0
        end
    else do
        if opt == m.j.cApp then
            aa = dsnAlloc(m.m.spec, 'MOD', m.m.defDD)
        else if opt == m.j.cWri then
            aa = dsnAlloc(m.m.spec, 'OLD', m.m.defDD)
        else
            call err 'fileTsoOpen('m',' opt') with bad opt'
        call tsoOpen word(aa, 1), 'W'
        m.m.jWriting = 1
        m.buf.0 = 0
        m.m.readIx = 'w'
        end
    parse var aa m.m.dd m.m.free
    m.m.dsn = mGet('TSO_DD.'m.m.dd)
    return m
endProcedure fileTsoOpen

fileTsoClose: procedure expose m.
parse arg m
    buf = m.m.buf
    if m.m.readIx \== 'c' then do
        if m.m.readIx == 'w' & m.buf.0 > 0 then
            call writeDD m.m.dd, 'M.'BUF'.'
        call tsoClose m.m.dd
        call tsoFree  m.m.free
        end
    m.buf.0 = 'closed'
    m.m.readIx = 'c'
    m.m.free  = ''
    m.m.dd    = ''
    return m
endProcedure fileTsoClose

fileTsoRead: procedure expose m.
parse arg m, var
    ix = m.m.readIx + 1
    buf = m.m.buf
    if ix > m.buf.0 then do
        res = readDD(m.m.dd, 'M.'buf'.')
        if \ res then
            return 0
        ix = 1
        end
    m.m.readIx = ix
    m.var = m.buf.ix
    call oMutate var, m.class.classV
    return 1
endProcedure fileTsoRead

fileTsoWrite: procedure expose m.
parse arg m, var
    buf = m.m.buf
    ix = m.buf.0 + 1
    m.buf.0 = ix
    if m.m.stripT then
        m.buf.ix = strip(var, 't')
    else
        m.buf.ix = var
    if ix > 99 then do
        call writeDD m.m.dd, 'M.'buf'.'
        m.buf.0 = 0
        end
    return
endProcedure fileTsoWrite

fileTsoWriteO: procedure expose m.
parse arg m, var
    if objClass(var, m.class.classV) == m.class.classV then do
        call fileTsoWrite m, m.var
        return
        end
    call err 'fileTsoWriteO('m',' var') cannot write objects of class',
                              objClass(var)
endProcedure fileTsoWriteO

fSub: procedure expose m.
    return file('.sysout(T) writer(intRdr)')
endProcedure fSub

fEdit: procedure expose m.
parse arg spec, vw
    if spec == '' then
        spec = 'new ::f'
    else if abbrev(spec, '::') then
        spec = 'new' spec
    else if abbrev(spec, ':') then
        spec = 'new' ':'spec
    f  = oNew('FileEdit', spec)
    m.f.editType = if(abbrev(translate(vw), 'V'), 'view', 'edit')
    return f
endProcedure fEdit

fileTsoEditClose: procedure expose m.
parse arg m
    dsn = m.m.dsn
    if dsn \== '' then do
        call fileTsoClose m
        call adrIsp m.m.editType "dataset('"dsn"')", 4
        return
        end
    fr = m.m.free
    dd = m.m.dd
    m.m.free = ''
    call fileTsoClose m
    call adrIsp "LMINIT DATAID(lmmId) ddName("dd") ENQ(SHRW)"
    eRc = adrIsp(m.m.editType "dataid("lmmId")", '*')
    lRc = adrIsp("LMFree DATAID("lmmId")", '*')
    call tsoFree fr
    if (eRc \== 0 & eRc \== 4) | lRc \== 0 then
        call err m.m.editType 'rc' eRc', lmFree rc' lRc
    return
endProcedure fileTsoEditClose

fileTsoIni: procedure expose m.
    if m.fileTso.ini == 1 then
        return
    m.fileTso.ini = 1
    m.file.sep = '.'
    m.fileTso.buf = 0
    call jIni
    um = "call err 'for tso undefined method'"
    call classNew "n File u JRW", "m",
        , "jOpen  call fileTsoOpen m, opt",
        , "jReset call fileTsoReset m, arg",
        , "jClose call fileTsoClose m",
        , "jRead return fileTsoRead(m, var)",
        , "jWrite call fileTsoWrite m, line",
        , "jWriteO call fileTsoWriteO m, var",
        , "filePath return word(m.m.spec, 1)"           ,
        , "fileIsFile" um "'fileIsFile'"      ,
        , "fileIsDir   return 1"              ,
        , "fileChild   return file(word(m.m.spec, 1)'.'name opt)",
        , "fileRm"     um "'fileRm'"          ,
        , "fileMkDir"  ,
        , "fileRmDir"  um "'fileRmDir'"
 /*     , "filePath return m.m.stream%%qualify",
        , "fileIsFile return sysIsFile(m.m.stream%%qualify)" ,
        , "fileIsDir return sysIsFileDirectory(m.m.stream%%qualify)" ,
        , "fileChild return file(m.m.stream%%qualify'/'name)",
        , "fileRm return adrSh(m.m.spec)",
        , "fileMkDir return adrSh('mkdir' m.m.stream%%qualify)" ,
        , "fileRmDir return fileLinuxRmDir(m, opt)" */
    call classNew "n FileList u JRW", "m",
        , "jReset if arg2 == 'r' then m.m.dsnMask=arg'.**';",
                                "else m.m.dsnMask=arg'.*';",
        , "jOpen  call csiOpen m, m.m.dsnMask",
        , "jClose" ,
        , "jRead return csiNext(m, var)"
    call classNew "n FileEdit u File", "m",
        , "jClose call fileTsoEditClose m"
    return
endProcedure fileTsoIni
/* copy fileTso end   *************************************************/
/* copy sqlDiv begin **************************************************/
/*--- generate the format m for a sql cx as specified in sp
          use the information from the sqlDa -------------------------*/
sqlFTabReset: procedure expose m.
parse arg ff, cx, tBef, tAft, m.ff.maxChar, m.ff.blobMax, m.ff.maxDec
    if m.ff.maxChar == '' then
        m.ff.maxChar = 32
    if m.ff.blobMax == '' then
        m.ff.blobMax = 200
    bf = '%-'max(m.ff.blobMax, 4)'C'
    m.ff.flds = ''
    m.ff.sqlX = cx
    call fTabReset ff, tBef, tAft, '-'
    m.ff.sql2fmt.384 = '%-10C' /* date    */
    m.ff.sql2fmt.388 = '%-8C'  /* time    */
    m.ff.sql2fmt.392 = '%-26C' /* timestamp */
    m.ff.sql2fmt.400 = 'c'     /* graphic string */
    m.ff.sql2fmt.404 = bf      /* BLOB           */
    m.ff.sql2fmt.408 = bf      /* CLOB           */
    m.ff.sql2fmt.412 = bf      /* DBCLOB         */
    m.ff.sql2fmt.448 = 'c'     /* varchar        */
    m.ff.sql2fmt.452 = 'c'     /* char           */
    m.ff.sql2fmt.452 = 'c'     /* long varchar   */
    m.ff.sql2fmt.460 = 'c'     /* null term. string */
    m.ff.sql2fmt.464 = 'c'     /* graphic varchar   */
    m.ff.sql2fmt.468 = 'c'     /* graphic char      */
    m.ff.sql2fmt.472 = 'c'     /* long graphic varchar   */
    m.ff.sql2fmt.480 = '%-7e'  /* float                  */
    m.ff.sql2fmt.484 = 'd'     /* packed decimal         */
    m.ff.sql2fmt.492 = '%20i'  /* bigInt                 */
    m.ff.sql2fmt.496 = '%11i'  /* int                    */
    m.ff.sql2fmt.500 = '%6i'   /* smallInt               */
    m.ff.sql2fmt.904 = '%-34H' /* rowID 17 Byte Binary   */
    return
endProcedure sqlFTabReset
/*--- set a defaultFormat for type tx in fTab ff ---------------------*/
sqlFTabDef: procedure expose m.
parse arg ff, tx, m.ff.sql2fmt.tx
return ff

sqlFTabAdd: procedure expose m.
parse arg m, c1 aDone, f1, l1
    if symbol('m.m.set.c1') == 'VAR' then do
        sx = m.m.set.c1
        if word(m.m.set.sx, 1) == c1 & sx <= m.m.set.0 then do
            parse var m.m.set.sx c1 aDone
            f1 = m.m.set.sx.fmt
            l1 = m.m.set.sx.label
            end
        end
    cx = m.m.sqlX
    kx = sqlCol2kx(cx, c1)
    if kx == '' then
        call err 'colName not found' c1
    do tx=2 to arg()-3
        if arg(tx+3) \== '' then
            call fTabAddTit m, tx, arg(tx+3)
        end
    if f1 \== '' then do
        if right(f1, 1) \== ' ' then
            f1 = f1' '
        return fTabAdd(m, c1 aDone, f1, l1)
        end
    ty = m.sql.cx.d.kx.sqlType
    le = m.sql.cx.d.kx.sqlLen
    withNulls = ty // 2
    ty = ty - withNulls
    if symbol('m.m.sql2fmt.ty') <> 'VAR' then
        call err 'sqlType' ty 'col' c1 'not supported'
    f2 = m.m.sql2fmt.ty
    if f2 == 'c' then
        f2 = '%-'min(le, m.m.maxChar)'C'
    else if f2 == 'd' then do
        trace ?r
        pr =  le % 256
        de =  le // 256
        f2 = '%'pr'.'de'i'
        end
    if \ abbrev(f2, '%') then
        call err 'sqlType' ty 'col' c1 'bad format' f2
    return fTabAdd(m, c1 aDone, f2' ', l1)
endProcedure sqlFTabAdd

sqlFTabOthers: procedure expose m.
parse arg m, doNot
    cx = m.m.sqlX
    do kx=1 to m.sql.cx.d.sqlD
        c1 = m.sql.cx.col.kx
        wx = wordPos(c1, m.m.cols)
        if (wx < 1 | m.m.wx.done \== 1) & wordPos(c1, doNot) < 1 then
            call sqlFTabAdd m, m.sql.cx.col.kx
        end
    return
endProcedure sqlFTabOthers

sqlFTab: procedure expose m.
parse arg m
    call fTabBegin m
    do while sqlRxFetch(m.m.sqlX, 'sqlFTab')
        call out f(m.m.fmt, 'sqlFTab')
        end
    return fTabEnd(m)
endProcedure sqlFTab

sqlFTabCol: procedure expose m.
parse arg m
    do rx=1 while sqlRxFetch(m.m.sqlX, 'sqlFTab')
        call out left('--- row' rx '', 100, '-')
        call fTabCol m, 'sqlFTab'
        end
    call out left('--- end of' (rx-1) 'rows ', 100, '-')
    return
endProcedure sqlFTabCol

sqlCatTb: procedure expose m.
parse arg ty gOnly, wh, ord, fTab, paPlus
    tb = tkrTable(, ty)
    if gOnly == 1 then
        edFun = ''
    else
        edFun = tkrTable(, ty, 'e')
    cx = 1
    ft = 'ft'm.tb.alias
    call sqlFTabReset ft, cx, 'c 1', '1 c', 12, if(fTab, , 2000)
    call sqlFTabDef      ft, 492, '%7e'
    call FTabSet         ft, 'CONTOKEN'  , '%-16H'
    call FTabSet         ft, 'DCONTOKEN'  , '%-16H'
    call FTabSet         ft, 'DBNAME'    , '%-8C', 'db'
    call FTabSet         ft, 'DSNAME'    , '%-44C'
    call FTabSet         ft, 'DSNUM'     , '%5i'
    call FTabSet         ft, 'PARTITION' ,'%5i' , 'part'
    call FTabSet         ft, 'PIT_RBA'   , '%-12H'
    call FTabSet         ft, 'RBA1'      , '%-12H'
    call FTabSet         ft, 'RBA2'      , '%-12H'
    call FTabSet         ft, 'START_RBA' ,'%-12H'
    call FTabSet         ft, 'TSNAME'    , '%-8C', 'ts'
    call FTabSet         ft, 'VERSION'   , '%-28C'
    if edFun \== '' then do
        interpret 'sq =' edFun'(ft, tb, wh, ord)'
        end
    else do
        cl = sqlColList(m.tb.table, m.ft.blobMax)
        sq = 'select' cl tkrTable( , tb, 'f') wh ,
             'order by' if(ord=='', m.tb.order, ord)
        call sqlQuery cx, sq
        call sqlFTabOthers ft
        call sqlCatTbVl ft, tb
        end
    if fTab then
        call sqlFTab ft
    else
        call sqlFTabCol ft
    call sqlClose cx
    call sqlCatTbTrailer space(m.TKR.path paPlus, 1), sq
    return 0
endProcedure sqlCatTb

sqlCatTbVlsep:
    return '+++'

sqlCatTbVl: procedure expose m.
parse arg ft, tb, sep
    if sep == '' then
        sep = sqlCatTbVLsep()
    if m.tb.vlKey == '' then
        return
    ky = m.tb.vlKey
    ff = ''
    tt = ''
    do kx=1 to m.ky.0
        tt = tt || sep || m.ky.kx.col
        ff = ff || sep'@'m.ky.kx.col'%S'
        end
    call fTabAdd ft, substr(tt,length(sep)+1), substr(ff,length(sep)+1)
    return
endProcedure sqlCatTbVl

sqlCatTbTrailer: procedure expose m.
parse arg pa, sq
    ox = lastPos(' order by ', sq)
    if ox < 1 then
        call err 'order by not found in' sq
    ord = substr(sq, ox+10)
    sq = left(sq, ox-1)
    sqUp = translate(sq)
    call out ''
    call out 'dbSys:' m.sql_dbSys
    call out 'path:' pa
    int = ''
    iNx = '  '
    br = ''
    cx = 1
    stops = '(select from where'
    do while cx < length(sq)
        nx = -1
        do sx=1 to words(stops)
            n2 = pos(word(stops, sx), sq, cx+1)
            if n2 > cx & (nx < 1 | n2 < nx) then
                nx = n2
            end
        if nx < 0 then
            leave
        call out int || substr(sq, cx, nx-cx)
        int = iNx
        if substr(sq, nx, 3) = '(se' then do
            iNx = iNx'  '
            br = left(br, length(int))')'
            end
        cx = nx
        end
    ll =  strip(substr(sq, cx))
    bq = strip(br)
    do while bq <> ''
        if right(bq, 1) \== ')' | right(ll, 1) \== ')' then
           call err 'missing ) bq:' bq', ll:' ll
        ll = strip(left(ll, length(ll) - 1))
        bq = strip(left(bq, length(bq) - 1))
        end
    call out int || ll
    if br <> '' then
        call out br
    if ord <> '' then
        call out '  order by' ord
    return
endProcedure sqlCatTbTrailer

sqlCatIxKeys: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select ikK.colSeq, ikK.colName, ikK.ordering, ikK.period' ,
             ', ik.creator, ik.name, ik.tbCreator, ik.tbName, ikC.*'  ,
          tkrTable(, tb ,'f') wh,
          'order by' if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, CREATOR, '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME   , '%-16C','index'
    call sqlFTabAdd      ft, colSeq  , '%5i',  'coSeq'
    call sqlFTabAdd      ft, colName, '%-16C', 'column'
    call sqlFTabAdd      ft, ordering
    call sqlFTabAdd      ft, period
    call sqlFTabAdd      ft, COLNO
    call sqlFTabAdd      ft, COLTYPE
    call sqlFTabAdd      ft, LENGTH
    call sqlFTabAdd      ft, SCALE
    call sqlFTabAdd      ft, NULLS
    call sqlFTabOthers ft, 'COL9 COL10 COL11 COL47'
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatIxKeys

sqlCatIXStats: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select *' tkrTable( , tb, 'f') wh ,
         'order by' if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, CREATOR, '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME   ,       , 'index'
    call sqlFTabAdd      ft, INSTANCE   , '%1i' , 'i'
    call sqlFTabAdd      ft, PARTITION , , 'part'
    call sqlFTabOthers ft
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatIXStats

sqlCatTables: procedure expose m.
parse arg ft, tb, wh, ord
    al = m.tb.alias
    sq = 'select' al'.*, tsX.type tsType, tsX.partitions',
            ', tsX.pgSize, tsX.dsSize' ,
            ', timestamp(rba1 || x''0000'') rba1Tst' ,
            ', timestamp(rba2 || x''0000'') rba2Tst' ,
          'from' m.tb.table 'left join sysibm.sysTablespace tsX',
            'on' al'.dbName = tsx.dbName and' al'.tsName = tsX.name',
            'where' m.tb.cond wh ,
            'order by'  if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, creator   , '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME      , '%-24C', 'table'
    call sqlFTabAdd      ft, type
    call sqlFTabAdd      ft, dbNAME    , '%-8C', 'db'
    call sqlFTabAdd      ft, tsNAME    , '%-8C', 'ts'
    call sqlFTabAdd      ft, tsType
    call sqlFTabAdd      ft, partitions,       , 'parts'
    call sqlFTabAdd      ft, pgSize
    call sqlFTabAdd      ft, dsSize
    call sqlFTabOthers ft, 'RBA1 RBA1TST RBA2 RBA2TST'
    call sqlFTabAdd      ft, rba1      , '%-12H'
    call sqlFTabAdd      ft, rba1Tst   ,       , 'rba1Timestamp:GMT'
    call sqlFTabAdd      ft, rba2      , '%-12H'
    call sqlFTabAdd      ft, rba2Tst   ,       , 'rba2Timestamp:GMT'
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatTables

sqlCatTSStats: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select' m.tb.alias'.*' ,
           tkrTable( , tb, 'f') wh ,
           'order by' if(ord == '', m.tb.order , ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, DBNAME, '%-8C', 'db'
    call sqlFTabAdd      ft, NAME   , '%-8C', 'ts'
    call sqlFTabAdd      ft, INSTANCE   , '%1i' , 'i'
    call sqlFTabAdd      ft, PARTITION , , 'part'
    call sqlFTabAdd      ft, NACTIVE   , , 'nActive'
    call sqlFTabAdd      ft, NPAGES    , , 'nPages'
    call sqlFTabAdd      ft, SPACE       , , 'spaceKB'
    call sqlFTabAdd      ft, TOTALROWS   , , 'totRows'
    call sqlFTabAdd      ft, DATASIZE         , , 'dataSz'
    call sqlFTabAdd      ft, LOADRLASTTIME    , , 'loadRLasttime'
    call sqlFTabAdd      ft, REORGLASTTIME    , , 'reorgLasttime'
    call sqlFTabAdd      ft, REORGINSERTS     , , 'inserts'
    call sqlFTabAdd      ft, REORGDELETES     , , 'deletes'
    call sqlFTabAdd      ft, REORGUPDATES     , , 'updates'
    call sqlFTabAdd      ft, REORGUNCLUSTINS  , , 'unClIns'
    call sqlFTabAdd      ft, REORGDISORGLOB   , , 'disorgL'
    call sqlFTabAdd      ft, REORGMASSDELETE  , , 'massDel'
    call sqlFTabAdd      ft, REORGNEARINDREF  , , 'nearInd'
    call sqlFTabAdd      ft, REORGFARINDREF   , , 'farInd'
    call sqlFTabAdd      ft, REORGCLUSTERSENS , , 'cluSens'
    call sqlFTabAdd      ft, REORGSCANACCESS  , , 'scanAcc'
    call sqlFTabAdd      ft, REORGHASHACCESS  , , 'hashAcc'
    call sqlFTabAdd      ft, STATSLASTTIME    , , 'statsLasttime'
    call sqlFTabAdd      ft, STATSINSERTS     , , 'inserts'
    call sqlFTabAdd      ft, STATSDELETES     , , 'deletes'
    call sqlFTabAdd      ft, STATSUPDATES     , , 'updates'
    call sqlFTabAdd      ft, STATSMASSDELETE  , , 'massDel'
    call sqlFTabAdd      ft, COPYLASTTIME     , , 'copyLasttime'
    call sqlFTabAdd      ft, COPYUPDATETIME   , , 'copyUpdatetime'
    call sqlFTabAdd      ft, COPYUPDATELRSN   , '%-12H', 'updateLRSN'
    call sqlFTabAdd      ft, COPYUPDATEDPAGES , , 'updaPgs'
    call sqlFTabAdd      ft, COPYCHANGES      , , 'changes'
    call sqlFTabOthers ft
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatTSStats

sql4obj: procedure expose m.
parse arg m, tb
    call out 'insert into' tb '--' className(objClass(m))
    line = ''
    ff = oFlds(m)
    pr = '   ('
    do fx=1 to m.ff.0
        call sql4ObjOut substr(m.ff.fx, 2)
        end
    call sql4ObjOut , 1
    call out '   ) values '
    pr = '   ('
    do fx=1 to m.ff.0
        f1 = substr(m.ff.fx, 2)
        v = strip(m.m.f1, 't')
        if dataType(v, n) then
            call sql4ObjOut v
        else do qx=1 until v == ''
            vx = verify(v, m.ut_alfPrint)
            if vx = 0 then do
                l1 = min(60, length(v))
                w = quote(left(v, l1), "'")
                end
            else if vx > 29 | vx = 0 then do
                l1 = min(60, vx)
                w = quote(left(v, l1), "'")
                end
            else do
                l1 = min(29, length(v))
                w = 'x'quote(c2x(left(v, l1)), "'")
                end
            if qx == 1 then
                call sql4ObjOut w
            else do
                if qx = 2 then
                    call sql4ObjOut , 1
                call out '   ||' w
                end
            v = substr(v, l1+1)
            end
        end
    call sql4ObjOut , 1
    call out '   ) ; '
    return
endProcedure
sql4objOut:
parse arg t1, force
    if (force == 1 & line \== '') | length(line t1) > 65 then do
        call out pr  substr(line, 3)
        pr = '   ,'
        line = ''
        end
    if force \== 1 then
        line = line',' t1
    return
endProcedure sql4objOut
/*--- -dis db interface ---------------------------------------------*/
/*--- do one -dis db... and insert it into stem  --------------------*/
sqlDisDb: procedure expose m.
parse upper arg o, cc
    do cx=1
        mid = strip(left(m.cc.cx, 10))
        if words(mid) > 1 then
            call err 'bad msgId' mid 'line:' m.cc.cx
        if mid == '' | wordPos(mid, 'DSNT360I DSNT361I DSNT365I') ,
                > 0 then
            iterate
        if mid == 'DSN9022I' then
            if cx = m.cc.0 then
                return m.o.0
            else
                call err 'not at end' cx':' m.cc.cx
        if mid \== 'DSNT362I' then
            call err 'DSNT362I expected not line:' m.cc.cx
        dx = pos('DATABASE =', m.cc.cx)
        sx = pos('STATUS ='  , m.cc.cx)
        if dx < 1 | sx <= dx then
            call err 'bad DSNT362I line' cx':' m.cc.cx
        db = word(substr(m.cc.cx, dx+10), 1)
        sta = strip(substr(m.cc.cx, sx+8))
        call sqlDisDbAdd o, db, ,0, 0, 'DB', sta
        do cx=cx+1 while abbrev(m.cc.cx, '   ')
            end
        if abbrev(m.cc.cx, 'DSNT397I ') then do
            cx = cx + 1
            if \ abbrev(space(m.cc.cx, 1),
                 , 'NAME TYPE PART STATUS ') then
                call err 'NAME TYPE PART STATUS mismatch' cx m.cc.cx
            txNa = pos('NAME', m.cc.cx)
            txTy = pos('TYPE', m.cc.cx)
            txPa = pos('PART', m.cc.cx)
            txSt = pos('STAT', m.cc.cx)
            txEn = verify(m.cc.cx, ' ', 'n', txSt+6)
            if 0 then say 'title' txNa txTy txPa txSt txEn cx m.cc.cx
            cx=cx+1
            do forever
                do while abbrev(m.cc.cx, '----')
                    cx = cx + 1
                    end
                if abbrev(m.cc.cx, '*') then
                    leave
                parse var m.cc.cx sp =(txTy)  ty . =(txPa)  paFr . ,
                                       =(txSt) sta   =(txEn)
                sp = strip(sp)
                if words(sp) \= 1 | wordPos(ty, 'TS IX') < 0 then
                    call err 'bad name or type' cx':'m.cc.cx
                if paFr == '' | paFr == 'L*' then
                    paFr = 0
                else if abbrev(paFr, 'D') | abbrev(paFr, 'L') then
                    paFr = substr(paFr, 2)
                if \ datatype(paFr, 'n') then
                    call err 'part not numeric' cx':'m.cc.cx
                paTo = paFr
                cw = cx
                cx = cx + 1
                if abbrev(m.cc.cx, '    -THRU ') then do
                    parse var m.cc.cx =(txPa)  paTo . =(txSt)
                    if \ datatype(paTo, 'n') then
                        call err '-thru part not numeric' cx':'m.cc.cx
                    cx = cx + 1
                    end
                call sqlDisDbAdd o, db, sp, paFr, paTo, ty, sta
                end
            end
        if m.cc.cx = '******** NO SPACES FOUND' then
            cx = cx + 1
        if abbrev(m.cc.cx, '******* DISPLAY OF DATABASE ') ,
          & word(m.cc.cx,5) == db then
            if word(m.cc.cx,6) == 'ENDED' then
                iterate
            else if word(m.cc.cx,6) == 'TERMINATED' then
                call err 'db display overflow' cx':' m.cc.cx
        call err 'database' db 'ENDED mismatch' cx':' m.cc.cx
        end
endProcedure sqlDbDis

/*--- insert one tuple into tDbState ---------------------------------*/
sqlDisDbAdd: procedure expose m.
    if arg(7) == '' | arg(7) == 'RW' then
         return
parse arg o
    m.o.0 = m.o.0 + 1
    q = o'.'m.o.0
parse arg , m.q.db, m.q.sp, m.q.paFr, m.q.paTo, m.q.ty, m.q.sta
 /* say added q m.q.db'.'m.q.sp':'m.q.paFr'-'m.q.paTo m.q.ty':'m.q.sta*/
    ky = m.q.db'.'m.q.sp
    if symbol('m.o.ky') \== 'VAR' then
        m.o.ky = m.o.0
    return
endProceedure sqlDisDbAdd

/*--- get index in o for db sp part ----------------------------------*/
sqlDisDbIndex: procedure expose m.
parse arg st, d, s, pa
     if symbol('m.st.d.s') \== 'VAR' then
         return 0
     ix = m.st.d.s
     if ix > m.st.0 | d \== m.st.ix.db | s \== m.st.ix.sp then
         return 0
     if pa == '' then
         return ix
     do ix=ix to m.st.0 while d == m.st.ix.db & s == m.st.ix.sp
         if pa < m.st.ix.paFr then
             return 0
         else if pa <= m.st.ix.paTo then
             return ix
         end
     return 0
endProcedure sqlDisDbIndex

/*--- dsn Command, return true if continuation needed ----------------*/
sqlDsnCont: procedure expose m.
parse arg cc, ssid, cmd
    say '???dsnCont' cmd
    cont = sqlDsn(cc, ssid, cmd, 12) <> 0
    if cont then do
        cz = m.cc.0
        cy = cz - 1
        if \ abbrev(m.cc.cy, DSNT311I) ,
                | \ abbrev(m.cc.cz, 'DSN9023I') then
            call err 'sqlDsn rc=12 for' cmd 'out='cz ,
                     '\n'cy'='m.cc.cy'\n'cz'='m.cc.cz
        m.cc.0 = cz-2
        end
    return cont
endProcedure sqlDsnCont
/* copy sqlDiv end   **************************************************/
/* copy db2Cat begin **************************************************/
catTbLastCol: procedure expose m.
parse upper arg cr, tb
    return sql2one( ,
          "select strip(char(colcount)) || ' ' || strip(c.name) one"  ,
              "from sysibm.sysTables t left join sysibm.sysColumns c" ,
                  "on c.tbCreator = t.creator and c.tbName = t.name"  ,
                       "and c.colNo = t.colCount"                     ,
               "where t.creator = '"cr"' and t.name = '"tb"'", ,'')
endProcedure catTbLastCol

catTbCols: procedure expose m.
parse upper arg cr, tb
    if sql2St("select strip(name) name "     ,
          "from sysibm.sysColumns " ,
          "where tbcreator = '"cr"' and tbname='"tb"'",
          "order by colNo", ggSt) < 1 then
        return ''
    res = m.ggst.1.name
    do cx=2 to m.ggst.0
        res = res m.ggst.cx.name
        end
    return res
endProcedure catTbCols

catTbColsTrunc: procedure expose m.
parse upper arg cr, tb, maxL
    if sql2St("select strip(name) name, colType, length, length2"     ,
          "from sysibm.sysColumns " ,
          "where tbcreator = '"cr"' and tbname='"tb"'",
          "order by colNo", ggSt) < 1 then
        return ''
    res = ''
    do cx=1 to m.ggst.0
        ty = m.ggSt.cx.colType
        if pos('LOB', ty) > 0 then
            res = res', substr('m.ggSt.cx.name', 1,' ,
                 min(maxL, m.ggSt.cx.length2)') 'm.ggSt.cx.name
        else if pos('CHAR', ty) > 0 & m.ggSt.cx.length > maxL then
            res = res', substr('m.ggSt.cx.name', 1,' maxL')',
                 m.ggSt.cx.name
        else
            res = res',' m.ggSt.cx.name
        end
    return substr(res, 3)
endProcedure catTbColsTrunc

catIxKeys: procedure expose m.
parse upper arg cr, ix
    sql = "select colSeq, colName, ordering"                          ,
              "from sysibm.sysKeys"                                   ,
               "where ixCreator = '"cr"' and ixName = '"ix"'" ,
               "order by colSeq"
    call sqlQuery 1, sql, '?sq col ord'
    res = ''
    drop d
    do kx=1 while sqlFetch(1, d)
        if m.d.sq \= kx then
            call err 'expected' kx 'but got colSeq' m.d.sq ,
                     'in index' cr'.'ix'.'m.d.col
        res = res || strip(m.d.col) || translate(m.d.ord, '<>?', 'ADR')
        end
    call sqlClose 1
    return res
endProcedure catIxKeys

catColCom: procedure expose m.
parse upper arg fCr, fTb, tCr, tTb
    sql = "select t.name, t.colType, t.nulls, t.""DEFAULT"""        ,
                    ", coalesce(f.nulls, 'new')"                    ,
              "from sysibm.sysColumns t"                            ,
                "left join sysibm.sysColumns f"                     ,
                  "on f.tbCreator = '"fCr"' and f.tbName = '"fTb"'" ,
                    "and f.name = t.name"                           ,
              "where t.tbCreator = '"tCr"' and t.tbName = '"tTb"'"  ,
              "order by t.colNo"
    call sqlQuery 1, sql, 'na ty nu de nn'
    pr = ' '
    do kx=1 while sqlFetch(1)
        /* say kx m..na m..ty m..nu m..de 'nn' m..nn */
        if pos('CHAR', m..ty) > 0 then
            dv = "''"
        else if pos('INT' ,m..ty) > 0 ,
                | wordPos(m..ty, 'REAL FLOAT') > 0 then
            dv = 0
        else if m..ty == 'TIMESTMP' then
            dv = '0001-01-01-00.00.00'
        else if pos('LOB', m..ty) > 0 then
            dv = m..ty"('')"
        else
            dv = '???'
        if m..nu = 'Y' then
            dv = 'case when 1=0 then' dv 'else null end'
        r = '???'
        if m..ty = 'ROWID' then do
            r = '--'
            end
        else if m..nn == 'new' then do
            if m..de = 'Y' then
                r = '--'
            else if m..nu == 'N' then
                r = dv
            else
                r = 'case when 1=0 then' dv 'else null end'
            end
        else do
            if m..nu = 'Y' | (m..nu = m..nn) then
                r = ''
            else
                r = 'coalesce('m..na',' dv')'
            end
        if abbrev(r, '--') then do
            r = ' ' r
            end
        else do
            r = pr r
            pr = ','
            end
        if pos('???', r) > 0 then
            call err 'no default for type' m..ty 'in' tCr'.'tTb'.'m..na
        call out r m..na
        end
    call sqlClose 1
    return
endProcedure catColCom
/* copy db2Cat end   **************************************************/
/* copy sqlO   begin ***************************************************
    sql interface  mit  o und j Anbindung
***********************************************************************/
sqlIni: procedure expose m.
    if m.sqlO_ini == 1 then
        return
    call sqlRxIni
    m.sqlO_ini = 1
    call jIni
    m.sqlO.cursors  = left('', 200)
    call classNew 'n SqlRdr u JRWO', 'm',
        , "jReset m.m.src = arg; m.m.type = arg2;",
        , "jOpen  call sqlRdrOpen m, opt",
        , "jClose call sqlRdrClose m",
        , "jReadO return sqlRdrReadO(m)"
    call classNew 'n SqlResRdr u JRWO', 'm',
        , "jReset m.m.cursor = arg; m.m.type = arg2;",
        , "jOpen  call sqlRdrO2 m" ,
        , "jClose call sqlClose m.m.cursor" ,
        , "jReadO return sqlRdrReadO(m)"
    call classNew 'n SqlRxConnection u', 'm',
        , "sqlQuery  return sqlRxQuery(cx, src, feVa, 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, feVa, 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, feVa, retOK)",
        , "sqlFetch  return sqlCsmFetch(cx, dst, retOk)",
        , "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, feVa, retOk)",
        , "sqlFetch  return sqlCsmFetch(m.cx.cursor, dst, retOk)",
        , "sqlClose  return 0",
        , "sqlUpdate call err 'implement sqlCsmUpdate'"   ,
        , "sqlCall   call err 'implement sqlCsmCall'"
    return 0
endProcedure sqlIni

/*--- connect to DB2 dsnRexx or csm ----------------------------------*/
sqlConnect: procedure expose m.
parse upper arg sys, retOk
    call sqlIni
    if sys == '' then
        sys = sqlDefaultSys()
    if pos('/', sys) <= 0 then do
        call  sqlRxConnect sys
        m.sql_connection = oNew('SqlRxConnection')
        end
    else do
        parse var sys m.sql_csmHost '/' m.sql_dbSys
        m.sql_connection = oNew('SqlCsmConnection')
        end
    return 0
endProcedure sqlConnect

/*--- disconnect from db2 --------------------------------------------*/
sqlDisconnect: procedure expose m.
parse arg retOk
    if m.sql_csmHost == '' then
        call sqlRxDisconnect
    else
        m.sql_csmHost = ''
    m.sql_dbSys = ''
    m.sql_connection = 'notConnected'
    return 0
endProcedure sqlDisonnect

/*--- execute a query from sql, with one resultset -------------------*/
sqlQuery: procedure expose m.
parse arg cx, src, feVa, retOk
    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')
endProcedue sqlUpdate

/*-- 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')
endProcedure sqlCall

/*--- return a free cursor -------------------------------------------*/
sqlGetCursor: procedure expose m.
parse arg rng
    if rng == '' then
        return sqlGetCursorRng(rng, 10, 48)
    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

/*--- execute stmts with options -------------------------------------*/
sqlStmtsOpt: procedure expose m.
parse arg src, opts
    upper opts
    sub = ''
    o = ''
    retOk = ''
    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
            retOk = retOk w
        else
            call err 'bad opt' w 'in opts' opts 'not -sql? -o or subsys'
        end
    return sqlStmts(src, strip(retOk), strip(o))
endProcedure sqlStmtsOpt

/*** execute sql's in a stream (separated by ;)
       opt: 'o' ==> write objects, otherwise fTabAuto
            'sql72' ==> spufi formatting (window 72) else linebreaks */
sqlStmts: procedure expose m.
parse arg sqlSrc, retOk, opt
   dlm = ';'
   isStr = oStrOrObj(sqlSrc, 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 scanSrc 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
       if translate(left(s1, 10)) == 'TERMINATOR' then do
            dlm = strip(substr(s1, 11))
            if length(dlm) \== 1 then
                call scanErr sqlStmts, 'bad terminator' dlm
            iterate
            end
       call outSt(splitNl(sqlTmp, sqlStmt(s1, retOk, opt)))
       end
   if \ isStr then
       call jClose fi
   return 0
endProcedure sqlStmts

/*--- execute a single sql statement --------------------------------*/
sqlStmt: procedure expose m.
parse arg src, retOk, opt
    cx = sqlGetCursor()
    r1 = sqlExecute(cx, src, retOK)
    res = m.sql_HaHi || sqlMsgLine(r1, m.sql.cx.updateCount, src)
    if m.sql.cx.resultSet \== '' then do
        rdr = jReset(oMutate('SQL_RDR.'cx, 'SqlResRdr'), cx)
        if pos('o', opt) > 0 then
            call pipeWriteAll rdr
        else
            call fTabAuto fTabReset(sql_StmtFmt, 1), rdr
        res = sqlMsgLine(m.rdr.rowCount 'rows fetched', , src)
        end
    call sqlFreeCursor cx
    return res
endProcedure sqlStmt

/*--- execute the given sql plus a commit
         until no more rows are updated -----------------------------*/
sqlUpdComLoop: procedure expose m.
parse arg src, retOk, opt
    src = inp2Str(src)
    crs = sqlGetCursor()
    upds = 0
    if retOk == '' then
        retOk = 100
    do coms=0
        cd = sqlExecute(crs, src, retOk)
        if m.sql.crs.updateCount < 1 then do
            call sqlFreeCursor(crs)
            return sqlMsgLine( , upds, src, coms 'commits')
            end
        upds = upds + m.sql.crs.updateCount
        call sqlCommit
        if coms // 20 = 19 then
            say sqlMsgLine(time(), upds, src, (coms+1) 'commits')
        end
endProcedure sqlUpdComLoop

/*--- sql call statement ---------------------------------------------*/
sqlStmtCall: procedure expose m.
parse arg src, retOk, 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.ut_alfDot) 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 fTabAuto 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

/*--- prepare and open cursor
      generate type and fetchList ------------------------------------*/
sqlRdr: procedure expose m.
parse arg src, type
     return oNew('SqlRdr', inp2str(src, '-sql'), type)
endProcedure sqlRdr

sqlRdrOpen: procedure expose m.
parse arg m, opt
    if opt\== m.j.cRead then
        call err 'opt not' m.j.cRead 'sqlRdrOpen('m',' opt')'
    m.m.cursor = sqlGetCursor()
    if m.m.type == '' then
        ff = ''
    else do
        ff = mCat(oClaMet(m.m.type, 'oFlds'), ' ')
        if abbrev(ff, '. ') then
            call err 'sqlRdrOpen implement value for type' m.m.type
        ff = space(translate(ff, ' ', '.'), 1)
        end
    call sqlQuery m.m.cursor, m.m.src, ff
    call sqlRdrO2 m
    return
endProcedure sqlRdrOpen

sqlRdrO2: procedure expose m.
parse arg m
    if m.m.type = '' then do
        ff = mCat('SQL.'m.m.cursor'.COL', ' v, f ')
        m.m.type = classNew('n* SQL u f' ff 'v')
        end
    m.m.rowCount = 0
    return
endProcedure sqlRdrO2

/*--- close sql Cursor -----------------------------------------------*/
sqlRdrClose: procedure expose m.
parse arg m, v
    call sqlClose m.m.cursor
    call sqlFreeCursor m.m.cursor
    m.m.cursor = ''
    return m
endProcedure sqlRdrClose

/*--- read next from cursor, return as object ------------------------*/
sqlRdrReadO: procedure expose m.
parse arg m
    v = oNew(m.m.type)
    if \ sqlFetch(m.m.cursor, v) then
        return mFree(v)
    m.m.rowCount = m.m.rowCount + 1
    return v
endProcedure sqlRdrReadO

/*--- select and write all to stdOut ---------------------------------*/
sqlSel: procedure expose m.
parse arg src, type
     s = sqlRdr(src, type)
     call pipeWriteAll s
     return m.s.rowCount
endProcedure sqlSel
/* copy sqlO   end   **************************************************/
/* copy sqlCsm begin **************************************************/
/*--- send an sql to csm and handle sqlCode --------------------------*/
sqlCsmExe:
parse arg cx, ggSqlStmt, ggRetOk
    sql_HOST =  m.sql_csmhost
    SQL_DB2SSID = m.sql_dbSys
    sql_query = ggSqlStmt
    address tso "CSMAPPC START PGM(CSMASQL)"
    if \ (rc = 0 |  rc = 4) then
        return 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\nsql =' ggSqlStmt
        else
            call errSay 'w}'sqlMsg(sqlCA2rx(sqlCa))
    return sqlCode
endProcedure sqlCsmExe

/*--- execute a query from sql, with one resultset -------------------*/
sqlCsmQuery: procedure expose m.
parse arg cx, sqlSrc, feVa, retOk, dst
    res = sqlCsmExe(cx, sqlSrc, 100 retOk)
    if res < 0 then
        return res
    if dst == '' then
        dst = 'SQL.'cx'.CSMDATA'
    m.dst.0 = 0
    m.dst.laIx = 0
    st = 'SQL.'cx'.COL'
    call sqlRemVars st
    feVa = translate(feVa, ' ', '?')
    if feVa = '' then do kx=1 to sqlD
        call sqlAddVar st, SQLDA_REXXNAME.kx, 1
        end
    else if abbrev(feVa, ':')  then
        return err('fetchVars with : for csm:' feVa)
    else if words(feVa) <> sqlD then
        return err('csm sqlD='sqlD', but' words(feVa) 'vars:' feVa)
    else do kx=1 to words(feVa)
        call sqlAddVar st, word(feVa, kx), 1
        end
    do kx=1 to sqlD
        rxNa = SQLDA_REXXNAME.kx
        cn = m.st.kx
        do rx=1 to sqlRow#
            if substr(sqlIndicator.rx, kx ,1) == 'ff'x then
                m.dst.rx.cn = m.sqlNull
            else
                m.dst.rx.cn = value(rxNa'.'rx)
            end
        end
    m.dst.0 = sqlRow#
    return 0
endProcedure sqlCsmQuery

sqlCsmFetch: procedure expose m.
parse arg cx, dst
    src = 'SQL.'cx'.CSMDATA'
    rx = m.src.laIx + 1
    if rx > m.src.0 then
        return 0
    m.src.laIx = 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 sqlRx  begin ***************************************************
       Achtung: inc generiert sql aus sqlRx, Aenderungen nur in sqlRx|
    sql interface
***********************************************************************/
/*--- initialize sqlRx -----------------------------------------------*/
sqlRxIni: procedure expose m.
    if m.sqlRx_ini == 1 then
        return
    m.sqlRx_ini = 1
    m.sqlNull = '---'
    m.sqlInd = 'sqlInd'
    m.sql_defCurs= 49
    m.sqlCAMsg = 0
    m.sqlSuMsg = 2
    m.sql_dbSys = ''
    m.sql_csmhost = ''
    isInProd = wordPos(sysvar(sysNode), 'RZ2') > 0
    m.sqlRetOK = 'dne' copies('rod', \ isInProd)
    return 0
endProcedure sqlRxIni

/*--- get default db2System ------------------------------------------*/
sqlDefaultSys: procedure expose m.
parse arg
    if sysvar(sysnode) == 'RZ1' then
        return 'DBAF'
    else if sysvar(sysnode) == 'RZ4' then
        return 'DP4G'
    else
        call err 'no default subsys for' sysvar(sysnode)
endProcedure sqlDefaultSys

/*--- 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
    if sys == '' then
        sys = sqlDefaultSys()
    m.sql_dbSys = sys
    ggSqlStmt =  'connect' sys
    address dsnRexx ggSqlStmt
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, ggRetOk, ggSqlStmt)
endProcedure sqlRxConnect

/*--- diconnect from db2 ---------------------------------------------*/
sqlRxDisconnect: procedure expose m.
parse arg retOk
    if m.sql_dbSys == '' then
        return 0
    ggSqlStmt =  'disconnect'
    m.sql_dbSys = ''
    m.sql_csmHost = ''
    address dsnRexx ggSqlStmt
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, retOk, ggSqlStmt)
endProcedure sqlRxDisconnect

/*--- reset sql cursor 'c'cx fields ----------------------------------*/
sqlReset: procedure expose m.
parse arg cx
     m.sql.cx.updateCount = ''
     m.sql.cx.resultSet   = ''
     m.sql.cx.d.sqlD = 'noSqlDA'
     m.sql.cx.i.sqlD = 'noDescInp'
     m.sql.cx.fetchVars = ''
     m.sql.cx.type  = ''
     call sqlRemVars 'SQL.'cx'.COL'
     return
endProcedue sqlReset

/*--- execute a query from sql, with one resultset -------------------*/
sqlRxQuery: procedure expose m.
parse arg cx, src, feVa, retOk
     call sqlReset cx
     src = inp2str(src, '%,%c ')
     s1 = ''
     if pos(left(feVa, 1), '?:') < 1 then
         s1 = 'into :M.SQL.'cx'.D'
     res = sqlExec('prepare s'cx s1 'from :src', retOk)
     if res < 0 then
         return res
     call sqlRxFetchVars cx, feVa
     call sqlExec 'declare c'cx 'cursor for s'cx
     res = sqlExec('open c'cx, retOk)
     if res < 0 then
         return res
     m.sql.cx.updateCount = sqlErrd.3
     m.sql.cx.resultSet = cx
     return res
endProcedure sqlRxQuery

/*--- prepare a query from sql, with one resultset -------------------*/
sqlQueryPrep: procedure expose m.
parse arg cx, src, feVa, retOk
     call sqlReset cx
     src = inp2str(src, '%,%c ')
     s1 = ''
     if feVa == '' | feVa = 'd' then
         s1 = 'into :M.SQL.'cx'.D'
     res = sqlExec('prepare s'cx s1 'from :src', retOk)
     if res < 0 then
         return res
     call sqlRxFetchVars cx, feVa
     call sqlExec 'declare c'cx 'cursor for s'cx
     return sqlExec('describe input s'cx 'into :M.SQL.'cx'.I')
endProcedure sqlQueryPrep

sqlQueryArgs: procedure expose m.
parse arg cx retOk
    do ix=1 to arg()-1
        call sqlDASet cx , 'I', ix, arg(ix+1)
        end
     res = sqlExec('open c'cx 'using descriptor :M.SQL.'cx'.I', retOk)
     if res < 0 then
         return res
     m.sql.cx.updateCount = sqlErrd.3
     m.sql.cx.resultSet = cx
     return res
endProcedure sqlQueryArgs

/*--- fetch next row to m.dst.* at end return false ------------------*/
sqlRxFetch: procedure expose m.
parse arg cx, dst, retOk
    fetCode = sqlExec('fetch c'cx 'into' m.sql.cx.fetchVars, 100 retOk)
    if fetCode == 100 then
        return 0
    if fetCode < 0 then
        return fetCode
    interpret m.sql.cx.fetchCode
    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 sqlExec('execute immediate :src', retOk)
        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, retOk)
        end
    if fun == 'DECLARE'  then do
        if 'GLOBAL' == translate(word(substr(src, bx), 2)) then
            return sqlExec('execute immediate :src', retOk)
        end
    res = sqlExec(src, retOk)
    if wordPos(fun, 'DELETE INSERT UPDATE') > 0 then
        m.sql.cx.updateCount = sqlErrd.3
    return res
endProcedure sqlRxUpdate

/*-- prepare an update -----------------------------------------------*/
sqlUpdPrep: procedure expose m.
parse arg cx, src, retOk
    res = sqlExec('prepare s'cx 'from :src', retOk)
    return sqlExec('describe input s'cx 'into :M.SQL.'cx'.I')
endProcedure sqlUpdPrep

/*-- execute a prepared update with the given arguments --------------*/
sqlUpdArgs: procedure expose m.
parse arg cx retOk
    do ix=1 to arg()-1
        call sqlDASet cx , 'I', ix, arg(ix+1)
        end
    res = sqlExec('execute s'cx 'using descriptor :M.SQL.'cx'.I',
                  , retOk)
    m.sql.cx.updateCount = sqlErrd.3
    return res
endProcedure sqlRxUpdArgs

/*-- execute a query, update or call ---------------------------------*/
sqlExecute: procedure expose m.
parse arg cx, src, retOk
    src = inp2Str(src, '-sql')
    f = translate(word(src, 1))
    bx = pos('(', f)
    if bx > 0 then
        f = left(f, max(1, bx-1))
    m.sql.cx.fun = f
    if f == 'SELECT' | f == 'WITH' | f == '(' 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

/*-- fetch all rows to stem and close --------------------------------*/
sqlFetch2St: procedure expose m.
parse arg cx, dst, retOK
    do sx=1 while sqlFetch(cx, dst'.'sx)
       end
    res = sx-1
    m.dst.0 = sx-1
    call sqlClose cx
    return m.dst.0
endProcedure sqlFetch2St

/*-- execute a query, copy result to stem ----------------------------*/
sql2St: procedure expose m.
parse arg src, dst, feVa, retOK
    cx = m.sql_defCurs
    res = sqlQuery(cx, src, feVa, retOk)
    return sqlFetch2St(cx, dst, retOk)
endProcedure sql2St

/*-- return first column of the only row and close -------------------*/
sqlFetch2One: procedure expose m.
parse arg cx, dst, retNone
    f1 = sqlFetch(cx, dst)
    if f1 then
        f2 = sqlFetch(cx, dst)
    call sqlClose cx
    if \ f1 then
        if retNone \== '' then
            return substr(retNone, 2)
        else
            call err 'sqlFetch2One: no row returned'
    else if f2 then
        call err 'sqlFetch2One: more than 1 row'
    c1 = m.sql.cx.col.1
    return m.dst.c1
endProcedure sqlFetch2One

/*-- fxecute a query and return first row of the only colun
           if > 1 row fail, if 0 rows return arg(3) or fail ----------*/
sql2One: procedure expose m.
parse arg src, dst, feVa, retOk, retNone
    cx = m.sql_defCurs
    call sqlQuery cx, src, feVa, retOk
    return sqlFetch2One(cx, dst, retNone)
endProcedure sql2One

/*--- describe table and return sqlDA --------------------------------*/
sqlDescribeTable: procedure expose m.
parse upper arg tb, force
    if force == 1 | \ datatype(m.sql.table.tb.sqlD, 'n') then
         call sqlExec 'describe table :tb into :M.SQL.TABLE.'tb
    return 'SQL.TABLE.'tb
endProcedure sqlDescribeTable

/*--- return select column list for table tb
      omit Blobs (blobMax < 0) or substr(blob, 1, blobMax) ----------*/
sqlColList: procedure expose m.
parse arg tb al, blobMax
    sd = sqlDescribeTable(tb)
    bs = ''
    lst = ''
    if al \== '' & right(al, 1) \== '.' then
        al = al'.'
    do sx=1 to m.sd.sqld
        if wordPos(m.sd.sx.sqlType, '404 405 408 409 412 413') < 1 then
            lst = lst',' al || m.sd.sx.sqlName
        else do
            bs = bs m.sd.sx.sqlName
            if blobMax >= 0 then
                lst = lst', length('al || m.sd.sx.sqlName')' ,
                                          m.sd.sx.sqlName'Len' ,
                     || ', substr('al  || m.sd.sx.sqlName ,
                     || ', 1,' blobMax')' m.sd.sx.sqlName
            end
        end
    m.sd.colList = substr(lst, 3)
    m.sd.blobs = strip(bs)
    return substr(lst, 3)
endProcedure sqlColList

/*--- use describe output to generate column names,
        ''         use names from ca (rexxified)
        nms+       use names, check ca for null values
        ?('?'?nm)+ use names, check for null if preceeded by ?
        :...       use string as is
                fetchVariables and sqlNull names ---------------------*/
sqlRxFetchVars: procedure expose m.
parse arg cx, src, cd
    m.sql.cx.fetchCode = cd
    st = 'SQL.'cx'.COL'
    call sqlRemVars st
    if abbrev(src, ':') then do
        m.sql.cx.fetchVars = src
        return
        end
    m.sql.cx.fetchVars = ''
    vrs = ''
    sNu = ''
    if abbrev(src, '?') then do
        r = substr(src, 2)
        do wx=1 to words(src)
            cn = word(src, wx)
            if abbrev(cn, '?') then
                call sqlRexxAddVar substr(cn, 2), 0, 1
            else
                call sqlRexxAddVar cn, 0, 0
            end
        end
    else if src <> '' then do kx=1 to words(src)
        cn = word(src, kx)
        call sqlRexxAddVar cn, 0, m.sql.cx.d.kx.sqlType // 2
        end
    else do kx=1 to m.sql.cx.d.sqlD
        call sqlRexxAddVar m.sql.cx.d.kx.sqlName, 1 ,
                       , m.sql.cx.d.kx.sqlType // 2
        end
    m.sql.cx.fetchVars = substr(vrs, 3)
    m.sql.cx.fetchCode = sNu cd
    return
endProcedure sqlRxFetchVars

sqlRexxAddVar:
parse arg nm, nicify, hasNulls
     nm = sqlAddVar(st, nm, nicify)
     if \ hasNulls then
          vrs = vrs', :m.dst.'nm
     else do
         vrs = vrs', :m.dst.'nm ':m.dst.'nm'.sqlInd'
         sNu = sNu 'if m.dst.'nm'.sqlInd < 0 then' ,
                    'm.dst.'nm '= m.sqlNull;'
         end
    return
endSubroutine sqlRexxAddVar

sqlCol2kx: procedure expose m.
parse arg cx, nm
    if symbol('M.SQL.CX.COL.NM') \== 'VAR' then
        return ''
    kx = m.sql.cx.col.nm
    if m.sql.cx.col.kx \== nm then
        call err 'sqlCol2kx' nm '==>' kx 'but' m.sql.cx.col.kx
    return kx
endProcedure sqlCol2kx

sqlRemVars: procedure expose m.
parse arg st
    if symbol('m.st.0') == 'VAR' then do
        do sx=1 to m.st.0
            nm = m.st.sx
            drop m.st.nm m.st.sx
            end
        end
    m.st.0 = 0
    return
endProcedure sqlRemVars

sqlAddVar: procedure expose m.
parse arg st, sNa, nicify
    sx = m.st.0 + 1
    if 1 | nicify then do
        cx = verifId(sNa)
        if cx > 0 then /* avoid bad characters for classNew| */
            sNa = left(sNa, cx-1)
        upper sNa
        if sNa == '' | symbol('m.st.sNa') == 'VAR' then
            sNa = 'COL'sx
        end
    m.st.0 = sx
    m.st.sx = sNa
    m.st.sNa = sx
    return sNa
endProcedure sqlAddVar

/*--- 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

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

/*--- execute sql thru the dsnRexx interface -------------------------*/
sqlExec: /* no procedure, to keep variables sql... */
    parse arg ggSqlStmt, ggRetOk
    m.sql_HaHi = ''
    address dsnRexx 'execSql' ggSqlStmt
    /* say 'sqlCode' sqlCode 'rc' rc 'for' ggSqlStmt ggNo */
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, ggRetOk, ggSqlStmt)
endProcedure sqlExec

sqlErrorHandler: procedure expose m. ,
                   sqlCode sqlErrMc sqlErrP sqlErrD. sqlWarn. sqlState
parse arg drC, retOk, verb rest
    if drC == 0 then
        return 'return 0'
    if wordPos(drC, '1 -1') < 0 then
        return "call err 'dsnRexx rc" drC"' sqlmsg()"
    if pos('-', retOK) < 1 then
        retOK = retOk m.sqlRetOk
    if pos('*', retOK) > 0 | wordPos(sqlCode, retOK) > 0 then do
        if sqlCode < 0 & pos('say', retOK) > 0 then
            return "call outSt errMsg(' }'sqlMsg()); return" sqlCode
        else
            return "return" sqlCode
        end
    upper verb
    if verb == 'DROP' then do
        if (sqlCode == -204 | sqlCode == -458) ,
                       & wordPos('dne', retok) > 0 then
            return 'return' sqlCode
        if sqlCode = -672 & wordPos('rod', retok) > 1 then do
            hahi = m.sql_HaHi ,
                 || sqlMsgLine(sqlCode, 'tb='sqlErrMc ,verb rest)'\n'
            call sqlExec 'alter table' SqlErrMc ,
                    'drop restrict on drop'
            hahi = hahi || m.sql_HaHi ,
                        || sqlMsgLine(sqlCode, , ggSqlStmt)'\n'
            call sqlExec verb rest
            m.sql_HaHi = hahi
            return 'return' sqlCode
            end
        end
    if drC < 0 then
         return "call err sqlmsg(); return" sqlCode
    if (sqlCode <> 0 | sqlWarn.0 ^==' ') & pos('w',retOK) < 1 then
        return "call outSt errMsg(' }'sqlMsg()); return" sqlCode
    return 'return' sqlCode
endProcedure sqlErrorHandler

sqlMsgLine: procedure expose m. sqlErrD.
parse arg res, cnt, src, plus
    verb = translate(word(src, 1))
    if datatype(res, 'n') then
        res = 'sqlCode' res
    if cnt \== '' then do
        res = res',' cnt
        vx = wordPos(translate(word(src,1)), 'DELETE INSERT UPDATE')
        if datatype(cnt, 'n') then
            if vx > 0 then
               res = res 'rows' word('deleted inserted updated', vx)
            else if cnt <> 0 then
                res = res 'rows updated'
        end
    if plus \== '' then
        res = res',' plus
    if abbrev(res, ', ') then
        res = substr(res, 3)
    if src \== '' then do
        ll = 75 - length(res)
        aa = strip(src)
        if length(aa) > ll then
            aa = space(aa, 1)
        if length(aa) > ll then
           aa = left(aa,  ll-3)'...'
        res = res':' aa
        end
    return res
endProcedure sqlMsgLine

/*--- 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

/*--- 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
    ggFrom = 'ggSqlStmt'
    ggW1 = translate(word(ggSqlStmt, 1))
    ggW2 = translate(word(ggSqlStmt, 2))
    if ggW1 == 'PREPARE' then
        ggFrom = sqlHostVarFind(ggSt, 'FROM')
    else if ggW1 ggW2 == 'EXECUTE IMMEDIATE' then
        ggFrom = sqlHostVarFind(ggSt, 1)
    ggPos = 0
    if datatype(sqlErrd.5, 'n') & sqlErrd.5 > 0 then do
        ggPos = sqlErrd.5
        ggRes = ggRes || sqlMsgSrcPos(value(ggFrom), sqlErrd.5)
        end
    if ggFrom == 'ggSqlStmt' then do
        ggRes = ggRes'\nsql =' sqlShorten(ggSqlStmt, 2000, ggPos)
        end
    else do
        ggRes = ggRes'\nsql =' sqlShorten(value(ggFrom), 2000, ggPos)
        ggRes = ggRes'\nstmt =' sqlShorten(ggSqlStmt, 2000)
        end
    ggPref = '\nwith'
    do ggXX=1 to m.ggSt.0
        if ggFrom = m.ggVa.ggXX then
            iterate
        ggRes = ggRes || ggPref m.ggBe.ggXX ':'m.ggVa.ggXX ,
                      '=' sqlShorten(value(m.ggVa.ggXX), 210)
        ggPref = '\n    '
        end
    if m.sqlSuMsg == 1 | (m.sqlSuMsg == 2 & m.sql_csmhost \== '') then
        ggRes = ggRes'\nsubsys =' m.sql_dbSys ,
             || ', host =' m.sql_csmhost
    return  ggRes
endSubroutine sqlMsg

sqlShorten: procedure expose m.
parse arg txt, maxL, pos
    if length(txt) <= maxL then
        return txt
    if \ datatype(pos, 'n') | pos < 1 then
        pos = 1
    ex = pos + min(60, maxL%7)
    if ex <= maxL - 4 then
        return left(txt, maxL-4) '...'
    if ex >= length(txt) then
        return left(txt, 67) '...\n'substr(txt, length(txt)-maxL+72)
    else
        return left(txt, 67) '...\n'substr(txt, ex-maxL+76, maxL-75) ,
                       '...'
endProcedure sqlShorten
/*--- 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 nop
    else if rc = 4 then say 'warn linkPgm dsnTiar rc' rc 'sqlCa' ca
    else                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: procedure expose m. ,
                   sqlCode sqlErrMc sqlErrP sqlErrD. sqlWarn. sqlState
    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 = 1
    do cnt
        cx = pos(':', src, cx) + 1
        if cx < 2 then
           leave
        if pos(substr(src, cx, 1), m.ut_rxN1) > 0 then
            iterate
        ex = verify(src, m.ut_rxDot, 'n', cx)
        if ex < 1 then
            m.st.var.sx = substr(src, cx)
        else
            m.st.var.sx = substr(src, cx, ex - cx)
        if m.st.var.sx == '' | length(m.st.var.sx) > 100 then
            iterate
                       /* 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.ut_alfa) > 0
            end
        if bB < bE & bB >= 0 then
            m.st.bef.sx = substr(src, bB+1, bE-bB)
        else
            m.st.bef.sx = ''
        sx = sx + 1
        end
    m.st.0 = sx-1
    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 sqlRx  end   **************************************************/
/* copy csi begin    ***************************************************
     csi interface: see dfs managing catalogs chapt. 11
         returncode/reason see message IDC3009I
**********************************************************************/
/*--- specify dsn mask and fields to start a csi catalog search --------
      arguments:
          m       objectPointer
          dsnMask specifies the dsns with wildcards:
              %  1 character
              *  0 - n character in one level
              ** 0 - n levels
          fields a (space separated) list of field names -------------*/
csiOpen: procedure expose m.
parse arg m, dsnMask, fields
    m.m.fld.0 = words(fields)
    ffix = d2c(m.m.fld.0, 2)
    do x=1 to m.m.fld.0
        m.m.fld.x = translate(word(fields, x))
        ffix = ffix || left(m.m.fld.x, 8)
        end
    if dsnMask \== '' & right(dsnMask, 1) \== ' ' ,
          & pos('*', dsnMask) < 1 & length(dsnMask) < 42 then
        dsnMask = dsnMask'.**'
    m.m.filt = left(dsnMask, 149) ,
             || left('Y', 3) ,        /* resume offset 149      */
             || ffix                  /* csiNumEn offset 152    */

    WORKLEN = 1024 * 64
    m.m.work = D2C(WORKLEN,4) || COPIES('00'X,WORKLEN-4)
    m.m.pos = workLen + 1
    return
endProcedure csiOpen

/*--- put the next dsn into m.o and m.o.* (for other fields)
      return 1 if next dsn exists 0 otherwise ------------------------*/
csiNext: procedure expose m.
parse arg m, o
    usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET AMOUNT OF WORK AREA USED */
    px = m.m.pos
    do forever
        if px > usedL then do
            if substr(m.m.filt, 150, 1) \== 'Y' then do
                m.m.pos = px
                m.o = ''
                return 0
                end
            reason = left('', 4)
            ADDRESS LINKPGM 'IGGCSI00  reason m.'m'.filt  m.'m'.work'
            if rc == 0 & substr(reason, 3, 2) == '0000'x then
                nop
            else if rc == 4 & substr(reason, 3, 2) == '0464'x then
                say 'data set entry with error'
            else
                call err 'call csi returns' rc,
                             'rc' c2d(substr(reason, 4,1)),
                             'reason' c2d(substr(reason, 3,1)),
                             'module' substr(reason, 1,2)
            usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET WORK AREA USED */
            numFD = C2D(SUBSTR(m.m.work,13,2)) /* no flds + 1 */
            if numFd <> m.m.fld.0 + 1 then
                call err 'csiNumFd' numFd 'not' m.m.fld.0 '+' 1
            px = 15
            iterate
            end
        eType =  substr(m.m.work, px+1, 1)
        m.o = strip(substr(m.m.work, px+2, 44), 't')
        flag = substr(m.m.work, px, 1)
        /* say 'eType' eType m.o 'flag' c2x(flag) */
        if eType == '0' then do
            if flag \== '00'x & flag \== '40'x then
                call err 'flag' c2x(flag) 'for catalog' m.o
            px = px + 50    /* length of catalog entry  */
            iterate
            end
        else do
            if \ abbrev(x2b(c2x(flag)), '101') then
                call err 'call csi entry flag' x2b(c2x(flag)),
                             'rc' c2d(substr(m.m.work, px+49,1)),
                             'reason' c2d(substr(m.m.work, px+48,1)),
                             'module' substr(m.m.work, px+46, 2),
                             'for entry' m.o,
                             'see qw IDC3009I'
            py = px + 46
            tl = c2d(substr(m.m.work, py, 2))
            pl = py + 4
            pf = py + m.m.fld.0 * 2 + 4
            do fx = 1 to m.m.fld.0
                fi = m.m.fld.fx
                fl = c2d(substr(m.m.work, pl, 2))
                m.o.fi = substr(m.m.work, pf, fl)
                if fi = 'MGMTCLAS' then
                    m.o.fi = substr(m.o.fi, 3, c2d(left(m.o.fi ,2)))
                else if wordPos(fi, 'COMUDSIZ NOBYTTRK') > 0 then
                    m.o.fi = utc2d(m.o.fi)
                pf = pf + fl
                pl = pl + 2
                end
            if py + tl <> pf then
                call err 'length mismatch for entry' m.o
            m.m.pos = pf
            return 1
            end
        end
endProcedure csiNext
/*--- if dsn is arcived return 'arcive'
      if dsn is tape return 'tape'
      otherwise return managment class ------------------------------*/
csiArcTape: procedure expose m.
parse arg vo, cl, dt, dsn
        if vo = '' then
            say err '||| no volume for dsn' dsn
        else if vo = 'ARCIVE' then
            res = 'arcive'
        else if cl <> '' then
            res = cl
        else if abbrev(vo, 'SHR') then
            res = 'SHR'
        else
            res = 'tape'
        if   res = 'arcive' then
            return res
      /*if   abbrev(res, 'ar') \= abbrev(dt, '00'x) , */
        if   abbrev(res, 'ta') \= abbrev(c2x(left(dt, 1)), '7') ,
           | (left(res, 1) >= 'A') \= abbrev(dt, '30'x) then
           say '||| mismatch cl' cl 'vo' vo 'dt' c2x(dt) 'dsn' dsn
        return res
endProcedure csiArcTape
/* copy csi end ******************************************************/
/* copy csm begin ******************************************************
    interface to csm,
        it is integrate with adrTso, eg. dsnAlloc , 'RZ3/..' uses csm
***********************************************************************/
/*--- execute a single csmExec command ------------------------------*/
adrCsm:
    if wordPos(translate(word(arg(1), 1)), 'COPY MBRLIST') > 0 then
       ggTO = ''
    else if symbol('m.csm_timeOut') == 'VAR' then
        ggTO = 'timeout('m.csm_timeOut')'
    else
        ggTO = 'timeout(30)'
    ggStart = time()
    if adrTso('csmExec' arg(1) ggTO, '*') == 0 then
        return 0
    if pos('CSMXC43E NO CONNECTION TO S', m.tso_trap) > 0 then
               /* CSMXC43E NO CONNECTION TO SYSPLEX:RR2 AVAILABLE */
        m.csm_err = 'noConn'
    else if pos('IKJ56225I', m.tso_trap) > 0             ,
               & ( pos('ALREADY IN USE', m.tso_trap) > 0 ,
                 | pos('CATED TO ANOTH', m.tso_trap) > 0) then
               /* 5 IKJ56225I DATA SET A540769.TMP.TEXT ALREADY IN USE,
                  6 IKJ56225I DATA SET IS ALLOCATED TO ANOTHER JOB */
        m.csm_err = 'inUse'
    else
        m.csm_err = ''
    if pos('*', arg(2)) > 0 then
        return m.tso_rc
    else if wordPos(m.tso_rc, arg(2)) > 0 then
        return m.tso_rc
    else
        call err strip('csmExec' m.csm_err) 'rc='m.tso_rc ,
            '\nstmt='subWord(m.tso_stmt, 2) m.tso_trap ,
            '\nend of csmExec, time='ggStart '-' time()
endProcedure adrCsm

csmDel: procedure expose m.
parse arg rz, dsn
    if dsnGetMbr(dsn) == '' then do
        call adrCsm "allocate system("rz") dataset('"dsn"')" ,
                         "disp(del) ddname(del1)"
        call adrTso 'free dd(del1)'
        end
    else do
        rr = adrCsm("mDelete system("rz") dataset('"dsnSetMbr(dsn)"')",
                          "member("dsnGetMbr(dsn)")", 8)
        if rr <> 0 then
            if pos('CSMEX77E Member:', m.tso_trap) < 1 ,
              | pos(' not found', m.tso_trap) < 1 then
                  call err 'rc='rr 'csm mDelete' rz'/'dsn':'m.tso_trap
        end
    return
endProcedure csmDel

csmCopy: procedure expose m.
parse arg aFr, aTo, retOk
    if dsnGetMbr(csnTo) \= '' & dsnGetMbr(csnTo) \= '' then do
        if dsnGetMbr(csnFr) <> dsnGetMbr(csnTo) then
            call err 'member rename' csnFr 'to' csnTo
        csnTo = dsnSetMbr(csnTo)
        end
    fr = csmSysDsn(aFr)
    frMbr = dsnGetMbr(fr)
    frDD = tsoDD('csmFr*', 'a')
    to = csmSysDsn(aTo)
    toMbr = dsnGetMbr(to, '=')
    toDD = tsoDD('csmTo*', 'a')
    call csmAlloc dsnSetMbr(fr) frDD 'shr'
    if frMbr == '' & m.tso_ddDsOrg.frDD == 'PO' then
        if toMbr \== '=' then
            call err 'csmCopy from' fr'(*) to ps' to
        else
            frMbr = '*'
    if frMbr == '' & (toMbr \== '' & toMbr \== '=') then
        psOrLib = 'dsorg(po) dsntype(library)'
    else if frMbr \== '' & toMbr == '' then
        psOrLib = 'dsorg(ps)'
    else
        psOrLib = ''
    call csmAlloc dsnSetMbr(to) toDD 'shr ::D'frDD psOrLib
    c = 'indd('frDD') outDD('toDD')'
    if frMbr \== '*' then do
        if frMbr \== '' then
            c = c 'member('frMbr')'
        if toMbr \== '' & toMbr \== '=' then
            c = c 'newName('toMbr')'
        call adrCsm 'copy' c
        end
    else do
        call adrCsm "mbrList ddName("frDD") index(' ') short"
        do mx=1 to mbr_mem#
            call adrCsm 'copy' c 'member('mbr_name.mx')'
            end
        end
    call tsoFree frDD toDD
    return
endProcedure csmCopy

csmAlloc: procedure expose m.
parse arg dsn dd disp rest ':' nn, retRc
    sys = ''
    a2 = ''
    upper dd disp
    m.tso_dd.dd = csmSysDsn(dsn)
    parse var m.tso_dd.dd sys '/' dsn
    if disp = '' then
        disp = 'shr'
    a1 = "SYSTEM("sys") DDNAME("dd")"
    if dsn == 'INTRDR' then do
        a1 = a1 'sysout(T) writer(intRdr)'
        end
    else do
        if dsn <> '' then do
            a1 = a1 "DATASET('"dsnSetMbr(dsn)"')"
            mbr = dsnGetMbr(dsn)
            if mbr <> '' then
                a1 = a1 'MEMBER('mbr')'
            end
        if abbrev(disp, 'SYSOUT(') then
            a1 = a1 disp
        else
            a1 = a1 "DISP("disp")"
        end
    nAtts = wordPos(disp, 'NEW MOD CAT') > 0 & nn \== ''
    if nAtts then
        rest = dsnCreateAtts( , nn) rest
    cx = pos(' UCOUNT(', ' 'translate(rest))
    if cx > 0 then do
         rest = left(rest, cx) || 'nitCnt(' || substr(rest, cx+7)
         say '???uCount ==>' rest
         end
    cx = pos(' RECFM(', ' 'translate(rest))
    if cx > 0 then do
        cy = pos(')', rest, cx)
        rest = left(rest, cx+5) || space(substr(rest,cx+6,cy-cx-6), 0) ,
                                || substr(rest,cy)
        say '???recfm ==>' rest
        end
    cx = pos(' CYLINDERS ', ' 'translate(rest)' ')
    if cx > 0 then do
        rest = delStr(rest, cx+8, 1)
        say '???cylinders ==>' rest
        end
    cx = pos(' CYL ', ' 'translate(rest)' ')
    if cx > 0 then do
        rest = insert('inder', rest, cx+2)
        say '???cyl ==>' rest
        end
    if retRc <> '' | nAtts | nn == '' then do
        alRc = adrCsm('allocate' a1 rest, retRc)
        m.tso_ddDsOrg.dd = subsys_dsOrg
        return alRc
        end
    alRc = adrCsm('allocate' a1 rest, '*')
    if alRc = 0 then
        return 0
    say 'csmAlloc rc' alRc 'for' a1 rest '...trying to create'
    call csmAlloc m.tso_dd.dd dd 'CAT' rest ':'nn
    call adrTso 'free  dd('dd')'
    return adrCsm('allocate' a1 rest)
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

csmLikeAtts: procedure expose m.
parse upper arg rz '/' dsn
    lc = adrCsm('dslist system('rz') dsnMask('dsn')')
    if stemsize <> 1 then
        call err 'csmLikeAtts stemSize='stemsize 'for dsn='dsn
    if abbrev(dsOrg.1, 'PO') then
        r = 'dsorg(po) dsnType(library)'
    else
        r = 'dsorg('dsOrg.1')'
    r = r 'mgmtClas('mgmtClas.1')'                       ,
       /* 'dataClas('dataClas.1')'   */                  ,
          'recFM('strip(translate('1 2 3', recFm.1, '123'))')'  ,
          'lRecl('lRecl.1')'                         ,
          'space('tracksused.1','  tracks.1') tracks'
    /*    if \ datatype(tracksused.1, 'n') then do
              if \ datatype(tracks.1, 'n') then
                  r = r 'space('tracks.1',' tracks.1')'
              if \ datatype(tracks.1, 'n') then
                  tracks.1 = tracksUsed.1   */

    say '???csmLike' rz'/'dsn '==>' r
    return r
endProcedure csmLikeAtts
/*--- execute a rexx (under tso) in another rz
           here we use rexx TPSYSIKJ which was written for
           jcl procedure RM@IKJ01
arguments
rz   which rz to run rexx
proc the (remote) procedure library to use
opt  options
cmd  the tso command to execute
----------------------------------------------------------------------*/
/*--- execute a rexx (under tso) in another rz
          directly (without TPSYSIKJ) --------------------------------*/
csmExRx: procedure expose m.
parse arg rz, proc, opt, cmd
    pStem = opt
    if pStem = '' then
        pStem ='CSMEXRX'
    do cx=1 to (length(cmd)-1) % 68
        cmd.cx = substr(cmd, 68*cx-67,68)'-'
        end
    cmd.cx = substr(cmd, 68*cx-67)
    cmd.0 = cx
    timeout = 11
    if 0 then do
        call adrTso  'free ed(rmtSys)'  ,'*'
        call tsoFree tsoDD(rmtsPrt, 'a')
        call adrTso  'free dd(rmtsIn)','*'
        call adrTso  'free dd(sysproc)' ,'*'
        end
    call dsnAlloc rz"/"proc "dd(rmSyPro) rmtDDn(sysProc)"
    call dsnAlloc rz"/tmp.tsin new dd(rmTsIn) rmtDdn(sysTsIn) ::f "
    call tsoOpen rmTsIn, 'w'
    call writeDD rmTsIn, cmd.
    call tsoClose rmtsin
    call dsnAlloc rz"/tmp.prt new dd(rmtsprt) rmtDdn(sysTsPrt)",
                    "::f133"
    call dsnAlloc rz"/tmp.rmt new dd(rmtSys) timeout("timeout")"
    call adrtso "csmappc start pgm(csmexec)" ,
           "parm('select tsocmd(''csmappc allocate plu(*.rmtSys)" ,
                 "tpname(sysikj) dealloc '')')",
           "timeout("timeOut")", '*'
    if rc <> 0 | appc_rc <> 0 then do
        ee = 'csm tso exec rc='rc 'appc_rc='appc_rc
        say ee
        say '  rexx rz='rz 'proc='proc 'opt='opt
        say '  cmd='cmd
        call csmappcRcSay ggTsoCmd
        call readDD 'rmTsPrt', 'M.'pStem'.'
        call tsoClose rmtsPrt
        say m.pStem.0 'tso output lines'
        do px=1 to m.pStem.0
            say ' ' strip(m.pStem.px, 't')
            end
        call err ee
        end
    if opt <> '' then do
        call readDD 'rmTsPrt', 'M.'pStem'.'
        call tsoClose rmtsPrt
        end
    call tsoFree rmSyPro rmtsPrt rmtSys rmtsIn
    return
 endProcedure csmExRx

/*--- sys the re and result variables from csmAppcRc -----------------*/
csmappcRcSay: procedure expose appc_rc appc_reason appc_msg. ,
           appc_state_c appc_state_f
 parse arg cmd
     say 'rc='appc_rc 'reason='appc_reason ,
         'state_c='appc_state_c appc_state_f
     say '  for' cmd
     do ix=1 to appc_msg.0
         say ' ' appc_msg.ix
         end
     return appc_rc
 endProcedure csmappcRcSay

/*--- execute a single csmAppc command ------------------------------*/
csmAppc:
    return adrTso('csmAppc' arg(1), arg(2))
endProcedure csmAppc
/* copy csm end *******************************************************/
/* copy timing begin *************************************************/
timing: procedure expose m.
parse arg typ, c2, txt
    e1 = time('E')
    c1 = strip(sysvar('syscpu'))
    s1 = sysvar('syssrv')
    if typ \== '' then do
        if symbol('m.timing_ela') \== 'VAR' then
            call err 'timing('typ',' c2',' txt') ohne ini'
        if symbol('m.timing.typ.ela') \== 'VAR' then do
            m.timing.typ.ela = 0
            m.timing.typ.cpu = 0
            m.timing.typ.su  = 0
            m.timing.typ.cnt = 0
            m.timing.typ.cn2 = 0
            if symbol('m.timing_types') == 'VAR' then
                m.timing_types = m.timing_types typ
            else
                m.timing_types = typ
            if symbol('m.timing_say') \== 'VAR' then
                m.timing_say = 0
            end
        m.timing.typ.ela = m.timing.typ.ela + e1 - m.timing_ela
        m.timing.typ.cpu = m.timing.typ.cpu + c1 - m.timing_cpu
        m.timing.typ.su  = m.timing.typ.su  + s1 - m.timing_su
        m.timing.typ.cnt = m.timing.typ.cnt + 1
        if c2 \== '' then
           m.timing.typ.cn2 = m.timing.typ.cn2 + c2
        end
    m.timing_ela = e1
    m.timing_cpu = c1
    m.timing_su  = s1
    if m.timing_say then
        say left(typ, 10)right(c2, 10) 'ela='e1 'cpu='c1 'su='s1 txt
    return
endProcedure timing

timingSummary: procedure expose m.
    say 'timing summary' time()
    do tx = 1 to words(m.timing_types)
        typ = word(m.timing_types, tx)
        say left(typ, 10)right(m.timing.typ.cnt,  7)       ,
                      || right(m.timing.typ.cn2,  7)       ,
                         'cpu='right(m.timing.typ.cpu, 10) ,
                         'su='right(m.timing.typ.su, 10)
        end
    return
endProcedure timingSummary
/* copy timing end ***************************************************/
/* copy ii begin ********* Installation Info *************************/
iiini: procedure expose m.
    if m.ii_ini == 1 then
        return
    m.ii_ini = 1
    m.ii_rz = ''
    m.ii.rzC = ''
    i = 'RZ1 1 S1 DBTF T DTF DVTB V DTB DBOC C DOC' ,
        'RZ2 2 S2 DBOF F DOF DVBP P DBP DP2G Q DP2' ,
        'RR2 R R2 DBOF F DOF DVBP P DBP DP2G Q DP2' ,
        'RZ4 4 S4 DBOL O DOL DP4G U DP4' ,
        'RZX X X2 DE0G E DE0 DEVG M DEV DPXG I DPX DX0G X DX0' ,
        'RZY Y Y2 DE0G E DE0 DEVG M DEV DPYG Y DPY' ,
        'RZZ Z Z2 DE0G E DE0 DEVG M DEV DPZG N DPZ'
    m.ii_rz = ''
    m.ii_rzC = ''
    do wx=1 by 3 to words(i)
        parse value subWord(i, wx, 3) with w1 w2 w3
        if abbrev(w1, 'R') & length(w1) == 3 then do
           rz = w1
           m.ii_DbSys.rz = ''
           m.ii_rz = strip(m.ii_rz rz)
           m.ii_rzC = m.ii_rzC || w2
           call iiA1 ii_sys2rz, w3, rz
           call iiA1 ii_rz2c, rz, w2
           end
        else if abbrev(w1, 'D') & length(w1) == 4 then do
           m.ii_DbSys.rz = strip(m.ii_DbSys.rz w1)
           call iiA1 ii_db2c, w1, w2
           call iiA1 ii_mbr2db, w3, w1
           end
        else
            call err 'bad w1' w1 w2 w3
        end
    return
endProcedure iiIni

iiA1: procedure expose m.
parse arg st, key ,val
    if symbol('m.st.key') \== 'VAR' then
        m.st.key = val
    else if m.st.key \== val then
        call err 'already <> defined' st'.'key'='m.st.key 'val='val
    return
endProcedure iiA1

iiMbr2DbSys: procedure expose m.
parse upper arg mbr, def
    return iiLazy(ii_mbr2db, left(mbr, 3), arg(), def, 'member')

iiRz2C: procedure expose m.
parse upper arg rz, def
    return iiLazy(ii_rz2c, rz, arg(), def, 'rz')

iiRz2Dsn: procedure expose m.
parse upper arg rz
    return overlay('Z', rz, 2)

iiDBSys2C: procedure expose m.
parse upper arg db, def
    return iiLazy(ii_db2c, db, arg(), def, 'dbSys')

iiSys2RZ: procedure expose m.
parse upper arg sys, def
    return iiLazy(ii_sys2rz, left(sys, 2), arg(), def, 'sys')

iiLazy: procedure expose m.
parse arg st, key, cA, def, txt
    if symbol('m.st.key') == 'VAR' then
        return m.st.key
    if m.ii_ini == 1 then
        if cA > 1 then
            return def
        else
            return err('no' txt'='key 'in ii' st)
    call iiIni
    return iiLazy(st, key, txt)
endProcedure iiRz2C
/* copy ii end   ********* Installation Info *************************/
/* 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 tsoOpen grp, 'R'
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 tsoClose 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 m.tso_stmt, ggRet
    call outtrap m.tso_trap.
    address tso m.tso_stmt
    m.tso_rc = rc
    call outtrap off
    if m.tso_rc == 0 then
        return 0
    m.tso_trap = ''
    do ggXx=1 to min(7, m.tso_trap.0)
        m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
        end
    if m.tso_trap.0 > 7 then do
        if m.tso_trap.0 > 14 then
            m.tso_trap = m.tso_trap'\n............'
        do ggXx=max(8, m.tso_trap.0-6) to m.tso_trap.0
            m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
            end
        end
    if ggRet == '*' then
        return m.tso_rc
    else if wordPos(m.tso_rc, ggRet) > 0 then
        return m.tso_rc
    else
        call err 'adrTso rc=' m.tso_rc 'stmt='m.tso_stmt m.tso_trap
endSubroutine 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 arg(2)
     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:  tsoOpen...'R', readDD*,  tsoClose
        write: tsoOpen...'W', writeDD*, tsoClose

        readDD returns true if data read, false at eof
        do not forget that open is mandatory to write empty file|
***********************************************************************/

/*--- open dd for read (rw='R') or write (rw='W') --------------------*/
tsoOpen: procedure expose m.
parse upper arg dd, rw
    return adrTso('execio' 0 'disk'RW dd '(open)')
return /* end tsoOpen */

/*--- close dd -----------------------------------------------------*/
tsoClose: procedure expose m.
    parse upper arg dd
    return adrTso('execio 0 diskR' dd '(finis)')
endProcedure tsoClose

/*--- 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 */

/*--- 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

/*--- 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
    parse value dsnAlloc('dd('m.m.dd')' m.m.dsn) with m.m.dd m.m.free
    call tsoOpen m.m.dd, 'R'
    return m
endProcedure readDDNxBegin

/*--- return the stem of the next line, or '' at end -----------------*/
readNx: procedure expose m.
parse arg m
    m.m.cx = m.m.cx + 1
    if m.m.cx <= m.m.0 then
        return m'.'m.m.cx
    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 stem of the curr line, '' at end --------------------*/
readNxCur: procedure expose m.
parse arg m
    if m.m.cx <= m.m.0 then
        return m'.'m.m.cx
    else
        return ''
endProcedure readNxCur

/*--- 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 tsoClose m.m.dd
    call tsoFree m.m.free
    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 pos('(', w) > 0 then
            leave
        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.tso_trap.1 = ''
        m.tso_trap.2 = ''
        m.tso_trap.3 = ''
        res = dsnAlloc(spec, pDi, pDD, '*')
        if \ datatype(res, 'n') then
            return res
        msg = m.tso_trap.1'\n'm.tso_trap.2'\n'm.tso_trap.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 dd == '-' & pDD \== '' then
        dd = pDD
    if dd == '-' then
        dd = 'DD*'
    dd = tsoDD(dd, 'a')
    m.tso_dd.dd = ''
    if na == '-' & di == '-' & rest = '' then
        return dd
    if di = '-' then
        if pDi == '' then
            di = 'SHR'
        else
            di = pDi
    if pos('(', na) < 1 then
        nop
    else if di = 'MOD' then
        call err 'disp mod for' na
    else
        di = 'SHR'
    if lastPos('/', na, 6) > 0 then
        rx = csmAlloc(na dd di rest, retRc)
    else
        rx = tsoAlloc(na dd di rest, retRc)
    if rx = 0 then
        return dd dd
    else
        return rx
endProcedure dsnAlloc

/*--- find a free dd with prefix dd ----------------------------------*/
tsoDD: procedure expose m.
parse upper arg dd, f, noErr
    if symbol('m.tso_ddAll') \== 'VAR' then do
        call errIni
        m.tso_ddAll = ''
        end
    if f == '-' then do
        ax = wordPos(dd, m.tso_ddAll)
        if ax > 0 then
            m.tso_ddAll = delWord(m.tso_ddAll, ax, 1)
        else if noErr \== 1 then
            call err 'tsoDD dd' dd 'not used' m.tso_ddAll
        end
    else if f <> 'A' then
        call err 'tsoDD bad fun' f
    else do
        if right(dd, 1) = '*' then do
            d0 = left(dd, length(dd)-1) || m.err.screen
            dd = d0
            do dx=1 while wordPos(dd, m.tso_ddAll) > 0
                dd = d0 || dx
                end
            end
        else if pos('?', dd) > 0 then
            dd = repAll(dd, '?', m.err.screen)
        if wordPos(dd, m.tso_ddAll) < 1 then
            m.tso_ddAll = strip(m.tso_ddAll dd)
        end
    return dd
endProcedure tsoDD

tsoAlloc: procedure expose m.
parse arg na dd disp rest ':' nn, retRc
    dd = translate(dd)
    c = 'alloc dd('dd')' disp
    if na == '-' then
        m.tso_dd.dd = ''
    else if na \== 'INTRDR' then do
        c = c "DSN('"na"')"
        m.tso_dd.dd = na
        end
    else do
        c = c "sysout(*) writer(intRdr)"
        m.tso_dd.dd = '*intRdr'
        end
    if wordPos(disp, 'NEW MOD') > 0 & nn \== '' then
        c = c dsnCreateAtts(,nn)
    if adrTso(c rest, '*') = 0 then
        return 0
    if pos('IKJ56246I', m.tso_trap) > 0 then
        if pos('TED, FILE IN USE', m.tso_trap) > 0 then do
     /* IKJ56246I DATA SET A540769.WK.REXX NOT ALLOCATED, FILE IN USE */
        say 'tsoAlloc dd' dd 'already in use:' substr(m.tso_trap, 3)
        say '.... trying to free'
        call tsoFree dd, 1
        say '.... retrying to allocate' c rest
        if adrTso(c rest, '*') = 0 then
            return 0
        end
    if nn \= '' & wordPos(disp, 'OLD SHR') > 0 ,
          & pos('IKJ56228I', m.tso_trap) > 0 ,
          & pos(' NOT IN CATALOG ', m.tso_trap) > 0 then do
       /* IKJ56228I DATA SET A540769.XY.REXX NOT IN CATALOG OR CATAL*/
        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
    if pos('*', retRc) < 1 & wordPos(m.tso_rc, retRc) < 1 then
        call err 'tsoAlloc rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
    call saySt(splitNl(err, 'tsoAlloc rc='m.tso_rc ,
                  'tsoStmt='m.tso_stmt m.tso_trap))
    return m.tso_rc
endProcedure tsoAlloc

dsnLikeAtts: procedure expose m.
parse upper arg dsn, isFile
     if isFile then do
         ddDsn = m.tso_dd.dsn
         if lastPos('/', m.tso_dd.dsn, 4) < 1 then
              return tsoLikeAtts(dsn, 1)
         dsn = m.tso_dd.dsn
         end
     sx = lastPos('/', dsn, 4)
     if sx < 1 then
         return tsoLikeAtts(dsn, 0)
     else if abbrev(dsn, '*/') | abbrev(dsn, sysVar('SYSNODE')'/') then
         return tsoLikeAtts(substr(dsn, sx+1), 0)
     else
         return csmLikeAtts(dsn)
endProcedure dsnLikeAtts

tsoLikeAtts: procedure expose m.
parse arg dsn, isFile
    rc = listDsi("'"dsn"'" copies('FILE', isFile) "SMSINFO")
    if rc = 0 then
        r = ''
    else if rc = 4 & sysReason = 19 then do
        r = 'UCOUNT(30)' /* 7.8.13 corrected attribute */
        say 'creating' dsn 'with multi volume' mv
        end
    else if rc ^= 0 then
        call err 'listDsi rc' rc 'reason' sysReason,
                             sysMsgLvl1 sysMsgLvl2
    if right(sysDsSms, 7) == 'LIBRARY' | abbrev(sysDsSms, 'PDS') ,
                      | sysDsOrg = 'PO' then
         r = 'dsOrg(PO) DSNTYPE(LIBRARY)' r
    else
         r = "dsOrg("sysDSorg")" r
    if sysUnits = 'TRACK' then
        sysUnits = 'TRACKS'
    return r "MGMTCLAS("sysMgmtClass")",
        "DATACLAS("sysDataClass")" ,
        "RECFM("strip(translate('1 2 3', ' 'sysREcFM, '123'))")",
        "LRECL("SYSLRECL")",
        "SPACE("sysPrimary"," sysSeconds")" ,
            sysUnits || left('S', sysUnits == 'TRACK')
    /*  "blksize("sysBLkSIZE")" removed 3.4.13: let sms do the magic */
endProcedure tsoLikeAtts

tsoFree: procedure expose m.
parse arg ddList, tryClose
    do dx=1 to words(ddList)
        dd = word(ddList, dx)
        if adrTso('free dd('dd')', '*') <> 0 then do
            if pos('IKJ56861I', m.tso_trap) > 0 & tryClose == 1 then
              if pos('NOT FREED, DATA SET IS OPEN', m.tso_trap),
                    > 0 then do
                  /* IKJ56861I  FILE A1 NOT FREED, DATA SET IS OPEN */
                say 'dataset open:' substr(m.tso_trap, 3)
                say '.... trying to close'
                if adrTso('execio 0 diskR' dd '(finis)', '*') = 0 then
                   call adrTso 'free dd('dd')', '*'
                end
            if m.tso_rc \== 0 then
                call saySt(splitNl(err, 'rc='m.tso_rc ,
                            'tsoStmt='m.tso_stmt m.tso_trap))
            end
        call tsoDD dd, '-', 1
        end
    return
endProcedure tsoFree

dsnCreateAtts: procedure expose m.
parse arg dsn, atts
    res = ''
    if dsn \== '' then
        res = "dataset('"dsnSetMbr(dsn)"')"
    if abbrev(atts, ':') then do
        parse var atts a1 atts
        rl = substr(a1, 3)
        if abbrev(a1, ':F') then do
            if rl = '' then
                rl = 80
            res =  res "recfm("space(f b)") lrecl("rl")"
            end
        else if abbrev(a1, ':V') then do
            if rl = '' then
                rl = 32756
            res =  res "recfm("space(v b)") lrecl("rl")"
            end
        else if abbrev(a1, ':L') then
            res = res dsnLikeAtts(rl, 0)
        else if abbrev(a1, ':D') then
            res = res dsnLikeAtts(rl, 1)
        else
            call err 'csnCreateAtt bad :' a1
        end
    aU = ' 'translate(atts)
    hasOrg = pos(' DSORG(', aU) > 0 | pos(' DSNTYPE(', aU) > 0
    if \ hasOrg & pos('(', dsn) > 0 then do
        hasOrg = 1
        atts = atts 'dsorg(po) dsntype(library)'
        end
    if hasOrg then do
         cx = pos(' DSORG(', ' 'translate(res))
         if cx > 0 then do
             cy = pos(')', res, cx)
             res = delstr(res, cx, cy+1-cx)
             say '???? del dsnOrg ==>' res
             end
         cx = pos(' DSNTYPE(', ' 'translate(res))
         if cx > 0 then do
             cy = pos(')', res, cx)
             res = delstr(res, cx, cy+1-cx)
             say '???? del dsntype ==>' res
             end
         end
    res = res atts
    aU = ' 'translate(res)
    if pos(' MGMTCLAS(', aU) < 1 then
        res = res 'mgmtclas(COM#A091)'
    if pos(' SPACE(', aU) < 1 then
        res = res 'space(100, 500) cylinders'
    return res
endProcedure dsnCreateAtts
/*--- check if a dataset is archive ------------------------------------
          returns 'ok'                    if dataset on disk
                  'not'                   if dataset is not catalogued
                  'arc'                   if dataset archived
                  listDsi errorMsg        otherwise ------------------*/
dsnArc: procedure expose m.
parse upper arg dsn
    lc = listDsi("'"strip(dsn)"' noRecall")
    if lc = 0 then
        return 'ok'
    else if lc=4 & sysReason = 19 then  /* multiple volumes */
        return 'ok'
    else if lc=16 & sysReason = 5 then
        return 'notCat'
    else if lc=16 & sysReason = 9 then
        return 'arc'
    else
        return 'listDsi cc='lc', sysReason='sysReason ,
                          'm2='sysMsgLvl2', m1='sysMsgLvl1
endProcedure dsnArc
/*--- 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)'
    call tsoFree word(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)'
    call tsoFree word(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
    parse value dsnAlloc(frSpec, 'SHR', 'FRDD') with frDD frFr
    parse value dsnAlloc(toSpec, 'OLD', 'TODD') with toDD toFr
    call tsoOpen frDD, 'R'
    call tsoOpen toDD, 'W'
    cnt = 0
    do while readDD(frDD, r.)
        call writeDD toDD, r.
        cnt = cnt + r.0
        end
    call tsoClose frDD
    call tsoClose toDD
    call tsoFree frFr toFr
    if ggSay == 1 | m.debug == 1 then
       say cnt 'records copied from' frSpec 'to' to toSpec
    return
endSubroutine writeDsn
/* copy adrTso end ****************************************************/
/* copy csv begin *****************************************************/
csvIni: procedure expose m.
    if m.csv.ini == 1 then
        return
    m.csv.ini = 1
    call jIni
    call classNew "n CsvRdr u JRWO, f RDR r", "m",
        , "jReset m.m.rdr = arg",
        , "jOpen call csvRdrOpen m, opt",
        , "jClose call jClose m.m.rdr; call oMutatName m, 'CsvRdr'"
    call classNew "n CsvRdrR u CsvRdr", "m",
        , "jReadO return csvRdrReadO(m)"
    call classNew "n CsvWrt u JRW, f RDR r", "m",
        , "jReset m.m.rdr = arg",
        , "jOpen call csvWrtOpen m, opt",
        , "jClose call jClose m.m.rdr; call oMutatName m, 'CsvWrt'"
    call classNew "n CsvWrtR u CsvWrt", "m",
        , "jRead return csvWrtRead(m, var)"
    return
endProcedure csvIni

/*--- create a new csvRdr --------------------------------------------*/
csvRdr: procedure expose m.
parse arg rdr
    return jReset(oNew('CsvRdr'), rdr)
endProcedure csvRdr

/*--- open csvRdr: read first line and create dataClass --------------*/
csvRdrOpen: procedure expose m.
parse arg m
    call jOpen m.m.rdr, '<'
    if jRead(m.m.rdr, m'.LINE') then do
        ff = 'f' repAll(m.m.line, ',', ' v, f ') 'v'
        m.m.class = classNew("n* CsvF u" ff)
        end
    call oMutatName m, 'CsvRdrR'
    return
endProcedure csvRdrOpen

/*--- read next line and return derived object -----------------------*/
csvRdrReadO: procedure expose m.
parse arg m
    do until m.m.line <> ''

        if \ jRead(m.m.rdr, m'.LINE') then
            return ''
        end
    var = oNew(m.m.class)
    ff = oClaMet(m.m.class, 'oFlds')
    s = m'.SCAN'
    call scanSrc s, m.m.line
    do fx=1
        f1 = substr(m.ff.fx, 2)
        if scanString(s, '"') then
            m.var.f1 = m.s.val
        else do
            call scanUntil s, ','
            m.var.f1 = m.s.tok
            end
        if scanEnd(s) then
            leave
        if \ scanLit(s, ',') then
            call scanErr s, ',' expected
        end
    if fx <> m.ff.0 then
        call scanerr s, 'csv cla' m.ff.0 'fields but' cx 'in line'
    return var
endProcedure csvRdrReadO

/*--- create a new csvRdr --------------------------------------------*/
csvWrt: procedure expose m.
parse arg rdr
    return jReset(oNew('CsvWrt'), rdr)
endProcedure csvWrt

/*--- open csvRdr: read first line and create dataClass --------------*/
csvWrtOpen: procedure expose m.
parse arg m
    call jOpen m.m.rdr, '<'
    m.m.class = ''
    m.m.o1    = ''
    call oMutatName m, 'CsvWrtR'
    return
endProcedure csvWrtOpen

/*--- read next line and return derived object -----------------------*/
csvWrtRead: procedure expose m.
parse arg m, var
    if m.m.o1 == '' then
        i1 = jReadO(m.m.rdr)
    else do
        i1 = m.m.o1
        m.m.o1 = ''
        end
    if i1 == '' then
        return 0
    if m.m.class == '' then do
        m.m.class = objClass(i1)
        m.m.o1 = i1
        t = ''
        ff = oFlds(i1)
        do fx=1 to m.ff.0
            t = t','substr(m.ff.fx, 2)
            end
        m.var = substr(t, 2)
        return 1
        end
    else do
        t = ''
        ff = oFlds(i1)
        do fx=1 to m.ff.0
            f1 = i1 || m.ff.fx
            val = m.f1
            if pos(',', val) > 0 | pos('"', val) > 0 then
                t = t','quote(val, '"')
            else
                t = t','val
            end
        m.var = substr(t, 2)
        return 1
        end
endProcedure csvWrtRead

/* copy csv end   *****************************************************/
/* copy j begin *******************************************************
    the j framework
         jReset
         jOpen
         jClose
         jRead
         jWrite
***********************************************************************/
jRead: procedure expose m.
parse arg m, var
    met = objMet(m, 'jRead')
    if m.m.jReading then
        interpret met
    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'
    met = objMet(m, 'jReadO')
    if m.m.jReading then
        interpret met
    else
        return err('jReadO('m',' var') but not opened r')
endProcedure jReadO

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

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

jWriteAll: procedure expose m.
parse arg m, rdr
    rdr = o2file(rdr)
    met = objMet(m, 'jWriteAll')
    if \ m.m.jWriting then
        return err('jWriteAll('m',' rdr') but not opened w')
    interpret met
    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')')
    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
    met = objMet(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 met
            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 met
            m.m.jWriting = 1
            end
        end
    m.m.jUsers = oUsers + 1
    return m
endProcedure jOpen

jClose: procedure expose m.
parse arg m
    met = objMet(m, 'jClose')
    oUsers = m.m.jUsers
    if oUsers = 1 then do
        interpret met
        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))
    fmt = '@cat%('fmt'%)'
    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'%.1', m.line)
        end
    call jClose m
    return res || f(fmt'%.2')
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 m.m.src == '' then
            m.m.src = ' '
        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
    sta = 'tt'
    res = ''
    do forever
        do while scanSBEnd(m)
            if \ jCatSqlNl(m) then
                return strip(res)
            end
        bx = m.m.pos
        sta = scanSql2Stop(m, sta, stop)
        s1 = left(sta, 1)
        if pos(s1, stop) > 0 then do
            if res <> '' then
                return strip(res)
            end
        else if s1 == '-' | s1 == '/' then
            res = res' '
        else if pos('/', sta) = 0 then
            res = res || substr(m.m.src, bx, m.m.pos - bx)
        end
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 classIni
    am = "call err 'call of abstract method"
    c1 = classNew('n JRW u ORun, f JREADING v, f JWRITING v', 'm',
        , "new return jReset("m.class.basicNew", 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")
    m.class.forceDown.c1 = c1'#new'
    c2 = classNew('n JRWDeleg u JRW', 'm',
        , "new return jReset("m.class.basicNew", arg)",
        , "jRead return jRead(m.m.deleg, var)" ,
        , "jReadO return jReadO(m.m.deleg)" ,
        , "jWrite  call jWrite(m.m.deleg, line)" ,
        , "jWriteO call jWrite(m.m.deleg, var)" ,
        , "jWriteAll call jWriteAll m.m.deleg, rdr",
        , "jWriteNow call jWriteNow m.m.deleg, rdr",
        , "jReset    if arg \== '' then m.m.deleg = arg;",
                                   "else call jReset m.m.deleg;",
        , "jOpen     call jOpen m.m.deleg,' opt; return m" ,
        , "jClose    call jClose m.m.deleg; return m" )
    m.class.forceDown.c2 = c2'#new'
    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)
    m.j.out = jOpen(oNew('JSay'), '>')
    m.j.errRead  = "return err('jRead('m',' var') but not opened r')"
    m.j.errReadO = "return err('jReadO('m',' var') but not opened r')"
    m.j.errWrite = "return err('jWrite('m',' line') but not opened w')"
    m.j.errWriteO= "return err('jWriteO('m',' var') but not opened w')"
    call classNew "n JBuf u JRWO, f BUF s r", "m",
        , "jOpen call jBufOpen m, opt",
        , "jClose call oMutatName m, 'JBuf'",
        , "jReset call jBufReset m, arg",
        , "jRead" m.j.errRead ,
        , "jReadO" m.j.errReadO ,
        , "jWrite" m.j.errWrite ,
        , "jWriteO" m.j.errWriteO
    call classNew "n JBufOR u JBuf", "m",
        , "jRead return jBufORead(m, var)",
        , "jReadO return jBufOReadO(m)"
    call classNew "n JBufSR u JBuf", "m",
        , "jRead return jBufSRead(m, var)",
        , "jReadO return jBufSReadO(m)"
    call classNew "n JBufOW u JBuf", "m",
        , "jWrite call jBufOWrite m, line",
        , "jWriteO call jBufOWriteO m, var"
    call classNew "n JBufSW u JBuf", "m",
        , "jWrite call jBufSWrite m, line",
        , "jWriteO call jBufSWriteO 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

/*--- 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

/*--- open for input given rdr, or stdIn if missing ------------------*/
inOpen: procedure expose m.
parse arg rdr
    if rdr == '' then
        return jOpen(m.j.in, '<')
    else
        return jOpen(o2file(rdr), '<')
endProcedure

j2Buf: procedure expose m.
    parse arg rdr
    if oStrOrObj(rdr, m.j.in) then
        return jBuf(ggStr)
    if oClaInheritsOf(ggCla, '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

outStem: procedure expose m.
parse arg st
    do sx=1 to m.st.0
       call jWrite m.j.out, m.st.sx
       end
    return 0
endProcedure out


JRWDeleg: procedure expose m.
parse arg arg
    return oNew('JRWDeleg', arg)
endProcedure JRWDeleg

/*--- 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.allS = 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.allS = 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.allS = 1
    return m
endProcedure jBufReset

jBufOpen: procedure expose m.
parse arg m, opt
    if opt == m.j.cRead then do
        m.m.readIx = 0
        if m.m.allS then
            call oMutatName m, 'JBufSR'
        else
            call oMutatName m, 'JBufOR'
        return m
        end
    if opt == m.j.cWri then do
        m.m.buf.0 = 0
        m.m.allS = 1
        end
    else if opt \== m.j.cApp then
         call err 'jBufOpen('m',' opt') with bad opt'
    if m.m.allS then
        call oMutatName m, 'JBufSW'
    else
        call oMutatName m, 'JBufOW'
    return m
endProcedure jBufOpen

jBufOWrite: procedure expose m.
parse arg m, line
    call mAdd m'.BUF', s2o(line)
    return
endProcedure jBufOWrite
jBufSWrite: procedure expose m.
parse arg m, line
    call mAdd m'.BUF', line
    return
endProcedure jBufWrite

jBufWriteStem: procedure expose m.
parse arg m, st
    ax = m.m.buf.0
    if m.m.allS 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

jBufOWriteO: procedure expose m.
parse arg m, ref
    call mAdd m'.BUF', ref
    return
endProcedure jBufOWriteO

jBufSWriteO: procedure expose m.
parse arg m, ref
    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
    do ax=1 to m.m.buf.0
        m.m.buf.ax = s2o(m.m.buf.ax)
        end
    m.m.allS = 0
    call oMutatName m, 'JBufOW'
    call mAdd m'.BUF', ref
    return
endProcedure jBufWriteO

jBufOReadO: procedure expose m.
parse arg m
    nx = m.m.readIx + 1
    if nx > m.m.buf.0 then
        return ''
    m.m.readIx = nx
    return m.m.buf.nx
endProcedure jBufOReadO

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

jBufORead: 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
    m.var = o2String(m'.BUF.'nx)
    return 1
endProcedure jBufORead

jBufSRead: 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
    m.var = m.m.buf.nx
    return 1
endProcedure jBufRead

jBufTxtWriteO: procedure expose m.
parse arg m, ref
    if m.m.allS \== 1 then
        call err '1 \== allS' m.m.allS '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 = oFlds(ref)
        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

jSingle: procedure expose m.
parse arg m
    call jOpen m, '<'
    res = jReadO(m)
    two = jReadO(m)
    call jClose m
    if res == '' then
        if arg() < 2 then
             call err 'empty file in jSingle('m')'
        else
            res = arg(2)
    if two \== '' then
        call err '2 or more recs in jSingle('m')'
    return res
endProcedure jSingle
/* copy j end *********************************************************/
/* copy o begin ******************************************************
    an object is register for a class in o2c
    a class has a list of parents in cParent
    a class has a methodTable cMet with lazy initialization
        if the parent is class OLazyMet, a methof found there is
             a method generator
        otherwise an existing method is simply copied
***********************************************************************/
oIni: procedure expose m.
    if m.o.ini == 1 then
        return
    m.o.ini = 1
    call mIni
    m.o.escW = '!'
    m.o.lazyGen = 'OLazyMetGen' /* lazy method generator */
    call oAddCla m.o.lazyGen
    return
endProcedure oIni

/*--- return whether cl is a currently defined class ----------------*/
oIsCla: procedure expose m.
parse arg cl
    return symbol('m.o.cParent.cl') == 'VAR'
endProcedure oIsCla

/*--- add class cl with the given parents ---------------------------*/
oAddCla: procedure expose m.
parse arg cl, parents
    if verifId(cl, '.') > 0 | pos('.', cl) <> lastPos('.', cl) then
        call err 'bad class name' cl 'in oAddCla('cl',' parents')'
    if oIsCla(cl) then
        call err 'duplicate class' cl 'in oAddCla('cl',' parents')'
    do px=1 to words(parents)
        if \ oIsCla(word(parents, px)) then
            call err word(parents, px) 'is no class' ,
                    'in oAddCla('cl',' parents')'
        end
    m.o.cParent.cl = parents
    return
endProcedure oAddCla

/*--- add to class cl method met ------------------------------------*/
oAddMet: procedure expose m.
parse arg cl, met, cont
    if \ oIsCla(cl) then
        call err 'undefined class' cl 'in oAddMet('cl',' met',' cont')'
    if symbol('m.o.cMet.cl.met') == 'VAR' then
       call err 'duplicate method' met 'in oAddMet('cl',' met',' cont')'
    m.o.cMet.cl.met = cont
    return
endProcedure oAddMet
/*--- create an an object of the class className
        and call it's new method ------------------------------------*/
oNew: procedure expose m.
parse arg cl, arg, arg2, arg3
    if symbol('m.o.cParent.cl') \== 'VAR' then
        cl = class4name(cl)
    interpret oClaMet(cl, 'new')
endProcedure oNew

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

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

oClaInheritsOf: procedure expose m.
parse arg cl, sup    /* wkTst optimierung in classAdded */
    if symbol('m.o.cParent.cl') \== 'VAR' then
         cl = class4name(cl)
    if symbol('m.o.cParent.sup') \== 'VAR' then
         sup = class4name(sup)
    if cl == sup then
        return 1
    do sx=1 to words(m.o.cParent.cl)
        if oClaInheritsOf(word(m.o.cParent.cl, sx), sup) then
            return 1
        end
    return 0
endProcedure oClaInheritsOf
/*--- return the code of method me of object m
         set m to the address and ggClass to the class ---------------*/
objMet: procedure expose m. m ggClass
parse arg m, me
    if symbol('m.o.o2c.m') == 'VAR' then
         ggClass = m.o.o2c.m
    else if abbrev(m, m.o.escW) then
         ggClass = "w"
    else if arg() >= 3 then
        return arg(3)
    else
        return err('no class found for object' m)
    if symbol('m.o.cMet.ggClass.me') == 'VAR' then
       return m.o.cMet.ggClass.me
    code = oClaMet(ggClass, me, '---')
    if code \== '---' then
        return code
    else if arg() >= 3 then
         return arg(3)
    return err('no method' me 'in class' className(ggClass) ,
               'of object' m)
endProcedure objMet

oClaMet: procedure expose m.
parse arg cl, me
    if symbol('m.o.cMet.cl.me') == 'VAR' then
       return m.o.cMet.cl.me
    if \ oIsCla(cl) then do
        c2 = class4Name(cl, '')
        if c2 \== ''  & oIsCla(c2) then do
            cl = c2
            if symbol('m.o.cMet.cl.me') == 'VAR' then
                return m.o.cMet.cl.me
            end
        else do
            if arg() >= 3 then
                return arg(3)
            else
                return err('no class' cl 'in oClaMet('cl',' me')')
            end
        end
    code = oLazyMetGen(m.o.lazyGen, cl, me)
    do px = 1 to words(m.o.cParent.cl) while code == '---'
        code = oClaMet(word(m.o.cParent.cl, px), me, '---')
        end
    if code == '---' then do
        if arg() >= 3 then
            return arg(3)
        else
            return err('no met' me 'in class' cl)
        end
    m.o.cMet.cl.me = code
    return code
endProcedure oClaMet

oLazyMetGen: procedure expose m.
parse arg lg, cl, me
    if symbol('m.o.cMet.lg.me') \== 'VAR' then
        return '---'
    interpret m.o.cMet.lg.me
endProcedure oLazyMetGen

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


oClear: procedure expose m.
parse arg m
    interpret objMet(m, 'oClear')
    return m
endProcedure oClear

/*--- mutate object m to the class cl -------------------------------*/
oMutate: procedure expose m.
parse arg m, cl
    m.o.o2c.m = cl
    return m
endProcedure oMutate

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

/*--- copy object m of class cl to t --------------------------------*/
oClaCopy: procedure expose m.
parse arg cl, m, t
    interpret "drop cl;" oClaMet(cl, 'oCopy')
endProcedure oClaCopy

/*--- copy object m to t / create a new object if t=='' -------------*/
oCopy: procedure expose m.
parse arg m, t
    interpret objMet(m, 'oCopy')
endProcedure oCopy

/*--- 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 of object m No Procedure:
        ??? optimize: class only run ???
         use from caller unprotected---------------------------------*/
oRunNP: procedure expose m.
    interpret objMet(arg(1), 'oRun')
    return
endProcedure oRunNP

/*--- run method oRun and return output in new JBuf ------------------*/
oRun2File: procedure expose m.
parse arg rn
    b = jBuf()
    call pipe '+F' , b
    call oRun rn
    call pipe '-'
    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 arg() = 1 then
        fmt = ' '
    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 = oFlds(m)
        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.o.escW || str
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 address (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
    m.class.in2 = 0
    call oIni
    call mapIni
    call mNewArea 'CLASS', 'CLASS'
    call mapReset 'CLASS.N2C'  /* name to class */
    m.class.classV = classBasicNew('u', 'v')
    m.class.classW = classBasicNew('u', 'w')
    m.class.classO = classBasicNew('u', 'o')

    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')
    m.class.basicNew = "oMutate(mNew(cl), cl)"
    call oAddMet m.o.lazyGen, 'new', "return classGenNew(cl, me)"
    call oAddMet m.o.lazyGen,'oClear',"return classGenClear(cl, me)"
    call oAddMet m.o.lazyGen,'oFlds',"return classGenFlds(cl, me)"
    call oAddMet m.o.lazyGen, 'oCopy', "return classGenCopy(cl, me)"

    cr = mIterBegin('CLASS')
    do while assNN('cr', mIter(cr))
        call classFinish cr
        call oClaMet cr, 'oFlds' /* generate flds */
        end
    m.class.in2 = 1

    call oAddMet m.class.classV, 'oCopy', "return oCopyV(m, t)"
    call classAddMet m.class.classV, 'o2String return m.m'
    call classAddMet m.class.classW, 'o2String return substr(m, 2)'
    call 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)'

    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 className

/*--- 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
    if ty == 'f' & nm == '=' then
        nm = ''
    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
    if length(ty) \== 1 | pos(ty, 'ufscrm') < 1 then
        call err 'bad type' ty': classBasicNew('ty',' nm',' cl')'
    else if nm == '' & pos(ty, 'm') > 0 then
        call err 'empty name: classBasicNew('ty',' nm',' cl')'
    else if nm \== '' & ty \== 'c' & verifId(nm) > 0 ,
        & \ (ty == 'f' & nm == '=') 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 isNew & m.class.in2 then
        call classFinish n
    if nmTy == '' | nmTy == '*' then
        call mapAdd class.n2c, clEx, n
    return n
endProcedure classNew

/*--- to the finish for new class cl -------------------------------*/
classFinish: procedure expose m.
parse arg cl, force
    call oMutate cl, m.class.class
                        /* find super and sub classes */
    m.cl.sub = ''
    sups = ''
    do ux=1 to m.cl.0
        u1 = m.cl.ux
        if m.u1 \== 'u' then
            iterate
        if wordPos(u1, sups) > 0 then
            call err u1 'already in sups' sups': classSuperSub('cl')'
        sups = sups u1
        if wordPos(m.cl.SUB, cl) > 0 | symbol('m.u1.sub') \== 'VAR' then
            call err cl 'is already in' u1'.sub' u1.SUB  ,
                || ': classSuperSub('cl')'
        m.u1.sub = strip(m.u1.sub cl)
        end
    m.cl.super = sups
                        /* add class to o */
    call oAddCla cl, sups
    if pos(m.cl, 'mfrsv') < 1 then do
        allMets = ''
        forceMets = ''
        do cx=1 to m.cl.0
            ch = m.cl.cx
            if m.ch == 'm' then do
                call oAddMet cl, m.ch.name, m.ch.met
                allMets = allMets m.ch.name
                end
            else if symbol('m.class.forceDown.ch') == 'VAR' then
                forceMets = forceMets m.class.forceDown.ch
            end
        myForce = ''
        do fx=1 to words(forceMets)
            parse value word(forceMets, fx) with fCla '#' fMet
            if wordPos(fMet, allMets) < 1 then do
                call oAddMet cl, fMet, m.o.cMet.fCla.fMet
                myForce = myForce cl'#'fMet
                allMets = allMets fMet
                end
            end
        if myForce \== '' then
            m.class.forceDown.cl = strip(myForce)
        end
    if cl == m.class.class then
        call mAlias 'CLASS', cl
    else  /* object addresses */
        call mNewArea cl, 'O.'substr(cl,7)
    if m.cl \== 'u' | m.cl.name == '' then
        return
    call mAlias cl, m.cl.name
    return
endProcedure classFinish

classAddMet: 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')'
    call mAdd cl, classNew('m' met code)
    call oAddMet cl, met, code
    return cl
endProcedure classAddMet
/*--- 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

classGenNew: procedure expose m.
parse arg cl, met
     return  "m=" m.class.basicNew";" oClaMet(cl, 'oClear') ";" ,
             "return m"
endProcedure classGenNew

classGenFlds: procedure expose m.
parse arg cl, met
    m.cl.flds.0 = 0
    m.cl.stms.0 = 0
    m.cl.stFl.0 = 0
    call classGenFldsAdd cl, cl
    return cl'.FLDS'
endProcedure classGenFlds

/*--- add the the fields of class cl to stem f ----------------------*/
classGenFldsAdd: procedure expose m.
parse arg f, cl, nm
    f2CNm = 'F2C'nm
    if cl == m.class.classV | cl == m.class.classW | m.cl=='r' then do
        if symbol('m.f.f2cNm') \== 'VAR' then
               m.f.f2cNm = cl
           else if m.f.f2cNm \== cl then
               return  err('f2c mismatch f='f f2cNm 'cl='cl ,
                           'old='m.f.f2cNm)
        if nm == '' then do
            call mMove f'.FLDS', 1, 2
            m.f.flds.1 = ''
            end
        else if pos('*', nm) < 1 then
            call mAdd f'.FLDS', nm
        else
            call mAdd f'.STFL', nm
        return 0
        end
    if m.cl == 's' | (m.cl == 'u' & m.cl.name \== '') then do
        nestNm = 'NEST'nm
        if symbol('m.f.nestNm') \== 'VAR' then
            m.f.nestNm = cl
        else if wordPos(cl, m.f.nestNm) > 0 then
            call err 'classGenFldsAdd dup' cl',' f nestNm m.f.nestNm
        else
            m.f.nestNm = m.f.nestNm cl
        end
    if m.cl = 's' then do
        if m.cl.class == '' then
            call err 'stem null class'
        call mAdd f'.STMS', nm
        m.f.f2cNm.0 = m.class.classV
          return classGenFldsAdd(f, m.cl.class, nm'.*')
        end
    if m.cl = 'f' then
        if m.cl.name == '' then
               return classGenFldsAdd(f, m.cl.class, nm)
        else
               return classGenFldsAdd(f, m.cl.class, nm'.'m.cl.name)
    if m.cl.class \== '' then
        return classGenFldsAdd(f, m.cl.class, nm)
    do tx=1 to m.cl.0
        call classGenFldsAdd f, m.cl.tx, nm
        end
    return 0
endProcedure classGenFldsAdd

classGenClear: procedure expose m.
parse arg cl, met
    r = ''
    call oClaMet cl, 'oFlds'
    do fx=1 to m.cl.flds.0
        f1 = m.cl.flds.fx
        if f1 == '' then
            c1 = cl
        else do
            c1 = substr(f1, 2)
            c1 = m.cl.f2c.c1
            end
        if c1 == m.class.classW then
            r = r classGenStmt(f1, "m.m~ = '"m.o.escW"';")
        else
            r = r classGenStmt(f1,  "m.m~ = '';")
        end
    do sx=1 to m.cl.stms.0
        if pos('*', m.cl.stms.sx) < 1 then
            r = r classGenStmt(m.cl.stms.sx, "m.m~.0 = 0;")
        end
    return r
endProcedure classGenClear

classGenStmt: procedure expose m.
parse arg f, st, resWo
    isNice = translate(f) == f
    resWo = translate(resWo) 'GGFF M'
    fDod = '.'f'.'
    do wx=1 to words(resWo) while isNice
        isNice = pos('.'word(resWo, wx)'.', fDot) < 1
        end
    if isNice then
        return repAll(st, '~', f)
    else
        return "ggFF = '"substr(f, 2)"';" repAll(st, '~', '.ggFF')
endProceduer classGenAss

/*--- return true if src is a rexxVariable a, m.a.c etc. -------------*/
rxIsVar: procedure expose m.
parse arg src
    if pos(left(src, 1), m.ut_rxN1) > 0 then
        return 0
    else
        return verify(src, m.ut_rxId) = 0
endProcedure rxIsVar

/*--- return true if src is a rexxConstant rerpresenting its value ---*/
rxIsConst: procedure expose m.
parse arg src, vars                                                   c
    if \ rxIsVar(src) then
        return 0
    srU = translate(src)
    if srU \== src then
        return 0
    srU = '.'srU'.'
    if pos('.GG', srU) > 0 then
        return 0
    if vars == '' then
        return 1
    upper vars
    do vx=1 to words(vars)
        if pos('.'word(vars, vx)'.', vars) > 0 then
            return 0
        end
    return 1
endProcedure rxIsConst

/*--- return rexx code m.cc or mGet('cc') ----------------------------*/
rxMGet: procedure expose m.
parse arg v1, cc, vars
    if cc == '' then
        return 'm.'v1
    else if rxIsConst(cc, vars) then
        return 'm.'v1'.'cc
    else
        return 'mGet('v1 || quote('.'cc, "'")')'
endProcedure rxMGet
classGenCopy: procedure expose m.
parse arg cl, me
    r = repAll("if t == '' then t =" m.class.basicNew ";" ,
               "else call oMutate t, cl;", 'cl', "'"cl"'")
    ff = oClaMet(cl, 'oFlds')            /* build code for copy */
    do fx=1 to m.cl.flds.0
        r = r classGenStmt(m.cl.flds.fx, 'm.t~ = m.m~;', 't')
        end
    do fx=1 to m.cl.stms.0
        nm = m.cl.stms.fx
        sc = m.cl.stms.fx.class
        if nm == '' then
            st = ''
        else do
            r = r "st = '"substr(nm, 2)"';"
            st = '.st'
            end
        r = r "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
    return r 'return t;'
endProcedure classGenCopy

/*--- oCopy for classW ----------------------------------------------*/
oCopyW: procedure expose m.
trace ?r
parse arg m, t
    if t == '' then
        return m
    m.t = o2String(m)
    return oMutate(t, m.class.classV)
endProcedure oCopyW
/*--- 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 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.ut_alfDot, '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 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 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>.** and m.<mbr>_**: every rexx Module (copy) should only
               allocate these addresses to avoid address conficts
               with <mbr> the name of therexx 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
    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
    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

mNew: 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 mNew

mFree: procedure expose m.
parse arg m
    p = 'M.P2A.'left(m, lastPos('.', m)-1)
    area = m.p
    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

/*--- get m.a --------------------------------------------------------*/
mGet: procedure expose m.
parse arg a
    return m.a
endProcedure mGet

/*--- put value v into m.a -------------------------------------------*/
mPut: procedure expose m.
parse arg a, v
    m.a = v
    return v
endProcedure mPut

/*--- 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, fx , tx
    dx = m.dst.0
    if fx == '' then
        fx = 1
    if tx == '' then
        tx = m.src.0
    do sx = fx to tx
        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
    return mCatFT(st, 1, m.st.0, fmt)

mCatFT: procedure expose m.
parse arg st, fx, tx, fmt
    if tx < fx then
        return ''
    fmt = '@cat%('fmt'%)'
    res = f(fmt, m.st.fx)
    do sx=fx+1 to tx
        res = res || f(fmt'%.1', m.st.sx)
        end
    return res || f(fmt'%.2')
endProcedure mCatFT

fGenCat: procedure expose m.
parse arg s, ax
    do fx=1 until \ scanLit(s, '%,')
        f.fx = fGen(s)
        end
    if \ scanLit(s, '%)') then
        call scanErr s, 'no %) after @fGenCat%('
    if \ scanEnd(s) then
        call scanErr s, 'mGenCat not at end'
    if fx < 2 | f.2 == "''" then
        f.2 = fGen(scanSrc(f_u, '%c'))
    if fx < 3 then
        f.3 = "''"
    if fx < 4 then
        f.4 = "''"
    adr = m.s.src'%'
    if f.1 == "''" then
        m.f_gen.adr.1 = 'return' f.2
    else
        m.f_gen.adr.1 = 'return' f.1 '||' f.2
    m.f_gen.adr.2 = 'return' f.4
    if f.3 == "''" then
        return f.2
    else
        return f.3 '||' f.2
endProcedure fGenCat

mIni: procedure expose m.
    if m.m.ini == 1 then
        return
    m.m.ini = 1
    call utIni
    m.mBase64 = m.ut_alfUC || m.ut_alfLc || m.ut_digits'+-'
    m.m.area.0 = 0
    call mNewArea
    return
endProcedure mIni

/* copy m end *********************************************************/
/* copy fTab begin ****************************************************/
fTabReset: procedure expose m.
parse arg m, m.m.titBef, m.m.titAft, m.m.opt
    m.m.generated = ''
    m.m.0 = 0
    m.m.len = 0
    m.m.cols = ''
    m.m.tit.0 = words(m.m.titBef m.m.titAft) + 5
    m.m.set.0 = 0
    do tx=1 to m.m.tit.0
        m.m.tit.tx = ''
        end
    return m
endProcedure fTabReset

/* add a piece to title tx at current pos */
fTabAddTit: procedure expose m.
parse arg m, tx, t1
    m.m.generated = ''
    m.m.tit.tx = left(m.m.tit.tx, m.m.len) || t1
    return m
endProcedure fTabAddTit

/*--- set the infos for one column -----------------------------------*/
fTabSet: procedure expose m.
parse arg m, c1 aDone, f1, l1
    sx = m.m.set.0 + 1
    m.m.set.0 = sx
    m.m.set.sx = c1 aDone
    m.m.set.sx.fmt = f1
    m.m.set.sx.label = l1
    m.m.set.c1 = sx
    return
endProcedure fTabSet

fTabAdd: procedure expose m.
parse arg m, c1 aDone, f1, l1
    cx = m.m.0 + 1
    m.m.generated = ''
    m.m.0 = cx
    k1 = c1
    if c1 == '' then
        k1 = '='
    else if c1 == '=' then
        c1 = ''
    m.m.cols = m.m.cols k1
    if words(m.m.cols) <> cx then
        call err 'mismatch of column number' cx 'col' c1  / 0
    if length(aDone) > 1 | wordPos('<'aDone'>', '<> <0> <1>') < 1 then
        call err 'bad done' length(aDone) '<'aDone'> after c1' c1
    m.m.cx.col = c1
    m.m.cx.done = aDone \== 0
    if l1 == '' then
        m.m.cx.label = k1
    else
        m.m.cx.label = l1
    if cx == 1 then
        if abbrev(f1, ' ') then
           f1 = substr(f1, 2)
    px = pos('%', f1)
    ax = pos('@', f1)
    if px < 1 | (ax > 0 & ax < px) then
        m.m.cx.fmt = f1
    else
        m.m.cx.fmt = left(f1, px-1)'@.'c1 || substr(f1, px)
    call mPut 'F_TEMP'left('.', c1\=='')c1, m.m.cx.label
    t1 = f(m.m.cx.fmt, 'F_TEMP')
    if pos(strip(t1), m.m.cx.label) < 1 then
        t1 = left(left('', max(0, verify(t1, ' ') -1))m.m.cx.label,
           , length(t1))
    m.m.cx.len = length(t1)
    call fTabAddTit m, 1, t1
    do tx=2 to arg()-3
        if arg(tx+3) \== '' then
            call fTabAddTit m, tx, arg(tx+3)
        end
    m.m.len = m.m.len + length(t1)
    return m
endProcedure fTabAdd

fTabGenerate: procedure expose m.
parse arg m
    f = ''
    do kx=1 to m.m.0
        f = f || m.m.kx.fmt
        end
    do while right(f, 1) == ' ' & f \== ''
        f = left(f, length(f)-1)
        end
    m.m.fmt = fCache('%.', f)

    cSta = m.m.tit.0+3
    do cEnd=cSta until kx > m.m.0
        cycs = ''
        do cx=cSta to cEnd
            m.m.tit.cx = ''
            cycs = cycs cx
            end
        cx = cSta
        ll = 0
        do kx=1 to m.m.0 while length(m.m.tit.cx) < max(ll,1)
            m.m.tit.cx = left(m.m.tit.cx, ll)m.m.kx.col
            cx = cx + 1
            if cx > cEnd then
                cx = cSta
            ll = ll + m.m.kx.len
            end
        end
    m.m.cycles = strip(cycs)
    if pos('-', m.m.opt) > 0 then
        m.m.tit.1 = translate(lefPad(m.m.tit.1, m.m.len), '-',' ')'---'
    m.m.generated = m.m.generated't'
    return
endProcedure fTabGenerate

fTabColGen: procedure expose m.
parse arg m
    do kx=1 to m.m.0
        l = if(m.m.kx.label == m.m.kx.col, , m.m.kx.label)
        f = lefPad(l, 10) lefPad(m.m.kx.col, 18)
        if length(f) > 29 then
           if length(l || m.m.kx.col) < 29 then
               f = l || left('', 29 - length(l||m.m.kx.col))m.m.kx.col
           else
               f = lefPad(strip(l m.m.kx.col), 29)
        g = strip(m.m.kx.fmt)
        o = right(g, 1)
        if pos(o, 'dief') > 0 then
            f = f '@'m.m.kx.col'%12e @'m.m.kx.col'%18c'
        else if o = 'C' then
            f = f left(g, length(g)-1)'c'
        else
            f = f g
        m.m.kx.colFmt = f
        end
    m.m.generated = m.m.generated'c'
    return
endProcedure fTabColGen

fTab: procedure expose m.
parse arg m, rdr
    call fTabBegin m
    call fAll m.m.fmt, rdr
    return fTabEnd(m)
endProcedure fTab

fTabCol: procedure expose m.
parse arg m, i
    if pos('c', m.m.generated) < 1 then
        call fTabColGen m
    do cx=1 to m.m.0 / 0
        call out f(m.m.cx.colFmt, i)
        end
    return 0
endProcedure fTabCol

fTabBegin: procedure expose m.
parse arg m
    if pos('t', m.m.generated) < 1 then
        call fTabGenerate m
    return fTabTitles(m, m.m.titBef)

fTabEnd: procedure expose m.
parse arg m
    return fTabTitles(m, m.m.titAft)

fTabTitles: procedure expose m.
parse arg m, list
    list = repAll(list, 'c', m.m.cycles)
    do tx=1 to words(list)
        t1 = word(list, tx)
        call out m.m.tit.t1
        end
    return m
endProcedure fTabTitles

/*--- auto tables detect format from data ---------------------------*/
/*--- detect fmt from a rdr and write it formatted to stdOut --------*/
fTabAuto: procedure expose m.
parse arg m, rdr, wiTi
    if m == '' then
        m = fTabReset(f_auto, 1)
    i = j2Buf(rdr)
    if m.i.buf.0 <= 0 then
        return m
    call fTabDetect m, i'.BUF', wiTi
    return fTab(m, i)
endProcedure fTabAuto

/*--- generate format for all fields of a stem of objects -----------*/
fTabDetect: procedure expose m.
parse arg m, b, wiTi
    ff = oFlds(m.b.1)
    do fx=1 to m.ff.0
        call fTabAddDetect m, substr(m.ff.fx, 2), b
        end
    return
endProcedure fTabDetect

/*--- detect format for one field in stem st ------------------------*/
fTabAddDetect: procedure expose m.
parse arg m, c1 aDone, st, l1
    lMa = -1
    rMa = -1
    bMa = -1
    aDiv = 0
    nMi =  9e999
    nMa = -9e999
    eMi =  9e999
    eMa = -9e999
    eDa = 2
    dMa = -9e999
    suf = left('.', c1 \== '')c1
    do sx=1 to m.st.0
        v = mGet(m.st.sx || suf)
        lMa = max(lMa, length(strip(v, 't')))
        rMa = max(rMa, length(strip(v, 'l')))
        bMa = max(bMa, length(strip(v, 'b')))
        if \ dataType(v, 'n') then do
            if length(v) > 100 then
                aDiv = 99
            else if aDiv <=3 then
                if aDiv.v \== 1 then do
                    aDiv.v = 1
                    aDiv = aDiv + 1
                    end
            iterate
            end
        v = strip(v)
        nMi = min(nMi, v)
        nMa = max(nMa, v)
        ex = verify(v, 'eEfF', 'm')
        if ex > 0 then do
            eMa = max(eMa, substr(v, ex+1))
            eMi = min(eMi, substr(v, ex+1))
            v = left(v, ex-1)
            do while pos(left(v,1), '+-0') > 0
                v = substr(v, 2)
                end
            eDa = max(eDa, length(v) - (pos('.', v) > 0))
            end
        dx = pos('.', v)
        if dx > 0 then do
            do while right(v, 1) == 0
                v = left(v, length(v)-1)
                end
            dMa = max(dMa, length(v)-dx)
            end
        end
    if nMi > nMa | aDiv > 3 then
        newFo = '-'max(1, (lMa+0))'C'
    else if eMi <= eMa then do
        newFo = ' ' || (eDa+max(length(eMa), length(eMi))+3) ,
             || '.'||(eDa-1)'e'
        end
    else do
        be = max(length(trunc(nMi)), length(trunc(nMa)))
        if dMa <= 0 then
            newFo = max(be, bMa)'I'
        else
            newFo = max(be+1+dMa, bMa)'.'dMa'I'
        end
    call fTabAdd m, c1 aDone, ' %'newFo, l1
  /*  say c1 '????==> %'newFo */
   return newFo
endProcedure fTabAddDetect

/*--- format with units seconds ==> 3d13 oder 5.43e6 ==> 5m43 --------*/
fTime: procedure expose m.
parse arg s, signed
    return fUnits(s, 't', signed==1)
endProcedure fTime

fDec: procedure expose m.
parse arg s, signed
    return fUnits(s, 'd', signed==1)
endProcedure fDec


fUnits: procedure expose m.
parse arg s, scale, signed
    if f_units_ini \== 1 then do
        f_units_ini = 1
        call fUnitsIni 't', '?smhd', 0 100 60 60 24 100, 0.01, 2
        call fUnitsIni 'd', '?.kMGTPE', 0 copies('1000 ',8), 0.001, 3
        end
    if s >= 0 then
        res = fUnitsNN(s, scale, wi)
    else
        res = '-'fUnitsNN(abs(s), scale, wi)
    len = m.fmt.units.scale.f.length + signed
    if length(res) <= len then
       return right(res, len)
    if \ abbrev(res, '-') then
        return right(right(res, 1), len, '+')
    if length(res) = len+1 & datatype(right(res, 1), 'n') then
        return left(res, len)
    return right(right(res, 1), len, '-')
endProcedure fUnits

fUnitsNN: procedure expose m.
parse arg s, scale
    sf = 'FMT.UNITS.'scale'.F'
    sp = 'FMT.UNITS.'scale'.P'
    if m.sf \== 1 then do
        call fmtIni
        if m.sf \== 1 then
            call err 'fUnitsNN bad scale' scale
        end

    do q=3 to m.sp.0 while s >= m.sp.q
        end
    do forever
        qb = q-2
        qu = q-1
        r = format(s / m.sp.qb, ,0)
        if q > m.sf.0 then
            return r || substr(m.sf.units, qb, 1)
        if r < m.sf.q * m.sf.qu then
            return (r % m.sf.qu) || substr(m.sf.units, qu, 1) ,
                              || right(r //m.sf.qu, m.sf.width, 0)
            /* overflow because of rounding, thus 1u000: loop back */
        q = q + 1
        end
endProcedure fUnitsNN

fUnitsIni: procedure expose m.
parse arg sc, us, fact, prod, wi
    sf = 'FMT.UNITS.'sc'.F'
    sp = 'FMT.UNITS.'sc'.P'
    m.sf.0 = words(fact)
    if length(us) + 1 <> m.sf.0 then
        call err 'fUnitsIni mismatch' us '<==>' fact
    m.sf.1 = word(fact, 1)
    m.sp.1 = prod
    do wx=2 to m.sf.0
        wx1 = wx-1
        m.sf.wx = word(fact, wx)
        m.sp.wx = m.sp.wx1 * m.sf.wx
        end
    m.sp.0 = m.sf.0
    m.sf.units = us
    m.sf.width = wi
    m.sf.length= 2 * wi + 1
    m.sf = 1
    return
endProcedure fUnitsIni
/* copy fTab end   ****************************************************/
/* copy f begin *******************************************************/
/*--- format with the given format ggA1, ggA2, etc. -----------------*/
f: procedure expose m.
parse arg ggFmt, ggA1, ggA2
    if symbol('M.f_gen.ggFmt') \== 'VAR' then
        call fCache ggFmt, ggFmt
    interpret m.f_gen.ggFmt
endProcedure f

fImm: procedure expose m.
parse arg ggFmt, ggA1
    interpret m.ggFmt
endProcedure fImm

/*--- compile format fmt put in the cache with address a ------------*/
fCache: procedure expose m.
parse arg a, fmt
    if a == '%.' then do
        if symbol('m.f_gen0') == 'VAR' then
            m.f_gen0 = m.f_gen0 + 1
        else
            m.f_gen0 = 1
        a = '%.'m.f_gen0
        end
    else if symbol('M.f_gen.a') == 'VAR' then
        return a
    if symbol('m.f_s_0') \== 'VAR' | m.f_s_0 == 0 then
        m.f_s_0 = 1
    else do
        m.f_s_0 = m.f_s_0 + 1
        f_s = 'F_S_'m.f_s_0
        end
    call scanSrc f_s, fmt
    m.f_gen.a = 'return' fGen(f_s)
    m.f_s_0 = m.f_s_0 - 1
    if \ scanEnd(f_s) then
        call scanErr f_s, "bad specifier '"m.f_s.tok"'"
    return a
endProcedure fCache

/*--------------------------------------------------------------------
 Format generator    should be compatible with fPrint|
 <<<< + extension of fPrint, - in fPrint but not implemented

   %%  %@ the escaped char
   ('@' argN? '.'? field)?'%' flags? width? ('.' precision)? specifier

 specifier: is the most significant one and defines the type

 - c  Character rigPad or lefPad, prec ==> substr(..., prec)
 -  C Cut %-nC = left(v,n), %nC = right(v,n) %n.mC = substr(m, n)
 - hH Characters in hex
 - iI Signed decimal integer (padded or cut)
 - eE Scientific notation (mantissa/exponent) using e character 3.92e+2
 - S  Strip (both)
 - txy time date formatting from format x to format y
 - (,) call format generator routine with name 'fGen' || field
 Flags:
 - -  Left-justify within the given field width; Right is default
 - +  print '+' before non negative numbers
 -' ' print ' ' before non negative numbers
 - /  cut to length
----------------------------------------------------------------------*/
fGen: procedure expose m.
parse arg f_s
    ax = 0
    cd = ''
    do forever
        txt = fText(f_s)
        if txt \== '' then
            cd = cd '||' quote(txt, "'")
        if scanEnd(f_s) then
            leave
        if \ scanLit(f_s, '@') then do
            ax = ax + 1
            af = ''
            hasDot = 0
            end
        else do
            if scanWhile(f_s, '0123456789') then
                ax = m.f_s.tok
            else if ax < 1 then
                ax = 1
            hasDot = scanLit(f_s, '.')
            af = fText(f_s)
            end
        if \ scanLit(f_s, '%') then
            call scanErr f_s, 'missing %'
        call scanWhile f_s, '-+ /'
        flags = m.f_s.tok
        if scanWhile(f_s, '0123456789') then
            len = m.f_s.tok
        else
            len = 0
        if \ scanLit(f_s, '.') then
            prec  = ''
        else do
            call scanWhile f_s, '0123456789'
            prec = m.f_s.tok
            end
        call scanChar f_s, 1
        sp = m.f_s.tok
        if ax < 3 then
            aa = 'ggA'ax
        else
            aa = 'arg(' || (ax+1) || ')'
        if af \== '' | hasDot then
            aa = rxMGet(aa, af)
        if sp == 'c' then do
            if prec \== '' then
                aa = 'substr('aa',' prec')'
            if len < 1 then
                cd = cd '||' aa
            else if pos('-', flags) > 0 then
                cd = cd '|| lefPad('aa',' len')'
            else
                cd = cd '|| rigPad('aa',' len')'
            end
        else if sp == 'C' then do
            if prec \== '' then do
                cd = cd '|| substr('aa',' prec
                if len < 1 then
                    cd = cd')'
                else
                    cd = cd',' len')'
                end
            else if len < 1 then
                cd = cd '||' aa
            else if pos('-', flags) > 0 then
                cd = cd '|| left('aa',' len')'
            else
                cd = cd '|| right('aa',' len')'
            end
        else if sp == 'H' then
            cd = cd "|| fH("aa"," len',' (pos('-', flags) > 0)')'
        else if sp == 'h' then
            cd = cd "|| translate(fH("aa", '"siL"'), 'abcdef','ABCDEF')"
        else if sp == 'i' then
            cd = cd "|| fI("aa"," len", '"flags"'," word(prec 0, 1)")"
        else if sp == 'I' then
            cd = cd "|| fI("aa"," len", '/"flags"'," word(prec 0, 1)")"
        else if sp == 'E' | sp == 'e' then do
            if len = 0 then
                len = 8
            if prec = '' then
                prec = len - 6
            cd = cd "|| fE("aa"," len"," prec", '"sp"', '"flags"')"
            end
        else if sp = 'S' then
            cd = cd '|| strip('aa')'
        else if sp = 't' then do
            call scanChar f_s, 2
            cd = cd '||' fTstGen(m.f_s.tok, aa)
            end
        else if sp = '(' then do
            if af == '' | flags \== '' | len \== 0 | prec \== '' then
                call scanErr f_s, "bad call shoud be @sub%("
            interpret "cRes = fGen"af"(f_s, ax)"
            cd = cd '||' cRes
            if \ scanLit(f_s, '%)') then
                if \ scanEnd(f_s) then
                    call scanErr f_s, '%) to end call' af 'expected'
            end
        else do
            call scanBack f_s, '%'sp
            leave
            end
        end
    if cd \== '' then
        return substr(cd, 5)
    else
        return "''"
endProcedure fGen

fText: procedure expose m.
parse arg f_s
    res = ''
    do forever
        if scanUntil(f_s, '@%') then
            res = res || m.f_s.tok
        if \ scanLit(f_s, '%%', '%@') then
            return res
        res = res || substr(m.f_s.tok, 2)
        end
endProcedure fText

fAll: procedure expose m.
parse arg fmt, rdr
    i = inOpen(rdr)
    o = jReadO(i)
    do while o \== ''
        call out f(fmt, o)
        o = jReadO(i)
        end
    call jClose i
    return
endProcedure fAll

/*--- format character2hex (if not sql null) -------------------------*/
fH: procedure expose m.
parse arg v, l, leftJ
    if v \== m.sqlNull then
        v = c2x(v)
    if length(v) > l then
        return v
    else if leftJ \== 1 then
        return right(v, l)
    else
        return left(v, l)
endProcedure fH

/*--- format integer or fixPoint Decimal -----------------------------*/
fI: procedure expose m.
parse arg v, l, flags, d
    if \ datatype(v, 'n') then
        return fRigLeft(strip(v), l, flags)
    v = format(v, , d, 0)
    if pos('+', flags) > 0 then
        if \ abbrev(v, '-') then
            v = '+'v
    if length(v) > l then
        if pos('/', flags) > 0 then
            return left('', l, '*')
        else
            return v
    return fRigLefPad(v, l, flags)
endProcedure fI

/*--- format with exponent l=total output len
                           d=number of digits after . in mantissa
                           c=exponent character
                           flags: - to ouput text left justified
    differences: exponent is always printed but without +
                 overflow ==> expand exponent, *****, 0e-999 ---------*/
fE: procedure expose m.
parse arg v, l, d, c, flags
    if \ datatype(v, 'n') then
        return fRigLeft(v, l, flags)
    if pos(' ', flags) < 1 then
        if v >=  0 then
            if pos('+', flags) > 0 then
                return '+'substr(fE(v, l, d, c, ' 'flags), 2)
            else
                return substr(fE(v, l+1, d+1, c, ' 'flags), 2)
    x = format(v, 2, d, 7, 0)
    m = 2 + d + (d>0)
    call assert "length(x) == m+9", 'm x length(x)'
    if substr(x, m+1) = '' then
        return left(x, m)c || left('', l-m-1, 0)
    call assert "substr(x, m+1, 1) == 'E'","exponenent in x not at m"
    y = verify(x, '0', 'n',  m+3)
    call assert 'y>0'
    if substr(x, m+1, 2) == 'E+' then do
        if m+10-y <= l-m-1 then
             return left(x,m)c || right(x, l-m-1)
        z = l - 4 - (m+10-y)
        end
    else if substr(x, m+1, 2) == 'E-' then do
        if m+10-y <= l-m-2 then
             return left(x,m)c'-'right(x, l-m-2)
        z = l - 5 - (m+10-y)
        end
    else
        call err 'bad x' x
    if z >= -1 & max(0, z) < d then
        return fE(v, l, max(0, z), c, flags)
    else if substr(x, m+1, 2) == 'E-' then
        return left(x,1)'0'c'-'left('', l-4, 9)
    else
        return left('', l, '*')
endProcedure fE

/*--- right or left with truncation ----------------------------------*/
fRigLeft: procedure expose m
parse arg s, len, flags
    if length(s) = len then
        return s
    else if pos('-', flags) > 0 | length(s) > len then
        return left(s, len)
    else
        return right(s, len)
endProcedure fRigLefPad

/*--- right or left pad without truncation --------------------------*/
fRigLefPad: procedure expose m
parse arg s, len, flags
    if pos('-', flags) > 0 then
        if length(strip(s, 't')) >= len then
            return strip(s, 't')
        else
            return left(s, len)
    else
        if length(strip(s, 'l')) >= len then
            return strip(s, 'l')
        else
            return right(s, len)
endProcedure fRigLefPad

/*--- generate timestamp formats: from format c to format d ----------*/
fTstGen: procedure expose m.
parse arg c 2 d, s
    cd = c || d
    if symbol('m.f_tstFo.c') \== 'VAR' ,
         | symbol('m.f_tstFo.d') \== 'VAR' then do
         if m.f_tstIni == 1 then
             call err "bad timestamp from or to format '"cd"'"
        m.f_tstIni = 1
        a = 'F_TSTFO.'
                      /* Y: A = 2010, M:B=Januar,
                         H=Stunde A=0 B=10 C=20
                         jkl = julian day
                         yyyy-mm-dd-hh.mm.ss.ffffff    */
        call mPut a'S', 'yz34-56-78-hi.mn.st.abcdef'
        call mPut a's', 'yz34-56-78-hi.mn.st'
        call mPut a' '
        call mPut a'D', 'yz345678'
        call mPut a'd',  '345678'
        call mPut a't',            'hi.mn.st'
        call mPut a'T',            'hi:mn:st'
        call mPut a'E', '78.56.yz34'
        call mPut a'e', '78.56.34'
        call mPut a'Y',      'YM78'
        call mPut a'M',    'M78himns'
        call mPut a'A',    'A8himnst'
        call mPut a'H',           'Himnst'
        call mPut a'n', 'yz345678 hi:mn:st'
        return fTstGen(cd, s)
        end

    if pos('(', s) > 0 | length(s) > 20 then
        return "fImm('F_GEN."fCache('%t'cd, '%t'cd)"'," s")"
    if c == ' ' then
        return "fImm('F_GEN."fCache('%tn'd, '%tn'd)"',date('S') time())"
    f = m.f_tstFo.c
    if d = ' ' then
        d = 's'
    t = m.f_tstFo.d
    if f == t then
        return s
    r = "translate("quote(t, "'")"," s "," quote(f, "'")")"

    if pos('yz34', t) > 0 & pos('yz34', f) < 1 then do
        if pos('34', f) > 0 then
            r = "overlay('20'," r"," pos('yz', t)")"
        else if pos('Y', f) > 0 then
            r = fTstGY2Year(r, "substr("s"," pos('Y', f)", 1)", 4,
                , pos('yz34', t))
        else
            r = "overlay('0000'," r"," pos('yz34', t)")"
        end
    else if pos('34', t) > 0 & pos('34', f) < 1 then do
        if pos('Y', f) > 0 then
            r = fTstGY2Year(r, "substr("s"," pos('Y', f)", 1)", 2,
                   , pos('34', t))
        else
            r = "overlay('00'," r"," pos('34', t)")"
        end
    if pos('Y', t) > 0 & pos('Y', f) < 1 then do
        if pos('yz34', f) > 0 then
            r = fTstGYear2Y(r, "substr("s"," pos('yz34', f)", 4)" ,
                   , pos('Y', t))
        else if pos('34', f) > 0 then
            r = fTstGYear2Y(r, "substr("s"," pos('34', f)", 2)" ,
                   , pos('Y', t))
        else
            r = "overlay('?'," r"," pos('Y', t)")"
        end
    if pos('M', t) > 0 & pos('M', f) < 1 then do
        if pos('56', f) > 0 then
            r = fTstG02A(r, "substr("s"," pos('56', f)", 2)" ,
                   , pos('M', t))
        else
            r  = "overlay('?'," r"," pos('M', t)")"
        end
    if pos('56', t) > 0 & pos('56', f) < 1 then
        if pos('M', f) > 0 then
            r = fTstGA2N2(r, "substr("s"," pos('M', f)", 1)" , "-1",
                   , pos('56', t))
        else
            r = "overlay('01'," r"," pos('56', t)")"
    if pos('78', t) > 0 & pos('78', f) < 1 then
        r = "overlay('01'," r"," pos('78', t)")"
    if pos('A8', t) > 0 & pos('A8', f) < 1 then
        if pos('78', f) > 0 then
            r = fTstG02A(r, "substr("s"," pos('78', f)", 1)" ,
                   , pos('A8', t))
        else
            r = "overlay('A1'," r"," pos('A8', t)")"
    if pos('hi', t) > 0 & pos('hi', f) < 1 then do
        if pos('H', f) > 0 then
            r = fTstGA2N1(r, "substr("s"," pos('H', f)", 1)" , "-1",
                   , pos('hi', t))
        else
            r = "overlay('00'," r"," pos('hi', t)")"
        end
    if pos('i', t) > 0 & pos('i', f) < 1 then
        r = "overlay('0'," r"," pos('i', t)")"
    if pos('H', t) > 0 & pos('H', f) < 1 then do
        if pos('hi', f) > 0 then
            r = fTstG02A(r, "substr("s"," pos('hi', f)", 1)" ,
                   , pos('H', t))
        else
            r  = "overlay('A'," r"," pos('H', t)")"
        end
    if pos('mn', t) > 0 & pos('mn', f) < 1 then
        r = "overlay('00'," r"," pos('mn', t)")"
    if pos('st', t) > 0 then do
        if pos('st', f) < 1 then
            if pos('s', f) > 0 then
                r = "overlay('0'," r"," pos('t', t)")"
            else
                r = "overlay('00'," r"," pos('st', t)")"
        end
    else if pos('s', t) > 0 & pos('s', f) < 1 then
        r = "overlay('0'," r"," pos('s', t)")"
    if pos('abcdef', t) > 0 & pos('abcdef', f) < 1 then
        r = "overlay('000000'," r"," pos('abcdef', t)")"
    return r
endProcedure fTstGen

/*--- from Number overlay Character A=0, B=1 etc.. ------------------*/
fTstG02A: procedure expose m.
parse arg r, n, tx
     return "overlay(substr(m.ut_alfUc," n "+ 1, 1)," r"," tx")"

/*--- from year overlay Y-Character  A=2010 etc..--------------------*/
fTstGYear2Y: procedure expose m.
parse arg r, y, tx
     return fTstG02A(r, "("y "+ 10) // 20", tx)

/*--- from character A=0 overlay 1 digit number ---------------------*/
fTstGA2N1: procedure expose m.
parse arg r, y, dlt, tx
     return "overlay(right(pos("y", m.ut_alfUc)" dlt", 1)," r"," tx")"

/*--- from character A=0 overlay 2 digit number ---------------------*/
fTstGA2N2: procedure expose m.
parse arg r, y, dlt, tx
     return "overlay(right('00' || (pos("y", m.ut_alfUc)" dlt"), 2)",
                    "," r"," tx")"

/*--- from Y yearCharacter overlay 2 or 4 digit year ----------------*/
fTstGY2Year: procedure expose m.
parse arg r, y, len, tx
     if m.f_tstY2ini \== 1 then do  /* generate lookup table */
          m.f_tstY2ini = 1
          n = left(f('%t  '), 4)
          do i = n-15 to n+4   /* 20 values: from -15 years to + 4 */
              j = left(f('%tDY', i'0101'), 1)
              m.f_tstY24.j = i
              m.f_tstY22.j = right(i, 2)
           /* say j m.f_tstY22.j m.f_tstY24.j  */
              end
          end
     if wordPos(len, 2 4) < 1 then
         call err "fTstGYear2Y bad len" len
     return "overlay(mGet('F_TSTY2"len".'"y")," r"," tx")"
endProcedure fTstGY2Year

/* copy f end   *******************************************************/
/* copy err begin *** errorhandling, messages, help    ****************/
errIni: procedure expose m.
    if m.err.ini == 1 then
        return
    call utIni
    m.err.ini     = 1
    m.err.handler = ''
    m.err.cleanup = ';'
    m.err.opt     = ''
    parse source m.err.os .
    m.err.ispf    = 0
    m.err.screen  = 0
    if m.err.os \== 'LINUX' then do
        call adrTso 'profile MsgId'   /* brauchen wir in tsoAlloc| */
        if sysVar('sysISPF') = 'ACTIVE' then do
            m.err.ispf = 1
            address ispExec 'vget zScreen shared'
            m.err.screen = zScreen
            end
        end
    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 & m.err.ispf then
        address ispExec '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
        interpret m.err.handler
    call errSay 'f}'ggTxt
    call errCleanup
    if ggOpt == '' then
        ggOpt = m.err.opt
    upper ggOpt
    if pos('T', ggOpt) > 0  then do
        trace ?r
        say 'trace ?r in err'
        end
    if pos('H', ggOpt) > 0 & m.err.eCat == 'f' then do
        call errSay ' }errorhandler exiting with divide by zero' ,
                                   'to show stackHistory'
        x = 1 / 0
        end
    call errSay ' }errorhandler exiting with exit(12)'
    exit errSetRc(12)
endSubroutine err

/*--- set rc for ispf: -------------------------------------------------
    if a cmd is run by ispStart, its RC is ignored,
         but ISPF passes the value of the shared variable zIspfRc
         back as return code
----------------------------------------------------------------------*/
errSetRc: procedure expose m.
parse arg zIspfRc
    if m.err.ispf then
        address ispExec vput 'zIspfRc' shared
    return zIspfRc
endProcedure errSetRc

/*--- error routine: user message cleanup exit -----------------------*/
errAddCleanup: procedure expose m.
parse arg code
    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
    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
    errCleanup = m.err.cleanup
    if errCleanup <> ';' then do
        m.err.cleanup = ';'
        say 'err cleanup begin' errCleanup
        interpret errCleanup
        say 'err cleanup end' errCleanup
        end
    if symbol('m.tso_ddAll') == 'VAR' then
        call tsoFree m.tso_ddAll, 1
    return
endProcedure errCleanup

/*--- say an errorMessage msg with pref and linesplits --------------*/
errSay: procedure expose m.
parse arg msg
    return saySt(errMsg(msg))

/*--- prefix an errormessage with pref,
          split it into lines at \n to stem m.err -------------------*/
errMsg: procedure expose m.
parse arg msg
    m.err.eCat = 'f'
    do while substr(msg, 2, 1) == '}'
        parse var msg m.err.eCat '}' msg
        end
    res = msg
    if m.err.eCat <> '' then do
       pTxt = ',error,fatal error,input error,s-}scanErr,warning,'
 /*    pTxt = ',error,fatal error,input error,syntax error,warning,' */
       px = pos(','m.err.eCat, pTxt)
       if px < 1 then do
           m.err.eCat = 'f'
           px = pos(','m.err.eCat, pTxt)
           end
       res = substr(pTxt, px+1, pos(',', pTxt, px+2)-px-1)
       if substr(res, 3, 1) == '}' then
           parse var res 2 opt 3 br 4 res
       if opt == '-' then
           res = res msg
       else do
           parse source . . s3 .              /* current rexx */
           res = res 'in' s3':' msg
           end
       end
    return splitNl(err, res)           /* split lines at \n */
endProcedure errMsg

splitNL: procedure expose m.
parse arg st, msg
    bx = 1
    do lx=1 to 20
        ex = pos('\n', msg, bx)
        if ex < bx then
            leave
        m.st.lx = substr(msg, bx, ex-bx)
        bx = ex+2
        end
    m.st.lx = substr(msg, bx)
    m.st.0 = lx
    return st
endProcedure splitNL

/*--- say (part of) the lines of a stem ----------------------------*/
saySt: procedure expose m.
parse arg st, fx, tx
    do lx=word(fx 1, 1) to word(tx m.st.0, 1)
        say m.st.lx
        end
    return st
endProcedure saySt

/*--- out (part of) the lines of a stem ----------------------------*/
outSt: procedure expose m.
parse arg st, fx, tx
    do lx=word(fx 1, 1) to word(tx m.st.0, 1)
        call out m.st.lx
        end
    return st
endProcedure outSt

/*--- say a trace message if m.trace is set --------------------------*/
debug: procedure expose m.
parse arg msg
    if m.debug == 1 then
        say 'debug:' msg
    return
endProcedure debug

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

/*--- assert that the passed rexx expression evaluates to true -------*/
assert:
    interpret 'if ('arg(1)') == 1 then return 1'
    interpret 'call err' quote('assert failed' arg(1) ':' arg(2) '==>'),
                        arg(2)
endProcedure assert

/*--- abend with Message after displaying help -----------------------*/
errHelp: procedure expose m.
parse arg msg
    call errSay 'i}'msg
    call help 0
    call err 'i}'msg
endProcedure errHelp

/*--- display the first comment block of the source as help ----------*/
help: procedure expose m.
parse arg doClear
    if doClear \== 0 then
        address tso 'clear'
    parse source . . s3 .
    say right(' help for rexx' s3, 72, '*')
    do lx=1 while pos('/*', sourceLine(lx)) < 1
        if lx > 10 then
            return err('initial commentblock not found for help')
        end
    doInc = 1
    ho = m.err.helpOpt
    do lx=lx+1 to sourceline() while pos('*/', sourceline(lx)) = 0
        li = strip(sourceLine(lx), 't')
        cx = lastPos('{', li)
        if cx > 0 then do
            if length(ho) = 1 then
                doInc = cx = length(li) | pos(ho, li, cx+1) > 0
            li = left(li, cx-1)
            end
        if doInc then
            say li
        end
    say right(' end help for rexx' s3, 72, '*')
    return 4
endProcedure help

/* copy err end   *****************************************************/
/* copy ut begin  *****************************************************/
utIni: procedure expose m.
    if m.ut_ini == 1 then
        return
    m.ut_ini = 1
    m.ut_digits = '0123456789'
    m.ut_alfLC  = 'abcdefghijklmnopqrstuvwxyz'
    m.ut_alfUC  = translate(m.ut_alfLc)
    m.ut_Alfa   = m.ut_alfLc || m.ut_alfUC
    m.ut_alfNum = m.ut_alfa || m.ut_digits
    m.ut_alfDot = m.ut_alfNum || '.'
    m.ut_alfId  = m.ut_alfNum'_' /* avoid rexx allowed @ # $ ¬ . | ? */
    m.ut_alfIdN1 = m.ut_digits    /* not as first character */
    m.ut_rxId   = m.ut_AlfNum'@#$?' /* charset puff mit ¬*/
    m.ut_rxDot  = '.'m.ut_rxId
    m.ut_rxN1   = '.0123456789'
    m.ut_alfPrint = m.ut_alfNum'+-*/=()¢!{}<> .:,;?|''"%&#@$£\_'
    return
endProcedure utIni
/*--- 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 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 ------------------------------*/
utTime: procedure expose m.
    return time() 'ela='time('E') 'cpu='sysvar('syscpu'),
            'su='sysvar('syssrv')

/*--- 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

/*--- left without truncation ----------------------------------------*/
lefPad: procedure expose m
parse arg s, len
    if length(strip(s, 't')) >= len then
        return strip(s, 't')
    return left(s, len)
endProcedure lefPad

/*--- right without truncation ---------------------------------------*/
rigPad: procedure expose m
parse arg s, len
    if length(strip(s, 'l')) >= len then
        return strip(s, 'l')
    return right(s, len)
endProcedure rigPad

/*--- 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

/*--- translate to lowercase -----------------------------------------*/
ut2lc: procedure expose m.
parse arg s
    return translate(s, m.ut_alfLc, m.ut_alfUc)

/*--- verify an id ---------------------------------------------------*/
verifId: procedure expose m.
    parse arg src, extra
    if pos(left(src, 1), m.ut_alfIdN1) > 0 then
        return 1
    else
        return verify(src, m.ut_alfId || extra, 'n')

/*--- 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
    do ax = 2 by 2 to arg()
        src = repAl2(src, src, arg(ax), arg(ax+1))
        end
    return src
endProcedure repAll

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

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

utc2d: procedure expose m.
parse arg ch
    cx = length(ch) // 3
    if cx = 0 then
        cx = 3
    res = c2d(left(ch, cx))
    do cx=cx+1 by 3 to length(ch)
        res = res * 16777216 + c2d(substr(ch, cx, 3))
        end
    return res
/* copy ut end ********************************************************/
}¢--- A540769.WK.REXX(WSHTR) cre=2016-05-31 mod=2016-06-03-18.00.08 A540769 ----
$#@
$<#¢
$$- sysvar(sysnode)
$@. fileList('A540769.WK')
call adrTso date
$! call csmExWsh rzy
$*(
call dsnAlloc rz"/tmp.wsh new dd(rmtWsh) rmtDdn(wsh) ::f "
call tsoOpen rmtWsh, 'w'           /* write tso cmd */
call mAdd mCut(xx, 0), $"$$- 'wie gehts' (3*3)"                  ,
       , $"$$  wshOut=$wshOut aaaaaaaaaaaaaaaaaaaaa|"            ,
       , $"call sqlConnect de0g"                                 ,
       , $"$<> $<=¢ select creator, name, dbName, tsName"        ,
       , $"            , current Server serv"                    ,
       , $"       from sysibm.sysTables where creator='SYSIBM'"  ,
       , $"       and name like 'SYSTAB%'"                       ,
       , $"       fetch first 3 rows only $!"                    ,
       , $"call sqlSel"
call writeDD rmtWsh, m.xx.
call tsoClose rmtWsh
call dsnAlloc rz"/tmp.out new dd(rmtOut) rmtDdn(out) ::v "
call csmExRx rz, A540769.wk.rexx, aa, $'wsh @ $ct $=wshOut=e70'
say 'cmsExRx rc='rc
do ax=1 to m.aa.0
   $$- ax length(m.aa.ax) m.aa.ax
   end
call readDD 'rmtOut', oo.
call tsoClose rmtOut
say oo.0 'ouput lines'
do ox=1 to oo.0
   $$- ox length(oo.ox) oo.ox
   end
$$ ***** f2v
call pipeWriteAll csvF2VRdr(file('dd(rmtOut)'))
$$ ***** internalize
call pipeWriteAll csvIntRdr(csvF2VRdr(file('dd(rmtOut)')))
call tsoFree rmtWsh rmtOut
$*)
$#out                                              20160603 17:59:44
$#out                                              20160603 17:58:17
RZY
A540769.WK.CLIST
A540769.WK.JCL
A540769.WK.REXX
A540769.WK.SQL
A540769.WK.TEXW
A540769.WK.WK
$#out                                              20160603 17:56:07
RZY
A540769.WK.CLIST
A540769.WK.JCL
A540769.WK.REXX
A540769.WK.SQL
A540769.WK.TEXW
A540769.WK.WK
$#out                                              20160603 17:55:32
RZY
A540769.WK.CLIST
A540769.WK.JCL
A540769.WK.REXX
A540769.WK.SQL
A540769.WK.TEXW
A540769.WK.WK
$#out                                              20160603 17:54:17
A540769.WK.CLIST
A540769.WK.DBR
A540769.WK.DBRM
A540769.WK.JCL
A540769.WK.LLB
A540769.WK.REXX
A540769.WK.SQL
A540769.WK.TEXV
A540769.WK.TEXW
A540769.WK.WK
$#out                                              20160603 17:53:11
A540769.WK.CLIST
A540769.WK.DBRM
A540769.WK.HIST
A540769.WK.JAVA
A540769.WK.JCL
A540769.WK.LCTL
A540769.WK.LISTDEF
A540769.WK.LOAD
A540769.WK.PLB
A540769.WK.REXX
A540769.WK.REXXRUN
A540769.WK.REXX2008
A540769.WK.SKELS
A540769.WK.SQL
A540769.WK.TESTCASE
A540769.WK.TEXT
A540769.WK.TEXV
A540769.WK.TEXW
A540769.WK.TMP
A540769.WK.WK
$#out                                              20160602 13:36:47
1 5 READY
2 22 wsh @ $ct $=wshOut=e70
3 70 ACF0C038 ACF2 LOGONID ATTRIBUTES HAVE REPLACED DEFAULT USER ATTRIBUTES
4 5 READY
5 3 END
1 13 v,wie gehts 9
2 36 v,wshOut=e70 aaaaaaaaaaaaaaaaaaaaa||
3 70 c Sql217 CLASS.217,u f CREATOR v,f NAME v,f DBNAME v,f TSNAME v,f SER&
4 3 V v
5 70 o CLASS.217 O.217.1,SYSIBM  ,SYSTABAUTH,DSNDB06 ,SYSTSTAU,CHROI00ZDE0&
6 6 G    |
7 70 o CLASS.217 O.217.2,SYSIBM  ,SYSTABCONST,DSNDB06 ,SYSTSTBC,CHROI00ZDE&
8 7 0G    |
9 70 o CLASS.217 O.217.3,SYSIBM  ,SYSTABLEPART,DSNDB06 ,SYSTSTPT,CHROI00ZD&
10 8 E0G    |
***** f2v
v,wie gehts 9
v,wshOut=e70 aaaaaaaaaaaaaaaaaaaaa|
c Sql217 CLASS.217,u f CREATOR v,f NAME v,f DBNAME v,f TSNAME v,f SERV v
o CLASS.217 O.217.1,SYSIBM  ,SYSTABAUTH,DSNDB06 ,SYSTSTAU,CHROI00ZDE0G
o CLASS.217 O.217.2,SYSIBM  ,SYSTABCONST,DSNDB06 ,SYSTSTBC,CHROI00ZDE0G
o CLASS.217 O.217.3,SYSIBM  ,SYSTABLEPART,DSNDB06 ,SYSTSTPT,CHROI00ZDE0G
***** internalize
wie gehts 9
wshOut=e70 aaaaaaaaaaaaaaaaaaaaa|
O.215.1=¢CREATOR=SYSIBM   NAME=SYSTABAUTH DBNAME=DSNDB06  TSNAME=SYSTSTAU SE...!
O.215.2=¢CREATOR=SYSIBM   NAME=SYSTABCONST DBNAME=DSNDB06  TSNAME=SYSTSTBC S...!
O.215.3=¢CREATOR=SYSIBM   NAME=SYSTABLEPART DBNAME=DSNDB06  TSNAME=SYSTSTPT ...!
$#out                                              20160602 13:35:51
1 5 READY
2 22 wsh @ $ct $=wshOut=e70
3 70 ACF0C038 ACF2 LOGONID ATTRIBUTES HAVE REPLACED DEFAULT USER ATTRIBUTES
4 5 READY
5 3 END
1 13 v,wie gehts 9
2 36 v,wshOut=e70 aaaaaaaaaaaaaaaaaaaaa||
3 70 c Sql217 CLASS.217,u f CREATOR v,f NAME v,f DBNAME v,f TSNAME v,f SER&
4 3 V v
5 70 o CLASS.217 O.217.1,SYSIBM  ,SYSTABAUTH,DSNDB06 ,SYSTSTAU,CHROI00ZDE0&
6 6 G    |
7 70 o CLASS.217 O.217.2,SYSIBM  ,SYSTABCONST,DSNDB06 ,SYSTSTBC,CHROI00ZDE&
8 7 0G    |
9 70 o CLASS.217 O.217.3,SYSIBM  ,SYSTABLEPART,DSNDB06 ,SYSTSTPT,CHROI00ZD&
10 8 E0G    |
***** f2v
v,wie gehts 9
v,wshOut=e70 aaaaaaaaaaaaaaaaaaaaa|
c Sql217 CLASS.217,u f CREATOR v,f NAME v,f DBNAME v,f TSNAME v,f SERV v
o CLASS.217 O.217.1,SYSIBM  ,SYSTABAUTH,DSNDB06 ,SYSTSTAU,CHROI00ZDE0G
o CLASS.217 O.217.2,SYSIBM  ,SYSTABCONST,DSNDB06 ,SYSTSTBC,CHROI00ZDE0G
o CLASS.217 O.217.3,SYSIBM  ,SYSTABLEPART,DSNDB06 ,SYSTSTPT,CHROI00ZDE0G
***** internalize
wie gehts 9
wshOut=e70 aaaaaaaaaaaaaaaaaaaaa|
O.215.1=¢CREATOR=SYSIBM   NAME=SYSTABAUTH DBNAME=DSNDB06  TSNAME=SYSTSTAU SE...!
O.215.2=¢CREATOR=SYSIBM   NAME=SYSTABCONST DBNAME=DSNDB06  TSNAME=SYSTSTBC S...!
O.215.3=¢CREATOR=SYSIBM   NAME=SYSTABLEPART DBNAME=DSNDB06  TSNAME=SYSTSTPT ...!
$#out                                              20160602 13:33:44
1 5 READY
2 22 wsh @ $ct $=wshOut=e70
3 70 ACF0C038 ACF2 LOGONID ATTRIBUTES HAVE REPLACED DEFAULT USER ATTRIBUTES
4 5 READY
5 3 END
1 7 O.217.1
2 7 O.217.2
3 7 O.217.3
4 11 wie gehts 9
5 33 wshOut=e70 aaaaaaaaaaaaaaaaaaaaa|
***** f2v
$#out                                              20160602 13:14:05
}¢--- A540769.WK.REXX(WSHTS) cre=2016-06-02 mod=2016-06-02-08.51.12 A540769 ----
$#=
$ct $=wshOut=e50
eins
zwei
$#out                                              20160602 08:49:51
v,eins                                           &
                                 |
v,zwei                                           &
                                 |
$#out                                              20160602 08:49:46
v,eins
v,zwei
$#out                                              20160602 08:49:03
v,eins
v,zwei
$#out                                              20160602 08:45:58
v,eins                                           &
                                 |
v,zwei                                           &
                                 |
$#out                                              20160602 08:45:39
v,eins
v,zwei
$#out                                              20160602 08:35:57
v,eins
v,zwei
$#out                                              20160602 08:19:12
$#out                                              20160602 08:18:56
$#out
}¢--- A540769.WK.REXX(WSHTT) cre=2016-06-03 mod=2016-06-03-17.48.15 A540769 ----
$#@
cc = classNew('n? TstCsmExWsh u f fEins v, v, f fZwei v')
$$. csv2o('csm_o1',cc, 'o1Feins,o1Val,o1   fZwei')
}¢--- A540769.WK.REXX(WSHTTT) cre=2016-06-23 mod=2016-07-15-09.45.04 A540769 ---
select count(*) tbs, current server loc from sysibm.sysTables;
say 'adf  dfas dfasdfas say'
$$ wie thest

















































           $=abc = und



















































einsi?
$#out                                              20160715 09:44:50
  TBS LOC
16520 CHSKAPTADVBP
1 rows fetched: select count(*) tbs, current server loc from sysibm.sysTables
fatal error in WST: SQLCODE = -104: ILLEGAL SYMBOL "SAY". SOME SYMBOLS THAT
    MIGHT BE LEGAL ARE: <ERR_STMT> <WNG_STMT> GET SQL SAVEPOINT HOLD
    FREE ASSOCIATE
src say 'adf dfas dfasdfas say' wie thest einsi?
  > <<<pos 1 of 7350<<<
sql = say 'adf  dfas dfasdfas say'                                           ...
sqlCode 0: rollback
from RR2 R25 DVBP
$#out                                              20160715 08:36:40
  TBS LOC
16520 CHSKAPTADVBP
1 rows fetched: select count(*) tbs, current server loc from sysibm.sysTables
fatal error in WST: SQLCODE = -104: ILLEGAL SYMBOL "SAY". SOME SYMBOLS THAT
    MIGHT BE LEGAL ARE: <ERR_STMT> <WNG_STMT> GET SQL SAVEPOINT HOLD
    FREE ASSOCIATE
src say 'adf dfas dfasdfas say' wie thest einsi?
  > <<<pos 1 of 7350<<<
sql = say 'adf  dfas dfasdfas say'                                           ...
sqlCode 0: rollback
from RR2 R25 DVBP
$#out                                              20160715 08:35:57
input error in WST: no rz/dbSys for rv
in wsh phase compile
$#out                                              20160715 08:35:42
input error in WST: no rz/dbSys for rv
in wsh phase compile
$#out                                              20160715 08:33:57
fatal error in WST: SQLCODE = -924: DB2 CONNECTION INTERNAL ERROR, 00000002,
    00000008, 00F30012
sql = connect DVBP
from RR2 R21 DVBP
$#out                                              20160715 08:33:09
fatal error in WST: SQLCODE = -924: DB2 CONNECTION INTERNAL ERROR, 00000002,
    00000008, 00F30002
sql = connect DVBP
from RR2 R21 DVBP
$#out                                              20160715 08:32:47
fatal error in WST: SQLCODE = -924: DB2 CONNECTION INTERNAL ERROR, 00000002,
    00000008, 00F30002
sql = connect DVBP
from RZ2 S21 DVBP
$#out                                              20160715 08:32:33
 TBS LOC
4508 CHSKA000DP4G
1 rows fetched: select count(*) tbs, current server loc from sysibm.sysTables
fatal error in WST: SQLCODE = -104: ILLEGAL SYMBOL "SAY". SOME SYMBOLS THAT
    MIGHT BE LEGAL ARE: <ERR_STMT> <WNG_STMT> GET SQL SAVEPOINT HOLD
    FREE ASSOCIATE
src say 'adf dfas dfasdfas say' wie thest einsi?
  > <<<pos 1 of 7350<<<
sql = say 'adf  dfas dfasdfas say'                                           ...
sqlCode 0: rollback
$#out                                              20160715 08:31:15
input error in WST: no rz/dbSys for
in wsh phase compile
$#out                                              20160715 08:29:10
input error in WST: no rz/dbSys for
in wsh phase compile
$#out                                              20160715 08:28:10
input error in WST: no rz/dbSys for
in wsh phase compile
$#out                                              20160715 08:27:40
input error in WST: no rz/dbSys for
in wsh phase compile
$#out                                              20160714 09:48:29
cmsExWsh sending to RZZ sqlsOut DE0G a
  TBS LOC
21616 CHROI00ZDE0G
1 rows fetched: select count(*) tbs, current server loc from sysibm.sysTables
fatal error in WST: SQLCODE = -104: ILLEGAL SYMBOL "SAY". SOME SYMBOLS THAT
    MIGHT BE LEGAL ARE: <ERR_STMT> <WNG_STMT> GET SQL SAVEPOINT HOLD
    FREE ASSOCIATE
src say 'adf dfas dfasdfas say' wie thest einsi?
  > <<<pos 1 of 7350<<<
sql = say 'adf  dfas dfasdfas say'                                           ...
sqlCode 0: rollback
from RZZ Z24 DE0G
$#out                                              20160714 09:47:43
 TBS LOC
4507 CHSKA000DP4G
1 rows fetched: select count(*) tbs, current server loc from sysibm.sysTables
fatal error in WST: SQLCODE = -104: ILLEGAL SYMBOL "SAY". SOME SYMBOLS THAT
    MIGHT BE LEGAL ARE: <ERR_STMT> <WNG_STMT> GET SQL SAVEPOINT HOLD
    FREE ASSOCIATE
src say 'adf dfas dfasdfas say' wie thest einsi?
  > <<<pos 1 of 7350<<<
sql = say 'adf  dfas dfasdfas say'                                           ...
sqlCode 0: rollback
$#out                                              20160704 08:59:01
TBS LOC
783 CHROI00ZDPZG
1 rows fetched: select count(*) tbs, current server loc from sysibm.sysTables
fatal error in WSH9: SQLCODE = -104: ILLEGAL SYMBOL "SAY". SOME SYMBOLS THAT
    MIGHT BE LEGAL ARE: <ERR_STMT> <WNG_STMT> GET SQL SAVEPOINT HOLD
    FREE ASSOCIATE
src say 'adf dfas dfasdfas say' wie thest einsi?
  > <<<pos 1 of 7350<<<
sql = say 'adf  dfas dfasdfas say'                                           ...
fatal error in WSH9: csmExWsh rc = M.O.190.1.EXRC
in wsh phase runSQL
$#out                                              20160704 08:58:11
TBS LOC
783 CHROI00ZDPZG
1 rows fetched: select count(*) tbs, current server loc from sysibm.sysTables
fatal error in WSH9: SQLCODE = -104: ILLEGAL SYMBOL "SAY". SOME SYMBOLS THAT
    MIGHT BE LEGAL ARE: <ERR_STMT> <WNG_STMT> GET SQL SAVEPOINT HOLD
    FREE ASSOCIATE
src say 'adf dfas dfasdfas say' wie thest einsi?
  > <<<pos 1 of 7350<<<
sql = say 'adf  dfas dfasdfas say'                                           ...
fatal error in WSH9: csmExWsh rc = M.O.190.1.EXRC
in wsh phase runSQL
$#out                                              20160704 08:55:51
TBS LOC
783 CHROI00ZDPZG
1 rows fetched: select count(*) tbs, current server loc from sysibm.sysTables
fatal error in WSH9: SQLCODE = -104: ILLEGAL SYMBOL "SAY". SOME SYMBOLS THAT
    MIGHT BE LEGAL ARE: <ERR_STMT> <WNG_STMT> GET SQL SAVEPOINT HOLD
    FREE ASSOCIATE
src say 'adf dfas dfasdfas say' wie thest einsi?
  > <<<pos 1 of 7350<<<
sql = say 'adf  dfas dfasdfas say'                                           ...
fatal error in WSH9: csmExWsh rc = M.O.190.1.EXRC
in wsh phase runSQL
$#out                                              20160704 08:52:46
}¢--- A540769.WK.REXX(WSHTTU) cre=2016-07-03 mod=2016-07-25-22.00.56 A540769 ---
call sqlWshIni
call sqlConnect 'rzy/dp?g', 'w'
$** call sqlSel  $<¢
trace ?r
$@. sqlRdr() $<¢
      select count(*) tbs, current server loc
   from sysibm.sysTables $!
$#out                                              20160725 22:00:40
fatal error in WST: sqlWshRdr got fatal error in WST: SQLCODE = -924: DB2 CON...
    0000000C, 00F30006
sql = connect DP?G
from RZY Y24 DP?G
in wsh phase run
$#out                                              20160714 10:22:48
fatal error in WST: sqlWshRdr got fatal error in WST: SQLCODE = -924: DB2 CON...
    0000000C, 00F30006
sql = connect DP?G
from RZY Y24 DP?G
in wsh phase run
$#out                                              20160714 10:22:15
O.216.1=¢TBS=944 LOC=CHROI00YDPYG!
$#out                                              20160714 10:18:47
cmsExWsh sending to RZY $#outFmt e $#sqlRdr DPYG
O.216.1=¢TBS=944 LOC=CHROI00YDPYG!
$#out                                              20160714 10:17:21
fatal error in WST: no class classMet(M.CLASS_SQLCSMCONN, sqlRdr)
in wsh phase run
$#out                                              20160714 10:14:30
fatal error in WST: no class classMet(M.CLASS_SQLCSMCONN, sqlRdr)
in wsh phase run
$#out                                              20160714 10:14:02
fatal error in WST: no class classMet(M.CLASS_SQLCSMCONN, sqlRdr)
in wsh phase run
$#out                                              20160714 10:13:52
O.200.1.BUFD.1=¢TBS=944 LOC=CHROI00YDPYG    !
$#out                                              20160714 10:11:42
O.200.1.BUFD.1=¢TBS=944 LOC=CHROI00YDPYG    !
$#out                                              20160714 09:53:41
fatal error in WST: SQLCODE = -518: THE EXECUTE STATEMENT DOES NOT IDENTIFY A
    VALID PREPARED STATEMENT
sql =    select count(*) tbs, current server loc                             ...
sqlCsmExe RZY/DPYG
in wsh phase run
$#out                                              20160714 09:52:36
fatal error in WST: SQLCODE = -518: THE EXECUTE STATEMENT DOES NOT IDENTIFY A
    VALID PREPARED STATEMENT
sql =    select count(*) tbs, current server loc                             ...
sqlCsmExe RZY/DPYG
in wsh phase run
$#out                                              20160714 09:52:10
fatal error in WST: no class classMet(M.CLASS_SQLCSMCONN, sqlRdr)
in wsh phase run
$#out                                              20160714 09:50:41
fatal error in WST: SQLCODE = -924: DB2 CONNECTION INTERNAL ERROR, 00000002,
    0000000C, 00F30006
sql = connect YE
in wsh phase run
$#out                                              20160714 09:50:30
fatal error in WST: SQLCODE = -924: DB2 CONNECTION INTERNAL ERROR, 00000002,
    0000000C, 00F30006
sql = connect YY
in wsh phase run
$#out                                              20160714 09:50:19
fatal error in WST: SQLCODE = -924: DB2 CONNECTION INTERNAL ERROR, 00000002,
    0000000C, 00F30006
sql = connect YV
in wsh phase run
$#out                                              20160714 09:49:49
O.200.1=¢TBS=4507 LOC=CHSKA000DP4G    !
$#out                                              20160714 09:49:13
O.200.1=¢TBS=4507 LOC=CHSKA000DP4G    !
$#out                                              20160714 09:49:02
fatal error in WST: SQLCODE = -104: ILLEGAL SYMBOL "FRO". SOME SYMBOLS THAT
    MIGHT BE LEGAL ARE: , FROM INTO
src select count(*) tbs, current server loc fro` sysibm.sysTables
  >                       >>>pos 85 of 106>>>
sql =    select count(*) tbs, current server loc                             ...
stmt = prepare s10 into :M.SQL.10.D from :src
with into :M.SQL.10.D = M.SQL.10.D
in wsh phase run
$#out                                              20160703 17:04:34
fatal error in WSH9: SQLCODE = -104: ILLEGAL SYMBOL "FRO". SOME SYMBOLS THAT
    MIGHT BE LEGAL ARE: , FROM INTO
src select count(*) tbs, current server loc fro` sysibm.sysTables
  >                       >>>pos 85 of 106>>>
sql =    select count(*) tbs, current server loc                             ...
stmt = prepare s10 into :M.SQL.10.D from :src
with into :M.SQL.10.D = M.SQL.10.D
in wsh phase run
$#out                                              20160703 17:04:14
O.200.1=¢TBS=4409 LOC=CHSKA000DP4G    !
$#out
}¢--- A540769.WK.REXX(WSHTTV) cre=2016-07-03 mod=2016-07-26-11.12.47 A540769 ---
$#@
   $*( 1. wsh
       2. dbarb dbof -t
       3 c )) ')) apreuse(warn)' all
   $*)
call sqlConnect
call sql2tab, , 'c'
$<=¢
   select 'alter table ' || strip(t.creator) || '.' || strip(t.name)
   ||  ' -- ' || s.type
      from sysibm.sysTables t join sysibm.sysTablespace s
         on t.dbName = s.dbname and t.tsName = s.name
      where t.type not in ('A', 'V')
          and t.dbName in ('AV15A1P', 'CT02A1P', 'PW01A1P', 'QZ01A1P')
          and s.alteredTs > current timestamp - 1 year
$!
$#out                                              20160725 22:03:08
--- row 1 ----------------------------------------------------------------------
           COL1               alter table OA1P.TQZ006GBGRTSHIST -- R
--- row 2 ----------------------------------------------------------------------
           COL1               alter table OA1P.TQZ005TECSVUNLOAD --
--- row 3 ----------------------------------------------------------------------
           COL1               alter table OA1P.TQZ005TECSVRTSLASTEQ --
--- row 4 ----------------------------------------------------------------------
           COL1               alter table OA1P.TQZ004TECSVLGRN --
--- row 5 ----------------------------------------------------------------------
           COL1               alter table OA1P.TQZ007GBGRIXHIST -- R
--- row 6 ----------------------------------------------------------------------
           COL1               alter table OA1P.TQZ006GBGRTSHJJJ -- R
--- row 7 ----------------------------------------------------------------------
           COL1               alter table OA1P.TQZ007GBGRIXHJJJ -- R
--- row 8 ----------------------------------------------------------------------
           COL1               alter table OA1P.TQZ006GBGRTSSTATS -- R
--- row 9 ----------------------------------------------------------------------
           COL1               alter table OA1P.TQZ007GBGRIXSTATS -- R
--- row 10 ---------------------------------------------------------------------
           COL1               alter table OA1P.TQZ121DDLRULE -- G
--- end of 10 rows -------------------------------------------------------------
$#out                                              20160725 22:02:43
COL1-------------------------------
alter table OA1P.TQZ006GBGRTSHIS
alter table OA1P.TQZ005TECSVUNLO
alter table OA1P.TQZ005TECSVRTSL
alter table OA1P.TQZ004TECSVLGRN
alter table OA1P.TQZ007GBGRIXHIS
alter table OA1P.TQZ006GBGRTSHJJ
alter table OA1P.TQZ007GBGRIXHJJ
alter table OA1P.TQZ006GBGRTSSTA
alter table OA1P.TQZ007GBGRIXSTA
alter table OA1P.TQZ121DDLRULE -
COL1-------------------------------
$#out                                              20160725 22:02:18
O.204.1=¢COL1=alter table OA1P.TQZ006GBGRTSHIST -- R!
O.204.2=¢COL1=alter table OA1P.TQZ005TECSVUNLOAD --  !
O.204.3=¢COL1=alter table OA1P.TQZ005TECSVRTSLASTEQ --  !
O.204.4=¢COL1=alter table OA1P.TQZ004TECSVLGRN --  !
O.204.5=¢COL1=alter table OA1P.TQZ007GBGRIXHIST -- R!
O.204.6=¢COL1=alter table OA1P.TQZ006GBGRTSHJJJ -- R!
O.204.7=¢COL1=alter table OA1P.TQZ007GBGRIXHJJJ -- R!
O.204.8=¢COL1=alter table OA1P.TQZ006GBGRTSSTATS -- R!
O.204.9=¢COL1=alter table OA1P.TQZ007GBGRIXSTATS -- R!
O.204.10=¢COL1=alter table OA1P.TQZ121DDLRULE -- G!
$#out                                              20160725 22:01:53
$#out                                              20160725 22:01:22
$#out                                              20160725 22:01:05
fatal error in WST: SQLCODE = -199: ILLEGAL USE OF KEYWORD T.  TOKEN ??( ¢
    CONCAT ¨¨ / MICROSECONDS MICROSECOND SECONDS SECOND WAS EXPECTED
src ...e ' || strip(t.creator) || '.' || strip(t.name) || ' -- ' || s.ty
  + pe from sysibm.sysTables t join sysibm.sysTablespace s on t.dbName =
  +  s.dbname and t.tsName = s.name where and t.type not in ('A', 'V'...
  >                        >>>pos 341 of 566>>>
sql =    select 'alter table ' || strip(t.creator) || '.' || strip(t.name)   ...
stmt = prepare s10 into :M.SQL.10.D from :src
with into :M.SQL.10.D = M.SQL.10.D
in wsh phase run
$#out                                              20160703 17:07:47
}¢--- A540769.WK.REXX(WSHTTW) cre=2016-08-11 mod=2016-08-11-13.51.08 A540769 ---
$#%
sy eins
                          * kommentar eins
do i=1 to 3 $@% sy zwei $i
                          * kommentar zwei
                          * kommentar zwei

                          * kommentar zwei


                          * kommentar zwei
sy drei
if 1 then

    sy vier
sy fuenf
$proc $@/sy/
$arg msg
    say 'sy:' $msg
$/sy/
$#out                                              20160811 10:12:08
$#out                                              20160811 10:05:10
$#out                                              20160811 10:04:43
$#out                                              20160811 10:04:28
$#out                                              20160811 09:59:18
scanErr wsh kindCall'%' expected: compile stopped before end of input
last token  scanPosition if 1 then
pos 1 in line 13: if 1 then
in wsh phase compile
$#out                                              20160811 09:57:25
scanErr wsh kindCall'%' expected: compile stopped before end of input
last token  scanPosition if 1 then
pos 1 in line 13: if 1 then
in wsh phase compile
$#out                                              20160811 09:54:15
fatal error in WST: undefined var if
in wsh phase run
$#out                                              20160811 09:53:56
$#out                                              20160811 09:52:42
$#out                                              20160811 09:52:11
$#out                                              20160811 09:51:35
$#out                                              20160811 09:51:29
$#out                                              20160811 09:50:19
$#out                                              20160811 09:50:01
scanErr primary, block or expression expected
last token  scanPosition
pos 4 in line 1: $@%
in wsh phase compile
$#out
}¢--- A540769.WK.REXX(WSHTTX) cre=2016-08-12 mod=2016-08-12-15.52.22 A540769 ---
select creator ||'!',name
    from sysibm.systables
    where creator = 'SYSIBM' and name like 'SYSTAB%'
$#out                                              20160812 15:37:48
scanErr ending Apostroph missing
last token ' scanPosition SY
pos 22 in line 3:     where creator = 'SY
in wsh phase runSQL
$#out                                              20160812 15:37:12
fatal error in WST: SQLCODE = -104: ILLEGAL SYMBOL "<END-OF-STATEMENT>". SOME
    SYMBOLS THAT MIGHT BE LEGAL ARE: + - ( TRIM XMLSERIALIZE CAST
    XMLCAST CASE CURRENT_D
src select creator ||'!' from sysibm.syst where creator =
  >                                    >>>pos 59 of 59>>>
sql = select creator ||'!'    from sysibm.syst    where creator =
stmt = prepare s49 into :M.SQL.49.D from :src
with into :M.SQL.49.D = M.SQL.49.D
sqlCode 0: rollback
$#out                                              20160812 15:36:58
input error in WST: no rz/dbSys for 20
in wsh phase compile
$#out                                              20160812 15:34:26
COL1                             NAME
SYSIBM  !                        SYSTABAUTH
SYSIBM  !                        SYSTABCONST
SYSIBM  !                        SYSTABLEPART
SYSIBM  !                        SYSTABLEPART_HIST
SYSIBM  !                        SYSTABLES
SYSIBM  !                        SYSTABLESPACE
SYSIBM!                          SYSTABLESPACESTATS
SYSIBM  !                        SYSTABLES_HIST
SYSIBM!                          SYSTABLES_PROFILES
SYSIBM  !                        SYSTABSTATS
SYSIBM  !                        SYSTABSTATS_HIST
11 rows fetched: select creator ||'!',name from sysibm.systables where cre...
$#out                                              20160812 15:34:14
O.205.1=¢COL1=SYSIBM  ! NAME=SYSTABAUTH!
O.205.2=¢COL1=SYSIBM  ! NAME=SYSTABCONST!
O.205.3=¢COL1=SYSIBM  ! NAME=SYSTABLEPART!
O.205.4=¢COL1=SYSIBM  ! NAME=SYSTABLEPART_HIST!
O.205.5=¢COL1=SYSIBM  ! NAME=SYSTABLES!
O.205.6=¢COL1=SYSIBM  ! NAME=SYSTABLESPACE!
O.205.7=¢COL1=SYSIBM! NAME=SYSTABLESPACESTATS!
O.205.8=¢COL1=SYSIBM  ! NAME=SYSTABLES_HIST!
O.205.9=¢COL1=SYSIBM! NAME=SYSTABLES_PROFILES!
O.205.10=¢COL1=SYSIBM  ! NAME=SYSTABSTATS!
O.205.11=¢COL1=SYSIBM  ! NAME=SYSTABSTATS_HIST!
11 rows fetched: select creator ||'!',name from sysibm.systables where cre...
$#out                                              20160812 15:33:45
O.205.1=¢CREATOR=SYSIBM   NAME=SYSTABAUTH!
O.205.2=¢CREATOR=SYSIBM   NAME=SYSTABCONST!
O.205.3=¢CREATOR=SYSIBM   NAME=SYSTABLEPART!
O.205.4=¢CREATOR=SYSIBM   NAME=SYSTABLEPART_HIST!
O.205.5=¢CREATOR=SYSIBM   NAME=SYSTABLES!
O.205.6=¢CREATOR=SYSIBM   NAME=SYSTABLESPACE!
O.205.7=¢CREATOR=SYSIBM NAME=SYSTABLESPACESTATS!
O.205.8=¢CREATOR=SYSIBM   NAME=SYSTABLES_HIST!
O.205.9=¢CREATOR=SYSIBM NAME=SYSTABLES_PROFILES!
O.205.10=¢CREATOR=SYSIBM   NAME=SYSTABSTATS!
O.205.11=¢CREATOR=SYSIBM   NAME=SYSTABSTATS_HIST!
11 rows fetched: select creator,name from sysibm.systables where creator =...
$#out                                              20160812 15:32:20
--- row 1 ----------------------------------------------------------------------
           CREATOR            SYSIBM
           NAME               SYSTABAUTH
--- row 2 ----------------------------------------------------------------------
           CREATOR            SYSIBM
           NAME               SYSTABCONST
--- row 3 ----------------------------------------------------------------------
           CREATOR            SYSIBM
           NAME               SYSTABLEPART
--- row 4 ----------------------------------------------------------------------
           CREATOR            SYSIBM
           NAME               SYSTABLEPART_HIST
--- row 5 ----------------------------------------------------------------------
           CREATOR            SYSIBM
           NAME               SYSTABLES
--- row 6 ----------------------------------------------------------------------
           CREATOR            SYSIBM
           NAME               SYSTABLESPACE
--- row 7 ----------------------------------------------------------------------
           CREATOR            SYSIBM
           NAME               SYSTABLESPACESTATS
--- row 8 ----------------------------------------------------------------------
           CREATOR            SYSIBM
           NAME               SYSTABLES_HIST
--- row 9 ----------------------------------------------------------------------
           CREATOR            SYSIBM
           NAME               SYSTABLES_PROFILES
--- row 10 ---------------------------------------------------------------------
           CREATOR            SYSIBM
           NAME               SYSTABSTATS
--- row 11 ---------------------------------------------------------------------
           CREATOR            SYSIBM
           NAME               SYSTABSTATS_HIST
--- end of 11 rows -------------------------------------------------------------
11 rows fetched: select creator,name from sysibm.systables where creator =...
$#out                                              20160812 15:32:13
CREATOR
       NAME
CREATO NAME
SYSIBM SYSTABAUTH
SYSIBM SYSTABCONST
SYSIBM SYSTABLEPART
SYSIBM SYSTABLEPART_HIST
SYSIBM SYSTABLES
SYSIBM SYSTABLESPACE
SYSIBM SYSTABLESPACESTATS
SYSIBM SYSTABLES_HIST
SYSIBM SYSTABLES_PROFILES
SYSIBM SYSTABSTATS
SYSIBM SYSTABSTATS_HIST
11 rows fetched: select creator,name from sysibm.systables where creator =...
$#out                                              20160812 15:32:04
CREATOR
       NAME
CREATO NAME
SYSIBM SYSTABAUTH
SYSIBM SYSTABCONST
SYSIBM SYSTABLEPART
SYSIBM SYSTABLEPART_HIST
SYSIBM SYSTABLES
SYSIBM SYSTABLESPACE
SYSIBM SYSTABLESPACESTATS
SYSIBM SYSTABLES_HIST
SYSIBM SYSTABLES_PROFILES
SYSIBM SYSTABSTATS
SYSIBM SYSTABSTATS_HIST
11 rows fetched: select creator,name from sysibm.systables where creator =...
$#out                                              20160812 15:31:41
CREATOR                          NAME
SYSIBM                           SYSTABAUTH
SYSIBM                           SYSTABCONST
SYSIBM                           SYSTABLEPART
SYSIBM                           SYSTABLEPART_HIST
SYSIBM                           SYSTABLES
SYSIBM                           SYSTABLESPACE
SYSIBM                           SYSTABLESPACESTATS
SYSIBM                           SYSTABLES_HIST
SYSIBM                           SYSTABLES_PROFILES
SYSIBM                           SYSTABSTATS
SYSIBM                           SYSTABSTATS_HIST
11 rows fetched: select creator,name from sysibm.systables where creator =...
$#out                                              20160812 15:26:24












11 rows fetched: select creator,name from sysibm.systables where creator =...
$#out                                              20160812 15:20:48












11 rows fetched: select creator,name from sysibm.systables where creator =...
$#out
}¢--- A540769.WK.REXX(WSHTUT0S) cre=2015-05-05 mod=2016-03-03-09.52.33 A540769 ---
$#:
ct $@ say 'compiling A540769.WK.REXX(WSHTUT0S)'
@ say 'executing A540769.WK.REXX(WSHTUT0S)'
@ aDb = '?'
sx = 0
@<~wk.jcl(jc)
forWith i $@¢
   if $db <> '' & aDb <> $db then $@=¢
       $@stepFinish
       $@ aDb = $db
       $= sx  =- $sx + 1
//**************** step $sx: db=$db
//S$sx     EXEC PGM=DSNUTILB,
//             PARM='$dbSys,A540769W.FULCOPY'
//SYSPRINT   DD SYSOUT=*
//UTPRINT    DD SYSOUT=*
//SYSUDUMP   DD SYSOUT=*
//SYSTEMPL   DD DSN=$dbSys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN      DD *
OPTIONS EVENT(ITEMERROR, SKIP)
LISTDEF LST
$! else $@:¢ db =- aDb $!
   if $ts <> '' then $@=¢
    INCLUDE TABLESPACE $db.$ts PARTLEVEL $!
$!
$@stepFinish

$proc $@=/stepFinish/
$@ if $sx < 1 then return
COPY LIST LST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/stepFinish/
$#out                                              20160303 09:21:54
$#out                                              20160301 07:46:22
}¢--- A540769.WK.REXX(WSHTUT0Y) cre=2015-05-04 mod=2015-05-04-11.17.46 A540769 ---
$#@
$@%¢cal - eins zwei drei, vier fuenf sechs
    cal   2ins 2wei 2rei, 2ier 2uenf 2echs$!
$@^¢ret - einR zweR dreR, vieR fuenR sechR
    ret   2inR 2weR 2reR, 2ieR 2uenR 2echR
    ret --¢ 2inR 2weR 2reR, 2ieR
    2uenR 2echR
    2sieb 2acht$! $!

$proc $@/cal/
$arg c1 c2, b1 b2
    $$ cal(c1=$c1 c2=$c2, b1=$b1 b2=$b2)
$/cal/
$proc $@/ret/
$arg r1 r2, b1 b2
    return 'ret(r1='$r1 'r2='$r2', b1='$b1 'b2='$b2'}'
$/ret/
$#out                                              20150504 11:15:34
}¢--- A540769.WK.REXX(WSHTUT00) cre=2010-12-29 mod=2016-01-20-10.01.19 A540769 ---
$#=
$=dbSys=DBOL
$=db=DA540769
$=ts=A977A
//P02     EXEC PGM=DSNUTILB,
//             PARM='$dbSys,A540769C.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$dbSys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
    INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$#out                                              20160120 09:58:00
//P02     EXEC PGM=DSNUTILB,
//             PARM='DBOL,A540769C.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=DBOL.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977A*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$#out                                              20110208 09:13:15
}¢--- A540769.WK.REXX(WSHTUT01) cre=2010-12-29 mod=2016-01-20-10.08.12 A540769 ---
$#=
$=dbSys=DP4G                    $** db subsystem group
$=db=DA540769                   $*( db name
                                    langer Kommentar   $*)
$=ts=A977A
$<>
$*(                         output redirection
  $>. fEdit()                   edit temporaeres dSN
  $>. fEdit('~tmp.jcl(t)')      edit existierendes DSN
  $>. fSub()                    direkt submit
$*)
$@<~wk.jcl(jc)                      $** include jobCards
//* $-¢date() time() '4|=' || (2*3*4) 'ts='$ts 'len='length($ts) $!
//P02     EXEC PGM=DSNUTILB,
//             PARM='$dbSys,A540769C.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$dbSys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
    INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
  $@ if right($ts, 2) == '7A' then
    FULL YES
  $@ else
    FULL NO
    SHRLEVEL CHANGE
$#out                                              20160120 10:06:34
}¢--- A540769.WK.REXX(WSHTUT02) cre=2010-12-29 mod=2016-02-29-22.02.39 A540769 ---
$#=
$*(
    $>. fEdit()
$*)
$=dbSys=DP4G
$=db=DA540769
$do tx = 975 to 977 $@=¢
    $=ts=A$tx
    $=jx=- $tx // 3
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$dbSys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$dbSys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$#out                                              20160229 22:02:18
}¢--- A540769.WK.REXX(WSHTUT03) cre=2010-12-29 mod=2016-03-03-09.11.14 A540769 ---
$#@
$*** $>intRdr
$=dbSys=DNOO
$@=¢    db         ts
        DGDB9998   A976
        DA540769   A977
$!
$| $@. csvWordRdr()
$*(
  1) das allein                           macht Objekte
  2) $|                                   pipe: output ==> input
  3) $| call fTabAuto                     formatiert als Tabelle
  4) $| $for o $$ o=$o ts=${o&ts}         loop ueber Objekte
  5) $| $forWith o $$ o=$o "$db.$ts"      loop+with ueber Objekte
$*)
$|
$=jx=0
$forWith o $@=¢
    $=jx=- $jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//           MSGCLASS=T,TIME=1440,CLASS=M1,SCHENV=DB2ALL,
//           NOTIFY=&SYSUID,REGION=0M
//S$ts    EXEC PGM=DSNUTILB,
//             PARM='$dbSys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$dbSys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
    INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$#out                                              20160303 09:10:27
}¢--- A540769.WK.REXX(WSHTUT04) cre=2010-12-29 mod=2016-03-03-09.16.51 A540769 ---
$#@
$=subsys=DP4G
$=db=DA540769
$@=¢  select dbName  db , name  ts
          from sysibm.sysTablespace
          where dbName = '$db' and name < 'A978'
          order by name desc
          fetch first 2 rows only
$!
$|
call sqlConnect $subsys
call sqlSel
$|
$=jx=0
$@forWith o $@=¢
    $=jx=- $jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,CLASS=M1,SCHENV=DB2ALL,
//         NOTIFY=&SYSUID,REGION=0M
//C$TS    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $DB.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
call sqlDisConnect
$#out                                              20160303 09:15:03
}¢--- A540769.WK.REXX(WSHTUT05) cre=2010-12-31 mod=2016-03-03-09.29.13 A540769 ---
$#:
dbSys = DP4G
$;
<=¢
    db        ts
    DGDB9998  A976
              A977
    DA540769

              S976
              S975
$!
>. fEdit()
@. csvColRdr()
|
@ aDb = '?'
sx = 0
@<~wk.jcl(jc)
forWith i $@¢
   if $db <> '' & aDb <> $db then $@=¢
       $@stepFinish
       $@ aDb = $db
       $= sx  =- $sx + 1
//**************** step $sx: db=$db
//S$sx     EXEC PGM=DSNUTILB,
//             PARM='$dbSys,A540769W.FULCOPY'
//SYSPRINT   DD SYSOUT=*
//UTPRINT    DD SYSOUT=*
//SYSUDUMP   DD SYSOUT=*
//SYSTEMPL   DD DSN=$dbSys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN      DD *
OPTIONS EVENT(ITEMERROR, SKIP)
LISTDEF LST
$! else $@:¢ db =- aDb $!
   if $ts <> '' then $@=¢
    INCLUDE TABLESPACE $db.$ts PARTLEVEL $!
$!
$@stepFinish

$proc $@=/stepFinish/
$@ if $sx < 1 then return
COPY LIST LST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/stepFinish/
$#out                                              20160303 09:21:54
$#out                                              20160301 07:46:22
}¢--- A540769.WK.REXX(WSHTUT06) cre=2010-12-31 mod=2016-03-03-09.53.59 A540769 ---
$#:
dbSys = DP4G
$;
<=¢
    db        ts
    DGDB9998  A976
              A977
    DA540769

              S976
              S975
$!
>. fEdit()
@. csvColRdr()
|
@.^¢compile $<~wk.rexx(wshTut0s) $!
$#out                                              20160303 09:53:37
}¢--- A540769.WK.REXX(WSHTUT07) cre=2010-12-31 mod=2016-03-03-10.07.30 A540769 ---
$#@
call sqlConnect dp4g
$<>
$<=¢
select creator cr, name tb
    from sysibm.sysTables where name like 'TQZ04%'
$!
   $@ call sqlSel
$|
    $= unionAll = $''
    $forWith $@=¢
$unionAll select count(*), '$TB'
    from $CR.$TB
    $= unionAll = union all
$!
$|
    call sqlStmts
$#out                                              20160303 10:07:21
}¢--- A540769.WK.REXX(WSHTUT08) cre=2016-03-03 mod=2016-03-03-10.09.27 A540769 ---
$#=
$@ call sqlConnect dp4g
$<>
$<=¢
select creator cr, name tb
    from sysibm.sysTables where name like 'TQZ04%'
$!
   $@ call sqlSel
$|
with r ("rows", "table") as
(
    $= unionAll = $''
    $forWith $@=¢
$unionAll select count(*), '$TB'
    from $CR.$TB
    $= unionAll = union all
$!
)
select * from r
$|
    $@ call sqlStmts
$#out                                              20160303 10:08:35
}¢--- A540769.WK.REXX(WSHTUT09) cre=2011-01-18 mod=2016-03-03-10.23.01 A540769 ---
$#@
$do dx=1 to dsnList(dl, 'A540769.TMP.TST') $@¢
    $= d =- m.dl.dx
    $$- right(dx, 4) $d
    call dsnCopy $d, 'RR2/'$d
    $!
$#out                                              20160303 10:20:39
}¢--- A540769.WK.REXX(WSHTUT2A) cre=2011-02-03 mod=2011-02-03-10.18.57 A540769 ---
STMT    TYPE    MSEC         ACCESS   INDEX         TABLE         TTYP
---------+---------+---------+---------+---------+---------+---------+--
000404  SELECT  268601       IX-SCAN  IELKS835A5C4  TELKS835C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       TS-SCAN                TCD600A1      T
000404  SELECT  268601       IX-SCAN  IEL2200       TEL220A1      T
000404  SELECT  268601       IX-SCAN  IELKS833A0C4  TELKS833C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       IX-SCAN  IELKS845A1C4  TELKS845C4    T
000404  SELECT  268601       IX-SCAN  IELKS858A2C4  TELKS858C4    T
000404  SELECT  268601       IX-SCAN  IELKS838A0C4  TELKS838C4    T
000404  SELECT  268601       IX-SCAN  IELKS843A0C4  TELKS843C4    T
000404  SELECT  268601       IX-SCAN  IEL0800       TEL080A1      T
000404  SELECT  268601       IX-SCAN  IEL1051       TEL105A1      T
000404  SELECT
000404  CORSUB  268601       IX-SCAN  IELKS843A3C4  TELKS843C4    T
000404  CORSUB  268601       IX-SCAN  IELKS843A2C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IELKS835A5C4  TELKS835C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS833A0C4  TELKS833C4    T
000405  SELECT  79740549     IX-SCAN  IELKS838A0C4  TELKS838C4    T
000405  SELECT  79740549     IX-SCAN  IELKS845A1C4  TELKS845C4    T
000405  SELECT  79740549     IX-SCAN  IELKS858A2C4  TELKS858C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS843A0C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IEL0800       TEL080A1      T
000405  SELECT  79740549     IX-SCAN  IEL1051       TEL105A1      T
000405  SELECT
000405  CORSUB  79740549     IX-SCAN  IELKS843A3C4  TELKS843C4    T
000405  CORSUB  79740549     IX-SCAN  IELKS843A2C4  TELKS843C4    T
DSNE610I NUMBER OF ROWS DISPLAYED IS 28
DSNE616I STATEMENT EXECUTION WAS SUCCESSFUL, SQLCODE IS 100
---------+---------+---------+---------+---------+---------+---------+--
}¢--- A540769.WK.REXX(WSHTUT2B) cre=2011-02-03 mod=2011-02-03-10.21.01 A540769 ---
$@|¢
STMT    TYPE    MSEC         ACCESS   INDEX         TABLE         TTYP
000404  SELECT  268601       IX-SCAN  IELKS835A5C4  TELKS835C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       TS-SCAN                TCD600A1      T
000404  SELECT  268601       IX-SCAN  IEL2200       TEL220A1      T
000404  SELECT  268601       IX-SCAN  IELKS833A0C4  TELKS833C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       IX-SCAN  IELKS845A1C4  TELKS845C4    T
000404  SELECT  268601       IX-SCAN  IELKS858A2C4  TELKS858C4    T
000404  SELECT  268601       IX-SCAN  IELKS838A0C4  TELKS838C4    T
000404  SELECT  268601       IX-SCAN  IELKS843A0C4  TELKS843C4    T
000404  SELECT  268601       IX-SCAN  IEL0800       TEL080A1      T
000404  SELECT  268601       IX-SCAN  IEL1051       TEL105A1      T
000404  SELECT
000404  CORSUB  268601       IX-SCAN  IELKS843A3C4  TELKS843C4    T
000404  CORSUB  268601       IX-SCAN  IELKS843A2C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IELKS835A5C4  TELKS835C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS833A0C4  TELKS833C4    T
000405  SELECT  79740549     IX-SCAN  IELKS838A0C4  TELKS838C4    T
000405  SELECT  79740549     IX-SCAN  IELKS845A1C4  TELKS845C4    T
000405  SELECT  79740549     IX-SCAN  IELKS858A2C4  TELKS858C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS843A0C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IEL0800       TEL080A1      T
000405  SELECT  79740549     IX-SCAN  IEL1051       TEL105A1      T
000405  SELECT
000405  CORSUB  79740549     IX-SCAN  IELKS843A3C4  TELKS843C4    T
000405  CORSUB  79740549     IX-SCAN  IELKS843A2C4  TELKS843C4    T
$!
$#out                                              20110203 10:20:43
}¢--- A540769.WK.REXX(WSHTUT2C) cre=2011-02-03 mod=2011-02-03-10.22.12 A540769 ---
$<|¢
STMT    TYPE    MSEC         ACCESS   INDEX         TABLE         TTYP
000404  SELECT  268601       IX-SCAN  IELKS835A5C4  TELKS835C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       TS-SCAN                TCD600A1      T
000404  SELECT  268601       IX-SCAN  IEL2200       TEL220A1      T
000404  SELECT  268601       IX-SCAN  IELKS833A0C4  TELKS833C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       IX-SCAN  IELKS845A1C4  TELKS845C4    T
000404  SELECT  268601       IX-SCAN  IELKS858A2C4  TELKS858C4    T
000404  SELECT  268601       IX-SCAN  IELKS838A0C4  TELKS838C4    T
000404  SELECT  268601       IX-SCAN  IELKS843A0C4  TELKS843C4    T
000404  SELECT  268601       IX-SCAN  IEL0800       TEL080A1      T
000404  SELECT  268601       IX-SCAN  IEL1051       TEL105A1      T
000404  SELECT
000404  CORSUB  268601       IX-SCAN  IELKS843A3C4  TELKS843C4    T
000404  CORSUB  268601       IX-SCAN  IELKS843A2C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IELKS835A5C4  TELKS835C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS833A0C4  TELKS833C4    T
000405  SELECT  79740549     IX-SCAN  IELKS838A0C4  TELKS838C4    T
000405  SELECT  79740549     IX-SCAN  IELKS845A1C4  TELKS845C4    T
000405  SELECT  79740549     IX-SCAN  IELKS858A2C4  TELKS858C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS843A0C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IEL0800       TEL080A1      T
000405  SELECT  79740549     IX-SCAN  IEL1051       TEL105A1      T
000405  SELECT
000405  CORSUB  79740549     IX-SCAN  IELKS843A3C4  TELKS843C4    T
000405  CORSUB  79740549     IX-SCAN  IELKS843A2C4  TELKS843C4    T
$!
call fmtfTab
$#out                                              20110203 10:22:01
}¢--- A540769.WK.REXX(WSHTUT2D) cre=2011-02-03 mod=2011-02-03-10.37.39 A540769 ---
$#@
$<|¢
STMT    TYPE    MSEC         ACCESS   INDEX         TABLE         TTYP
000404  SELECT  268601       IX-SCAN  IELKS835A5C4  TELKS835C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       TS-SCAN                TCD600A1      T
000404  SELECT  268601       IX-SCAN  IEL2200       TEL220A1      T
000404  SELECT  268601       IX-SCAN  IELKS833A0C4  TELKS833C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       IX-SCAN  IELKS845A1C4  TELKS845C4    T
000404  SELECT  268601       IX-SCAN  IELKS858A2C4  TELKS858C4    T
000404  SELECT  268601       IX-SCAN  IELKS838A0C4  TELKS838C4    T
000404  SELECT  268601       IX-SCAN  IELKS843A0C4  TELKS843C4    T
000404  SELECT  268601       IX-SCAN  IEL0800       TEL080A1      T
000404  SELECT  268601       IX-SCAN  IEL1051       TEL105A1      T
000404  SELECT
000404  CORSUB  268601       IX-SCAN  IELKS843A3C4  TELKS843C4    T
000404  CORSUB  268601       IX-SCAN  IELKS843A2C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IELKS835A5C4  TELKS835C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS833A0C4  TELKS833C4    T
000405  SELECT  79740549     IX-SCAN  IELKS838A0C4  TELKS838C4    T
000405  SELECT  79740549     IX-SCAN  IELKS845A1C4  TELKS845C4    T
000405  SELECT  79740549     IX-SCAN  IELKS858A2C4  TELKS858C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS843A0C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IEL0800       TEL080A1      T
000405  SELECT  79740549     IX-SCAN  IEL1051       TEL105A1      T
000405  SELECT
000405  CORSUB  79740549     IX-SCAN  IELKS843A3C4  TELKS843C4    T
000405  CORSUB  79740549     IX-SCAN  IELKS843A2C4  TELKS843C4    T
$!
wh = ''
$@forWith s $@¢
    if $TABLE \== '' then
        wh = wh", '"$TABLE"'"
    $!
$$- wh
$#out                                              20110203 10:24:50
}¢--- A540769.WK.REXX(WSHTUT2E) cre=2011-02-03 mod=2011-02-03-10.40.49 A540769 ---
$#@
$<|¢
STMT    TYPE    MSEC         ACCESS   INDEX         TABLE         TTYP
000404  SELECT  268601       IX-SCAN  IELKS835A5C4  TELKS835C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       TS-SCAN                TCD600A1      T
000404  SELECT  268601       IX-SCAN  IEL2200       TEL220A1      T
000404  SELECT  268601       IX-SCAN  IELKS833A0C4  TELKS833C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       IX-SCAN  IELKS845A1C4  TELKS845C4    T
000404  SELECT  268601       IX-SCAN  IELKS858A2C4  TELKS858C4    T
000404  SELECT  268601       IX-SCAN  IELKS838A0C4  TELKS838C4    T
000404  SELECT  268601       IX-SCAN  IELKS843A0C4  TELKS843C4    T
000404  SELECT  268601       IX-SCAN  IEL0800       TEL080A1      T
000404  SELECT  268601       IX-SCAN  IEL1051       TEL105A1      T
000404  SELECT
000404  CORSUB  268601       IX-SCAN  IELKS843A3C4  TELKS843C4    T
000404  CORSUB  268601       IX-SCAN  IELKS843A2C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IELKS835A5C4  TELKS835C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS833A0C4  TELKS833C4    T
000405  SELECT  79740549     IX-SCAN  IELKS838A0C4  TELKS838C4    T
000405  SELECT  79740549     IX-SCAN  IELKS845A1C4  TELKS845C4    T
000405  SELECT  79740549     IX-SCAN  IELKS858A2C4  TELKS858C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS843A0C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IEL0800       TEL080A1      T
000405  SELECT  79740549     IX-SCAN  IEL1051       TEL105A1      T
000405  SELECT
000405  CORSUB  79740549     IX-SCAN  IELKS843A3C4  TELKS843C4    T
000405  CORSUB  79740549     IX-SCAN  IELKS843A2C4  TELKS843C4    T
$!
wh = ''
$@forWith r $@¢
    if $TABLE \== '' then
        wh = wh", '"$TABLE"'"
    $** $$ tbl $TABLE
    $!
$@=¢
select *
    from sysibm.sysTables
    where creator = 'OA1P' and name in ($-{substr(wh, 3)})
$!
$#out                                              20110203 10:39:18
}¢--- A540769.WK.REXX(WSHTUT2F) cre=2011-02-03 mod=2011-02-03-10.44.48 A540769 ---
$#@
$<|¢
STMT    TYPE    MSEC         ACCESS   INDEX         TABLE         TTYP
000404  SELECT  268601       IX-SCAN  IELKS835A5C4  TELKS835C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       TS-SCAN                TCD600A1      T
000404  SELECT  268601       IX-SCAN  IEL2200       TEL220A1      T
000404  SELECT  268601       IX-SCAN  IELKS833A0C4  TELKS833C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       IX-SCAN  IELKS845A1C4  TELKS845C4    T
000404  SELECT  268601       IX-SCAN  IELKS858A2C4  TELKS858C4    T
000404  SELECT  268601       IX-SCAN  IELKS838A0C4  TELKS838C4    T
000404  SELECT  268601       IX-SCAN  IELKS843A0C4  TELKS843C4    T
000404  SELECT  268601       IX-SCAN  IEL0800       TEL080A1      T
000404  SELECT  268601       IX-SCAN  IEL1051       TEL105A1      T
000404  SELECT
000404  CORSUB  268601       IX-SCAN  IELKS843A3C4  TELKS843C4    T
000404  CORSUB  268601       IX-SCAN  IELKS843A2C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IELKS835A5C4  TELKS835C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS833A0C4  TELKS833C4    T
000405  SELECT  79740549     IX-SCAN  IELKS838A0C4  TELKS838C4    T
000405  SELECT  79740549     IX-SCAN  IELKS845A1C4  TELKS845C4    T
000405  SELECT  79740549     IX-SCAN  IELKS858A2C4  TELKS858C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS843A0C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IEL0800       TEL080A1      T
000405  SELECT  79740549     IX-SCAN  IEL1051       TEL105A1      T
000405  SELECT
000405  CORSUB  79740549     IX-SCAN  IELKS843A3C4  TELKS843C4    T
000405  CORSUB  79740549     IX-SCAN  IELKS843A2C4  TELKS843C4    T
$!
wh = ''
$@forWith r $@¢
    if $TABLE \== '' then
        wh = wh", '"$TABLE"'"
    $** $$ tbl $TABLE
    $!
$@=¢
select *
    from sysibm.sysTables
    where creator = 'OA1T' and name in ($-{substr(wh, 3)})
$!
$|
call sqlConnect dbtf
call sqlSel
call sqlDisconnect
$#out                                              20110203 10:43:32
}¢--- A540769.WK.REXX(WSHTUT2G) cre=2011-02-03 mod=2011-02-03-10.47.59 A540769 ---
$#@
$<|¢
STMT    TYPE    MSEC         ACCESS   INDEX         TABLE         TTYP
000404  SELECT  268601       IX-SCAN  IELKS835A5C4  TELKS835C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       TS-SCAN                TCD600A1      T
000404  SELECT  268601       IX-SCAN  IEL2200       TEL220A1      T
000404  SELECT  268601       IX-SCAN  IELKS833A0C4  TELKS833C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       IX-SCAN  IELKS845A1C4  TELKS845C4    T
000404  SELECT  268601       IX-SCAN  IELKS858A2C4  TELKS858C4    T
000404  SELECT  268601       IX-SCAN  IELKS838A0C4  TELKS838C4    T
000404  SELECT  268601       IX-SCAN  IELKS843A0C4  TELKS843C4    T
000404  SELECT  268601       IX-SCAN  IEL0800       TEL080A1      T
000404  SELECT  268601       IX-SCAN  IEL1051       TEL105A1      T
000404  SELECT
000404  CORSUB  268601       IX-SCAN  IELKS843A3C4  TELKS843C4    T
000404  CORSUB  268601       IX-SCAN  IELKS843A2C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IELKS835A5C4  TELKS835C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS833A0C4  TELKS833C4    T
000405  SELECT  79740549     IX-SCAN  IELKS838A0C4  TELKS838C4    T
000405  SELECT  79740549     IX-SCAN  IELKS845A1C4  TELKS845C4    T
000405  SELECT  79740549     IX-SCAN  IELKS858A2C4  TELKS858C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS843A0C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IEL0800       TEL080A1      T
000405  SELECT  79740549     IX-SCAN  IEL1051       TEL105A1      T
000405  SELECT
000405  CORSUB  79740549     IX-SCAN  IELKS843A3C4  TELKS843C4    T
000405  CORSUB  79740549     IX-SCAN  IELKS843A2C4  TELKS843C4    T
$!
wh = ''
$@forWith r $@¢
    if $TABLE \== '' then
        wh = wh", '"$TABLE"'"
    $** $$ tbl $TABLE
    $!
$@=¢
select *
    from sysibm.sysTables
    where creator = 'OA1T' and name in ($-{substr(wh, 3)})
$!
$|
call sqlConnect dbtf
call sqlSel
call sqlDisconnect
$|
call fmtFTab
$#out                                              20110203 10:43:32
}¢--- A540769.WK.REXX(WSHTUT2H) cre=2011-02-03 mod=2011-02-03-10.47.48 A540769 ---
$#@
$<|¢
STMT    TYPE    MSEC         ACCESS   INDEX         TABLE         TTYP
000404  SELECT  268601       IX-SCAN  IELKS835A5C4  TELKS835C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       TS-SCAN                TCD600A1      T
000404  SELECT  268601       IX-SCAN  IEL2200       TEL220A1      T
000404  SELECT  268601       IX-SCAN  IELKS833A0C4  TELKS833C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       IX-SCAN  IELKS845A1C4  TELKS845C4    T
000404  SELECT  268601       IX-SCAN  IELKS858A2C4  TELKS858C4    T
000404  SELECT  268601       IX-SCAN  IELKS838A0C4  TELKS838C4    T
000404  SELECT  268601       IX-SCAN  IELKS843A0C4  TELKS843C4    T
000404  SELECT  268601       IX-SCAN  IEL0800       TEL080A1      T
000404  SELECT  268601       IX-SCAN  IEL1051       TEL105A1      T
000404  SELECT
000404  CORSUB  268601       IX-SCAN  IELKS843A3C4  TELKS843C4    T
000404  CORSUB  268601       IX-SCAN  IELKS843A2C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IELKS835A5C4  TELKS835C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS833A0C4  TELKS833C4    T
000405  SELECT  79740549     IX-SCAN  IELKS838A0C4  TELKS838C4    T
000405  SELECT  79740549     IX-SCAN  IELKS845A1C4  TELKS845C4    T
000405  SELECT  79740549     IX-SCAN  IELKS858A2C4  TELKS858C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS843A0C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IEL0800       TEL080A1      T
000405  SELECT  79740549     IX-SCAN  IEL1051       TEL105A1      T
000405  SELECT
000405  CORSUB  79740549     IX-SCAN  IELKS843A3C4  TELKS843C4    T
000405  CORSUB  79740549     IX-SCAN  IELKS843A2C4  TELKS843C4    T
$!
wh = ''
$@forWith r $@¢
    if $TABLE \== '' then
        wh = wh", '"$TABLE"'"
    $** $$ tbl $TABLE
    $!
$@=¢
select *
    from sysibm.sysTables
    where creator = 'OA1T' and name in ($-{substr(wh, 3)})
$!
$|
call sqlConnect dbtf
call sqlSel
$|
call fmtFTab
call sqlDisconnect
$#out                                              20110203 10:47:39
}¢--- A540769.WK.REXX(WSHTUT2I) cre=2011-02-03 mod=2011-02-03-10.51.04 A540769 ---
$#@
$>.fEdit('~wk.texv(ttt)')
$<|¢
STMT    TYPE    MSEC         ACCESS   INDEX         TABLE         TTYP
000404  SELECT  268601       IX-SCAN  IELKS835A5C4  TELKS835C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       TS-SCAN                TCD600A1      T
000404  SELECT  268601       IX-SCAN  IEL2200       TEL220A1      T
000404  SELECT  268601       IX-SCAN  IELKS833A0C4  TELKS833C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       IX-SCAN  IELKS845A1C4  TELKS845C4    T
000404  SELECT  268601       IX-SCAN  IELKS858A2C4  TELKS858C4    T
000404  SELECT  268601       IX-SCAN  IELKS838A0C4  TELKS838C4    T
000404  SELECT  268601       IX-SCAN  IELKS843A0C4  TELKS843C4    T
000404  SELECT  268601       IX-SCAN  IEL0800       TEL080A1      T
000404  SELECT  268601       IX-SCAN  IEL1051       TEL105A1      T
000404  SELECT
000404  CORSUB  268601       IX-SCAN  IELKS843A3C4  TELKS843C4    T
000404  CORSUB  268601       IX-SCAN  IELKS843A2C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IELKS835A5C4  TELKS835C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS833A0C4  TELKS833C4    T
000405  SELECT  79740549     IX-SCAN  IELKS838A0C4  TELKS838C4    T
000405  SELECT  79740549     IX-SCAN  IELKS845A1C4  TELKS845C4    T
000405  SELECT  79740549     IX-SCAN  IELKS858A2C4  TELKS858C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS843A0C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IEL0800       TEL080A1      T
000405  SELECT  79740549     IX-SCAN  IEL1051       TEL105A1      T
000405  SELECT
000405  CORSUB  79740549     IX-SCAN  IELKS843A3C4  TELKS843C4    T
000405  CORSUB  79740549     IX-SCAN  IELKS843A2C4  TELKS843C4    T
$!
wh = ''
$@forWith r $@¢
    if $TABLE \== '' then
        wh = wh", '"$TABLE"'"
    $** $$ tbl $TABLE
    $!
$@=¢
select *
    from sysibm.sysTables
    where creator = 'OA1T' and name in ($-{substr(wh, 3)})
$!
$|
call sqlConnect dbtf
call sqlSel
$|
call fmtFTab
call sqlDisconnect
$#out                                              20110203 10:50:11
}¢--- A540769.WK.REXX(WSHTUT2J) cre=2011-02-03 mod=2011-02-03-10.54.29 A540769 ---
$#@
$<|¢
STMT    TYPE    MSEC         ACCESS   INDEX         TABLE         TTYP
000404  SELECT  268601       IX-SCAN  IELKS835A5C4  TELKS835C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       TS-SCAN                TCD600A1      T
000404  SELECT  268601       IX-SCAN  IEL2200       TEL220A1      T
000404  SELECT  268601       IX-SCAN  IELKS833A0C4  TELKS833C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       IX-SCAN  IELKS845A1C4  TELKS845C4    T
000404  SELECT  268601       IX-SCAN  IELKS858A2C4  TELKS858C4    T
000404  SELECT  268601       IX-SCAN  IELKS838A0C4  TELKS838C4    T
000404  SELECT  268601       IX-SCAN  IELKS843A0C4  TELKS843C4    T
000404  SELECT  268601       IX-SCAN  IEL0800       TEL080A1      T
000404  SELECT  268601       IX-SCAN  IEL1051       TEL105A1      T
000404  SELECT
000404  CORSUB  268601       IX-SCAN  IELKS843A3C4  TELKS843C4    T
000404  CORSUB  268601       IX-SCAN  IELKS843A2C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IELKS835A5C4  TELKS835C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS833A0C4  TELKS833C4    T
000405  SELECT  79740549     IX-SCAN  IELKS838A0C4  TELKS838C4    T
000405  SELECT  79740549     IX-SCAN  IELKS845A1C4  TELKS845C4    T
000405  SELECT  79740549     IX-SCAN  IELKS858A2C4  TELKS858C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS843A0C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IEL0800       TEL080A1      T
000405  SELECT  79740549     IX-SCAN  IEL1051       TEL105A1      T
000405  SELECT
000405  CORSUB  79740549     IX-SCAN  IELKS843A3C4  TELKS843C4    T
000405  CORSUB  79740549     IX-SCAN  IELKS843A2C4  TELKS843C4    T
$!
wh = ''
$@forWith r $@¢
    if $TABLE \== '' then
        wh = wh", '"$TABLE"'"
    $** $$ tbl $TABLE
    $!
$@=¢
select *
    from sysibm.sysTables
    where creator = 'OA1T' and name in ($-{substr(wh, 3)})
$!
$|
call sqlConnect dbtf
call sqlSel
$|
$@forWith r $@¢
    if $STATSTIME < '2011-01' then
       $$ $'  INCLUDE TABLESPACE' $*+
$-{strip($DBNAME)}.$-{strip($TSNAME)} -- $STATSTIME  $SPACEF
    else
       $$ $'--' ${DBNAME}.$TSNAME -- $STATSTIME
    $!
call sqlDisconnect
$#out                                              20110203 10:53:45
}¢--- A540769.WK.REXX(WSHTUT2K) cre=2011-02-03 mod=2011-02-03-11.06.59 A540769 ---
$#@
$<|¢
STMT    TYPE    MSEC         ACCESS   INDEX         TABLE         TTYP
000404  SELECT  268601       IX-SCAN  IELKS835A5C4  TELKS835C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       TS-SCAN                TCD600A1      T
000404  SELECT  268601       IX-SCAN  IEL2200       TEL220A1      T
000404  SELECT  268601       IX-SCAN  IELKS833A0C4  TELKS833C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       IX-SCAN  IELKS845A1C4  TELKS845C4    T
000404  SELECT  268601       IX-SCAN  IELKS858A2C4  TELKS858C4    T
000404  SELECT  268601       IX-SCAN  IELKS838A0C4  TELKS838C4    T
000404  SELECT  268601       IX-SCAN  IELKS843A0C4  TELKS843C4    T
000404  SELECT  268601       IX-SCAN  IEL0800       TEL080A1      T
000404  SELECT  268601       IX-SCAN  IEL1051       TEL105A1      T
000404  SELECT
000404  CORSUB  268601       IX-SCAN  IELKS843A3C4  TELKS843C4    T
000404  CORSUB  268601       IX-SCAN  IELKS843A2C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IELKS835A5C4  TELKS835C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS833A0C4  TELKS833C4    T
000405  SELECT  79740549     IX-SCAN  IELKS838A0C4  TELKS838C4    T
000405  SELECT  79740549     IX-SCAN  IELKS845A1C4  TELKS845C4    T
000405  SELECT  79740549     IX-SCAN  IELKS858A2C4  TELKS858C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS843A0C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IEL0800       TEL080A1      T
000405  SELECT  79740549     IX-SCAN  IEL1051       TEL105A1      T
000405  SELECT
000405  CORSUB  79740549     IX-SCAN  IELKS843A3C4  TELKS843C4    T
000405  CORSUB  79740549     IX-SCAN  IELKS843A2C4  TELKS843C4    T
$!
wh = ''
$@forWith r $@¢
    if $TABLE \== '' then
        wh = wh", '"$TABLE"'"
    $** $$ tbl $TABLE
    $!
$@=¢
select *
    from sysibm.sysTables
    where creator = 'OA1T' and name in ($-{substr(wh, 3)})
$!
$|
call sqlConnect dbtf
call sqlSel
$|
$@forWith r $@¢
    if $STATSTIME < '2011-01' then
       $$ $'  INCLUDE TABLESPACE' $*+
$-{strip($DBNAME)}.$-{strip($TSNAME)} -- $STATSTIME  $SPACEF
    else
       $$ $'--' ${DBNAME}.$TSNAME -- $STATSTIME
    $!
call sqlDisconnect
$| $@=¢
//A540769S JOB (CP00,KE50),
//       'CATALOG',MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//*MAIN CLASS=LOG
//STEP1 EXEC PGM=DSNUTILB,TIME=1440,
//   PARM=(DBAF,'A540769S.RUNSTA'),
//   REGION=0M
//*DSSPRINT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//UTPRINT  DD SYSOUT=*
//SYSOUT   DD SYSOUT=*
//RNPRIN01 DD SYSOUT=*
//SYSIN DD *
     LISTDEF LST#STA
     $@for i $$ $i
     OPTIONS EVENT (ITEMERROR, SKIP)

   RUNSTATS TABLESPACE LIST LST#STA
         SHRLEVEL CHANGE
           TABLE(ALL)
           INDEX (ALL  KEYCARD -- FREQVAL NUMCOLS 1  COUNT 10 BOTH
                               -- FREQVAL NUMCOLS 2  COUNT 10 BOTH
                 )
           UPDATE ALL REPORT NO
$!
$#out                                              20110203 10:56:21
}¢--- A540769.WK.REXX(WSHTUT2L) cre=2011-02-03 mod=2011-02-03-11.19.42 A540769 ---
$#@
$>.fSub()
$<|¢
STMT    TYPE    MSEC         ACCESS   INDEX         TABLE         TTYP
000404  SELECT  268601       IX-SCAN  IELKS835A5C4  TELKS835C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       TS-SCAN                TCD600A1      T
000404  SELECT  268601       IX-SCAN  IEL2200       TEL220A1      T
000404  SELECT  268601       IX-SCAN  IELKS833A0C4  TELKS833C4    T
000404  SELECT  268601       IX-SCAN  IELKS831A3C4  TELKS831C4    T
000404  SELECT  268601       IX-SCAN  IELKS845A1C4  TELKS845C4    T
000404  SELECT  268601       IX-SCAN  IELKS858A2C4  TELKS858C4    T
000404  SELECT  268601       IX-SCAN  IELKS838A0C4  TELKS838C4    T
000404  SELECT  268601       IX-SCAN  IELKS843A0C4  TELKS843C4    T
000404  SELECT  268601       IX-SCAN  IEL0800       TEL080A1      T
000404  SELECT  268601       IX-SCAN  IEL1051       TEL105A1      T
000404  SELECT
000404  CORSUB  268601       IX-SCAN  IELKS843A3C4  TELKS843C4    T
000404  CORSUB  268601       IX-SCAN  IELKS843A2C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IELKS835A5C4  TELKS835C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS833A0C4  TELKS833C4    T
000405  SELECT  79740549     IX-SCAN  IELKS838A0C4  TELKS838C4    T
000405  SELECT  79740549     IX-SCAN  IELKS845A1C4  TELKS845C4    T
000405  SELECT  79740549     IX-SCAN  IELKS858A2C4  TELKS858C4    T
000405  SELECT  79740549     IX-SCAN  IELKS831A3C4  TELKS831C4    T
000405  SELECT  79740549     IX-SCAN  IELKS843A0C4  TELKS843C4    T
000405  SELECT  79740549     IX-SCAN  IEL0800       TEL080A1      T
000405  SELECT  79740549     IX-SCAN  IEL1051       TEL105A1      T
000405  SELECT
000405  CORSUB  79740549     IX-SCAN  IELKS843A3C4  TELKS843C4    T
000405  CORSUB  79740549     IX-SCAN  IELKS843A2C4  TELKS843C4    T
$!
wh = ''
$@forWith r $@¢
    if $TABLE \== '' then
        wh = wh", '"$TABLE"'"
    $** $$ tbl $TABLE
    $!
$@=¢
select *
    from sysibm.sysTables
    where creator = 'OA1T' and name in ($-{substr(wh, 3)})
$!
$|
call sqlConnect dbtf
call sqlSel
$|
$@forWith r $@¢
    if $STATSTIME < '2011-01' then
       $$ $'  INCLUDE TABLESPACE' $*+
$-{strip($DBNAME)}.$-{strip($TSNAME)} -- $STATSTIME  $SPACEF
    else
       $$ $'--' ${DBNAME}.$TSNAME -- $STATSTIME
    $!
call sqlDisconnect
$| $@=¢
//A540769S JOB (CP00,KE50),
//       'CATALOG',MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//*MAIN CLASS=LOG
//STEP1 EXEC PGM=DSNUTILB,TIME=1440,
//   PARM=(DBAF,'A540769S.RUNSTA'),
//   REGION=0M
//*DSSPRINT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//UTPRINT  DD SYSOUT=*
//SYSOUT   DD SYSOUT=*
//RNPRIN01 DD SYSOUT=*
//SYSIN DD *
     LISTDEF LST#STA
     $@for i $$ $i
     OPTIONS EVENT (ITEMERROR, SKIP)

   RUNSTATS TABLESPACE LIST LST#STA
         SHRLEVEL CHANGE
           TABLE(ALL)
           INDEX (ALL  KEYCARD -- FREQVAL NUMCOLS 1  COUNT 10 BOTH
                               -- FREQVAL NUMCOLS 2  COUNT 10 BOTH
                 )
           UPDATE ALL REPORT NO
$!
$#out                                              20110203 11:07:11
$#out                                              20110203 10:59:46
}¢--- A540769.WK.REXX(WSH0908) cre=2009-04-21 mod=2009-09-03-11.01.23 A540769 ---
/* rexx ****************************************************************
     wsh
***********************************************************************/
/*--- main code wsh --------------------------------------------------*/
    call errReset 'h'
    parse arg fun rest
    os = errOS()

    if 0 then do            /* for special tests */
        .output$mc$lineOut('hello walti')
        x = .output
        say .output$mc$class()
        say x$mc$class()
        x = file('&out')
        call jWrite x, 'hallo walti'
        call jClose x
        exit
        end
    if 0 then do
        call tstSort
        call envIni
        call tstFile
        call tstTotal
        exit
        end
    if 0 then do
        do 2
            call tstAll
            end
        exit
        end
    if 0 then do
        call compIni
        call tstScanWin
        exit
        call envIni
        call tstFile
        call tstFileList
        call tstTotal
        exit
        call tstAll
        call envIni
        call tstTotal
        exit
        end
    call compIni
 /* if os == 'TSO' then
        call oSqlIni
 */ if fun = '' & os == 'TSO' then do    /* z/OS edit macro */
        parse value wshEditMacro() with done fun rest
        if done then
            return
        end
    fun = translate(fun)
    if fun = '' then
        fun = 'S'
    if fun = 'S' | fun = 'D' then        /* batch interface */
        if os == 'TSO' then
            exit wshBatchTSO(fun)
        else if os == 'LINUX' then
            exit wshBatch(fun, '<-%' file('&in'), '>-%' file('&out'))
        else
            call err 'implemnt wshBatch' os
    if wordPos(fun, 'R E S D') > 0 then    /* interpreter */
        exit wshInter('-'fun rest)
    if wordPos(fun, '-R -E -S -D') > 0 then
        exit wshInter(fun rest)

    if \ abbrev(fun, 'T') then
        call err 'bad fun' fun 'in arg' arg
    if fun <> 'T' then do                /* list of tests */
        c = call fun rest
        end
    else do
        c = ''
        do wx=1 to words(rest)
            c = c 'call tst'word(rest, wx)';'
            end
        if c = '' then
            c = call 'tstAct;'
        else if wx > 2 then
            c = c 'call tstTotal;'
        end
    say 'wsh interpreting' c
    interpret c
exit 0

/*--- actual test case ----------------------------------------------*/
tstAct: procedure expose m.
    call classOut m.class.class, m.class.class
    return 0
endProcedure tstAct

/*--- batch: compile shell or data from inp and
             run it to output out -----------------------------------*/
wshBatch: procedure expose m.
parse upper arg ty, inp, out
    i = cat(inp)
    cmp = comp(i)
    if pos('D', ty) || pos('d', ty) > 0 then
        ty = 'd'
    else
        ty = 's'
    r = compile(cmp, ty)
    if out \== '' then
        call envPush out
    call oRun r
    if out \== '' then
        call envPop
    return 0
endProcedure wshBatch

/*--- interpret user input: rexx, expr, data or shell ---------------*/
wshInter: procedure expose m.
parse arg inp
    do forever
        w1 = translate(word(inp, 1))
        if abbrev(w1, '-') then do
            mode = substr(w1, 2)
            inp = subWord(inp, 2)
            if mode = '' then
                return 0
            end
        if inp <> '' then do
            say 'as' mode 'interpreting' inp
            if mode = 'R' then
                interpret inp
            else if mode = 'E' then
                interpret 'say' inp
            else if mode = 'S' | mode = 'D' then do
                call errReset 'h', 'say "******* intercepting error";',
                    'call errSay ggTxt; return "";'
                call oRun compile(comp(jBuf(inp)),
                           , translate(mode, 'ds', 'DS'))
                call errReset 'h'
                end
            else
                say 'mode' mode 'not implemented yet'
            end
        say 'enter' mode 'expression,  - for end, -r or -e for Rexx' ,
                                                 '-s or -d for WSH'
        parse pull inp
        end
endProcedure wshInter

/*--- batch under tso: input dd(WSH), output dd(OUT) if allocated ---*/
wshBatchTSO: procedure expose m.
parse upper arg ty
    i = cat("-WSH")
    useOut = listDsi('OUT FILE')
    if \ (useOut = 16 & sysReason = 2) then
        out = '> -out'
    else
        out = ''
    call wshBatch ty, '< -wsh', out
    return 0
endProcedure wshBatchTso

/*--- if we are called
        not as editmacro return 0
        as an editmacro with arguments: return 0 arguments
        without arguments: run editMacro interface ------------------*/
wshEditMacro: procedure expose m.
    if \ (adrEdit('macro (mArgs) NOPROCESS', '*') == 0) then
        return 0
    if mArgs \== '' then
        return 0 mArgs
     call adrEdit '(d) = dataset'
    call adrEdit '(m) = member'
    if dsnSetMbr(d, m) = 'A540769.WK.REXX(WSH)' then
        return 0

    call adrIsp 'control errors return'
    pc = adrEdit("process dest range Q", 0 4 8 12 16)
    dst = ''
    if pc = 0 | pc = 8 then do
        call adrEdit "(rFi) = lineNum .zFrange"
        call adrEdit "(rLa) = lineNum .zLrange"
        if pc = 0 then
            call adrEdit "(dst) = lineNum .zDest"
        else
            dst = rLa
        end
    else if pc = 12 then do
        if adrEdit("find first '$***out' 1", 4) = 0 then do
            call adrEdit "(dst) = cursor"
            call adrEdit "(li) = line" dst
            li = overlay(date(s) time(), li, 20)
            call adrEdit "line_before" dst "= (li)"
            rFi = 1
            rLa = dst-1
            end
        end
    if dst = '' then
        msg = 'bitte Bereich mit q oder qq auswaehlen ???' rc ,
                'oder $***out Zeile einfuegen'
    else if rLa < rFi then
        msg = 'firstLine' rFi 'before last' rLa
    else
        msg = ''
    if msg \== '' then do
        say msg
        return 1
        end
    /*  say 'range' c1 'rc' pc':' rFi '-' rLa   'after' dst */

    i = jOpen(jBuf(), m.j.cWri)
    o = jBuf()
    do lx=rFi to rLa
        call adrEdit "(li) = line" lx
        call jWrite i, li
        end
    cmp = comp(jClose(i))
    if pos('D', mArgs) > 0 then
        ty = 'd'
    else
        ty = 's'
    call errReset 'h',
             , 'return wshEditCompErrH(ggTxt, ' rFi',' rLa')'
    r = compile(cmp, ty)
    call errReset 'h',
             , 'return wshEditRunErrH(ggTxt, ' quote(o)',' dst')'
    call envPush '>%' o
    call oRun r
    call envPop
    lab = wshEditInsLinSt(dst+1, , o'.BUF')
    call wshEditLocate dst-7
    return 1
endProcedure wshEditMacro

wshEditLocate: procedure
parse arg ln
    call adrEdit '(la) = linenum .zl'
    if la < 40 then
        return
    if ln < 7 then
        ln = 1
    else
        ln = min(ln, la - 40)
    call adrEdit 'locate ' ln
    return
endProcedure wshEditLocate

wshEditCompErrH: procedure expose m.
parse arg ggTxt, rFi, rLa
    call errReset 'h'
    oo = outDest('=')
    call outDest 'i', outDest()';'outDest('s', mCut(ggStem, 0))
    call errSay 'compErr' ggTxt
    call outDest 'i', oo
    parse var m.ggStem.3 "pos " pos .  " in line " lin":"
    if pos = '' then do
        parse var m.ggStem.3 " line " lin":"
        pos = 0
        end
    lab = rFi + lin
    if pos \= '' then
        lab = wshEditInsLin(lab, 'msgline', right('*',pos))
    lab = wshEditInsLinSt((rFi+lin), 'msgline', ggStem)
    call wshEditLocate rFi+lin-25
    exit 0
endSubroutine wshEditCompErrH

wshEditRunErrH: procedure expose m.
parse arg ggTxt, so, dst
    call errReset 'h'
    call errSay ggTxt, '*** run error'
    lab = wshEditInsLinSt(dst+1, , so'.BUF')
    call outDest 's', mCut(ggStem, 0)
    call errSay ggTxt, '*** run error'
    call wshEditInsLinSt dst+1, msgline, ggStem
    exit 0
endSubroutine wshEditRunErrH

wshEditInsLinCmd: procedure
parse arg wh
    if dataType(wh, 'n')  then do
        if adrEdit("label" wh "= .a", 0 8 12) \= 12 then
            return 'line_before .a ='
        else
            return 'line_after .zl ='
        end
    else if left(wh, 1) == '.' then
        return 'line_before' wh '='
    else
        return wh
endProcedure wshEditInsLinCmd

wshEditInsLin: procedure
parse arg wh, type
    cmd = wshEditInsLinCmd(wh)
    do ax=3 to arg()
        li = strip(arg(ax), 't')
        if li == '' then
            iterate
        if translate(type) = 'MSGLINE' then do while length(li) > 72
            sx = lastPos(' ', li, 72)
            if sx < 10 then
                sx = 72
            one = left(li, sx)
            li = '    'strip(substr(li, sx))
            call adrEdit cmd type "(one)"
            end
        call adrEdit cmd type "(li)", 0 4
        end
    return cmd
endProcedure wshEditInsLin

wshEditInsLinSt: procedure expose m.
parse arg wh, type, st
    cmd = wshEditInsLinCmd(wh)
    do ax=1 to m.st.0
        call wshEditInsLin cmd, type, m.st.ax
        end
    return cmd
endProcedure wshEditInsLinSt
/*** end wsh, begin all copies ****************************************/

/* copy tstAll begin  *************************************************/
tstAll: procedure expose m.
    call tstBase
    call tstComp
    call tstDiv
    return 0
endProcedure tstAll

/* copx tstZos begin **************************************************/
tstZOs:
    call sqlIni
    call tstSql
    call tstSqlO
    call tstSqlEnv
    call tstTotal
    return
endProcedure tstZOs

tstWshBatch:
    call adrTso 'alloc dd(WSH) shr dsn(WK.TEXT(WSHBATCH))'
    call wshBatch
    return adrTso('free dd(WSH)')

tstLmdTiming:
parse arg lev
    say timing() lev
    call lmdBegin abc, lev
    c = 0
    do while lmdNext(abc, st.)
        c = c + st.0
        end
    call lmdEnd   abc
    say timing() lev 'with group - without reading' c
    call adrIsp 'lmdinit listid(lmdId) level('lev')'
    do c=0 while adrIsp('lmdlist listid(&lmdId) dataset(abc)', 8) = 0
        end
    call adrIsp 'lmdfree listid(&lmdId)'
    say timing() lev 'with list' c
    return
endProcedure lmdTiming

tstCsi: procedure expose m.
    if 0 then do
        call lmd 'A540769.*K'
        call tstCsiCla 'A540769.WK.REXX'
        call tstCsiCla 'A540769.AAA.DATASETS'
        call tstCsiCla 'A540769.RRR.DATASETS'
        end
    if 0 then do
        call tstCsiOpNx 'A540769.WK.*E*'
        call tstCsiOpNx 'A540769.AAA.DATASETS'
        call tstCsiOpNx 'A540769.RRR.DATASETS'
        end
    if 1 then do
        call tstCsiNxCl 'A540769.WK.**'
        call tstCsiNxCl 'DBTF.M*.**'
        call tstCsiNxCl 'DBTF.BE*.**'
        end
    return
tstCsi: procedure expose m.
 /* call lmd 'A540769.*K' */
    call tstCsi1   'A540769.WK.REXX'
    call tstCsi1   'A540769.AAA.DATASETS'
    call tstCsi1   'A540769.RRR.DATASETS'
    return

tstCsiCla:
parse arg ds
    say ds '-->' csiCla(ds)
    return

tstCsiOpNx: procedure expose m.
parse arg ds
    m = 'NUE123'
    s = 'res89'
    flds = 'devtyp volser mgmtclas comudsiz NOBYTTRK UDATASIZ HARBA'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    do while csiNext(m, s)
        say m.s     'dev' c2x(m.s.devTyp) ,
                    'vol' m.s.volSer 'cla' m.s.mgmtclas,
                    'comuDsiz' m.s.comuDsiz 'noBytTrk' m.s.noBytTrk,
                    'udatasiz' c2x(m.s.udatasiz) ,
                    'harba' c2x(m.s.harba)
        end
    return

tstCsiNxCl: procedure expose m.
parse arg ds
    m = 'ABC123'
    s = 'efg89'
    flds = 'devtyp volser mgmtclas'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    say timing() 'begin'
    do i=1 while csiNext(m, s)
        nn = csiArcTape(m.s.volser, m.s.mgmtClas, m.s.devtyp, m.s)
/*      oo = csiCla(strip(m.s))
        if oo <> nn then
            say nn '<>' oo m.s
 */     if i // 1000 = 0 then
            say timing() i nn m.s
        end
    say timing() (i-1) nn m.s
    return
endProcedure tstCsiNxCl

listCatClass: procedure expose m.
parse upper arg dsn
    call outtrap x., '*'
    call adrTso "listcat volume entry('"dsn"')", 4
    rt = rc
    call outtrap off
    /* say 'listct rc =' rt 'lines' x.0 */
    cl = ''
    vo = ''
    if word(x.1, 3) \== dsn then
        call jOut 'err ??? for dsn' dsn 'bad first line' x.1
    else if pos('NOT FOUND', x.1) > 0 then
        return 'notFound'
    else if word(x.1, 1)\== 'NONVSAM' then
        call jOut 'err ??? for dsn' dsn 'bad first line' x.1
    do x=2 to x.0 while vo = '' & left(x.x, 1) = ' '
     /* say x.x */
        p = pos('MANAGEMENTCLASS-', x.x)
        if p > 0 then
            vo = strip(word(substr(x.x, p+16), 1), 'l', '-')
        p = pos('VOLSER--', x.x)
        if p > 0 then
            vo = strip(word(substr(x.x, p+6), 1), 'l', '-')
        p = pos('DEVCLASS--', x.x)
            dt = strip(word(substr(x.x, p+8), 1), 'l', '-')
        end
   /*  say 'lc' cl 'vo' vo 'dt' dt 'dsn' dsn */
    if vo = '' then
        call jOut '??? err no volume for dsn' dsn
    else if vo = 'ARCIVE' then
        res =  'arcive'
    else if cl = '' then
        res = 'tape'
    else
        res = cl
    if   abbrev(res, 'ar') \= abbrev(dt, "X'0") ,
       | abbrev(res, 'ta') \= abbrev(dt, "X'7") ,
       | (left(res, 1) >= 'A') \= abbrev(dt, "X'3") then
       call jOut 'err ??? mismatch cl' cl 'vo' vo 'dt' dt 'dsn' dsn
    return res
endProcedure listCatClass

/* copx tstZos end   **************************************************/

/* copx tstDiv begin **************************************************/
tstDiv:
    call tstSort
    call tstMatch
    call tstTotal
    return
endProcedure tstDiv

tstSort: procedure expose m.
    call tstSortComp
    call tstSortComp '<<='
    call tstSortComp 'm.aLe <<= m.aRi'
    call tstSortComp 'if 1 then cmp = m.aLe <<= m.aRi; else call err sd'
    return
endProcedure tstSort

tstSortComp: procedure expose m.
parse arg cmp
/*<<tstSort
    ### start tst tstSort #############################################
    sort 29  c ACHT DREI DREIZEHN EINS ELF FUENF M.I.25 M.I.26 M.I.27 M+
    ..I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI ZWOELF 0 1 1 1 2 2+
    . 3 3 4 4
    sort 22  c ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27 M.I.29 NEUN VIERZ+
    EHN ZEHN ZWOELF 0 1 1 1 2 2 3 3 4 4
    sort 15  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1 1 2 2 3 3 4 4
    sort 8  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1
    sort 1  M.I.29
tstSort */
/*<<tstSortAscii
    ### start tst tstSortAscii ########################################
    sort 29  0 1 1 1 2 2 3 3 4 4 ACHT DREI DREIZEHN EINS ELF FUENF M.I.+
    25 M.I.26 M.I.27 M.I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI Z+
    WOELF c
    sort 22  0 1 1 1 2 2 3 3 4 4 ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27+
    . M.I.29 NEUN VIERZEHN ZEHN ZWOELF c
    sort 15  0 1 1 1 2 2 3 3 4 4 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 8  0 1 1 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 1  M.I.29
tstSortAscii */
    say '### start with comparator' cmp '###'
    if errOS() == 'LINUX' then
        call tst t, "tstSortAscii"
    else
        call tst t, "tstSort"
    call mAdd mCut(i, 0), eins, zwei, drei, vier, fuenf, sechs,
        ,sieben, acht, neun, zehn, elf, zwoelf, dreizehn, vierzehn
    call mAdd i, 1, 2, 3, 4, 4, 3, 2, 1, 0, 1,
        , 'M.I.25', 'M.I.26', 'M.I.27', 'c', 'M.I.29'

    do yy = m.i.0 by -1 to 1
        do x = 0 to yy
            m.i.0 = x
            call sort i, o, cmp
            m = ''
            la = ''
            if x <> m.o.0 then
                call err 'size mismatch' x '<>' m.o.0
            do y=1 to m.o.0
                m = m m.o.y
                if \ (la << m.o.y) then
                    call err 'sort mismatch' yy x y '\' la '<<' m.o.y
                end
            end
        if yy // 7 = 1 then
           call tstOut t, 'sort' yy m
        do x = 2 to yy
            x1 = x-1
            m.i.x1 = m.i.x
            end
        end
    call tstEnd t
    return
endProcedure tstSort

tstMatch: procedure expose m.
/*<<tstMatch
    ### start tst tstMatch ############################################
    match(eins, e?n*) 1 1 2,i,s trans(E?N*) EiNs
    match(eins, eins) 1 1 0 trans(EINS) EINS
    match(e1nss, e?n*) 1 1 2,1,ss trans(?*) 1ss
    match(eiinss, e?n*) 0 0 -9
    match(einss, e?n *) 0 0 -9
    match(ein s, e?n *) 1 1 2,i,s trans(E?N *) EiN s
    match(ein abss  , ?i*b*) 1 1 3,e,n a,ss   trans(?I*B*) eIn aBss  .
    match(ein abss wie gehtsssxdirx und auch , ) 0 0 -9
    match(ies000, *000) 1 1 1,ies trans(*000) ies000
    match(xx0x0000, *000) 1 1 1,xx0x0 trans(*000) xx0x0000
    match(000x00000xx, 000*) 1 1 1,x00000xx trans(000*) 000x00000xx
    match(000xx, *0*) 1 1 2,00,xx trans(ab*cd*ef) ab00cdxxef
tstMatch */
    call tst t, "tstMatch"
    call tstOut t, matchTest1('eins', 'e?n*'                        )
    call tstOut t, matchTest1('eins', 'eins'                        )
    call tstOut t, matchTest1('e1nss', 'e?n*', '?*'                 )
    call tstOut t, matchTest1('eiinss', 'e?n*'                      )
    call tstOut t, matchTest1('einss', 'e?n *'                      )
    call tstOut t, matchTest1('ein s', 'e?n *'                      )
    call tstOut t, matchTest1('ein abss  ', '?i*b*'                 )
    call tstOut t, matchTest1('ein abss wie gehtsssxdirx und auch ' )
    call tstOut t, matchTest1('ies000', '*000'                      )
    call tstOut t, matchTest1('xx0x0000', '*000'                    )
    call tstOut t, matchTest1('000x00000xx', '000*'                 )
    call tstOut t, matchTest1('000xx', '*0*', 'ab*cd*ef'            )
    call tstEnd t
return

matchTest1:
parse arg w, m, m2
    r = 'match('w',' m')' match(w, m) match(w, m, vv) m.vv.0
    do x=1 to m.vv.0
        r = r','m.vv.x
        end
    if m2 = '' then
        m2 = translate(m)
    if m.vv.0 >= 0 then
        r = r 'trans('m2')' matchTrans(m2, vv)
    return r
endProcedure matchTest1
/* copx tstDiv end   **************************************************/

/* copx tstSql begin **************************************************/
tstSqlUtils: procedure expose m.
    call sqlConnect 'DBAF'
    id = 'A540769.dsnUtils'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "TEMPLATE TCOPYD",
            "DSN('&SSID..&JO..&US..P&PART(2)..D&DATE(3)..T&TIME.')",
            "DATACLAS(NULL12) MGMTCLAS(COM#A011) STORCLAS(FAR$N)",
            "SPACE (150,3750) TRK UNCNT 59;",
         "listdef abc include tablespace DA540769.A002* partlevel;",
         "listdef mf  include tablespace MF01A1A.A110A partlevel;",
         "copy list abc copyddn(tcopyd) shrlevel change;"
    st = translate(st)
    call sqlExec "call SYSPROC.DSNUTILS ( :id, :rst,",
                   ":st,:retcode, :ANY" copies(',:e,:e,:z',12) ")"
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.DSNUTILS'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    exit
endProcedure tstSqlUtils

tstSqlStored: procedure expose m.
    call sqlConnect 'DBAF'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "abc"
    call sqlExec "call SYSPROC.COMMAND ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.COMMAND'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStored

tstSqlStoredWK: procedure expose m.
    call sqlConnect 'DBAF'
 /* st = "direct wkUtiSub"
    rst = 'NO'
    say 'before call st='st 'rst='rst
    call sqlExec "call A540769.WKUTILSUB ( :st, :rst)"
    say 'after call st='st 'rst='rst
 */ rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    prc = 'DB2UTIL.DB2UTIL'
    st = "DA540769.A2*" /*  DA540769.A1*" */
    say  "call" prc "("st", ...)"
    call sqlExec "call" prc "(:st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE' prc
 /* say 'results' results  */
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say ''
    say '***** utility output'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say '***** end output'
    call sqlDisconnect
    return 0
endProcedure tstSqlStoredWK

tstSqlStoredSQL1: procedure expose m.
    call sqlConnect 'DBIA'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "DA540769.A2* DA540769.A1*"
    call sqlExec "call A540769.WKSQL1 ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE A540769.WKUTIL'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStoredSQL1

tstSqlTriggerTiming:
    parse upper arg tb ni
    m.noInsert = ni == 0
    cnt = 200000
    if tb = '' then
        TB = 'GDB9998.TWK511TRI'
    call sqlConnect dbaf
    say timing() 'noInsert' m.noInsert 'tb' tb
    call sql2St 49, '*', cc, 'select max(pri) MX from' tb
    if m.cc.1.mx == m.sqlNull then
        m.cc.1.mx = 0
    von = m.cc.1.mx + 1
    bis = m.cc.1.mx + cnt
    say m.cc.0 'max' m.cc.1.mx 'von' von 'bis' bis 'count' cnt tb
    if right(tb, 2) = 'A1' then do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long, par)' ,
                   'values (?, ?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    else do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long)' ,
                   'values (?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    /*  call sqlLn 5,,, 'select * from' tb 'where pri >=' von */
    call sqlExImm 'commit'
    say timing()
    call sqlDisconnect
    return
endProcedure tstSqlTriggerTiming

tstSql: procedure expose m.
    cx = 2
/*<<tstSql
    ### start tst tstSql ##############################################
    *** err: sqlCode -204 A540769.SYSDUMMY IS AN UNDEFINED NAME .
    .    e 1: warnings
    .    e 2: state 42704
    .    e 3: stmt =  execSql prepare s7 from :src
    .    e 4: with src = select * from sysdummy
    fetchA 1 ab= m.abcdef.123.AB abc ef= efg
    fetchA 0 ab= m.abcdef.123.AB abc ef= efg
    sqlVars :M.STST.A :M.STST.A.SQLIND, :M.STST.B :M.STST.B.SQLIND, +
    :M.STST.C :M.STST.C.SQLIND
    1 all from dummy1
    a=a b=2 c=0
    sqlVarsNull 1
    a=a b=2 c=---
    fetchBT 1 SYSTABLES
    fetchBT 0 SYSTABLES
    fetchBI 1 SYSINDEXES
    fetchBI 0 SYSINDEXES
    opAllCl 3
    fetchC 1 SYSTABLES
    fetchC 2 SYSTABLESPACE
    fetchC 3 SYSTABLESPACESTATS
    PreAllCl 3
    fetchD 1 SYSIBM.SYSTABLES
    fetchD 2 SYSIBM.SYSTABLESPACE
    fetchD 3 SYSIBM.SYSTABLESPACESTATS
tstSql */
    call tst t, "tstSql"
    call sqlConnect 'DBAF'
    src = 'select * from sysdummy'
    call sqlExec 'prepare s7 from :src'
    call sqlPrepare cx, "select 'abc' , 'efg'",
                            'from sysibm.sysDummy1'
    call sqlExec 'declare c'cx 'cursor for s'cx
    call sqlOpen cx
    a = 'abcdef'
    b = 123
    do i=1 to 2
        call jOut 'fetchA' sqlFetchInto(cx, ':m.a.b.ab, :m.a.b.ef') ,
            'ab= m.'a'.' || b'.'ab m.a.b.ab 'ef=' m.a.b.ef
        end
    call sqlClose cx
    drop stst a b c m.stst.a m.stst.b m.stst.c
    sv = sqlVars('M.STST',  A B C , 1)
    call jOut 'sqlVars' sv
    call jOut sqlPreAllCl(cx,
           , "select 'a', 2, case when 1=0 then 1 else null end ",
                 "from sysibm.sysDummy1",
           , stst, sv) 'all from dummy1'
    call jOut 'a='m.stst.a 'b='m.stst.b 'c='m.stst.c
    call jOut 'sqlVarsNull' sqlVarsNull(stst,  A B C)
    call jOut 'a='m.stst.a 'b='m.stst.b 'c='m.stst.c
    call sqlPreDeclare cx, "select name" ,
                            "from sysibm.sysTables" ,
                            "where creator = 'SYSIBM' and name = ?"
    call sqlOpen cx, 'SYSTABLES'
    call jOut 'fetchBT' sqlFetchInto(cx, ':NM') nm
    call jOut 'fetchBT' sqlFetchInto(cx, ':NM') nm
    call sqlClose cx
    call sqlOpen cx, 'SYSINDEXES'
    a = 'a b c'
    b = 1234565687687234
    call jOut 'fetchBI' sqlFetchInto(cx, ':NM') nm
    call jOut 'fetchBI' sqlFetchInto(cx, ':NM') nm
    call sqlClose cx
    src = "select name" ,
              "from sysibm.sysTables" ,
              "where creator = 'SYSIBM' and name like 'SYSTABLES%'",
              "fetch first 3 rows only"
     call sqlPreDeclare cx, src
     st = 'wie geht'' s'
     call jOut 'opAllCl' sqlOpAllCl(cx, st, ':M.ST.SX.NAME')
     do x=1 to m.st.0
         call jOut 'fetchC' x m.st.x.name
         end
     st = 'auch noch'
     src = "select strip(creator) || '.' || strip(name)" substr(src,12)
     call jOut 'PreAllCl' sqlPreAllCl(cx+11, src, st, ':M.ST.SX.NAME')
     do x=1 to m.st.0
         call jOut 'fetchD' x m.st.x.name
         end
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSql

tstSqlO: procedure expose m.
/*<<tstSqlO
    ### start tst tstSqlO #############################################
    *** err: sqlCode -204 A540769.SYSDUMMY IS AN UNDEFINED NAME .
    .    e 1: warnings
    .    e 2: state 42704
    .    e 3: stmt =  execSql prepare s7 from :src
    .    e 4: with src = select * from sysdummy
    REQD=Y col=123 case=--- col5=anonym
    NAME            T DBNAME          TSNAME         .
    SYSTABAUTH      T DSNDB06         SYSDBASE       .
    SYSTABCONST     T DSNDB06         SYSOBJ         .
    SYSTABLEPART    T DSNDB06         SYSDBASE       .
    SYSTABLEPART_HI T DSNDB06         SYSHIST        .
    SYSTABLES       T DSNDB06         SYSDBASE       .
    NAME              T DBNAME  TSNAME  .
    SYSTABAUTH        T DSNDB06 SYSDBASE
    SYSTABCONST       T DSNDB06 SYSOBJ  .
    SYSTABLEPART      T DSNDB06 SYSDBASE
    SYSTABLEPART_HIST T DSNDB06 SYSHIST .
    SYSTABLES         T DSNDB06 SYSDBASE
tstSqlO */
    call tst t, "tstSqlO"
    call sqlConnect 'DBAF'
    src = 'select * from sysdummy'
    call sqlExec 'prepare s7 from :src'
    call sql2Cursor 13,
          , 'select d.*, 123, current timestamp "jetzt und heute",',
                    'case when 1=0 then 1 else null end caseNull,',
                    "'anonym'" ,
               'from sysibm.sysdummy1 d'
    call sqlOpen 13
    do while sqlFetch(13, abc)
        call jOut 'REQD='m.ABC.IBMREQD 'col='m.ABC.col2,
                  'case='m.ABC.CASENULL,
                  'col5='m.ABC.col5
        je    = 'jetzt'
        jetzt = m.ABC.je
        dd = date('s')
        dd = left(dd, 4)'-'substr(dd, 5, 2)'-'right(dd, 2)'-' ,
                || left(time(), 2)'.'
        say 'jetzt='jetzt 'date time' dd
        if \ abbrev(jetzt, dd) then
            call err 'date mismatch abbrev' dd
        end
    call sqlClose 13
    call sql2Cursor 13 ,
            , 'select name, class, dbName, tsName'           ,
                              /* ,alteredTS, obid, cardf'*/ ,
                 'from sysibm.systables'                    ,
                 "where creator = 'SYSIBM' and name like 'SYSTA%'"  ,
                 "fetch first 5 rows only",
            , , 'sl<15'
    call sqlOpen 13
    call jOut fmtFldTitle(m.sql.13.fmt)
    do while sqlFetchLn(13, li)
        call jOut m.li
        end
    call sqlClose 13
    call sqlGenFmt m.sql.13.fmt, 13, 'sst'
    call sqlOpen 13
    do ix=1 while sqlFetch(13, fe.ix)
        end
    m.fe.0 = ix-1
    call fmtFldSquash sqFmt, sqlClass(13), fe
    call jOut fmtFldTitle(sqFmt)
    do ix=1 to m.fe.0
        call jOut oFldCat(sqlClass(13), fe.ix, sqFmt)
        end
    call sqlClose 13
    if 0 then do
        call sql2Cursor 13 ,
            , 'select *',
                 'from sysibm.systables'                    ,
                 "where creator = 'SYSIBM' and name like 'SYSTA%'"  ,
                 "fetch first 1 rows only",
            , , 'sl<15'
        call sqlOpen 13
        call jOut fmtFldTitle(m.sql.13.fmt)
        do while sqlFetchLn(13, li)
            call jOut m.li
            end
        call sqlClose 13
        end
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSqlO

tstSqlEnv: procedure expose m.
/*<<tstSqlEnv
    ### start tst tstSqlEnv ###########################################
    REQD=Y COL2=123 case=--- COL5=anonym
    sql fmtFldRw sl<15
    NAME            T DBNAME          TSNAME         .
    SYSTABAUTH      T DSNDB06         SYSDBASE       .
    SYSTABCONST     T DSNDB06         SYSOBJ         .
    SYSTABLEPART    T DSNDB06         SYSDBASE       .
    SYSTABLEPART_HI T DSNDB06         SYSHIST        .
    SYSTABLES       T DSNDB06         SYSDBASE       .
    sql fmtFldSquashRW
    NAME              T DBNAME  TSNAME  .
    SYSTABAUTH        T DSNDB06 SYSDBASE
    SYSTABCONST       T DSNDB06 SYSOBJ  .
    SYSTABLEPART      T DSNDB06 SYSDBASE
    SYSTABLEPART_HIST T DSNDB06 SYSHIST .
    SYSTABLES         T DSNDB06 SYSDBASE
    sqlLn  sl=
    COL1          T DBNAME                   COL4    .
    SYSTABAUTH    T DSNDB06                  SYSDBASE
    SYSTABCONST   T DSNDB06                  SYSOBJ  .
    SYSTABLEPART  T DSNDB06                  SYSDBASE
    SYSTABLEPART_ T DSNDB06                  SYSHIST .
    SYSTABLES     T DSNDB06                  SYSDBASE
    sqlLn  ---
    NAME              T DBNAME  TSNAME  .
    SYSTABAUTH        T DSNDB06 SYSDBASE
    SYSTABCONST       T DSNDB06 SYSOBJ  .
    SYSTABLEPART      T DSNDB06 SYSDBASE
    SYSTABLEPART_HIST T DSNDB06 SYSHIST .
    SYSTABLES         T DSNDB06 SYSDBASE
tstSqlEnv */
    call tst t, "tstSqlEnv"
    call sqlConnect 'DBAF'
    call envBarBegin
    call jOut 'select d.*, 123, current timestamp "jetzt und heute",'
    call jOut       'case when 1=0 then 1 else null end caseNull,'
    call jOut       "'anonym'"
    call jOut  'from sysibm.sysdummy1 d'
    call envBar
    call sql 13
    call envBarLast
    do while envRead(abc)
        call jOut 'REQD='envGet('ABC.IBMREQD'),
                  'COL2='envGet('ABC.COL2'),
                  'case='envGet('ABC.CASENULL'),
                  'COL5='envGet('ABC.COL5')
        jetzt = envGet('ABC.jetzt')
        say 'jetzt='jetzt
        dd = date('s')
        dd = left(dd, 4)'-'substr(dd, 5, 2)'-'right(dd, 2)'-' ,
                || left(time(), 2)'.'
        if \ abbrev(jetzt, dd) then
            call err 'date mismatch abbrev' dd
        end
    call envBarEnd
    call jOut 'sql fmtFldRw sl<15'
    call envBarBegin
    call jOut 'select name, class, dbName, tsName'
                                  /* ,alteredTS, obid, cardf'*/
    call jOut    'from sysibm.systables'
    call jOut    "where creator = 'SYSIBM' and name like 'SYSTA%'"
    call jOut    "fetch first 5 rows only"
    call envBar
    call sql 13
    call envBarLast
    call fmtFldRW sqlGenFmt(fmtAbc, 13, 'sl<15')
    call envBarEnd
    call jOut 'sql fmtFldSquashRW'
    call envBarBegin
    call jOut 'select name, class, dbName, tsName'
                                  /* ,alteredTS, obid, cardf'*/
    call jOut    'from sysibm.systables'
    call jOut    "where creator = 'SYSIBM' and name like 'SYSTA%'"
    call jOut    "fetch first 5 rows only"
    call envBar
    call sql 13
    call envBarLast
    call fmtFldSquashRW
    call envBarEnd
    call jOut 'sqlLn  sl='
    call envBarBegin
    call jOut 'select char(name, 13),  class, dbName, char(tsName, 8)'
                                  /* ,alteredTS, obid, cardf'*/
    call jOut    'from sysibm.systables'
    call jOut    "where creator = 'SYSIBM' and name like 'SYSTA%'"
    call jOut    "fetch first 5 rows only"
    call envBarLast
    call sqlLn 13, , ,'sl='
    call envBarEnd
    call jOut 'sqlLn  ---'
    call envBarBegin
    call jOut 'select name,  class, dbName, tsName'
                                  /* ,alteredTS, obid, cardf'*/
    call jOut    'from sysibm.systables'
    call jOut    "where creator = 'SYSIBM' and name like 'SYSTA%'"
    call jOut    "fetch first 5 rows only"
    call envBarLast
    call sqlLn 13
    call envBarEnd
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSqlEnv
/* copx tstSql end  ***************************************************/
/* copx tstComp begin **************************************************
    test the wsh compiler
***********************************************************************/
tstComp: procedure expose m.
    call compIni
    call tstCompDataConst
    call tstCompDataVars
    call tstCompShell
    call tstCompPrimary
    call tstCompStmt
    call tstCompDataIO
    call tstCompPipe
    call tstCompRedir
    call tstCompComp
    call tstTotal
    return
endProcedure tstComp

tstCompRun: procedure expose m.
parse arg class cnt
  src = jBuf()
  call jOpen src, m.j.cWri
  do sx=2 to arg()
      call jWrite src, arg(sx)
      end
  cmp = comp(jClose(src))
  call jOut 'compile' class',' (sx-2) 'lines:' arg(2)
  r = compile(cmp, class)
  say "compiled: >>>>" r "<<<<" m.r.code
  call jOut "run without input"
  call mCut 'T.IN', 0
  call oRun r
  if cnt == 3 then do
      call jOut "run with 3 inputs"
      call mAdd 'T.IN', "eins zwei drei", "zehn elf zwoelf?",
                                        , "zwanzig 21 22 23 24 ... 29|"
      m.t.inIx = 0
      call oRun r
      end
  return
endProcedure tstCompRun

tstCompDataConst: procedure expose m.
/*<<tstCompDataConst
    ### start tst tstCompDataConst ####################################
    compile d, 8 lines:       Lline one, $** asdf
    run without input
    .      Lline one, .
    line two..
    line threecontinued on 4
    line five  fortsetzung
    line six   fortsetzung
tstCompDataConst */
    call tst t, 'tstCompDataConst'
    call tstCompRun 'd' ,
        , '      Lline one, $** asdf',
        , 'line two.',
        , 'line three$*+ bla bla' ,
        , 'continued on 4',
        , 'line five $*( und so',
        , 'weiter $abc $? $''$*)'' $"$*)" und weiter $*) fortsetzung',
        , 'line six  $*( und $*( $** $*( so',
        , 'weiter $abc $? $*)'' $"$*)" und weiter $*) fortsetzung'
    call tstEnd t
    return
endProcedure tstCompDataComm

tstCompDataVars: procedure expose m.
/*<<tstCompDataVars
    ### start tst tstCompDataVars #####################################
    compile d, 4 lines:       Lline one, $** asdf
    run without input
    .      Lline one, .
    lline zwei output
    lline 3 .
    variable v1 = valueV1 ${v1}= valueV1| .
tstCompDataVars */
    call tst t, 'tstCompDataVars'
    call tstCompRun 'd' ,
        , '      Lline one, $** asdf',
        , '   $$ lline zwei output',
        , 'lline 3 $=v1= valueV1 ' ,
        , 'variable v1 = $v1 $"${v1}=" ${  v1  }| '
    call tstEnd t
    return
endProcedure tstCompDataVars

tstCompShell: procedure expose m.
/*<<tstCompShell
    ### start tst tstCompShell ########################################
    compile s, 9 lines:   $$  Lline one, $** asdf
    run without input
    Lline one,
    lline zwei output
    v1 = valueV1 ${v1}= valueV1|
    REXX JOUT L5 CONTINUED L6 CONTINUED L7
    L8 ONE
    L9 END
tstCompShell */
    call tst t, 'tstCompShell'
    call tstCompRun 's' ,
        , '  $$  Lline one, $** asdf',
        , '   $$ lline zwei output',
        , '        $=v1= valueV1 ' ,
        , '$$       v1 = $v1 $"${v1}=" ${  v1  }| ' ,
        , 'call jOut rexx jout l5, ' ,
        , '     continued l6 , ' ,
        , '     continued l7   ' ,
        , 'call jOut l8 one    ' ,
        , 'call jOut l9 end'
    call tstEnd t
    return
endProcedure tstCompDataVars

tstCompPrimary: procedure expose m.
/*<<tstCompPrimary
    ### start tst tstCompPrimary ######################################
    compile d, 11 lines: Strings $"$""$""""$""" $'$''$''''$'''
    run without input
    Strings $"$""$" $'$''$'
    rexx 3*5 = 15
    data  line three line four  bis hier
    shell line five line six bis hier
    var get   v1 value Eins, v1 value Eins .
    var isDef v1 1, v2 0 .
    #jIn eof 1#
    var read  >1 0 rr undefined
    #jIn eof 2#
    var read  >2 0 rr undefined
    run with 3 inputs
    Strings $"$""$" $'$''$'
    rexx 3*5 = 15
    data  line three line four  bis hier
    shell line five line six bis hier
    var get   v1 value Eins, v1 value Eins .
    var isDef v1 1, v2 0 .
    #jIn 1# eins zwei drei
    var read  >1 1 rr eins zwei drei
    #jIn 2# zehn elf zwoelf?
    var read  >2 1 rr zehn elf zwoelf?
tstCompPrimary */

    call tst t, 'tstCompPrimary'
    call envRemove 'v2'
    call tstCompRun 'd' 3 ,
        , 'Strings $"$""$""""$"""' "$'$''$''''$'''",
        , 'rexx 3*5 = $( 3 * 5 $)',
        , 'data $-¢ line three',
        , 'line four $! bis hier',
        , 'shell $-{ $$ line five',
        , '$$ line six $} bis hier',
        , '$= v1  =   value Eins  $=rr=undefined',
        , 'var get   v1 $v1, v1 ${  v1  } ',
        , 'var isDef v1 ${? v$(  1  * 1  + 0  $) }, v2 ${?v2 } ',
        , 'var read  >1 ${> rr} rr $rr' ,
        , 'var read  >2 ${> rr} rr $rr'
    call tstEnd t
    return
endProcedure tstCompPrimary

tstCompStmt: procedure expose m.
/*<<tstCompStmt1
    ### start tst tstCompStmt1 ########################################
    compile s, 8 lines: $= v1 = value eins  $= v2  % 3*5*7 .
    run without input
    data v1 value eins v2 105
    eins
    zwei
    drei
    vier
    fuenf
    elf
    zwoelf  dreiZ  .
    vierZ .
    fuenfZ
    lang v1 value eins v2 945
    oRun ouput 1
tstCompStmt1 */
    call tst t, 'tstCompStmt1'
    call envPut 'oRun', oRunner('call jOut "oRun ouput" (1*1)')
    call envRemove 'v2'
    call tstCompRun 's' ,
        , '$= v1 = value eins  $= v2  % 3*5*7 ',
        , '$$ data v1 $v1 v2 ${   v2  }',
        , '$$eins $@{$$ zwei $$ drei  ',
        , '   $@{   $} $@{ $@{ $$vier $} $} $} $$fuenf',
        , '$$elf $@¢ zwoelf  dreiZ  ',
        , '   $@¢   $! $@¢ $@¢ vierZ $! $! $! $$fuenfZ',
        , '$% "lang v1" $v1 "v2" ${v2}*9',
        , '$@run $oRun'
    call tstEnd t
/*<<tstCompStmt2
    ### start tst tstCompStmt2 ########################################
    compile s, 1 lines: $@for qq $$ loop qq $qq
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    loop qq eins zwei drei
    #jIn 2# zehn elf zwoelf?
    loop qq zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    loop qq zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
tstCompStmt2 */
    call tst t, 'tstCompStmt2'
    call tstCompRun 's' 3 ,
        , '$@for qq $$ loop qq $qq'
    call tstEnd t
    return
endProcedure tstCompStmt

tstCompDataIO: procedure expose m.
/*<<tstCompDataHereData
    ### start tst tstCompDataHereData #################################
    compile d, 13 lines:  herdata $<<stop    .
    run without input
    . herdata .
    heredata 1 $x
    heredata 2 $y
    nach heredata
    . herdata ¢ .
    heredata 1 xValue
    heredata 2 yValueY
    nach heredata ¢
    . herdata { .
    HEREDATA 1 xValue
    heredata 2 yValueY
    nach heredata {
tstCompDataHereData */
    call tst t, 'tstCompDataHereData'
    call tstCompRun 'd' ,
        , ' herdata $<<stop    ',
        , 'heredata 1 $x',
        , 'heredata 2 $y',
        , 'stop $$ nach heredata',
        , ' herdata ¢ $<<¢stop    ',
        , '$=x=xValue$=y=yValueY',
        , 'heredata 1 $x',
        , 'heredata 2 $y',
        , 'stop $$ nach heredata ¢',
        , ' herdata { $<<{st',
        , 'call jOut heredata 1 $x',
        , '$$heredata 2 $y',
        , 'st $$ nach heredata {'
    call tstEnd t
/*<<tstCompDataIO
    ### start tst tstCompDataIO #######################################
    compile d, 5 lines:  input 1 $<$dsn $*+.
    run without input
    . input 1 .
    readInp line 1                       .
    readInp line 2                       .
    . nach dsn input und nochmals mit & .
    readInp line 1                       .
    readInp line 2                       .
    . und schluiss..
tstCompDataIO */
    dsn = word(tstPdsMbr(tstFilename('lib37', 'r'), 'readInp'), 1)
    dsnFB = dsn tstFB('::F37', 0)
    b = jBuf(tstFB('readInp line 1', 37),
            ,tstFB('readInp line 2', 37))
    extFd = tstFB('&dsn('dsn') dd(xyz)', 0)
    extFd = tstFB(dsn 'dd(xyz) ::f', 0)
    if extFd = '' then
        extFd = dsn
    call jCat file(dsnFB), b
    call envPut 'dsn', dsn
    call tst t, 'tstCompDataIO'
    call tstCompRun 'd' ,
        , ' input 1 $<$dsn $*+',
        , tstFB('::f', 0),
        , ' nach dsn input und nochmals mit & ' ,
        , '         $<'extFD,
        , ' und schluiss.'
    call tstEnd t
    return
endProcedure tstCompDataIO

tstCompPipe: procedure expose m.
/*<<tstCompPipe1
    ### start tst tstCompPipe1 ########################################
    compile s, 1 lines:  call envPreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    (1 eins zwei drei 1)
    #jIn 2# zehn elf zwoelf?
    (1 zehn elf zwoelf? 1)
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    (1 zwanzig 21 22 23 24 ... 29| 1)
    #jIn eof 4#
tstCompPipe1 */
    call tst t, 'tstCompPipe1'
    call tstCompRun 's' 3 ,
        , ' call envPreSuf "(1 ", " 1)"'
    call tstEnd t
/*<<tstCompPipe2
    ### start tst tstCompPipe2 ########################################
    compile s, 2 lines:  call envPreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    ¢2 (1 eins zwei drei 1) 2!
    ¢2 (1 zehn elf zwoelf? 1) 2!
    ¢2 (1 zwanzig 21 22 23 24 ... 29| 1) 2!
tstCompPipe2 */
    call tst t, 'tstCompPipe2'
    call tstCompRun 's' 3 ,
        , ' call envPreSuf "(1 ", " 1)"' ,
        , ' $| call envPreSuf "¢2 ", " 2!"'
    call tstEnd t
/*<<tstCompPipe3
    ### start tst tstCompPipe3 ########################################
    compile s, 3 lines:  call envPreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <3 ¢2 (1 eins zwei drei 1) 2! 3>
    <3 ¢2 (1 zehn elf zwoelf? 1) 2! 3>
    <3 ¢2 (1 zwanzig 21 22 23 24 ... 29| 1) 2! 3>
tstCompPipe3 */
    call tst t, 'tstCompPipe3'
    call tstCompRun 's' 3 ,
        , ' call envPreSuf "(1 ", " 1)"' ,
        , ' $| call envPreSuf "¢2 ", " 2!"',
        , ' $| call envPreSuf "<3 ", " 3>"'
    call tstEnd t
/*<<tstCompPipe4
    ### start tst tstCompPipe4 ########################################
    compile s, 7 lines:  call envPreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <3 ¢222 ¢221 ¢21 ¢20 (1 eins zwei drei 1) 20! 21! 221! 222! 3>
    <3 ¢222 ¢221 ¢21 ¢20 (1 zehn elf zwoelf? 1) 20! 21! 221! 222! 3>
    <3 ¢222 ¢221 ¢21 ¢20 (1 zwanzig 21 22 23 24 ... 29| 1) 20! 21! 221!+
    . 222! 3>
tstCompPipe4 */
    call tst t, 'tstCompPipe4'
    call tstCompRun 's' 3 ,
        , ' call envPreSuf "(1 ", " 1)"' ,
        , ' $| $@{    call envPreSuf "¢20 ", " 20!"',
        ,        ' $| call envPreSuf "¢21 ", " 21!"',
        ,        ' $| $@{      call envPreSuf "¢221 ", " 221!"',
        ,                 ' $| call envPreSuf "¢222 ", " 222!"',
        ,     '$}     $} ',
        , ' $| call envPreSuf "<3 ", " 3>"'
    call tstEnd t
    return
endProcedure tstCompPipe

tstCompRedir: procedure expose m.
/*<<tstCompRedir
    ### start tst tstCompRedir ########################################
    compile s, 6 lines:  $>#eins $@for vv $$<$vv> $; .
    run without input
    #jIn eof 1#
    output eins .
    output piped zwei .
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    output eins <eins zwei drei> <zehn elf zwoelf?> <zwanzig 21 22 23 2+
    4 ... 29|>
    output piped zwei ab<eins zwei drei>yz ab<zehn elf zwoelf?>yz ab<zw+
    anzig 21 22 23 24 ... 29|>yz
tstCompRedir */
    call tst t, 'tstCompRedir'
    dsn = word(tstPdsMbr(tstFilename('libvb', 'r'), 'redir1'), 1)
    call envPut 'dsn', dsn
    call tstCompRun 's' 3 ,
        , ' $>#eins $@for vv $$<$vv> $; ',
        , ' $$ output eins $-{$<#eins$}$; ',
        , ' $@for ww $$b${ww}y ',
        , '     $> $dsn 'tstFB('::v', 0),
        ,         '$| call envPreSuf "a", "z" $<# eins',
        , '$;$$ output piped zwei $-{$<$dsn$} '
    call tstEnd t
    return
endProcedure tstCompRedir

tstCompComp: procedure expose m.
/*<<tstCompCompShell
    ### start tst tstCompCompShell ####################################
    compile s, 5 lines: $$compiling shell $; $= rrr = $-cmpShell $<<aaa
    run without input
    compiling shell
    running einmal
    RUN 1 COMPILED einmal
    #jIn eof 1#
    running zweimal
    RUN 1 COMPILED zweimal
    #jIn eof 2#
    run with 3 inputs
    compiling shell
    running einmal
    RUN 1 COMPILED einmal
    #jIn 1# eins zwei drei
    compRun eins zwei dreieinmal
    #jIn 2# zehn elf zwoelf?
    compRun zehn elf zwoelf?einmal
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    compRun zwanzig 21 22 23 24 ... 29|einmal
    #jIn eof 4#
    running zweimal
    RUN 1 COMPILED zweimal
    #jIn eof 5#
tstCompCompShell */
    call tst t, 'tstCompCompShell'
    call tstCompRun 's' 3 ,
        ,  "$$compiling shell $; $= rrr = $-cmpShell $<<aaa",
        ,  "call jOut run 1*1*1 compiled $cc; $@for v $$ compRun $v$cc",
        ,  "aaa $;",
        ,  "$=cc=einmal $$ running $cc $@run $rrr",
        ,  "$=cc=zweimal $$ running $cc $@run $rrr"
    call tstEnd t
/*<<tstCompCompData
    ### start tst tstCompCompData #####################################
    compile s, 5 lines: $$compiling data $; $= rrr = $-cmpData  $<<aaa
    run without input
    compiling data
    running einmal
    call jOut run 1*1*1 compiled einmal
    running zweimal
    call jOut run 1*1*1 compiled zweimal
    run with 3 inputs
    compiling data
    running einmal
    call jOut run 1*1*1 compiled einmal
    running zweimal
    call jOut run 1*1*1 compiled zweimal
tstCompCompData */
    call tst t, 'tstCompCompData'
    call tstCompRun 's' 3 ,
        ,  "$$compiling data $; $= rrr = $-cmpData  $<<aaa",
        ,  "call jOut run 1*1*1 compiled $cc",
        ,  "aaa $;",
        ,  "$=cc=einmal $$ running $cc $@run $rrr",
        ,  "$=cc=zweimal $$ running $cc $@run $rrr"
    call tstEnd t
    return
endProcedure tstCompComp
/* copx tstComp end   *************************************************/
/* copx tstBase begin **************************************************
     test the basic classes
***********************************************************************/
tstBase: procedure expose m.
    call tstTstSay
    call oIni
    call tstM
    call tstMap
    call tstMapVia
    call tstClass
    call tstClass2
    call tstO
    call jIni
    call tstJSay
    call tstJ
    call tstJ2
    call catIni
    call tstCat
       call envIni
    CALL TstEnv
    CALL TstEnvCat
    call tstEnvBar
    call tstEnvVars
    call tstTotal
    call tstEnvLazy
    call tstEnvClass
    call tstFile /* reimplent zOs ||| */
    call tstFileList
    call tstFmt
    call tstTotal
    call scanIni
    call tstScan
    call ScanReadIni
    call tstScanRead
    call tstScanWin
    call tstScanSQL
    call tstTotal
    return
endProcedure tstBase

/*--- test the tst Module, check the output visually  ----------------*/
tstTstSay: procedure expose m.
    call tstIni
    oldErr = m.tst.err
    oldNames = m.tst.errNames
    say '+++ tstTstSay start with' oldErr 'totErrs and',
            m.tst.tests 'tests'
/*<<tstTstSayEins
    ### start tst tstTstSayEins #######################################
    test eins einzige testZeile
tstTstSayEins */

    call tst x, 'tstTstSayEins'
    call tstOut x, "test eins einzige testZeile"
    call tstEnd x
    if m.x.err <> 0 then
        call err '+++ tstTstSay errs' m.x.err 'expected' 0

/*<<tstTstSayZwei
    ### start tst tstTstSayZwei #######################################
    zwei 1. testZeile
    zwei 2. und letzte testZeile
tstTstSayZwei */

    call tst x, 'tstTstSayZwei'
    call tstOut x, "zwei 1. testZeile"
    call tstOut x, "zwei 2. und letzte testZeile"
    call tstEnd x
    if m.x.err <> 0 then
        call err '+++ tstTstSay errs' m.x.err 'expected' 0

    call tst x, 'tstTstSayZwei'
    call tstOut x, "zwei 1. testZeile "    /* ein space zuviel */
    call tstOut x, "zwei 2. und letzte testZeile"
    call tstOut x, "zwei 3. zuviel"
    call tstEnd x
    if m.x.err <> 3 then
        call err '+++ tstTstSay errs' m.x.err 'expected' 3

/*<<tstTstSayDrei
    ### start tst tstTstSayDrei #######################################
    drei 1. testZeile vor empty Zeile
    ..
    drei 3. testZeile vor 10 space
    .          .
    drei 5. testZeile ziemlich lang 66                                 +
    .                                77  5   10   15++++++++++++++++++++
    .+++++++++++++++++++++++++++++++++++++++++++.
tstTstSayDrei */

    call tst y, 'tstTstSayDrei'
    call tstOut y, 'drei 1. testZeile vor empty Zeile'
    call tstOut y, ''
    call tstOut y, 'drei 3. testZeile vor 10 space'
    call tstOut y, left('', 10)
    call tstOut y, 'drei 5. testZeile ziemlich lang',
                left(66, 66) left('77  5   10   15', 77, '+')
    call tstEnd y
    if m.y.err <> 0 then
        call err '+++ tstTstSay errs' m.x.err 'expected' 0
    if m.tst.err <> oldErr + 3 then
        call err '+++ tstTstSay totErrs' m.tst.err 'expected' 3
    say '+++ tstTstSay end Ok with' m.tst.err 'totErrs and' ,
        m.tst.tests 'tests'
    m.tst.err = oldErr
    m.tst.errNames = oldNames
    return
endProcedure tstTstSay

tstM: procedure expose m.
/*<<tstM
    ### start tst tstM ################################################
    symbol m.b LIT
    mInc b 2 m.b 2
    symbol m.a LIT
    mAdd a A.2
    mAdd a A.3
    m.a: 3: 1=eins 2=zwei 3=drei 4=M.A.4
    m.c: 5: 1=c vorAddSt a 2=eins 3=zwei 4=drei 5=c nacAddSt a 6=M.C.6
    tstMSubj1 tstMSubj1 added listener 1
    tstMSubj1 notified list1 1 arg tstMSubj1 registered list
    tstMSubj1 notified list1 1 arg tstMSubj1 notify 11
    tstMSubj1 tstMSubj1 added listener 2
    tstMSubj1 notified list2 2 arg tstMSubj1 registered list
    tstMSubj1 notified list1 1 arg tstMSubj1 notify 12
    tstMSubj1 notified list2 2 arg tstMSubj1 notify 12
    tstMSubj2 tstMSubj2 added listener 1
    tstMSubj2 notified list1 1 arg tstMSubj2 registered list
    tstMSubj2 tstMSubj2 added listener 2
    tstMSubj2 notified list2 2 arg tstMSubj2 registered list
    tstMSubj1 notified list1 1 arg tstMSubj1 notify 13
    tstMSubj1 notified list2 2 arg tstMSubj1 notify 13
    tstMSubj2 notified list1 1 arg tstMSubj2 notify 24
    tstMSubj2 notified list2 2 arg tstMSubj2 notify 24
tstM */


    drop m.b m.a m.a.0 m.a.1 m.a.2
    call tst t, 'tstM'
    call tstOut t, 'symbol m.b' symbol('m.b')
    m.b = 1
    call tstOut t, 'mInc b' mInc(b) 'm.b' m.b
    call tstOut t, 'symbol m.a' symbol('m.a')
    call tstOut t, 'mAdd a' mAdd(mCut(a, 0), 'eins', 'zwei')
    call tstOut t, 'mAdd a' mAdd(a, 'drei')
    call tstOut t, 'm.a:' m.a.0': 1='m.a.1 '2='m.a.2 '3='m.a.3 '4='m.a.4
    call mAdd mCut(c, 0), 'c vorAddSt a'
    call mAddSt c, a
    call mAdd c, 'c nacAddSt a'
    call tstOut t, 'm.c:' m.c.0': 1='m.c.1 '2='m.c.2 '3='m.c.3,
                    '4='m.c.4 '5='m.c.5 '6='m.c.6
    s1 = 'tstMSubj1'
    s2 = 'tstMSubj2'
    /* we must unregister for the second test */
    drop m.m.subLis.s1 m.m.subLis.s1.0 m.m.subLis.s2 m.m.subLis.s2.0
    call mRegisterSubject s1,
        , 'call tstOut t, "'s1'" subject "added listener" listener;',
            'call mNotify1 "'s1'", listener, "'s1' registered list"'
    call mRegister s1,
        , 'call tstOut t, subject "notified list1" listener "arg" arg'
    call mNotify s1, s1 'notify 11'
    call mRegister s1,
        , 'call tstOut t, subject "notified list2" listener "arg" arg'
    call mRegister s2,
        , 'call tstOut t, subject "notified list1" listener "arg" arg'
    call mRegister s2,
        , 'call tstOut t, subject "notified list2" listener "arg" arg'
    call mNotify s1, s1 'notify 12'
    call mRegisterSubject s2,
        , 'call tstOut t, "'s2'" subject "added listener" listener;',
            'call mNotify1 "'s2'", listener, "'s2' registered list"'
    call mNotify s1, s1 'notify 13'
    call mNotify s2, s2 'notify 24'

    call tstEnd t
    return
endProcedure tstM

tstMap: procedure expose m.
/*<<tstMap
    ### start tst tstMap ##############################################
    mapNew m keys m-keys 0
    map m zwei --> 2
    map m Zwei is not defined
    map stem m-keys 4
    map m eins --> 1
    map m zwei --> 2
    map m drei --> 3
    map m vier --> 4
    *** err: duplicate key eins in map m
    map m zwei is not defined
    q 2 zw dr
    map stem Q 2
    map Q zw --> 2Q
    map Q dr --> 3Q
    map stem m 3
    map m eins --> 1
    map m zwei --> 2PUT
    map m vier --> 4PUT
    *** err: duplicate key zwei in map m
    tstMapLong eins keys 3
    tstMapLong zweiMal keys 48
    tstMapLong dreiMal keys 93
    tstMapLong vier    keys 138
    tstMapLong <fuenf> keys 188
    tstMap clear keys 0
    inline1 3
    inline1 1 ==    inline1 eins==
    inline1 2 ====
    inline1 3 ==    inline1 drei==
    inline2 1 1 ==    inline2 eins==
    inline3 nicht gefunden
tstMap */
/*<<tstMapInline1
    inline1 eins

    inline1 drei
tstMapInline1 */
/*<<tstMapInline2
    inline2 eins
tstMapInline2 */

    call tst t, 'tstMap'
    m = mapNew('K')
    ky = mapKeys(m)
    call mAdd t'.TRANS', m 'm', ky 'm-keys'
    call tstOut t, 'mapNew' m 'keys' ky m.ky.0
    call mapAdd m, 'eins', 1
    call mapAdd m, 'zwei', 2
    call mapAdd m, 'drei', 3
    call mapAdd m, 'vier', 4
    call tstMapShow m, 'zwei'
    call tstMapShow m, 'Zwei'
    call tstMapShowSt m, mapKeys(m)
    call mapAdd m, 'eins', 1
    call mapReset m, '='
    call tstMapShow m, 'zwei'
    call mapAdd m, 'eins', 1
    call mapAdd m, 'zwei', 2
    call mapPut m, 'zwei', 2Put
    call mapPut m, 'vier', 4Put
    call mapReset q, '='
    call mapAdd q, 'zw', 2q
    call mapAdd q, 'dr', 3q
    call tstOut t, 'q' m.q.0 m.q.1 m.q.2
    call tstMapShowSt q, mapKeys(q)
    call tstMapShowSt m, mapKeys(m)
    call mapAdd m, 'zwei', 2addDup
    call tstMapLong m, 'eins'      ,201, 2000, -2, 2
    call tstMapLong m, 'zweiMal'   ,201, 2000, -2, 2
    call tstMapLong m, 'dreiMal'   ,201, 2000,  2,-2
    call tstMapLong m, 'vier   '   ,2010, 201, -2, 2
    call tstMapLong m, '<fuenf>'   ,2010, 201,  2,-2
    call mapClear m
    keys = mapKeys(m)
    call tstOut t, 'tstMap clear keys' m.keys.0
    i = mapInline('tstMapInline1')
    call tstOut t, 'inline1' m.i.0
    do x=1 to m.i.0
        call tstOut t, 'inline1' x '=='m.i.x'=='
        end
    i = mapInline('tstMapInline2')
    call tstOut t, 'inline2' m.i.0 '1 =='m.i.1'=='
    call tstOut t, 'inline3' mapInline('tstMapInline3', 'nicht gefunden')
    call tstEnd t
    return
endProcedure tstMap

tstMapLong: procedure expose m.
parse arg m, w, f1, t1, f2, t2
    if f1 < t1 then
        b1 = 201
    else
        b1 = -201
    if f2 < t2 then
        b2 = 1
    else
        b2 = -1
    lo = copies(w, 2100 % length(w))
    keys = mapKeys(m)
    keyCn = m.keys.0
    call tstOut t, 'tstMapLong' w 'keys' keyCn
    do x = f1 by b1 to t1
        do y = x+f2 by b2 to x+t2
            k = left(lo, y)
            if mapHasKey(m, k) then
                call err 'mapLong hasKey before' w y
            call mapAdd m, k, w y
            if \ mapHasKey(m, k) then
                call err 'mapLong \ hasKey after' w y
            if mapGet(m, k) \== w y then
                call err 'mapLong \ get <> ' w y
            keys = mapKeys(m)
            if keyCn + 1 \= m.keys.0 then
                call err 'mapLong keys .0 <> ' w y
            keyCn = m.keys.0
            if k \== m.keys.keyCn then
                call err 'mapLong keys . ' keyCn '<>' w y
            end
        end
    return
endProcedure tstMapLong

tstMapVia: procedure expose m.
/*<<tstMapVia
    ### start tst tstMapVia ###########################################
    map M K --> A
    mapVia(m, K)      A
    *** err: missing m.A at 3 in mapVia(M, K*)
    mapVia(m, K*)     M.A
    mapVia(m, K*)     valAt m.a
    mapVia(m, K*)     valAt m.a
    *** err: missing m.A.aB at 5 in mapVia(M, K*aB)
    mapVia(m, K*aB)   M.A.aB
    mapVia(m, K*aB)   valAt m.A.aB
    *** err: missing m.valAt m.a at 4 in mapVia(M, K**)
    mapVia(m, K**)    M.valAt m.a
    mapVia(m, K**)    valAt m.valAt m.a
    mapVia(m, K**F)   valAt m.valAt m.a.F
tstMapVia */
    call tst t, 'tstMapVia'
    u = 'A.aB'
    v = 'valAt m.a'
    drop m.a m.u m.v m.v.f
    call mapReset m, 'K'
    call mapAdd m, k, a
    call tstMapShow m, k
    call tstOut t, 'mapVia(m, K)     ' mapVia(m, 'K')
    call tstOut t, 'mapVia(m, K*)    ' mapVia(m, 'K*')
    m.a = v
    call tstOut t, 'mapVia(m, K*)    ' mapVia(m, 'K*')
    call tstOut t, 'mapVia(m, K*)    ' mapVia(m, 'K*')
    call tstOut t, 'mapVia(m, K*aB)  ' mapVia(m, 'K*aB')
    m.u = 'valAt m.'u
    call tstOut t, 'mapVia(m, K*aB)  ' mapVia(m, 'K*aB')
    call tstOut t, 'mapVia(m, K**)   ' mapVia(m, 'K**')
    v = m.a
    m.v = 'valAt m.'v
    m.v.f = 'valAt m.'v'.F'
    call tstOut t, 'mapVia(m, K**)   ' mapVia(m, 'K**')
    call tstOut t, 'mapVia(m, K**F)  ' mapVia(m, 'K**F')
    call tstEnd t
    return
endProcedure tstMapVia

tstMapShow: procedure expose m.
parse arg a, key
    if mapHasKey(a, key) then
        call tstOut t, 'map' a key '-->' mapGet(a, key)
    else
        call tstOut t, 'map' a key 'is not defined'
    return
endProcedure tstMapShow

tstMapShowSt: procedure expose m.
parse arg a, st
    call tstOut t, 'map stem' st m.st.0
    do wx=1 to m.st.0
        call tstMapShow a, m.st.wx
        end
    return
endProcedure tstMapShow

tstClass2: procedure expose m.
/*<<tstClass2
    ### start tst tstClass2 ###########################################
    @CLASS.4 isA :class union
    . choice n union
    .  .NAME = class
    .  .CLASS refTo @CLASS.3 :class union
    .   choice u stem 8
    .    .1 refTo @CLASS.11 :class union
    .     choice c union
    .      .NAME = v
    .      .CLASS refTo @CLASS.1 :class union
    .       choice v = v
    .    .2 refTo @CLASS.12 :class union
    .     choice c union
    .      .NAME = r
    .      .CLASS refTo @CLASS.7 :class union
    .       choice f union
    .        .NAME = CLASS
    .        .CLASS refTo @CLASS.6 :class union
    .         choice r .CLASS refTo @CLASS.4 done :class @CLASS.4
    .    .3 refTo @CLASS.13 :class union
    .     choice c union
    .      .NAME = s
    .      .CLASS refTo @CLASS.7 done :class @CLASS.7
    .    .4 refTo @CLASS.15 :class union
    .     choice c union
    .      .NAME = u
    .      .CLASS refTo @CLASS.14 :class union
    .       choice s .CLASS refTo @CLASS.6 done :class @CLASS.6
    .    .5 refTo @CLASS.16 :class union
    .     choice c union
    .      .NAME = f
    .      .CLASS refTo @CLASS.8 :class union
    .       choice u stem 2
    .        .1 refTo @CLASS.5 :class union
    .         choice f union
    .          .NAME = NAME
    .          .CLASS refTo @CLASS.1 done :class @CLASS.1
    .        .2 refTo @CLASS.7 done :class @CLASS.7
    .    .6 refTo @CLASS.17 :class union
    .     choice c union
    .      .NAME = n
    .      .CLASS refTo @CLASS.8 done :class @CLASS.8
    .    .7 refTo @CLASS.18 :class union
    .     choice c union
    .      .NAME = c
    .      .CLASS refTo @CLASS.8 done :class @CLASS.8
    .    .8 refTo @CLASS.19 :class union
    .     choice c union
    .      .NAME = m
    .      .CLASS refTo @CLASS.10 :class union
    .       choice u stem 2
    .        .1 refTo @CLASS.5 done :class @CLASS.5
    .        .2 refTo @CLASS.9 :class union
    .         choice f union
    .          .NAME = MET
    .          .CLASS refTo @CLASS.1 done :class @CLASS.1
tstClass2 */
    call tst t, 'tstClass2'
    call classOut , m.class.class
    call tstEnd t
/*  call out 'nach pop'   *** ???wktest */
    return
endProcedure tstClass2

tstClass: procedure expose m.
/*<<tstClass
    ### start tst tstClass ############################################
    Q n =className= tstClassTf12
    Q.eins.zwei v ==> M.Q.eins.zwei
    *** err: basicClass v end of Exp expected: v tstClassTf12 .
    R n =className= uststClassTf12
    R n =className= uststClassTf12in
    R n =className= tstClassTf12
    R.eins.zwei v ==> M.R.eins.zwei
    R s =stem.0= 2
    R.1 r ==> M.R.1
    R.1 n =className= tstClassTf12
    R.1.eins.zwei v ==> M.R.1.eins.zwei
    R.2 r ==> M.R.2
    R.2 n =className= tstClassTf12
    R.2.eins.zwei v ==> M.R.2.eins.zwei
    S s =stem.0= 2
    S.1.eins v ==> M.S.1.eins
    S.1 m =met-metA--> say "metA"
    S.1 m =met-metB--> say "metB"
    S.2.zwei v ==> M.S.2.zwei
    S.2 m =met-metA--> say "metA"
    S.2 m =met-metB--> say "metB"
    class of mutate qq tstClassTf12
tstClass */

    f = 'eins.zwei'
    e = 'eins'
    z = 'zwei'
    drop m.q.f m.r.f m.r.0 m.r.1 m.r.1.f m.r.2 m.r.2.f
    drop m.s.0 m.s.1 m.s.1.e m.s.2 m.s.2.z
    call classIni
    call tst t, 'tstClass'
    t1  =  classNew('n tstClassTf12 f eins f zwei v')
    call tstClassOut t, t1, q
    z = m.class.0
    errDef = 'n tstClassB n tstClassC u tstClassTf12, s u v tstClassTf12'
    if class4name(errDef, ' ') == ' ' then
        t2 = classNew(errDef)
    else    /* the second time we do not get the error anymore,
                because the err did not abend | */
        call tstOut t,
            ,'*** err: basicClass v end of Exp expected: v tstClassTf12 '
    t2 = classNew('n uststClassTf12 n uststClassTf12in u tstClassTf12',
        , classNew('s u r, tstClassTf12'))
    m.r.0 = 2
    call tstClassOut t, t2, r
    t3 = classNew('s u c 1 f eins v, c 2 f zwei v',
        ,'m', 'metA say "metA"', 'metB say "metB"')
    m.s.0 = 2
    m.s.1 = 1
    m.s.2 = 2
    call tstClassOut t, t3, s
    call oMutate qq, 'tstClassTf12'
    tt = objClass(qq)
    call tstOut t, 'class of mutate qq' m.tt.name
    call tstEnd t
    return
endProcedure tstClass

tstClassOut: procedure expose m.
parse arg o, t, a
    if pos(m.t, 'vr') > 0 then
        return tstOut(o, a m.t '==>' m.a)
    if m.t == 'n' then do
        call tstOut o, a m.t '=className=' m.t.name
        return tstClassOut(o, m.t.class, a)
        end
    if m.t == 'f' then
        return tstClassOut(o, m.t.class, a'.'m.t.name)
    if m.t = 'u' then do
        do ux=1 to m.t.0
            call tstClassOut o, m.t.ux, a
            end
        return 0
        end
    if m.t = 's' then do
        call tstOut o, a m.t '=stem.0=' m.a.0
        do ux=1 to m.a.0
            call tstClassOut o, m.t.class, a'.'ux
            end
        return 0
        end
    if m.t = 'c' then do
        if m.t.name = m.a then
            call tstClassOut o, m.t.class, a
         return 0
        end
    if m.t = 'm' then
        return tstOut(o, a m.t '=met-'m.t.name'-->' m.t.met)
    call err 'bad class type' m.t 1/0
endProcedure tstClassOut

tstO: procedure expose m.
/*<<tstO
    ### start tst tstO ################################################
    class method calls of TstOEins
    .  met Eins.eins M
     FLDS of <obj e of TstOEins> .FEINS, .FZWEI
     methodcalls of object e of TstOEins
    .  met Eins.eins <obj e of TstOEins>
    .  met Eins.zwei <obj e2 of TstOEins>
    *** err: no method nein in class TstOEins of object <obj e+
    . of TstOEins>
    *** err: no class found for object noObj
    class method calls of TstOEins
    .  met Elf.zwei M
    FLDS of <obj f of TstOElf> .FEINS, .FZWEI, .FELF
    methodcalls of object f of TstOElf
    .  met Eins.eins <obj f of TstOElf>
    .  met Elf.zwei <obj f of TstOElf>
    .  met Elf.drei <obj f of TstOElf>
    methodcalls of object f cast To TstOEins
    .  met Eins.eins <obj f of TstOElf>
    .  met Eins.zwei <obj f of TstOElf>
    FLDS of <cast(f, TstOEins)> .FEINS, .FZWEI
    oCopy c1 of class TstOEins, c2
    C1 n =className= TstOEins
    C1.FEINS v ==> M.C1.FEINS
    C1.FZWEI v ==> M.C1.FZWEI
    C1 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C1 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    C2 n =className= TstOEins
    C2.FEINS v ==> M.C1.FEINS
    C2.FZWEI v ==> M.C1.FZWEI
    C2 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C2 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    oCopy c3 of class TstOElf, c4
    C4 n =className= TstOElf
    C4 n =className= TstOEins
    C4.FEINS v ==> M.C3.FEINS
    C4.FZWEI v ==> M.C3.FZWEI
    C4 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C4 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    C4.FELF r ==> M.C3.FELF
    C4 m =met-zwei--> call tstOut t, "  met Elf.zwei" m
    C4 m =met-drei--> call tstOut t, "  met Elf.drei" m
    tEinsDop <class TstOEins>
    oRun 7*3 21
    oRun 12*12 144
tstO */

    call tst t, 'tstO'
    tEins = classNew('n TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
    call mAdd t.trans, tEins '<class TstOEins>'
    call tstOut t, 'class method calls of TstOEins'
    interpret classMet('TstOEins', 'eins')
    e = oNew('TstOEins')
    e2 = oNew('TstOEins')
    call mAdd t.trans, e '<obj e of TstOEins>'
    call mAdd t.trans, e2 '<obj e2 of TstOEins>'
    tEins = classNew('n TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
    call tstOut t, 'FLDS of' e mCat(oFlds(e), ', ')
    call tstOut t, 'methodcalls of object e of TstOEins'
    call tstOmet e, 'eins'
    call tstOmet e2, 'zwei'
    call tstOmet e, 'nein'
    call tstOmet 'noObj', 'nein'
    tElf = classNew('n TstOElf u TstOEins, f FELF r', 'm',
        , 'zwei call tstOut t, "  met Elf.zwei" m',
        , 'drei call tstOut t, "  met Elf.drei" m')
    call tstOut t, 'class method calls of TstOEins'
    interpret classMet('TstOElf', 'zwei')
    f = oNew('TstOElf')
    call mAdd t.trans, f '<obj f of TstOElf>'
    call tstOut t, 'FLDS of' f mCat(oFlds(f), ', ')
    call tstOut t, 'methodcalls of object f of TstOElf'
    call tstOmet f, 'eins'
    call tstOmet f, 'zwei'
    call tstOmet f, 'drei'
    call tstOut t, 'methodcalls of object f cast To TstOEins'
    call tstOmet oCast(f, 'TstOEins'), 'eins'
    call tstOmet oCast(f, 'TstOEins'), 'zwei'
    call tstOut t, 'FLDS of <cast(f, TstOEins)>',
        mCat(oFlds(oCast(f, 'TstOEins')), ', ')

    call oMutate c1, 'TstOEins'
    call tstOut t, 'oCopy c1 of class TstOEins, c2'
    call tstClassOut t, tEins, c1
    call oCopy c1, c2
    call tstClassOut t, tEins, c2
    call tstOut t, 'oCopy c3 of class TstOElf, c4'
    call oMutate c3, 'TstOElf'
    call oCopy c3, c4
    call tstClassOut t, tElf, c4

    tEinsDop = classNew('n TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
    call tstOut t, 'tEinsDop' tEinsDop
    e3 = oNew('TstOEins')
    if e3 <<= e | e3 <<= e2 then
        call err 'doppelt reinitialised objects'

    rr = oRunner('return 7 * 3')
    call tstOut t, 'oRun 7*3' oRun(rr)
    r12 = oRunner('return 12 * 12')
    call tstOut t, 'oRun 12*12' oRun(r12)
    call tstEnd t
    return
endProcedure tstO

tstOmet: procedure expose m.
parse arg m, met
    interpret objMet(m, met)
    return
endProcedure tstOmet

tstJSay: procedure expose m.
/*<<tstJSay
    ### start tst tstJSay #############################################
    *** err: call of abstract method jOpen(<obj j of JRW>, openArg)
    *** err: jWrite(<obj j of JRW>, writeArg) but not opened w
    *** err: can only write JRWSay.jOpen(<obj s of JRWSay>, open<Arg)
    *** err: jWrite(<obj s of JRWSay>, write s vor open) but not opened+
    . w
    *** err: can only read JRWEof.jOpen(<obj e of JRWEof>, open>Arg)
    *** err: jRead(<obj e of JRWEof>, XX) but not opened r
    read e vor open 0 m.xx valueBefore
    read e nach open 0 m.xx M.XX
    out eins
    #jIn 1# tst in line 1 eins ,
    out zwei jIn 1 vv=readAdrVV
    #jIn 2# tst in line 2 zwei ;   .
    out drei jIn 1 vv=readAdrVV Schluss
tstJSay */

    call tst t, 'tstJSay'
    call jIni
    j = oNew('JRW')
    call mAdd t'.TRANS', j '<obj j of JRW>'
    call jOpen j, 'openArg'
    call jWrite j, 'writeArg'
    s = oNew('JRWSay')
    call mAdd t'.TRANS', s '<obj s of JRWSay>'
    call jOpen s, 'open<Arg'
    call jWrite s, 'write s vor open'
    call jOpen s
    call jWrite s, 'write s nach open'
    e = oNew('JRWEof')
    call mAdd t'.TRANS', e '<obj e of JRWEof>'
    call jOpen e, 'open>Arg'
    m.xx = 'valueBefore'
    call tstOut t, 'read e vor open' jRead(e, xx) 'm.xx' m.xx
    call jOpen e
    call tstOut t, 'read e nach open' jRead(e, xx) 'm.xx' m.xx
    call jOut 'out eins'
    vv = 'readAdrVV'
    m.vv = 'readAdrVVValueBefore'
    call jOut 'out zwei jIn' jIn(vv) 'vv='vv
    m.vv = 'readAdrVVValueBefore'
    call jOut 'out drei jIn' jIn(vv) 'vv='vv 'Schluss'
    call tstEnd t
    return
endProcedure tstJSay

tstJ: procedure expose m.
/*<<tstJ
    ### start tst tstJ ################################################
    out eins
    #jIn 1# tst in line 1 eins ,
    1 jIn() tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    2 jIn() tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    3 jIn() tst in line 3 drei .schluss..
    #jIn eof 4#
    jIn() 3 reads vv VV
    *** err: already opened jOpen(<buf b>, <)
    line buf line one
    line buf line two
    line buf line three
    line buf line four
    *** err: jWrite(<buf b>, buf line five while reading) but not opene+
    d w
tstJ */

    call jIni
    call tst t, "tstJ"
    b = jOpen(jBuf(), '>')
    call mAdd t'.TRANS', b '<buf b>'
    call jOut 'out eins'
    do lx=1 by 1 while jIn(var)
        call jOut lx 'jIn()' m.var
        end
    call jOut 'jIn()' (lx-1) 'reads vv' vv
    call jWrite b, 'buf line one'
    call mAdd m.b.stem, 'buf line two', 'buf line three'
    call jWrite b, 'buf line four'
    call jOpen b, '<'
    call jClose b
    call jOpen b, '<'
    do while (jRead(b, line))
        call jOut 'line' m.line
        end
    call jWrite b, 'buf line five while reading'
    call jClose b
    call tstEnd t
    return
endProcedure tstJ

tstJ2: procedure expose m.
/*<<tstJ2
    ### start tst tstJ2 ###############################################
    class1 <Tst?1 class> <Tst?1 name>
    class2 <Tst?1 class> <Tst?1 name>
    class3 <Tst?1 class> <Tst?1 name>
    b read EINS feld eins, ZWEI feld zwei, DREI feld drei
    b read EINS feld eins, ZWEI feld zwei 2, DREI feld drei
    c read EINS feld eins, ZWEI feld zwei, DREI drei cat 1
    tstR: @CCC isA :<Tst?1 name> union
    tstR:  .EINS = feld eins
    tstR:  .ZWEI = feld zwei
    tstR:  .DREI = drei cat 1
    c read EINS feld eins, ZWEI feld zwei 2, DREI drei cat 2
    tstR: @CCC isA :<Tst?1 name> union
    tstR:  .EINS = feld eins
    tstR:  .ZWEI = feld zwei 2
    tstR:  .DREI = drei cat 2
tstJ2 */

    call tst t, "tstJ2"
    ty = classNew('n Tst* u f EINS v, f ZWEI v, f DREI v')
    call mAdd t'.TRANS', ty '<Tst?1 class>'
    call mAdd t'.TRANS', m.ty.name '<Tst?1 name>'
    call tstOut t, 'class1' ty m.ty.name
    ty = classNew('n Tst* u f EINS v, f ZWEI v, f DREI v')
    call tstOut t, 'class2' ty m.ty.name
    ty = classNew('n Tst*  u f  EINS v, f  ZWEI v, f  DREI v')
    call tstOut t, 'class3' ty m.ty.name
    call oMutate qq, m.ty.name
    m.qq.eins = 'feld eins'
    m.qq.zwei = 'feld zwei'
    m.qq.drei = 'feld drei'
    b = jBuf()
    call jOpen b, '>'
    call jWriteR b, qq
    m.qq.zwei = 'feld zwei 2'
    call jWriteR b, qq
    call jOpen jClose(b), '<'
    c = jOpen(jBuf(), '>')
    do xx=1 while jRead(b, res)
        call jOut 'b read EINS' m.res.eins', ZWEI' m.res.zwei,
                                     || ', DREI' m.res.drei
        m.res.drei = 'drei cat' xx
        call jWriteR c, res
        end
    call jOpen jClose(c), '<'
    do while jRead(c, ccc)
        call jOut 'c read EINS' m.ccc.eins', ZWEI' m.ccc.zwei,
                                     || ', DREI' m.ccc.drei
        call jOuR ccc
        end
    call tstEnd t
    return
endProcedure tstJ2

tstCat: procedure expose m.
/*<<tstCat
    ### start tst tstCat ##############################################
    catRead 1 line 1
    catRead 2 line 2
    catRead 3 line 3
    appRead 1 line 1
    appRead 2 line 2
    appRead 3 line 3
    appRead 4 append 4
    appRead 5 append 5
tstCat */
    call tst t, "tstCat"
    i = cat('%' jBuf('line 1', 'line 2'), '%' jBuf('line 3'))
    call jOpen i, m.j.cRead
    do lx=1 by 1 while jRead(i, v)
        call tstOut t, 'catRead' lx m.v
        end
    call jOpen jClose(i), m.j.cApp
    call jWrite i, 'append 4'
    call jWrite i, 'append 5'
    call jOpen jClose(i), m.j.cRead
    do lx=1 by 1 while jRead(i, v)
        call tstOut t, 'appRead' lx m.v
        end
    call tstEnd t
    return
endProcedure tstCat

tstEnv: procedure expose m.
/*<<tstEnv
    ### start tst tstEnv ##############################################
    before envPush
    after envPop
    *** err: jWrite(<jBuf c>, write nach pop) but not opened w
    #jIn 1# tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    before writeNow 2 c --> std
    before writeNow 1 b --> c
    b line eins
    b zwei |
    nach writeNow 1 b --> c
    add nach pop
    after push c only
    tst in line 1 eins ,
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    nach writeNow 2 c --> std
    *** err: jWrite(<jBuf c>, ) but not opened w
tstEnv */

    call tst t, "tstEnv"
    c = jBuf()
    call mAdd t'.TRANS', c '<jBuf c>'
    call jOut 'before envPush'
    b = jBuf("b line eins", "b zwei |")
    call envPush '<%' b, '>%' c
    call jOut 'before writeNow 1 b --> c'
    call envwriteNow
    call jOut 'nach writeNow 1 b --> c'
    call envPop
    call jOut 'after envPop'
    call jWrite c, 'write nach pop'
    call mAdd c'.BUF', 'add nach pop'
    call envPush '>>%' c
    call jOut 'after push c only'
    call envwriteNow
    call envPop
    call envPush '<%' c
    call jOut 'before writeNow 2 c --> std'
    call envwriteNow
    call jOut 'nach writeNow 2 c --> std'
    call envPop
    call jWrite c
    call tstEnd t
    return
endProcedure tstEnv

tstEnvCat: procedure expose m.
/*<<tstEnvCat
    ### start tst tstEnvCat ###########################################
    c1 contents
    c1 line eins |
    before writeNow 1 b* --> c*
    b1 line eins|
    b2 line eins
    b2 zwei |
    c2 line eins |
    after writeNow 1 b* --> c*
    c2 contents
    c2 line eins |
tstEnvCat */

    call tst t, "tstEnvCat"

    b0= jBuf()
    b0= jBuf()
    b1= jBuf("b1 line eins|")
    b2 = jBuf("b2 line eins", "b2 zwei |")
    c1 = jBuf("c1 line eins |")
    c2 = jBuf("c2 line eins |")
    call envPush '<+%' b0, '<+%' b1, '<+%' b2, '<%' c2,'>>%' c1

    call jOut 'before writeNow 1 b* --> c*'
    call envwriteNow
    call jOut 'after writeNow 1 b* --> c*'
    call envPop
    call jOut 'c1 contents'
    call envPush '<%' c1
    call envwriteNow
    call envPop
    call envPush '<%' c2
    call jOut 'c2 contents'
    call envwriteNow
    call envPop
    call tstEnd t
    return
endProcedure tstEnvCat

tstEnvBar: procedure expose m.
/*<<tstEnvBar
    ### start tst tstEnvBar ###########################################
    .+0 vor envBarBegin
    #jIn 1# tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    .+7 nach envBarLast
    ¢7 +6 nach envBar 7!
    ¢7 +2 nach envBar 7!
    ¢7 +4 nach nested envBarLast 7!
    ¢7 (4 +3 nach nested envBarBegin 4) 7!
    ¢7 (4 (3 +1 nach envBarBegin 3) 4) 7!
    ¢7 (4 (3 tst in line 1 eins , 3) 4) 7!
    ¢7 (4 (3 tst in line 2 zwei ;    3) 4) 7!
    ¢7 (4 (3 tst in line 3 drei .schluss. 3) 4) 7!
    ¢7 (4 (3 +1 nach writeNow vor envBar 3) 4) 7!
    ¢7 (4 +3 nach preSuf vor nested envBarLast 4) 7!
    ¢7 +4 nach preSuf vor nested envBarEnd 7!
    ¢7 +5 nach nested envBarEnd vor envBar 7!
    ¢7 +6 nach writeNow vor envBarLast 7!
    .+7 nach writeNow vor envBarEnd
    .+8 nach envBarEnd
tstEnvBar */

    call tst t, 'tstEnvBar'
    call jOut '+0 vor envBarBegin'
    call envBarBegin
    call jOut '+1 nach envBarBegin'
    call envwriteNow
    call jOut '+1 nach writeNow vor envBar'
    call envBar
    call jOut '+2 nach envBar'
    call envBarBegin
    call jOut '+3 nach nested envBarBegin'
    call envPreSuf '(3 ', ' 3)'
    call jOut '+3 nach preSuf vor nested envBarLast'
    call envBarLast
    call jOut '+4 nach nested envBarLast'
    call envPreSuf '(4 ', ' 4)'
    call jOut '+4 nach preSuf vor nested envBarEnd'
    call envBarEnd
    call jOut '+5 nach nested envBarEnd vor envBar'
    call envBar
    call jOut '+6 nach envBar'
    call envwriteNow
    say 'jOut +6 nach writeNow vor envBarLast'
    call jOut '+6 nach writeNow vor envBarLast'
    call envBarLast
    call jOut '+7 nach envBarLast'
    call envPreSuf '¢7 ', ' 7!'
    call jOut '+7 nach writeNow vor envBarEnd'
    call envBarEnd
    call jOut '+8 nach envBarEnd'
    call tstEnd t
    return
endProcedure tstEnvBar

tstEnvVars: procedure expose m.
/*<<tstEnvVars
    ### start tst tstEnvVars ##########################################
    put v1 value eins
    v1 hasKey 1 get value eins
    v2 hasKey 0
    via v1.fld via value
    one to theBur
    two to theBuf
tstEnvVars */
    call tst t, "tstEnvVars"
    call envRemove 'v2'
    put1 = envPut('v1', 'value eins')
    call tstOut t, 'put v1' put1
    call tstOut t, 'v1 hasKey' envHasKey('v1') 'get' envGet('v1')
    call tstOut t, 'v2 hasKey' envHasKey('v2')
    if 0 then
        call tstOut t, 'v2 get'    envGet('v2')
    m.put1.fld = 'via value'
    call tstOut t, 'via v1.fld' envVia('v1*FLD')

    call envPush '># theBuf'
    call jOut 'one to theBur'
    call jOut 'two to theBuf'
    call envPop
    call envPush '<# theBuf'
    call envwriteNow
    call envPop
    call tstEnd t
    return
endProcedure tstEnvVars

tstEnvLazy: procedure expose m.
/*<<tstEnvLazy
    ### start tst tstEnvLazy ##########################################
    a1 vor envBarBegin loop lazy 0 writeNow *** <class TstEnvLazyBuf>
    bufOpen <%
    bufClose
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    a5 vor 2 writeNow jIn inIx 4
    a2 vor writeNow jBuf
    jBuf line 1
    jBuf line 2
    a3 vor writeNow jIn inIx 1
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    a4 vor barLast inIx 4
    a6 vor barEnd inIx 4
    a7 nach barEnd lazy 0 writeNow ***
    b1 vor barBegin lazy 0 writeNow *** <class TstEnvLazyRdr>
    RdrOpen <%
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    rdrClose
    b4 vor writeNow
    b2 vor writeNow rdr inIx 2
    jRead lazyRdr
    tst in line 3 drei .schluss..
    jRead lazyRdr
    b3 vor barLast inIx 4
    b5 vor barEnd inIx 4
    b6 nach barEnd lazy 0 writeNow ***
    a1 vor envBarBegin loop lazy 1 writeAll *** <class TstEnvLazyBuf>
    a5 vor 2 writeAll jIn inIx 0
    a2 vor writeAll jBuf
    bufOpen <%
    jBuf line 1
    jBuf line 2
    bufClose
    a3 vor writeAll jIn inIx 0
    #jIn 1# tst in line 1 eins ,
    tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    #jIn eof 4#
    a4 vor barLast inIx 0
    a6 vor barEnd inIx 4
    a7 nach barEnd lazy 1 writeAll ***
    b1 vor barBegin lazy 1 writeAll *** <class TstEnvLazyRdr>
    b4 vor writeAll
    b2 vor writeAll rdr inIx 1
    RdrOpen <%
    *** err: jWrite(ENV.lazyNoOut, jRead lazyRdr) but not opened w
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    *** err: jWrite(ENV.lazyNoOut, jRead lazyRdr) but not opened w
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    *** err: jWrite(ENV.lazyNoOut, jRead lazyRdr) but not opened w
    #jIn eof 4#
    rdrClose
    b3 vor barLast inIx 1
    b5 vor barEnd inIx 4
    b6 nach barEnd lazy 1 writeAll ***
tstEnvLazy */
    call tst t, "tstEnvLazy"
    do lz=0 to 1
        if lz then
            w = 'writeAll'
        else
            w = 'writeNow'
        m.t.inIx = 1-lz
        ty = classNew('n TstEnvLazyBuf u JBuf', 'm',
            , 'jOpen call tstOut "T", "bufOpen" opt;',
                'return jOpen(oCast(m, "JBuf"), opt)',
            , 'jClose call tstOut "T", "bufClose";',
                'return jClose(oCast(m, "JBuf"), opt)')
        if \ lz then
             call mAdd t'.TRANS', ty '<class TstEnvLazyBuf>'
        call jOut 'a1 vor envBarBegin loop lazy' lz w '***' ty
        call envBarBegin
        call jOut 'a2 vor' w 'jBuf'
        b = oMutate(jBuf('jBuf line 1','jBuf line 2'),
                ,'TstEnvLazyBuf')
        interpret 'call env'w '"<%" b'
        call jOut 'a3 vor' w 'jIn inIx' m.t.inIx
        interpret 'call env'w
        call jOut 'a4 vor barLast inIx' m.t.inIx
        call envBarLast
        call jOut 'a5 vor 2' w 'jIn inIx' m.t.inIx
        interpret 'call env'w
        call jOut 'a6 vor barEnd inIx' m.t.inIx
        call envBarEnd
        call jOut 'a7 nach barEnd lazy' lz w '***'

        ty = classNew('n TstEnvLazyRdr u JRW', 'm',
            , 'jOpen call tstOut "T", "RdrOpen" opt;m.m.jReading=1',
            , 'jRead call jOut "jRead lazyRdr"; return jIn(var);',
            , 'jClose call tstOut "T",  "rdrClose";')
        if \ lz then
             call mAdd t'.TRANS', ty '<class TstEnvLazyRdr>'

        r = oNew('TstEnvLazyRdr')
         if lz then
             call mAdd t'.TRANS', r '<lazyRdr>'
     m.t.inIx = 2-lz
        call jOut 'b1 vor barBegin lazy' lz w '***' ty
     call envBarBegin
        if lz then
             call mAdd t'.TRANS', m.j.jOut '<barBegin out>'
     call jOut 'b2 vor' w 'rdr inIx' m.t.inIx
     interpret 'call env'w 'm.j.cRead || m.j.cObj r'
        call jOut 'b3 vor barLast inIx' m.t.inIx
     call envBarLast
        call jOut 'b4 vor' w
        interpret 'call env'w
        call jOut 'b5 vor barEnd inIx' m.t.inIx
        call envBarEnd
     call jOut 'b6 nach barEnd lazy' lz w '***'
        end
    call tstEnd t
    return
endProcedure tstEnvLazy

tstEnvClass: procedure expose m.
/*<<tstEnvClass
    ### start tst tstEnvClass #########################################
    a0 vor envBarBegin loop lazy 0 writeNow *** TY
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    a5 vor writeNow
    a1 vor jBuf()
    a2 vor writeNow b
    tstR: @LINE isA :TstEnvClass10 union
    tstR:  .f11 = M.<o20 of TstEnvClass10>.f11
    tstR:  .F12 = value F12 of o1 <o20 of TstEnvClass10>
    tstR:  .f13 = M.<o20 of TstEnvClass10>.f13
    writeR o2
    tstR: @LINE isA :TstEnvClass20 union = valueO2Lazy0
    tstR:  .f24 = M.<o20 of TstEnvClass20>.f24
    tstR:  .F25 = value F25 of o20 <o20 of TstEnvClass20>
    a3 vor writeNow
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    a4 vor barLast inIx 4
    a6 vor barEnd
    a7 nach barEnd lazy 0 writeNow ***
    a0 vor envBarBegin loop lazy 1 writeAll *** TY
    a5 vor writeAll
    a1 vor jBuf()
    a2 vor writeAll b
    tstR: @LINE isA :TstEnvClass10 union
    tstR:  .f11 = M.<o21 of TstEnvClass10>.f11
    tstR:  .F12 = value F12 of o1 <o21 of TstEnvClass10>
    tstR:  .f13 = M.<o21 of TstEnvClass10>.f13
    writeR o2
    tstR: @LINE isA :TstEnvClass20 union = valueO2Lazy1
    tstR:  .f24 = M.<o21 of TstEnvClass20>.f24
    tstR:  .F25 = value F25 of o21 <o21 of TstEnvClass20>
    a3 vor writeAll
    #jIn 1# tst in line 1 eins ,
    tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    #jIn eof 4#
    a4 vor barLast inIx 0
    a6 vor barEnd
    a7 nach barEnd lazy 1 writeAll ***
tstEnvClass */

    call tst t, "tstEnvClass"
    do lz=0 to 1
        if lz then
            w = 'writeAll'
        else
            w = 'writeNow'
        m.t.inIx = 1-lz
        t10 = classNew('n TstEnvClass10 u f f11 v, f F12 v, f f13 v')
        t20 = classNew('n TstEnvClass20 u v, f f24 v, f F25 v')
        call jOut 'a0 vor envBarBegin loop lazy' lz w '***' ty
        call envBarBegin
        call jOut 'a1 vor jBuf()'
        b = jOpen(jBuf(), m.j.cWri)
        o1 = oNew('TstEnvClass10')
        m.o1.F12 = 'value F12 of o1' o1
        call mAdd t'.TRANS', o1 '<o2'lz 'of TstEnvClass10>'
        call jWriteR b, o1
        call jWrite b, 'writeR o2'
        o2 = oNew('TstEnvClass20')
        m.o2 = 'valueO2Lazy'lz
        m.o2.F25 = 'value F25 of o2'lz o2
        oc = oCopyNew(oCopyNew(o2))
        call mAdd t'.TRANS', o2 '<o2'lz 'of TstEnvClass20>'
        call mAdd t'.TRANS', oc '<oc'lz 'of TstEnvClass20>'
        call jWriteR b, oc
        call jOut 'a2 vor' w 'b'
        interpret 'call env'w '"<%"' jClose(b)
        call jOut 'a3 vor' w
        interpret 'call env'w
        call jOut 'a4 vor barLast inIx' m.t.inIx
        call envBarLast
        call jOut 'a5 vor' w
        interpret 'call env'w
        call jOut 'a6 vor barEnd'
        call envBarEnd
        call jOut 'a7 nach barEnd lazy' lz w '***'
        end
    call tstEnd t
    return
endProcedure tstEnvClass

tstFile: procedure expose m.
/*<<tstFile
    ### start tst tstFile #############################################
    write read 0 last 10 vor anfang
    write read 1 last 80  links1 1   und rechts |  ..
    write read 2 last 80 liinks2 2   und rechts |  ..
    write read 5 last 80 links5 5 rechts5
    write read 99 last 80 links99 99 rechts
    write read 100 last 80 links100 100 rechts
    write read 101 last 80 links101 101 rechts
    write read 999 last 80 links999 999 rechts
    write read 1000 last 80 links1000 1000 rechts
    write read 1001 last 80 links1001 1001 rechts
    write read 2109 last 80 links2109 2109 rechts
    out > eins 1                                                       +
    .             .
    out > eins 2 schluss.                                              +
    .             .
    buf eins
    buf zwei
    buf drei
    out > zwei mit einer einzigen Zeile                                +
    .             .
    . links1 1   und rechts |  .                                       +
    .              .
tstFile */
    call tst t, "tstFile"
    pds = tstFilename('lib', 'r')
    call tstFileWr pds, 0, ' links0', '  und rechts |  .  '
    call tstFileWr pds, 1, ' links1', '  und rechts |  .  '
    call tstFileWr pds, 2, 'liinks2', '  und rechts |  .  '
    call tstFileWr pds, 5, 'links5', 'rechts5'
    call tstFileWr pds, 99, 'links99', 'rechts'
    call tstFileWr pds, 100, 'links100', 'rechts'
    call tstFileWr pds, 101, 'links101', 'rechts'
    call tstFileWr pds, 999, 'links999', 'rechts'
    call tstFileWr pds, 1000, 'links1000', 'rechts'
    call tstFileWr pds, 1001, 'links1001', 'rechts'
    call tstFileWr pds, 2109, 'links2109', 'rechts'
    pd2 = tstFilename('li2', 'r')
    call envPush '>' tstPdsMbr(pd2, 'eins')
    call jOut tstFB('out > eins 1') /* simulate fixBlock on linux */
    call jOut tstFB('out > eins 2 schluss.')
    call envPop
    call envPush '>' tstPdsMbr(pd2, 'zwei')
    call jOut tstFB('out > zwei mit einer einzigen Zeile')
    call envPop
    b = jBuf("buf eins", "buf zwei", "buf drei")
    call envPush '<' tstPdsMbr(pd2, 'eins'), '<%' b,
                    ,'<%' jBuf(),
                    ,'<' tstPdsMbr(pd2, 'zwei'),
                    ,'<' tstPdsMbr(pds, 'wr0'),
                    ,'<' tstPdsMbr(pds, 'wr1')
    call envwriteNow
    call envPop
    call tstEnd t
    return
endProcedure tstFile

/*--- simulate fixBlock 80 on linux ---------------------------------*/
tstFB: procedure expose m.
parse arg line, recL
    if errOS() \== 'LINUX' then
        return line
    else if recL == '' then
        return left(line, 80)
    else
        return left(line, recL)
endProcedure tstFB

tstPdsMbr: procedure expose m.
parse arg pds, mbr
    os = errOS()
    if os = 'TSO' then
        return pds'('mbr') ::F'
    if os = 'LINUX' then
        return pds'.'mbr
    call err 'tstPdsMbr implement os' os
endProcedure tstPdsMbr

tstFileWR: procedure expose m.
parse arg dsn, num, le, ri
    io = file(tstPdsMbr(dsn, 'wr'num))
    call jOpen io, m.j.cWri
    do x = 1 to num /* simulate fixBlock 80 on LINUX*/
        call jWrite io, tstFB(le x ri)
        end
    if num > 100 then
        call jReset jClose(io), tstPdsMbr(dsn, 'wr'num)
    call jOpen jClose(io), m.j.cRead
    m.vv = 'vor anfang'
    do x = 1 to num
        if \ jRead(io, vv) then
            call err x 'not jRead'
        else if m.vv <> le x ri then
            call err x 'read mismatch' m.vv
        end
    if jRead(io, vv) then
        call err x 'jRead'
    if jRead(io, vv) then
        call err x'+1 jRead'
    call jClose io
    call tstOut t, 'write read' num 'last' length(m.vv) strip(m.vv,'t')
    return
endProcedure tstFileRW

tstFileList: procedure expose m.
/*<<tstFileList
    ### start tst tstFileList #########################################
    empty dir
    filled dir
    <<pref 2 List>>drei
    <<pref 2 List>>eins
    <<pref 2 List>>zwei
    <<pref 2 List>>vier
    filled dir recursive
    <<pref 2 List>>drei
    <<pref 2 List>>eins
    <<pref 2 List>>zwei
    <<pref 2 List>>vier
    <<pref 1 vier>>drei
    <<pref 1 vier>>eins
    <<pref 1 vier>>zwei
tstFileList */
/*<<tstFileListTSO
    ### start tst tstFileListTSO ######################################
    empty dir
    filled dir
    <<pref 2 LIST>>DREI
    <<pref 2 LIST>>EINS
    <<pref 2 LIST>>ZWEI
    filled dir recursive
    <<pref 2 LIST>>DREI
    <<pref 2 LIST>>EINS
    <<pref 1 VIER>>DREI
    <<pref 1 VIER>>EINS
    <<pref 1 VIER>>ZWEI
    <<pref 2 LIST>>ZWEI
tstFileListTSO */
    if errOS() = 'TSO' then
        call tst t, "tstFileListTSO"
    else
        call tst t, "tstFileList"
    fi = file(tstFileName('FileList', 'r'))
    call fileMkDir fi
    fl = fileList(fi)
    call tstOut t, 'empty dir'
    call jWriteNow t, fl
    call tstFileListMake t, fi, 2
    call tstOut t, 'filled dir'
    call jWriteNow t, fl
    call tstOut t, 'filled dir recursive'
    call jWriteNow t, fileList(fi, 'r')
    call tstEnd t
    return
endProcedure tstFileList

tstFileListMake: procedure expose m.
parse arg t, fi, lev
    if \ fileIsDir(fi) then
        call fileMkDir fi
    call mAdd t'.TRANS', filePath(fi) || m.file.sep ,
                '<<pref' lev right(filePath(fi),4)'>>'
    call jCat fileChild(fi, 'eins','::F'),jBuf('zeile eins', 'eins' lev)
    call jCat fileChild(fi, 'zwei','::F'),jBuf('zeile zwei', 'zwei' lev)
    call jCat fileChild(fi, 'drei','::F'),jBuf('zeile drei', 'drei' lev)
    if lev > 1 then
        call tstFileListMake t, fileChild(fi, 'vier'), lev-1
    return
endProcedure tstFileListMake


tstFmt: procedure expose m.
/*<<tstFmt
    ### start tst tstFmt ##############################################
    =   a2i b3b   d4                       fl5          ex6
    -11 -11 b3    d4-11+d4++++    -111.1000000 -1.11000E-12
    -1  -10 b     d4-10+d4+++            null1        null3
    -    -9 b3b-9 d4-9+d4+++       -11.0000000 -1.11900E-10
    -8+  -8 b3b-  d4-8+d4++        -18.0000000 -1.18000E010
    -7   -7 b3b   d4-7+d4+          -7.0000000 -1.70000E-07
    -    -6 b3    d4-6+d4           -0.1111160 -6.00000E006
    -5+  -5 b     d4-5+d                 null2        null2
    -4   -4 b3b-4 d4-4+         -11114.0000000 -1.11140E008
    -    -3 b3b-  d4-3              -0.1130000 -1.13000E-04
    -2+  -2 b3b   d4-               -0.1200000 -1.20000E001
    -1   -1 b3    d4                -0.1000000 -1.00000E-02
    0     0 b     d                      null1        null1
    1+    1 b3    d4                 0.1000000  1.00000E-02
    2++   2 b3b   d42                0.1200000  1.20000E001
    3     3 b3b3  d43+               0.1130000  1.13000E-04
    4+    4 b3b4+ d44+d          11114.0000000  1.11140E008
    5++   5 b     d45+d4                 null2        null2
    6     6 b3    d46+d4+            0.1111160  1.11116E005
    7+    7 b3b   d47+d4++           0.1111117  7.00000E-08
    8++   8 b3b8  d48+d4+++          8.0000000  1.80000E009
    9     9 b3b9+ d49+d4++++         0.9000000  1.19000E-08
    10   10 b     d410+d4++++            null1        null3
    11+  11 b3    d411+d4+++++       0.1110000  1.00000E-12
    1    12 b3b   d412+d4++++++  11112.0000000  2.00000E012
    13   13 b3b1  d               1111.3000000  1.13000E-12
    14+  14 b3b14 d4            111111.0000000  1.40000E013
    1    15 b     d41                    null2        null1
    16   16 b3    d416               6.0000000  1.16000E003
    17+  17 b3b   d417+              0.7000000  1.11170E-03
    1    18 b3b1  d418+d            11.0000000  1.11800E003
    19   19 b3b19 d419+d4            0.1190000  9.00000E-05
    20+  20 b     d420+d4+               null1        null2
    2    21 b3    d421+d4++         11.1210000  1.11000E-05
    22   22 b3b   d422+d4+++     11111.2000000  2.00000E007
    23+  23 b3b2  d423+d4++++        0.1111123  1.11230E-09
    c3L      a2i drei  d4                 fl5          ex6
    -11 -1.10E01 b3    d4-11+d   -111.1000000 -1.11000E-12
    -1  -1.00E01 b     d4-10+d          null1        null3
    -   -9.00E00 b3b-9 d4-9+d4    -11.0000000 -1.11900E-10
    -8+ -8.00E00 b3b-  d4-8+d4    -18.0000000 -1.18000E010
    -7  -7.00E00 b3b   d4-7+d4     -7.0000000 -1.70000E-07
    -   -6.00E00 b3    d4-6+d4     -0.1111160 -6.00000E006
    -5+ -5.00E00 b     d4-5+d           null2        null2
    -4  -4.00E00 b3b-4 d4-4+   -11114.0000000 -1.11140E008
    -   -3.00E00 b3b-  d4-3        -0.1130000 -1.13000E-04
    -2+ -2.00E00 b3b   d4-         -0.1200000 -1.20000E001
    -1  -1.00E00 b3    d4          -0.1000000 -1.00000E-02
    0    0.00E00 b     d                null1        null1
    1+   1.00E00 b3    d4           0.1000000  1.00000E-02
    2++  2.00E00 b3b   d42          0.1200000  1.20000E001
    3    3.00E00 b3b3  d43+         0.1130000  1.13000E-04
    4+   4.00E00 b3b4+ d44+d    11114.0000000  1.11140E008
    5++  5.00E00 b     d45+d4           null2        null2
    6    6.00E00 b3    d46+d4+      0.1111160  1.11116E005
    7+   7.00E00 b3b   d47+d4+      0.1111117  7.00000E-08
    8++  8.00E00 b3b8  d48+d4+      8.0000000  1.80000E009
    9    9.00E00 b3b9+ d49+d4+      0.9000000  1.19000E-08
    10   1.00E01 b     d410+d4          null1        null3
    11+  1.10E01 b3    d411+d4      0.1110000  1.00000E-12
    1    1.20E01 b3b   d412+d4  11112.0000000  2.00000E012
    13   1.30E01 b3b1  d         1111.3000000  1.13000E-12
    14+  1.40E01 b3b14 d4      111111.0000000  1.40000E013
    1    1.50E01 b     d41              null2        null1
    16   1.60E01 b3    d416         6.0000000  1.16000E003
    17+  1.70E01 b3b   d417+        0.7000000  1.11170E-03
    1    1.80E01 b3b1  d418+d      11.0000000  1.11800E003
    19   1.90E01 b3b19 d419+d4      0.1190000  9.00000E-05
    20+  2.00E01 b     d420+d4          null1        null2
    2    2.10E01 b3    d421+d4     11.1210000  1.11000E-05
    22   2.20E01 b3b   d422+d4  11111.2000000  2.00000E007
    23+  2.30E01 b3b2  d423+d4      0.1111123  1.11230E-09
tstFmt */

    call tst t, "tstFmt"
    b = jBuf()
    st = b'.BUF'
    call envPush m.j.cWri || m.j.cObj b
    call tstDataClassOut '. c3 a2i i b3b c5 d4 c13 fl5 f8n2 ex6 e9n3',
        , -11, + 23
    call envPop
    call fmtFWriteAll fmtFreset(abc), m.j.cRead || m.j.cObj b
    call fmtFAddFlds fmtFReset(abc), oFlds(st'.'1)
    m.abc.1.tit = 'c3L'
    m.abc.2.fmt = 'e'
    m.abc.3.tit = 'drei'
    m.abc.4.fmt = 'l7'
    call fmtFWriteAll abc, m.j.cRead || m.j.cObj b
    call tstEnd t
/*<<tstFmtCSV
    ### start tst tstFmtCSV ###########################################
    , a2i, b3b, d4, fl5, ex6
    -5+, -5, b, d4-5+d, null2, null2
    -4, -4, b3b-4, d4-4+, -11114, -11114e4
    -, -3, b3b-, d4-3, -.113, -.113e-3
    -2+, -2, b3b, d4-, -.12, -.12e2
    -1, -1, b3, d4, -.1, -.1e-1
    0, 0, b, d, null1, null1
    1+, 1, b3, d4, .1, .1e-1
    2++, 2, b3b, d42, .12, .12e2
    3, 3, b3b3, d43+, .113, .113e-3
    4+, 4, b3b4+, d44+d, 11114, 11114e4
    5++, 5, b, d45+d4, null2, null2
    6, 6, b3, d46+d4+, .111116, .111116e6
    7+, 7, b3b, d47+d4++, .1111117, .7e-7
tstFmtCSV */
    call tst t, 'tstFmtCSV'
    call envBarBegin
    call tstDataClassOut '. c3 a2i i b3b c5 d4 c13 fl5 f8n2 ex6 e9n3',
        , -5, + 7
    call envBarLast
    call fmtFCsvAll
    call envBarEnd
    call tstEnd t
    return
endProcedure tstFmt

tstScan: procedure expose m.
/*<<tstScan.1
    ### start tst tstScan.1 ###########################################
    scan src a034,Und hr123sdfER"st1" 'str2''mit''apo''s' .
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    scan n tok 3: Und key  val .
    scan v tok 1:   key  val .
    scan n tok 10: hr123sdfER key  val .
    scan q tok 5: "st1" key  val st1
    scan v tok 1:   key  val st1
    scan a tok 19: 'str2''mit''apo''s' key  val str2'mit'apo's
    scan v tok 1:   key  val str2'mit'apo's
tstScan.1 */
    call tst t, 'tstScan.1'

    call tstScan1 ,'l"litEins"l"litZwei"ndv" "aq1' ,
        ,"a034,Und hr123sdfER""st1"" 'str2''mit''apo''s' "
    call tstEnd t

/*<<tstScan.2
    ### start tst tstScan.2 ###########################################
    scan src a034,Und hr123sdfER"st1" 'str2''mit''apo''s' .
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    scan n tok 3: Und key  val .
    scan b tok 0:  key  val .
    scan n tok 10: hr123sdfER key  val .
    scan " tok 5: "st1" key  val st1
    scan b tok 0:  key  val st1
    scan ' tok 19: 'str2''mit''apo''s' key  val str2'mit'apo's
    scan b tok 0:  key  val str2'mit'apo's
tstScan.2 */
    call tst t, 'tstScan.2'
    call tstScan1 , 'ndsb1' ,
        ,"a034,Und hr123sdfER""st1"" 'str2''mit''apo''s' "
    call tstEnd t

/*<<tstScan.3
    ### start tst tstScan.3 ###########################################
    scan src a034,'wie 789abc
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    *** err: scanErr ending Apostroph(') missing
    .    e 1: last token  scanPosition 'wie 789abc
    .    e 2: pos 6 in string a034,'wie 789abc
    scan ' tok 1: ' key  val .
    scan n tok 3: wie key  val .
    scan s tok 0:  key  val .
    *** err: scanErr illegal number end after 789
    .    e 1: last token 789 scanPosition abc
    .    e 2: pos 14 in string a034,'wie 789abc
    scan d tok 3: 789 key  val .
    scan n tok 3: abc key  val .
tstScan.3 */
    call tst t, 'tstScan.3'
    call tstScan1 , 'nds1' ,
        ,"a034,'wie 789abc"
    call tstEnd t

/*<<tstScan.4
    ### start tst tstScan.4 ###########################################
    scan src litEinsefr 23 sdfER'str1'litZwei "str2""mit quo"s .
    scan l tok 7: litEins key  val .
    scan n tok 3: efr key  val .
    scan b tok 0:  key  val .
    scan d tok 2: 23 key  val .
    scan b tok 0:  key  val .
    scan n tok 5: sdfER key  val .
    scan a tok 6: 'str1' key  val str1
    scan l tok 7: litZwei key  val str1
    scan b tok 0:  key  val str1
    scan q tok 15: "str2""mit quo" key  val str2"mit quo
    scan n tok 1: s key  val str2"mit quo
    scan b tok 0:  key  val str2"mit quo
tstScan.4 */
    call tst t, 'tstScan.4'
    call tstScan1 , 'l"litEins"l"litZwei"ndbaq1' ,
                  ,"litEinsefr 23 sdfER'str1'litZwei ""str2""""mit quo""s "
    call tstEnd t

/*<<tstScan.5
    ### start tst tstScan.5 ###########################################
    scan src  aha;+-=f ab=cdEf eF='strIng' .
    scan b tok 0:  key  val .
    scan k tok 4:  no= key aha val def
    scan ; tok 1: ; key aha val def
    scan + tok 1: + key aha val def
    scan - tok 1: - key aha val def
    scan = tok 1: = key aha val def
    scan k tok 4:  no= key f val def
    scan k tok 4: cdEf key ab val cdEf
    scan b tok 4: cdEf key ab val cdEf
    scan k tok 8: 'strIng' key eF val strIng
    scan b tok 8: 'strIng' key eF val strIng
tstScan.5 */
    call tst t, 'tstScan.5'
    call tstScan1 , 'bk1'," aha;+-=f ab=cdEf eF='strIng' "
    call tstEnd t
    return
endProcedure tstScan

tstScanRead: procedure expose m.
/*<<tstScanRead
    ### start tst tstScanRead #########################################
    name erste
    space
    name Zeile
    space
    nextLine
    nextLine
    space
    name dritte
    space
    name Zeile
    space
    name schluss
    space
tstScanRead */
    call tst t, 'tstScanRead'
    b = jBuf('erste Zeile  ',,'  dritte Zeile  schluss  ')
    s = jOpen(scanRead(b))
    do while \scanAtEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanVerify(s, ' ') then call tstOut t, 'space'
        else if scanReadNL(s) then      call tstOut t, 'nextLine'
        else if \scanAtEnd(s) then      call scanErr s, 'cannot scan'
        end
    call jClose s
    call tstEnd t

/*<<tstScanReadMitSpaceLn
    ### start tst tstScanReadMitSpaceLn ###############################
    name erste
    spaceLn
    name Zeile
    spaceLn
    name dritte
    spaceLn
    name Zeile
    spaceLn
    name schluss
    spaceLn
tstScanReadMitSpaceLn */
    call tst t, 'tstScanReadMitSpaceLn'
    s = jOpen(scanRead(b))
    do forever
        if scanName(s) then         call jOut 'name' m.s.tok
        else if scanSpaceNL(s) then call jOut 'spaceLn'
        else if \scanAtEnd(s) then      call scanErr s, 'cannot scan'
        else                        leave
        end
    call jClose s
    call tstEnd t

/*<<tstScanJRead
    ### start tst tstScanJRead ########################################
    1 jRead n tok erste val .
    2 jRead s tok  val .
    3 jRead n tok Zeile val .
    4 jRead s tok  val .
    5 jRead n tok dritte val .
    6 jRead s tok  val .
    7 jRead n tok Zeile val .
    8 jRead s tok  val .
    9 jRead n tok schluss val .
    10 jRead s tok  val .
    11 jRead 0 tok 1 val 1
    12 jRead s tok  val 1
    13 jRead + tok + val 1
    14 jRead s tok  val 1
    15 jRead 0 tok 2. val 2..
    16 jRead s tok  val 2..
    17 jRead + tok + val 2..
    18 jRead . tok . val 2..
    19 jRead s tok  val 2..
    20 jRead 0 tok +.3 val +.3
    21 jRead 0 tok -45e-3 val -45E-3
    22 jRead s tok  val -45E-3
    23 jRead " tok "a""b" val a"b
    24 jRead s tok  val a"b
    25 jRead ' tok 'c''d' val c'd
    className 1: Scan 18: Scan
tstScanJRead */

    call tst t, 'tstScanJRead'
    call jWrite jOpen(b,'>>'), '1  + 2. +. +.3-45e-3 "a""b"' "'c''d'"
    s = jOpen(scanRead(jClose(b)))
    do x=1 while jRead(s, v.x)
        call jOut x 'jRead' m.v.x.type 'tok' m.v.x.tok 'val' m.v.x.val
        end
    call jClose s
    call jOut 'className 1:' className(objClass(v.1)),
                    '18:' className(objClass(v.18))
    call tstEnd t
    return
endProcedure tstScanRead

tstScanWin: procedure expose m.
/*<<tstScanWin
    ### start tst tstScanWin ##########################################
    info 0: last token  scanPosition erste     Zeile                 dr+
    itteZe\npos 1 in line 1: erste     Zeile
    name erste
    spaceNL
    name Zeile
    spaceNL
    name dritteZeeeile
    info 5: last token dritteZeeeile scanPosition    zeile4            +
    .    fuenfueberSechs\npos 1 in line 4:    zeile4
    spaceNL
    name zeile4
    spaceNL
    name fuenfueberSechsUnddSiebenUNDundUndUAcht
    spaceNL
    info 10: last token  scanPosition undZehnueberElfundNochWeiterZwoel+
    fundim1\npos 9 in line 10:         undZehn
    name undZehnueberElfundNochWeiterZwoelfundim13
    spaceNL
    name Punkt
    infoE 14: last token Punkt scanPosition \natEnd after line 13: im13+
    .      Punkt
tstScanWin */
    call scanWinIni
    call tst t, 'tstScanWin'
    b = jBuf('?erste     Zeile?',,'?  dritteZeeeile?', '?   zeile4 ',
       ,'?          fuenf?', '?ueberSechsUnddS?', '?iebenUNDundUndU?',
       ,'?Acht           ?', '?               ?', '?        undZehn?',
       ,'?ueberElfundNoch?', '?WeiterZwoelfund?', '?im13      Punkt?')
    s = jOpen(scanWin(b, , , 2, 15))
    call tstOut t, 'info 0:' scanInfo(s)
    do sx=1 while \scanAtEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanSpaceNL(s) then call tstOut t, 'spaceNL'
        else if \scanAtEnd(s) then      call scanErr s, 'cannot scan'
        if sx // 5 = 0 then
            call tstOut t, 'info' sx':' scanInfo(s)
        end
    call tstOut t, 'infoE' sx':' scanInfo(s)
    call tstEnd t

/*<<tstScanWinRead
    ### start tst tstScanWinRead ######################################
    info 0: last token  scanPosition erste     Zeile                z3 +
    com Ze\npos 1 in line 1: erste     Zeile
    name erste
    spaceNL
    name Zeile
    spaceNL
    name z3
    info 5: last token z3 scanPosition  com Zeeeile z4 come4          f+
    uenf\npos 4 in line 3:  z3 com Zeeeile
    spaceNL
    name z4
    spaceNL
    name fuenf
    spaceNL
    info 10: last token  scanPosition com    Sechs  com  sieben   comAc+
    ht  com\npos 15 in line 5:     fuenf     c
    name com
    spaceNL
tstScanWinRead */
    call tst t, 'tstScanWinRead'
    call mAdd t.cmp,
       ,  "name Sechs",
       ,  "spaceNL",
       ,  "name com",
       ,  "info 15: last token com scanPosition   sieben   comAcht  c",
       || "om com    com\npos 2 in line 7: m  sieben   com" ,
       ,  "spaceNL",
       ,  "name sieben",
       ,  "spaceNL",
       ,  "name Acht",
       ,  "spaceNL",
       ,  "info 20: last token  scanPosition ueberElfundNochWeit com ",
       || "elfundim13\npos 1 in line 11: ueberElfundNoch",
       ,  "name ueberElfundNochWeit",
       ,  "spaceNL",
       ,  "name im13",
       ,  "spaceNL",
       ,  "name Punkt",
       ,  "info 25: last token Punkt scanPosition \natEnd after line ",
       || "13: im13      Punkt",
       ,  "infoE 26: last token Punkt scanPosition \natEnd after line",
       || " 13: im13      Punkt"
    b = jBuf('?erste     Zeile?',,'? z3 com Zeeeile?', '? z4 come4 ',
       ,'?    fuenf     c?', '?om    Sechs  co?', '?m  sieben   com?',
       ,'?Acht  com com  ?', '?  com          ?', '?  com   undZehn?',
       ,'?ueberElfundNoch?', '?Weit com elfund?', '?im13      Punkt?')
    s = scanWin(b, , , 2, 15)
    call scanOpts s, , , 'com'
    call tstOut t, 'info 0:' scanInfo(jOpen(s))
    do sx=1 while \scanAtEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanSpaceNL(s) then call tstOut t, 'spaceNL'
        else if \scanAtEnd(s) then      call scanErr s, 'cannot scan'
        if sx // 5 = 0 then
            call tstOut t, 'info' sx':' scanInfo(s)
        end
    call tstOut t, 'infoE' sx':' scanInfo(s)
    call tstEnd t
    return
endProcedure tstScanWin

tstScanSql: procedure expose m.
    call scanWinIni
/*<<tstScanSqlId
    ### start tst tstScanSqlId ########################################
    sqlId ABC
    spaceNL
    sqlId AB__345EF
    spaceNL
tstScanSqlId */
    call tst t, 'tstScanSqlId'
    b = jBuf('abc  --  kommentar', right('ab_', 72), '_345ef-- kom')
    s = jOpen(scanSql(b))
    do sx=1 while \scanAtEnd(s)
        if scanSqlID(s) then       call tstOut t, 'sqlId' m.s.val
        else if scanSpaceNL(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*<<tstScanSqlDelimited
    ### start tst tstScanSqlDelimited #################################
    sqlDeId ABC
    spaceNL
    sqlDeId AB_3F
    spaceNL
    sqlDeId abc
    spaceNL
    sqlDeId ab_Ef
    spaceNL
tstScanSqlDelimited */
    call tst t, 'tstScanSqlDelimited'
    b = jBuf('abc  --  kommentar',,'  -- ',,right('ab_', 72),'3F-- kom',
           , '"abc"  --  ko', ' -- ',,right('"ab_', 72),'Ef"-- kom')
    s = jOpen(scanSql(b))
    do sx=1 while \scanAtEnd(s)
        if scanSqlDeID(s) then       call tstOut t, 'sqlDeId' m.s.val
        else if scanSpaceNL(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*<<tstScanSqlQualified
    ### start tst tstScanSqlQualified #################################
    sqlQuId ABC 1 ABC
    sqlQuId AB_3F 1 AB_3F
    sqlQuId abc 1 abc
    sqlQuId ab_Ef 1 ab_Ef
    sqlQuId EINS.Zwei.DREI 3 EINS
    sqlQuId vi er.fu  enf 2 vi er
tstScanSqlQualified */
    call tst t, 'tstScanSqlQualified'
    b = jBuf('abc  --  kommentar',,'  -- ',,right('ab_', 72),'3F-- kom',
           , '"abc"  --  ko', ' -- ',right('"ab_', 72),'Ef"-- kom',
           , 'eins."Zwei', '" -- com', ' . -- com', '  -- com',
           , 'drei -- ko', '"vi er"."fu  enf   " -- co')
    s = jOpen(scanSql(b))
    do sx=1 while \scanAtEnd(s)
        if scanSqlQuID(s) then
            call tstOut t, 'sqlQuId' m.s.val m.s.val.0 m.s.val.1
        else if scanSpaceNL(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*<<tstScanSqlNum
    ### start tst tstScanSqlNum #######################################
    sqlNum 1
    spaceNL
    sqlNum 2..
    spaceNL
    sqlNum .3
    spaceNL
    sqlNum 4.5
    spaceNL
    sqlNum +6
    spaceNL
    sqlNum +7.03
    spaceNL
    sqlNum -8
    spaceNL
    sqlNum -.9
    spaceNL
    sqlNum 1E2
    spaceNL
    sqlNum -2.E-2
    spaceNL
    sqlNum +.3E+3
    spaceNL
tstScanSqlNum */
    call tst t, 'tstScanSqlNum'
    b = jBuf('1 2. .3 4.5 +6 + --kom', , ' -- com ', , ' 7.03 -8 -  .9',
             '1e2 - 2.e-2 + .3e+3')
    s = jOpen(scanSql(b))
    do sx=1 while \scanAtEnd(s)
        if scanSqlNum(s) then
            call tstOut t, 'sqlNum' m.s.val
        else if scanSpaceNL(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*<<tstScanSqlNumUnit
    ### start tst tstScanSqlNumUnit ###################################
    sqlNumUnit 1 KB
    spaceNL
    sqlNumUnit .3 MB
    sqlNumUnit .5
    sqlNumUnit +6.E-5 B
    spaceNL
    sqlNumUnit -7
    char *
    spaceNL
    sqlNumUnit -.8
    char T
    char B
    spaceNL
    *** err: scanErr scanSqlNumUnit after +9. bad unit TB
    .    e 1: last token Tb scanPosition .
    .    e 2: pos 41 in line 1: 1 kb .3mB.5 +   6.e-5B -7* -.8 TB + 9.T+
    b
    sqlNumUnit +9..
    spaceNL
tstScanSqlNumUnit */
    call tst t, 'tstScanSqlNumUnit'
    b = jBuf('1 kb .3mB.5 +   6.e-5B -7* -.8 TB + 9.Tb')
    s = jOpen(scanSql(b))
    do sx=1 while \scanAtEnd(s)
        if scanSqlNumUnit(s, 0, 'B KB MB') then
            call tstOut t, 'sqlNumUnit' m.s.val
        else if scanSpaceNL(s) then call tstOut t, 'spaceNL'
        else if scanChar(s ,1) then call tstOut t, 'char' m.s.tok
        else call scanErr s, 'cannot scan'
        end
    call tstEnd t
    return
endProcedure tstScanSql

/*--- one single test scan with lines to scan in stem ln -------------*/
tstScan1:
parse arg sc, classs, ln
    if sc == '' then do
        call tstOut t, 'scan src' ln
        call scanSrc scanReset(s), ln
        end
    else do
        call tstOut t, 'scan scanner' sc
        s = sc
        end
    do forever
        x = tstScanType(s, classs)
        if x == '' then
           leave
        call tstOut t, 'scan' x 'tok' length(m.s.tok)':' m.s.tok ,
                  'key' m.s.key 'val' m.s.val
        end
    return
endProcedure tstScan1

tstScanType: procedure expose m.
parse arg s, opt
    cx = 1
    a2 = ''
    res = 0
    do while cx <= length(opt)
        f = substr(opt, cx, 1)
        cx = cx + 1
        if pos(substr(opt, cx, 1), "'""") > 0 then do
            m.tstScanType.src = opt
            m.tstScanType.pos = cx
            call scanString tstScanType
            a2 = m.tstScanType.val
            cx = m.tstScanType.pos
            end
        if      f == 'a' then
            res = scanString(s, "'")
        else if f == 'b' then
            res = scanSpaceNl(s)
        else if f == 'c' then
            res = scanChar(s, a2)
        else if f == 'd' then
            res = scanNat(s, a2)
        else if f == 'k' then
            res = scanKeyValue(s, 'def')
        else if f == 'l' then
            res = scanLit(s, a2)
        else if f == 'q' then
            res = scanString(s, '"')
        else if f == 'v' then
            res = scanVerify(s, a2)
        else if f == 'w' then
            res = scanWord(s)
        else if f == 'y' then
            res = scanVerify(s, a2, 'm')
        if res then
            return f
        end
    return scanType(s)
endProcedure tstScanType

/* copx tstBase end   *************************************************/

/* copx tst begin ****************************************************
      test infrastructure
***********************************************************************/
/*--- migrate all compares to new ones:
        tstCI input compare
        tstCO ouptut migrated compares
        tstCIO inpunt and output -------------------------------------*/
tstCI: procedure expose m.
parse arg m, nm
    m.m.CIO = 0
    signal tstCIwork
tstCIO: procedure expose m.
parse arg m, nm
    m.m.CIO = 1
tstCIwork:
    m.m.name = nm
    m.m.cmp.1 = left('### start tst' nm '', 67, '#')

    do ix=2 to arg()-1
        m.m.cmp.ix = arg(ix+1)
        end
    m.m.cmp.0 = ix-1
    if m.m.CIO then
        call tstCO m
    return

tstCO: procedure expose m.
parse arg m
    call tst2dpSay m.m.name, m'.CMP', 68
    return
/*--- initialise m as tester with name nm
        use inline input nm as compare lines -----------------------*/
tst: procedure expose m.
parse arg m, nm, cmpSt
    call tstIni
    m.m.name = nm
    m.m.inIx  = 0
    m.m.out.0 = 0
    m.m.err   = 0
    m.tst.act = m
    m.tst.tests = m.tst.tests+1
    m.m.trans.0 = 0
    if cmpSt == '' then do
        cmpSt = mCut(t'.CMP', 0)
        call tst4dp cmpSt, mapInline(nm)
        end
    m.m.cmp = cmpSt
    call mAdd mCut(m'.IN', 0), 'tst in line 1 eins ,'    ,
                     , 'tst in line 2 zwei ;   ' ,
                     , 'tst in line 3 drei .schluss.'
    call tstOut m, left('### start tst' nm '', 67, '#')
    call errReset 'h', 'return tstErrHandler(ggTxt)'
    if m.tst.ini.j \== 1 then do
        call outDest 'i', 'call tstOut' quote(m)', msg'
        end
    else do
        call oMutate m, 'Tst'
        m.m.jReading = 1
        m.m.jWriting = 1
        if m.tst.ini.e \== 1 then do
            m.m.oldJin = m.j.jIn
            m.m.oldJOut = m.j.jOut
            m.j.jIn = m
            m.j.jOut = m
            end
        else do
            if m.env.0 <> 1 then
                call tstErr m, 'm.env.0' m.env.0 '<> 1'
            call envPush '<-%' m, '>-%' m
            end
        end
    return m
endProcedure tst

tstEnd: procedure expose m.
parse arg m, opt
    cmp = m.m.cmp
    m.m.jReading = 0
    m.m.jWriting = 0
    m.tst.act = ''
    if m.tst.ini.j == 1 then do
        m.m.jReading = 0
        m.m.jWriting = 0
           if m.tst.ini.e \== 1 then do
            m.j.jIn = m.m.oldJin
            m.j.jOut = m.m.oldJOut
            end
        else do
            if m.j.jIn \== m | m.j.jOut \== m then
                call tstErr m, m.j.jIn '\==' m '|' m.j.jOut '\==' m
            call envPop
            if m.env.0 <> 1 then
                call tstErr m, 'm.env.0' m.env.0 '<> 1'
            end
        end
    if m.m.out.0 \= m.cmp.0 then do
        call tstErr m, 'old' m.cmp.0 'lines \= new' m.m.out.0
        do nx = m.m.out.0 + 1 to min(m.m.out.0+10, m.cmp.0)
            say 'old -  ' m.cmp.nx
            end
        end
    call errReset 'h'
    if m.m.err > 0 then do
        say 'new lines:' (m.m.out.0 - 1)
           call tst2dpSay m.m.name, m'.OUT', 68
        end
    say left('###' m.m.name 'end with' m.m.err 'errors ', 67, '#')
    return
endProcedure tstEnd

tst2dp: procedure expose m.
parse arg st, dp, ml
    dx = m.dp.0
    do sx=1 to m.st.0
        li = m.st.sx
        cx = 1
        do until cx > length(li)
            c = substr(li, cx, 1)
            ou = left('.', strip(c) == '' | pos(c, '.+') > 0)
            ex = min(length(li),cx+ml-length(ou)-2)
            ou = ou || substr(li, cx, ex+1-cx)
            dx = dx + 1
            c = right(ou, 1)
            if ex < length(li) then
                m.dp.dx = ou || '+'
            else if strip(c) == '' | pos(c, '.+') > 0 then
                m.dp.dx = ou || '.'
            else
                m.dp.dx = ou
            cx = ex +1
            end
        end
    m.dp.0 = dx
    return
endProcedure tst2dp

tst2dpSay: procedure expose m.
parse arg name, st, ml
    say '/*<<'name
    call tst2dp st, mCut('TST.TMP', 0), 68
    do nx=1 to m.tst.tmp.0
           say '   ' m.tst.tmp.nx
           end
     say name '*/'
return tst2dpSay

tst4dp: procedure expose m.
parse arg st, dp
    sx = m.st.0
    inData = 0
    data = ''
    do dx=1 to m.dp.0
        li = strip(m.dp.dx)
        if pos(left(li, 1), '.+') > 0 then
         li = substr(li, 2)
        if right(li, 1) == '+' then do
            inData = 1
            data = data || left(li, length(li)-1)
            iterate
            end
        if right(li, 1) == '.' then
            li = left(li, length(li)-1)
        sx = sx + 1
        m.st.sx = data || li
        inData = 0
        data = ''
        end
    m.st.0 = sx
    if inData then
        call err 'end inData'
    return
endProcedure tst4dp

/*--- write to test: say lines and compare them ----------------------*/
tstWrite: procedure expose m.
parse arg m, arg
    call tstOut m, 'jOut:' arg
    return
endProcedure tstWrite

tstOut: procedure expose m.
parse arg m, arg
    do tx=m.m.trans.0 by -1 to 1
        arg = repAll(arg, word(m.m.trans.tx, 1), subword(m.m.trans.tx, 2))
        end
    call mAdd m'.OUT', arg
    nx = m.m.out.0
    cmp = m.m.cmp
    c = m.cmp.nx
    if nx > m.cmp.0 then do
        if nx = m.cmp.0+1 then
            call tstErr m, 'more new Lines' nx
        end
    else if c \== arg then do
        do cx=1 to min(length(c), length(arg)) ,
             while substr(c, cx, 1) == substr(arg, cx, 1)
         end
     msg = 'old line' nx '<> new overnext, firstDiff' cx',',
             'len old' length(c)', new' length(arg)

     if cx > 10 then
         msg = overlay('|', msg, cx-10)
        call tstErr m, msg
        say c
        end
    say arg
    return 0
endProcedure tstOut

tstWriteR: procedure expose m.
parse arg m, var
    if symbol('m.class.o2c.var') \== 'VAR' then
        call tstOut t, m.var
    else do
        oo = outDest('=')
        call outDest 'i', 'call tstOut "'m'", msg'
        call classOut , var, 'tstR: '
        call outDest 'i', oo
        end
    return
endProcedure tstWriteR

tstRead: procedure expose m.
parse arg m, arg
    ix = m.m.inIx + 1
    m.m.inIx = ix
    if ix <= m.m.in.0 then do
        m.arg = m.m.in.ix
        drop m.class.o2c.arg
        call tstOut m, '#jIn' ix'#' m.arg
        return 1
        end
    call tstOut m, '#jIn eof' ix'#'
    return 0
endProcedure tstRead

tstFilename: procedure
parse arg suf, opt
    os = errOS()
    if os == 'TSO' then do
        dsn = dsn2jcl('~tmp.tst.'suf)
        if opt = 'r' then do
            if sysDsn("'"dsn"'") \== 'DATASET NOT FOUND' then
                call adrTso "delete '"dsn"'"
            end
            call csiOpen 'TST.CSI', dsn'.**'
            do while csiNext('TST.CSI', 'TST.FINA')
                say 'deleting csiNext' m.tst.fina
                call adrTso "delete '"m.tst.fina"'"
                end
            end
        return dsn
        end
    else if os == 'LINUX' then do
        if abbrev(suf, '/') then
            fn = suf
        else
            fn = .Stream$mc$new('~/tmp/tst/'suf)$mc$qualify /* full path */
        if opt \= 'r' then
            nop
        else if sysIsFile(fn) then
            call sysFileDelete fn
        else if sysIsFileDirectory(fn) then
            call adrSh 'rm -r' fn
        return fn
        end
    else
        call err 'tstFilename does not implement os' os
endProcedure tstFilename

/*--- say total errors and fail if not zero --------------------------*/
tstTotal: procedure expose m.
    say '######'
    say '######'
    say '######' m.tst.tests 'tests with' ,
                 m.tst.err 'errors in' m.tst.errNames
    say '######'
    say '######'
    if m.tst.err \== 0 then
        call err m.tst.err 'errors total'
    return
endProcedure tstTotal

/*--- test err: message, count it and continue -----------------------*/
tstErr: procedure expose m.
parse arg m, msg
    say '### error' msg
    m.m.err = m.m.err + 1
    m.tst.err = m.tst.err + 1
    nm = m.m.name
    if wordPos(nm, m.tst.errNames) < 1 then
        m.tst.errNames = m.tst.errNames nm
    return
endProcedure tstErr

/*--- tstErrHandler: intercept errors --------------------------------*/
tstErrHandler: procedure expose m.
parse arg ggTxt
    if m.tst.act == '' then
        call err ggTxt
    m.tstErrHandler.0 = 0
    oo = outDest('=')
    call outDest 's', tstErrHandler
    call errSay ggTxt
    call outDest 'i', oo
    call tstOut m.tst.act, '*** err:' m.tstErrHandler.1
        do x=2 to m.tstErrHandler.0
            call tstOut m.tst.act, '    e' (x-1)':' m.tstErrHandler.x
            end

    return 0
endSubroutine tstErrHandler

/*--- tstIni: global initialization ----------------------------------*/
tstIni: procedure expose m.
    if m.tst.ini \== 1 then do
        m.tst.ini = 1
     call mapIni
         m.tst.err = 0
         m.tst.errNames = ''
         m.tst.tests = 0
         m.tst.act = ''
         end
    if m.tst.ini.j \== 1 & m.j.ini == 1 then do
        m.tst.ini.j = 1
          call classNew 'n Tst u JRW', 'm',
             , "jRead return tstRead(m, var)",
             , "jWrite call tstOut m, line",
             , "jWriteR call tstWriteR m, var"
        end
    if m.tst.ini.e \== 1 & m.env.ini == 1 then do
        m.tst.ini.e = 1
        end
    return
endProcedure tstIni
/* copx tst    end   **************************************************/
/* copx tstData begin *************************************************/
tstData: procedure expose m.
parse arg ty 2 le 'n' nu, l, r, num
    abs = abs(num)
    if nu \== '' then do
        if abs // 5 = 0 then
            return 'null' || (abs % 5 // nu + 1)
        end
    if ty = 'c' then do
        if le = '' then
            le = 8
        le = abs // le + 1
        if r = '' then
            r = '+'
        return left(l || num || r, le, right(r, 1))
        end
    if pos(ty, 'ief') < 1 then
        call err 'bad type' ty
    nn = abs
    if abbrev(num, '-') | abbrev(num, '+') then
        parse var num si 2 nn
    else
        si = ''
    if ty == 'e' then
        ex = 'e' || left('-', abs // 2) || (abs // 15)
    else
        ex = ''
    if le \== '' then
        nn = right(nn, abs // max(1, le - length(si||ex)) + 1, 1)
    if ty \== 'i' & (abs // 4) \= 0 & length(nn) > 1 then
            nn = overlay('.', nn, length(nn) - abs // length(nn))
     return si || nn || ex
endProcedure tstData

tstDataClassFo: procedure expose m.
parse arg flds
    ty = ''
    do fx=1 by 2 to words(flds)
        if word(flds, fx) = '.' then
            ty = ty', v'
        else
            ty = ty', f' word(flds, fx) 'v,'
        end
    t = classNew('n tstData* u' substr(ty, 2))
    fo = oNew(m.t.name)
    fs = oFlds(fo)
    do fx=1 to m.fs.0
        f = fo || m.fs.fx
        m.f = word(flds, 2*fx)
        end
    return fo
endProcedure tstDataClassFo


tstDataClassOut: procedure expose m.
parse arg flds, f, t
    fo = tstDataClassFo(flds)
    fs = oFlds(fo)
    do x=f to t
        o = oCopyNew(fo)
        do fx=1 to m.fs.0
            na = substr(m.fs.fx, 2)
            f = o || m.fs.fx
            m.f = tstData(m.f, na, '+'na'+', x)
            end
        call jOuR o
        end
    return
endProcedure tstDataClassOut
/* copx tstData end ***************************************************/
/* copy tstAll end   **************************************************/
/* copy fmt    begin **************************************************/
/*--- format the first arg by the format in the second ---------------*/
fmt: procedure
parse arg v, f 2 l
/* say 'fmt' v',' f l */
    if abbrev('-', f) then
        return v
    else if f == 'l' then
        return left(v, l)
    else if f == 'r' then
        return right(v, l)
    else if f == 'f' then do
        parse value l'.0.0.' with b '.' a '.' e '.'
        return format(v, b, a, e, 0)
        end
    else if f == 'e' then do
        parse var l b '.' a '.' e '.'
        if b == '' then b = 2
        if a == '' then a = 2
        if e == '' then e = 2
        res = format(v, b, a, e, 0)
         y = length(res)-e-1
        if substr(res, y) = '' then
            return left(res, y-1)left('E', e+1, 0)
        else if substr(res, y+1, 1) == '+' then
            return left(res, y)substr(res, y+2)
        else if substr(res, y+2, 1) == '0' then
            return left(res, y+1)substr(res, y+3)
        else
            call err 'formatoverflow' f || l 'for' v '-->' res
        end
     else if f = 's' then
        if l == '' then
            return strip(v, 't')
        else
            return strip(v, l)
    else if f == 'w' then do
        parse var l st ',' le
        return substr(v, st, le)
        end
    else
        call err 'bad format' f l 'for' v
endProcedure fmt

/*--- format special (title, null Value also for numbers) ------------*/
fmtS: procedure expose m.
parse arg v, ty 2 l
    if ty == 'f' then do
        if \ dataType(v, 'n') then do
            parse value l'.0.0.' with b '.' a '.' e '.'
            return right(v, b + a + (a \== 0) + e + 2 * (e > 0))
            end
        end
    else if ty == 'e' then do
        if \ dataType(v, 'n') then do
            parse var l b '.' a '.' e '.'
            if b == '' then b = 2
            if a == '' then a = 2
            if e == '' then e = 2
            return right(v, b + a + (a \== 0) + e + (e > 0))
            end
        end
    return fmt(v,  ty || l)
endProcedure fmtS
/* copy fmt    end   **************************************************/
/* copy fmtF   begin **************************************************/
fmtFCsvAll: procedure expose m.
parse arg fSep
    if fSep = '' then
        fSep = ','
    if \ jIn(i) then
        return
    f = oFlds(i)
    li = ''
    do fx=1 to m.f.0
        li = li',' substr(m.f.fx, 2)
        end
    call jout substr(li, 3)
    do until \ jIn(i)
        li = ''
        do fx=1 to m.f.0
            if m.f.fx = '' then do
                li = li',' m.i
                end
            else do
                fld = substr(m.f.fx, 2)
                li = li',' m.i.fld
                end
            end
        call jout substr(li, 3)
        end
    return
endProcedure fmtFCsvAll

fmtFAdd: procedure expose m.
parse arg m
    fx = m.m.0
    do ax=2 to arg()
        fx = fx + 1
        parse value arg(ax) with m.m.fx.fld m.m.fx.fmt m.m.fx.tit
        end
    m.m.0 = fx
    return m
endProcedure fmtFAdd

fmtFAddFlds: procedure expose m.
parse arg m, st
    fx = m.m.0
    do sx=1 to m.st.0
        fx = fx + 1
        parse value m.st.sx with m.m.fx.fld m.m.fx.fmt m.m.fx.tit
        end
    m.m.0 = fx
    return m
endProcedure fmtFAddFlds

fmtF: procedure expose m.
parse arg m, st
    if arg() >= 3 then
        mid = arg(3)
    else
        mid = ' '
    li = ''
    do fx=1 to m.m.0
        f = st || m.m.fx.fld
        li = li || mid || fmtS(m.f, m.m.fx.fmt)
        end
    return substr(li, 1 + length(mid))
endProcedure fmtF

fmtFReset: procedure expose m.
parse arg m
    m.m.0 = 0
    return m
endProcedure fmtFReset

fmtFWriteAll: procedure expose m.
parse arg m, optRdr, wiTi
    b = env2buf(optRdr)
    st = b'.BUF'
    if m.st.0 < 1 then
        return
    if m.m.0 < 1 then
        call fmtFAddFlds m, oFlds(st'.1')
    call fmtFDetect m, st
    if wiTi \== 0 then
        call jOut fmtFTitle(m)
    do sx=1 to m.st.0
        call jOut fmtF(m, st'.'sx)
        end
    return
fmtFWriteAll

fmtFTitle: procedure expose m.
parse arg m
    if arg() >= 2 then
        mid = arg(2)
    else
        mid = ' '
    li = ''
    do fx=1 to m.m.0
        if m.m.fx.tit \= '' then
            t = m.m.fx.tit
        else if m.m.fx.fld = '' then
            t = '='
        else
            t = substr(m.m.fx.fld, 1+abbrev(m.m.fx.fld, '.'))
        li = li || mid || fmtS(t, m.m.fx.fmt)
        end
    return substr(li, 1 + length(mid))

    res = ''
    fs = m.form.FLDS
    do ix=1 to m.fs.0
        res = res fmtS(m.fs.ix, m.form.ix)
        end
    return substr(res, 2)
endProcedure fmtFldTitle


fmtFldTitle: procedure expose m.
parse arg form
    res = ''
    fs = m.form.FLDS
    do ix=1 to m.fs.0
        res = res fmtS(m.fs.ix, m.form.ix)
        end
    return substr(res, 2)
endProcedure fmtFldTitle

fmtFld: procedure expose m.
parse arg form, st
    res = ''
    fs = m.form.FLDS
    do ix=1 to m.fs.0
        f = m.fs.ix
        res = res fmt(m.st.f, m.form.ix)
        end
    return substr(res, 2)
endProcedure fmtData

fmtFldSquash: procedure expose m.
parse arg newFo, class, src
    fs = oFlds(class)
    do fx = 1 to m.fs.0
        fd = m.fs.fx
        lMi = 9e9
        lMa = 0
        rMi = 9e9
        rMa = 0
        len = 0
        do sx = 1 to m.src.0
            x = verify(m.src.sx.fd, ' ', 'n')
            if x < 1 then
                iterate
            lMi = min(lMi, x)
            lMa = max(lMa, x)
            x = length(strip(m.src.sx.fd, 't'))
            rMi = min(rMi, x)
            rMa = max(rMa, x)
            end
        if rMa = 0  then
            m.newFo.fx = 'w1,1'len
        else
            m.newFo.fx = 'w'lMi',' || (rMa+1-lMi)
        end
    m.newFo.0 = m.fs.0
    m.newFo.flds = fs
    return newFo
endProcedure fmtFldSquash

fmtFDetect: procedure expose m.
parse arg m, st
    do fx=1 to m.m.0
        if m.m.fx.fmt = '' then
            m.m.fx.fmt = fmtFDetect1(st, m.m.fx.fld)
        end
    return m
endProcedure fmtDetect

fmtFDetect1: procedure expose m.
parse arg st, suf
    aMa = -1
    aCnt = 0
    aDiv = 0
    nCnt = 0
    nMi = ''
    nMa = ''
    nDi = -1
    nBe = -1
    nAf = -1
    eMi = ''
    eMa = ''
    do sx=1 to m.st.0
        f = st'.'sx || suf
        v = m.f
        aMa = max(aMa, length(v))
        if \ dataType(v, 'n') then do
            aCnt = aCnt + 1
            if aDiv <=3 then
                if aDiv.v \== 1 then do
                    aDiv.v = 1
                    aDiv = aDiv + 1
                    end
            iterate
            end
        nCnt = nCnt + 1
        if nMi == '' then
            nMi = v
        else
            nMi = min(nMi, v)
        if nMa == '' then
            nMa = v
        else
            nMa = max(nMa, v)
        parse upper var v man 'E' exp
        if exp \== '' then do
            en = substr(format(v, 2, 2, 9, 0), 7)
            if en = '' then
                en = exp
            if eMi == '' then
                eMi = en
            else
                eMi = min(eMi, en)
            if eMa == '' then
                eMa = en
            else
                eMa = max(eMa, en)
            end
        parse upper var man be '.' af
        nBe = max(nBe, length(be))
        nAf = max(nAf, length(af))
        nDi = max(nDi, length(be || af))
        end
/*  say 'suf' suf aCnt 'a len' aMa 'div' aDiv
    say '   ' nCnt 'n' nMi'-'nMa 'be' nBe 'af' nAf 'di' nDi 'ex' eMi'-'eMa
*/  if nCnt = 0 | aDiv > 3 then
        newFo = 'l'max(0, aMa)
    else if eMi \== '' then do
        eMa = max(eMa, substr(format(nMa, 2, 2, 9, 0), 7))
        newFo = 'e' || (1+(eMi < 0)) || '.' || (max(0, nDi-1))'.' ,
            || max(length(eMa+0), length(eMi+0))
        end
    else if nAf > 0 then
        newFo ='f'nBe'.'nAf
    else
        newFo ='f'nBe'.0'
/*  say '   ' newFo
*/ return newFo
endProcedure fmtFDetect1

fmtFldRW: procedure expose m.
parse arg fo
    ty = oGetClassPara(m.j.jIn)
    call assert 'oFlds(ty) == m.fo.flds', 'fo different flds than class'
    call jOut fmtFldTitle(fo)
    do while jIn(ii)
        call jOut fmtFld(fo, ii)
        end
    return
endProcedure fmtClassRW

fmtFldSquashRW: procedure expose m.
parse arg in, opCl
    if in = '' then
        in = m.j.jIn
    if opCl == 'opCl' then
        call jOpen in, 'r'
    ty = oGetClassPara(in)
    flds = oFlds(ty)
    st = 'FMT.CLASSAD'
    do ix=1 while jRead(in, st'.'ix)
        end
    m.st.0 = ix - 1
    fo = fmtFldSquash(sqFo, ty, st)
    call jOut fmtFldTitle(fo)
    do ix = 1 to m.st.0
        call jOut fmtFld(fo, st'.'ix)
        end
    if opCl == 'opCl' then
        call jClose in
    return
endProcedure fmtFldSquashRW
/* copy fmtF  end  * **************************************************/
/* copy sort begin ****************************************************/
sort: procedure expose m.
parse arg i, o, cmp
    if cmp == '' then
        m.sort.comparator = "cmp = m.l.l0 <<= m.r.r0"
    else if length(cmp) < 6 then
        m.sort.comparator = "cmp = m.l.l0" cmp "m.r.r0"
    else if pos(';', cmp) < 1 then
        m.sort.comparator = "aLe = l'.'l0; aRi = r'.'r0; cmp =" cmp
    else
        m.sort.comparator = "aLe = l'.'l0; aRi = r'.'r0;" cmp
    call sort1 i, 1, m.i.0, o, 1, sort.work, 1
    m.o.0 = m.i.0
    return
endProcedure sort

sort1: procedure expose m.
parse arg i, i0, le, o, o0, w, w0
    if le <= 3 then do
        if le = 3 then do
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, w, w0
            call sortMerge i, i0+2, i0+3, w, w0, w0+2, o, o0
            end
        else if le = 2 then
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, o, o0
        else if le = 1 then
            m.o.o0 = m.i.i0
        return
        end
    h = (le + 1) % 2
    call sort1 i, i0,   h,    o, o0+le-h, w, w0
    call sort1 i, i0+h, le-h, w, w0,      o, o0
    call sortMerge o, o0+le-h, o0+le, w, w0, w0+le-h, o, o0
    return
endProcedure sort1

sortMerge: procedure expose m.
parse arg l, l0, le, r, r0, re, o, o0
    do while l0 < le & r0 < re
        interpret m.sort.comparator
        if cmp then do
            m.o.o0 = m.l.l0
            l0 = l0 + 1
            end
        else do
            m.o.o0 = m.r.r0
            r0 = r0 + 1
            end
        o0 = o0 + 1
        end
    do while l0 < le
        m.o.o0 = m.l.l0
        l0 = l0 + 1
        o0 = o0 + 1
        end
    do while r0 < re
        m.o.o0 = m.r.r0
        r0 = r0 + 1
        o0 = o0 + 1
        end
    return
endProcedure sortMerge
/* copy sort end   ****************************************************/
/* 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 comp begin *****************************************************
    the shell compiler
         syntax and semantics of the shell language see javaDoc
***********************************************************************/
/*--- module initialisation ------------------------------------------*/
compIni: procedure expose m.
    if m.compIni = 1 then
        return
    m.compIni = 1
    call envIni
    call scanReadIni
    cc = classNew('n Compiler u')
    return
endProcedure compIni

/*--- constructor of Compiler ----------------------------------------*/
comp: procedure expose m.
parse arg src
    nn = oNew('Compiler')
    m.nn.scan = jOpen(scanRead(src))
    return compReset(nn, src)
endProcedure comp

compReset: procedure expose m.
parse arg m, src
    call scanReadReset m.m.scan, src, , ,'$*'
    m.m.chDol = '$'
    m.m.chSpa = ' '
    m.m.chNotWord = '${}=%:' || m.m.chSpa
    m.m.stack = 0
    return m
endProceduere compReset

/*--- push an empty stem on the stack --------------------------------*/
compPushStem: procedure expose m.
parse arg m
    m.m.stack = m.m.stack + 1
    pp = m'.STACK'm.m.stack
    m.pp.0 = 0
    return pp
endProcedure compPushStem

/*--- push a stem from the stack -------------------------------------*/
compPop: procedure expose m.
parse arg m, pp
    if pp \== m'.STACK'm.m.stack then
        call err 'compPop pp' pp 'mismatch to' m'.STACK'm.m.stack
    m.m.stack = m.m.stack - 1
    return m
endProcedure compPop

/*--- compile the source and return an oRunner with the code ---------*/
compile: procedure expose m.
parse arg m, type
    if type == 's' then do
        what = "shell"
        expec = "pipe or $;";
        call compSpNlComment m
        src = compShell(m)
        end
    else if type == 'd' then do
        what = "data";
        expec = "sExpression or block";
        src = compData(m, 0)
        end
    else do
       call err "bad type" type
       end
    if \ scanAtEnd(m.m.scan) then
       call scanErr m.m.scan, expec  "expected: compile" what ,
                               " stopped before end of input"
    call jClose m.m.scan
    r = oRunner(src)
    return r
endProcedure compile

/*--- compile data lines return stmts or expr ------------------------*/
compData: procedure expose m.
parse arg m, makeExpr
    s = m.m.scan
    exprs = compPushStem(m)
    do forever
        aftEol = 0
        do forever
            text = "";
            do forever
                if scanVerify(s, m.m.chDol, 'm') then
                    text = text || m.s.tok
                if \ compComment(m) then
                    leave
                end
            nd = compExpr(m, 'd')
            befEol = scanReadNL(s)
            if nd <> '' | (aftEol & befEol) ,
                     | verify(text, m.m.chSpa) > 0 then do
                if text \== '' then
                    text = quote(text)
                if text \== '' & nd \= '' then
                    text = text '|| '
                call mAdd exprs, 'e' compNull2EE(text || nd)
                end
            if \ befEol then
                 leave
            aftEol = 1
            end
        one = compStmt(m)
        if one == '' then
            one = compRedirIO(m, 0)
        if one == '' then
            leave
        call mAdd exprs, 's' one
        end
    if m.exprs.0 < 1 then do
        if makeExpr then
            res = '""'
        else
            res = ';'
        end
    else do
        do x=1 to m.exprs.0 while left(m.exprs.x, 1) = 'e'
            end
        res = ''
        if makeExpr & x > m.exprs.0 then do
            res = substr(m.exprs.1, 3)
            do x=2 to m.exprs.0
                res = res substr(m.exprs.x, 3)
                end
            end
        else do
            do x=1 to m.exprs.0
                if left(m.exprs.x, 1) = 'e' then
                    res = res 'call jOut'
                res = res substr(m.exprs.x, 3)';'
                end
            if makeExpr then
                res = "mCat("compStmts2ExprBuf(res)"'.BUF', ' ')"
            end
        end
    call compPop m, exprs
    return res
endProcedure compData

/*--- compile shell and return code ----------------------------------*/
compShell: procedure expose m.
parse arg m
    res = ''
    do forever
        one = compPipe(m)
        if one \== '' then
            res = res one
        if \ scanLit(m.m.scan, '$;') then
            return strip(res)
        call compSpNlComment m
        end
endProcedure compShell

/*--- compile an expression, type d=data, w=word, s=strip ------------*/
compExpr: procedure expose m.
parse arg m, type
    res = ''
    if type == 'w' then
        charsNot = m.m.chNotWord
    else
        charsNot = m.m.chDol
    s = m.m.scan
    if pos(type, 'sw') > 0 then
        call compSpComment m
    do forever
        txt = ''
        do forever
            if scanVerify(s, charsNot, 'm') then
                txt = txt || m.s.tok
            if \ compComment(m) then
                leave
            end
        pr = compPrimary(m)
        if pr = '' & pos(type, 'sw') > 0 then
            txt = strip(txt, 't')
        if txt \== '' then
            res = res '||' quote(txt)
        if pr = '' then do
            if pos(type, 'sw') > 0 then
                call compSpComment m
            if res == '' then
                return ''
            return substr(res, 5)
            end
        res = res '||' pr
        end
    return ''
endProcedure compExpr

/*--- push an empty stem on the stack --------------------------------*/
compPushStem: procedure expose m.
parse arg m
    m.m.stack = m.m.stack + 1
    pp = m'.STACK'm.m.stack
    m.pp.0 = 0
    return pp
endProcedure compPushStem

/*--- push a stem from the stack -------------------------------------*/
compPop: procedure expose m.
parse arg m, pp
    if pp \== m'.STACK'm.m.stack then
        call err 'compPop pp' pp 'mismatch to' m'.STACK'm.m.stack
    m.m.stack = m.m.stack - 1
    return m
endProcedure compPop

/*--- compile a primary and return code ------------------------------*/
compPrimary: procedure expose m.
parse arg m
    s = m.m.scan
    if \ scanLit(s, '$') then
        return ''
    if scanString(s) then
        return m.s.tok
    if scanLit(s, '(') then do
        one = compCheckNN(m, compLang(m, 0), 'rexx expexted after $(')
        if \ scanLit(s, '$)') then
            call scanErr s, 'closing $) missing after $(...'
        return '('one')'
        end
    if scanLit(s, '-¢') then do
        res  = compData(m, 1)
        if \scanLit(s, '$!') then
            call scanErr s, 'closing $! missing after $-¢ data'
        return res
        end
    if scanLit(s, '-{') then do
        res  = compShell(m)
        if \scanLit(s, '$}') then
            call scanErr s, 'closing $} missing after $-{ shell'
        return "mCat("compStmts2ExprBuf(res)"'.BUF', ' ')"
        end
    if scanLit(s, '-cmpShell', '-cmpData') then do
        return 'compile(comp(env2Buf()),' ,
               '"'substr('ds', 1+(m.s.tok == '-cmpShell'), 1)'")'
        end
    if scanLit(s, '{') then do
        if scanLit(s, '?') then
            f = 'envIsDefined'
        else if scanLit(s, '>') then
            f = 'envRead'
        else
            f = 'envGet'
        nm = compExpr(m, 'w')
        if \scanLit(s, '}') then
            call scanErr s, 'closing } missing after ${'
        return f'('nm')'
        end
    if scanName(s) then
        return 'envGet('quote(m.s.tok)')'
    call scanBack s, '$'
    return ''
endProcedure compPrimary

/*--- compile a pipe and return code ---------------------------------*/
compPipe: procedure expose m.
parse arg m
    s = m.m.scan
    ios = ''
    stmts = ''
    stmtLast = ''
    do forever
        io1 = compRedirIO(m, 1)
        if io1 \== '' then do
            ios = ios',' io1
            call compSpNlComment m
            end
        else do
            if stmtLast \== '' then do
                if \ scanLit(s, '$|') then
                    leave
                call compSpNlComment m
                end
            one = compStmts(m)
            if one == '' then do
                if stmtLast \== '' then
                    call scanErr s, 'stmts expected afte $|'
                if ios == '' then
                    return ''
                leave
                end
            if stmtLast \== '' then
                stmts = stmts 'call envBar;' stmtLast
            stmtLast = one
            end
        end
    if stmts \== '' then
        stmtLast = insert('Begin', stmts, pos('envBar;', stmts)+5) ,
                  'call envBarLast;' stmtLast 'call envBarEnd;'
    if ios \== '' then do
        if stmtLast == '' then
            stmtLast = 'call envWriteAll;'
        stmtLast = 'call envPush 'substr(ios, 3)';' stmtLast ,
                   'call envPop;'
        end
    return stmtLast
endProcedure compPipe

/*--- compile an io redirection, return
        if makeExpr then "option", expr
                    else code write inut to standard out -------------*/
compRedirIO: procedure expose m.
parse arg m, makeExpr
    s = m.m.scan
    if \ scanLit(s, '$&', '$<<', '$<', '$>>', '$>') then
        return ''
    opt = substr(m.s.tok, 2)
    call scanVerify s, '+-%#¢{'
    opt = opt || m.s.tok
  /* ????  call compSpComment m */
    if left(opt, 2) \== '<<' then do
        if verify(opt, '¢{', 'm') > 0 ,
                | (left(opt, 1) == '&' & pos('%', opt) > 0) then
            call scanErr s, 'inconsistent io redirection option' opt
        ex = compCheckNN(m, compExpr(m, 's'),
                      , 'expression expected after $'opt)
        end
    else do
        if verify(opt, '-%#', 'm') > 0 then
            call scanErr s, 'inconsistent io redirection option' opt
        if \ scanName(s) then
            call scanErr s, 'stopper expected in heredata after $'opt
        stopper = m.s.tok
        call scanVerify s, m.m.chSpa
        if \ scanReadNl(s) then
            call scanErr s,
                , 'space nl expected in heredata after $'opt||stopper
        buf = jOpen(jBuf(), m.j.cWri)
        do while \ scanLit(s, stopper)
            call jWrite buf, m.s.src
            if \ scanReadNl(s, 1) then
                call scanErr s, 'eof in heredata after $'opt||stopper
            end
        call jClose buf
        if verify(opt, '¢{', 'm') > 0 then do
            if pos('¢', opt) > 0 then
                ex = compile(comp(buf), 'd')
            else
                ex = compile(comp(buf), 's')
            if makeExpr then
                return "'<%' envRun("quote(ex)")"
            else
                return "call oRun" quote(ex)";"
            end
        opt = '<%'
        ex = quote(buf)
        end
    if makeExpr then
        return "'"opt"'" ex
    else if left(opt, 1) = '>' then
        call scanErr s, 'cannot write ioRedir $'opt
    else
        return "call envWriteAll '"opt"'" ex
endProcedure compRedirIO

/*--- compile stmts (stmt or java) -----------------------------------*/
compStmts: procedure expose m.
parse arg m
    res = ''
    do forever
       one = compStmt(m)
       if one == '' then
           one = compLang(m, 1)
       if one == '' then
           return res
       res = res strip(one)
       call compSpNlComment m
       end
endProcedure compStmts

/*--- compile a single statement -------------------------------------*/
compStmt: procedure expose m.
parse arg m
    s = m.m.scan
    if scanLit(s, "$=") then do
        nm = compCheckNN(m, compExpr(m, 'w'), "variable name")
        if scanLit(s, "=") then
            vl = compExpr(m, 's')
        else if scanLit(s, "%") then
            vl = compCheckNN(m, compLang(m, 0),
                   , 'java expression after $= .. %')
        else
            call scanErr s, '= or % expected after $= name'
        return 'call envPut' nm',' vl';'
        end
    else if scanLit(s, '$@{') then do
        call compSpNlComment m
        one = compShell(m)
        if \ scanLit(s, "$}") then
            call scanErr s, "closing $} missing for $@{ shell"
        return "do;" one "end;"
        end
    else if scanLit(s, '$@¢') then do
        call compSpNlComment m
        one = compData(m, 0)
        if \ scanLit(s, "$!") then
            call scanErr s, "closing $! missing for $@! data"
        return "do;" one "end;"
        end
    else if scanLit(s, '$$') then do
        return 'call jOut' compExpr(m, 's')';'
        end
    else if scanLit(s, '$%') then do
        return 'call jOut' compCheckNN(m, compLang(m, 0),
                  , 'language expression after $%')';'
        end
    else if scanLit(s, '$@for') then do
        v = compCheckNN(m, compExpr(m, 'w') ,
               , "variable name after $@for")
        call compSpNlComment m
        return 'do while envRead('v');',
             compCheckNN(m, compStmt(m),
                 , "statement after $@for variable") 'end;'
        end
    else if scanLit(s, '$@run') then do
        return 'call oRun' compCheckNN(m, compExpr(m, 's'),
                 , 'expression after $@run') ';'
        end
    return ''
endProcedure compStmt

/*--- compile a language clause
           multi=0 a single line for a rexx expression
           multi=1 mulitple lines for rexx statements
                 (with rexx line contiunation) -----------------------*/
compLang: procedure expose m.
parse arg m, multi
    s = m.m.scan
    res = ''
    do forever
       if scanVerify(s, m.m.chDol, 'm') then do
           res = res || m.s.tok
           end
       else do
           one = compPrimary(m)
           if one \== '' then
               res = res || one
           else if compComment(m) then
               res = res || ' '
           else if \multi then
               return res
           else if \ scanReadNl(s) then do
               if res == '' then
                   return res
               else
                   return strip(res)';'
               end
           else do
               res = strip(res)
               if right(res, 1) = ',' then
                   res = strip(left(res, length(res)-1))
               else
                   res = res';'
               end
           end
       end
endProcedure compLang

/*--- convert stmts to an expression yielding the output ------------*/
compStmts2ExprBuf: procedure expose m.
parse arg stmts
    rr = oRunner(stmts)
    return "envRun('"rr"')"
endProcedure compStmts2ExprBuf

/*--- convert '' to an empty expression ------------------------------*/
compNull2EE: procedure
parse arg e
    if e = '' then
        return '""'
    return e
endProcedure compNull2EE

/*--- if va == '' then issue an error with msg -----------------------*/
compCheckNN: procedure expose m.
parse arg m, va, msg
    if va == '' then
        call scanErr m.m.scan, msg 'expected'
    return va
endProcedure compCheckNN

/*--- skip a comment. return 0 if there is none ----------------------*/
compComment: procedure expose m.
parse arg m
    s = m.m.scan
    if scanLit(s, '$**') then
        m.s.pos = 1 + length(m.s.src) /* before next nl */
    else if scanLit(s, '$*+') then
        call scanReadNl s, 1
    else if scanLit(s, '$*(') then do
        do forever
            if scanVerify(s, m.m.chDol, 'm') then iterate
            if scanReadNl(s) then iterate
            if compComment(m) then iterate
            if \ scanLit(s, '$') then
                call scanErr s, 'source end in comment'
            if scanLit(s, '*)') then
                return 1
            if scanLit(s, '$') then iterate
            if scanString(s) then iterate
            end
        end
    else
        return 0
    return 1
endProcedure compComment

/*--- skip spaces and comments ---------------------------------------*/
compSpComment: procedure expose m.
parse arg m
    found = 0
    do forever
        if scanVerify(m.m.scan, m.m.chSpa) then
            found = 1
        else if compComment(m) then
            found = 1
        else
            return found
        end
endProcedure compSpComment

/*--- skip spaces, NLs and comments ----------------------------------*/
compSpNlComment: procedure expose m.
parse arg m
    found = 0
    do forever
        if compSpComment(m) then
            found = 1
        else if scanReadNl(m.m.scan) then
            found = 1
        else
            return found
        end
endProcedure compSpComment
/* copy comp end ******************************************************/
/* copy scan begin ****************************************************
Scan: scan an input:
    scanLine(m,ln) : begin scanning a single line (string)
    scanRead??(m,ln): begin scanning all lines of an opened reader
    scanAtEnd(m)   : returns whether we reached end of input
    scanLit(m,lit) : scan Literal lit if present or return 0
    scanChar(m,n)  : scan next n characters
    scanName(m)    : scan a name
    ScanNat(m)     : scan a natural number (without sign)
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanKeyValue(m): scan a key = value clause (with spaces)
    scanWord(m,u)  : scan a space delimited word or a string,
                          if u=1 then uppercase non-strings
    scanErr(m, txt): error with current scan location

    m is an adress, to store our state
    if a scan function succeeds, the scan posititon is moved

returns: true if scanned, false otherwise
         m.m.tok  ==> last token
         m.m.val  ==> last value for scanString/Word/KeyValue
         m.key    ==> key for scanKeyValue
         m.m.pos ==> scan position
         m.m.src ==> scan source
***********************************************************************/
scanIni: procedure expose m.
    if m.scan.ini == 1 then
        return
    m.scan.ini = 1
    m.scan.alfLC = 'abcdefghijklmnopqrstuvwxyz'
    m.scan.alfUC = translate(m.scan.alfLC)
    m.scan.alfa = m.scan.alfLC || m.scan.alfUC
    m.scan.alfNum = m.scan.alfa || '0123456789'
    return
endProcedure scanIni

scanReset: procedure expose m.
parse arg m, n1, np, co
    m.m.rdr = ''
    m.m.jReading = 0 /* if called without jReset */
    m.m.jWriting = 0
    return scanOpts(m, n1, np, co)
endProcedure scanReset

scanOpts: procedure expose m.
parse arg m, m.m.scanName1, namePlus, m.m.scanComment
    if m.m.scanName1 == '' then
        m.m.scanName1 = m.scan.alfa
    if namePlus == '' then
        m.m.scanNameR = m.m.scanName1 || '0123456789'
    else
        m.m.scanNameR = m.m.scanName1 || namePlus
    return m
endProcedure scanOpts


/*--- begin scanning a single line -----------------------------------*/
scanSrc: procedure expose m.
parse arg m, m.m.src
    return scanOpen(m)
endProcedure scanSrc

scanOpen: procedure expose m.
parse arg m
    m.m.tok = ''
    m.m.val = ''
    m.m.key = ''
    m.m.pos = 1
    m.m.atEnd = m.m.rdr == ''
    m.m.jReading = 1
    return m
endProcedure scanOpen

/*--- return the next len characters ---------------------------------*/
scanLook: procedure expose m.
parse arg m, len
    if len == '' then
        return substr(m.m.src, m.m.pos)
    else
        return substr(m.m.src, m.m.pos,
                     , min(len, 1 + length(m.m.src) - m.m.pos))
endProcedure scanLook

/*--- scan the literal lit ------------------------------------------*/
scanLit: procedure expose m.
parse arg m
    do ax=2 to arg()
        if abbrev(substr(m.m.src, m.m.pos), arg(ax)) then do
            m.m.tok = arg(ax)
            m.m.pos = m.m.pos + length(arg(ax))
            return 1
            end
        end
    m.m.tok = ''
    return 0
endProcedure scanLit

/*--- scan the next len characters -----------------------------------*/
scanChar: procedure expose m.
parse arg m, len
    nx = 1 + length(m.m.src)
    if len \= '' then
        nx = min(m.m.pos + len, nx)
    m.m.tok = substr(m.m.src, m.m.pos, nx - m.m.pos)
    m.m.pos = nx
    return m.m.tok \== ''
endProcedure scanChar

/*--- scan a string with quote char qu -------------------------------*/
scanString: procedure expose m.
parse arg m, prefs
    m.m.tok = ''
    bx = m.m.pos
    if prefs = '' then do
        qu = substr(m.m.src, bx, 1)
        if pos(qu, "'""") < 1 then
            return 0
        ax = bx + 1
        end
    else do
        do px=1 until abbrev(substr(m.m.src, bx), p1)
            p1 = word(prefs, px)
            if p1 = '' then
                return 0
            end
        qu = right(p1, 1)
        ax = bx + length(p1)
        end
    m.m.val = ''
    do forever
        qx = pos(qu, m.m.src, ax)
        if qx < 1 then
            return scanErr(m, 'ending Apostroph('qu') missing')
        m.m.val = m.m.val || substr(m.m.src, ax, qx-ax)
        if qx >= length(m.m.src) then
            leave
        else if substr(m.m.src, qx+1, 1) <> qu then
            leave
        ax = qx+2
        m.m.val = m.m.val || qu
        end
    m.m.tok = substr(m.m.src, bx, qx+1-bx)
    m.m.pos = qx+1
    return 1
endProcedure scanString

/*--- scan a Name, first char in *.scanName1, rest in *.scanNameR ----*/
scanName: procedure expose m.
parse arg m
    if pos(substr(m.m.src, m.m.pos, 1),
                 , m.m.scanName1) <= 0 then do
        m.m.tok = ''
        return 0
        end
    return scanVerify(m, m.m.scanNameR)
endProcedure scanName

/*--- scan with verify, vOpt is passed to verify ---------------------*/
scanVerify: procedure expose m.
parse arg m, alpha, vOpt, onlyIfMatch
    if vOpt == '' then   /* empty string does not take default */
        nx = verify(m.m.src, alpha, , m.m.pos)
    else
        nx = verify(m.m.src, alpha, vOpt, m.m.pos)
    if nx = 0 then
        if onlyIfMatch == 1 then
            nx = m.m.pos
        else
            nx = length(m.m.src) + 1
    m.m.tok = substr(m.m.src, m.m.pos, nx - m.m.pos)
    m.m.pos = nx
    return m.m.tok \== ''
endProcedure scanVerify

/*--- scan a natural number (no sign, decpoint ...) ------------------*/
scanNat: procedure expose m.
parse arg m, chEn
    if \ scanVerify(m, '0123456789') then
        return 0
    if chEn \== 0 then
        if pos(scanLook(m , 1), m.m.scanNameR) > 0 then
            call scanErr m, 'illegal number end after' m.m.tok
    return 1
endProcedure ScanNat

/*--- scan an integer (optional sign, no decpoint ...) ---------------*/
scanInt: procedure expose m.
parse arg m, chEn
    call scanLit m, '+', '-'
    si = m.m.tok
    if \ scanNat(m, chEn) then do
        m.m.pos = m.m.pos - si
        return 0
        end
    m.m.tok = si || m.m.tok
    return 1
endProcedure scanInt

/*--- scan a number (optional sign, decpoint, exponent) ------------*/
scanNum: procedure expose m.
parse arg m, chEn
    sx = m.m.pos
    call scanLit m, '+', '-'
    po = scanLit(m, '.')
    if \ scanNat(m, 0) then do
        m.m.pos = sx
        return 0
        end
    if  \ po then
        if scanLit(m, '.') then
            call scanNat m, 0
       if scanLit(m, 'e', 'E') then
           if \ scanInt(m, 0) then
               call scanErr 'exponent expected after' ,
                   substr(m.m.src, sx, m.m.pos-sx)
    m.m.tok = substr(m.m.src, sx, m.m.pos-sx)
    m.m.val = translate(m.m.tok)
    if chEn \== 0 then
        if pos(scanLook(m , 1), m.m.scanNameR) > 0 then
            call scanErr m, 'illegal number end after' m.m.tok
    return 1
endProcedure scanNum

/*--- scan a word and put value into *.val
           a word is either delimited by space or stopper
                     or a string (with single or double quotes -------*/
scanWord: procedure expose m.
parse arg m, stopper
    if scanString(m)                   then return 1
    if \scanVerify(m, ' 'stopper, 'm') then return 0
    m.m.val = m.m.tok
    return 1
endProcedure scanWord

scanType: procedure expose m.
parse arg m, opt
    m.m.tok = ''
    if scanName(m) then
        m.m.type = 'n'
    else if scanNum(m) then
        m.m.type = 0
    else if scanString(m) then
        m.m.type = left(m.m.tok, 1)
    else if scanSpaceNl(m) then
        m.m.type = 's'
    else do
        call scanChar m, 1
        m.m.type = m.m.tok
        end
    return m.m.type
endProcedure scanType

scanBack: procedure expose m.
parse arg m, tok
    if m.m.pos <= length(tok) then
        call scanErr sc, 'cannot back "'tok'" length'
    cx = m.m.pos - length(tok)
    if substr(m.m.src, cx, length(tok)) \== tok then
        call scanErr sc, 'cannot back "'tok'" value'
    m.m.pos = cx
    return
endProcedure scanBack

/*--- scan a key = word phrase
          put key into m.key and word into m.m.val -------*/
scanKeyValue: procedure expose m.
parse arg m, def
    if \ scanName(m) then
        return 0
    m.m.key = m.m.tok
    if \ scanLit(scanSkip(m), '=') then do
        m.m.val = def
        m.m.tok = ' no='
        end
    else if \scanWord(scanSkip(m)) then
        return scanErr(m, 'word expected after' m.m.key '=')
    return 1
endProcedure scanKeyValue

/*--- return true/false whether we are at the end of input ----------*/
scanAtEnd: procedure expose m.
parse arg m
    return m.m.atEnd & m.m.pos > length(m.m.src)
endProcedure scanAtEnd

/*--- skip over spaces, nl and comments (if option set) --------------*/
scanSpaceNL: procedure expose m.
parse arg m
    lastTok = m.m.tok
    if m.m.rdr \== '' then
        interpret 'res = ' objMet(m, 'scanSpaceNl')
    else
        res = scanSpaceCom(m)
    m.m.tok = lastTok
    return res
endProcedure scanSpaceNL

scanSpaceCom: procedure expose m.
parse arg m
    res = scanVerify(m, ' ')
    if m.m.scanComment \== '' then
        if abbrev(substr(m.m.src, m.m.pos), m.m.scanComment) then do
            m.m.pos = 1 + length(m.m.src)
            return 1
            end
    return res
endProcedure scanSpaceCom

/*--- skip over space, nl and comments and return m -----------------*/
scanSkip: procedure expose m.
parse arg m
    call scanSpaceNl m
    return m
endProcedure scanSkip

/*--- emit an error with current scan pos ----------------------------*/
scanErr: procedure expose m.
parse arg m, txt
    m.m.err.0 = 0
    call err 'scanErr' txt'\n'scanInfo(m, m'.ERR')
    return 0
endProcedure scanErr

scanPos: procedure expose m.
parse arg m
    if m.m.rdr \== '' then
        interpret 'return' objMet(m, 'scanPos')
    else if scanAtEnd(m) then
        return E
    else
        return 1 m.m.pos
endProcedure scanPos

scanInfo: procedure expose m.
parse arg m
    msg = 'last token' m.m.tok 'scanPosition' ,
          strip(left(substr(m.m.src, m.m.pos), 40), 't')
    if m.m.rdr == '' then
        return msg'\npos' m.m.Pos 'in string' strip(m.m.src, 't')
    else
        interpret 'return msg ||' objMet(m, 'scanInfo')
endProcedure scanInfo
/* copy scan end   ****************************************************/

/* copy scanRead begin ************************************************/
scanReadIni: procedure expose m.
    if m.scanRead.ini = 1 then
        return
    m.scanRead.ini = 1
    call scanIni
    call jIni
    ts = classNew('n Scan u f TOK v, f VAL v, f KEY v, f TYPE v')
    call classNew 'n ScanRead u JRW', 'm',
        , 'jReset call scanReadReset m, arg, arg2, arg3',
        , 'jOpen call scanReadOpen m',
        , 'jClose call jClose m.m.rdr',
        , 'jRead call scanType m; call oClaCopy "'ts'", m, var;' ,
            'return m.m.type \== ""',
        , 'scanReadNl return scanReadNlImpl(m, unCond)',
        , 'scanSpaceNl scanReadSpaceNl(m)',
        , 'scanInfo scanReadInfo(m)',
        , 'scanPos scanReadPos(m)'
    return
endProcedure scanReadIni

/*--- begin scanning the lines of a reader ---------------------------*/
scanRead: procedure expose m.
parse arg rdr, n1, np, co
    return scanOpts(oNew('ScanRead', rdr), n1, np, co)

scanReadReset: procedure expose m.
parse arg m, r, n1, np, co
    call scanReset m, n1, np, co
    m.m.rdr = r
    return m
endProcedure scanReadReset

scanReadOpen: procedure expose m.
parse arg m, r, n1, np, co
    call scanOpen m
    m.m.atEnd = 0
    m.m.lineX = 0
    call jOpen m.m.rdr, m.j.cRead
    call scanReadNl m, 1
    return m
endProcedure scanReadOpen

/*--- scan over next newLine
        if unCond \== 1 only if we are already at endOfLine
         return true if we scanned a NL ------------------------------*/
scanReadNl: procedure expose m.
parse arg m, unCond
    interpret objMet(m, 'scanReadNl')
endProcedure scanReadNl

/*--- implementation of scanReadNl ----------------------------------*/
scanReadNLimpl: procedure expose m.
parse arg m, unCond
    if unCond \== 1 then
        if m.m.pos <= length(m.m.src) then
            return 0
    if m.m.atEnd then
        return 0
    m.m.atEnd = \ jRead(m.m.rdr, m'.SRC')
    if m.m.atEnd then do
        m.m.pos = 1 + length(m.m.src)
        end
    else do
        m.m.pos = 1
        m.m.lineX = m.m.lineX + 1
        end
    return \ m.m.atEnd
endProcedure scanReadNLimpl

scanReadSpaceNl: procedure expose m.
parse arg m
    fnd = 0
    do forever
        if scanSpaceCom(m) then
            fnd = 1
        if \ scanReadNl(m) then
             return fnd
        fnd = 1
        end
endProcedure scanReadSpaceNl

scanReadPos: procedure expose m.
parse arg m, msg
    if scanAtEnd(m) then
        return E
    else
        return m.m.lineX m.m.pos
endProcedure scanReadPos

scanReadInfo: procedure expose m.
parse arg m, msg
    if scanAtEnd(m) then
        msg = msg'\natEnd after'
    else
        msg = msg'\npos' m.m.pos 'in'
    return msg 'line' m.m.lineX':' strip(m.m.src, 't')
endProcedure scanReadInfo
/* copy scanRead end **************************************************/
/* copy scanWin begin *************************************************
     scan the the concatenation of the lines of a reader
         any token my be split over several line
         except the end-of-line-comment-token
***********************************************************************/
scanWinIni: procedure expose m.
    if m.scanWin.ini = 1 then
        return
    m.scanWin.ini = 1
    call scanIni
    call jIni
    call classNew 'n ScanWin u JRW', 'm',
        , 'jReset call scanWinReset m, arg, arg2, arg3',
        , 'jOpen call scanWinOpen m ',
        , 'jClose call scanWinClose m ',
        , 'scanReadNl return scanWinNl(m, unCond)',
        , 'scanSpaceNl scanWinSpaceNl(m)',
        , 'scanInfo scanWinInfo(m)',
        , 'scanPos  scanWinPos(m)'
    return
endProcedure scanReadIni

/*--- instanciate a new window scanner, open rdr ---------------------*/
scanWin: procedure expose m.
parse arg rdr, wiSz, wiBa, cuPo, cuLe
    return scanWinOpts(oNew('ScanWin', rdr), wiSz, wiBa, cuPo, cuLe)

/*--- set the reader and attributes of window scanner m -------------*/
scanWinReset: procedure expose m.
parse arg m, r, wiSz, wiGa, cuPo, cuLe
    call scanReset m
    m.m.rdr = r
    m.m.atEnd = 'still closed'
    return scanWinOpts(m, wiSz, wiGa, cuPo, cuLe)
endProcedure scanWinReset

/*--- set the attributes of window scanner m ------------------------*/
scanWinOpts: procedure expose m.
parse arg m, wiSz, wiGa, cuPo, cuLe
    wiSz = word(wiSz 5, 1)
    wiGa = word(wiGa 1, 1)
    m.m.cutPos = word(cuPo 1, 1)
    m.m.cutLen = word(cuLe 72, 1)
    m.m.winTot = (wiSz * 2 + wiGa) * m.m.cutLen
    m.m.posLim = (wiSz     + wiGa) * m.m.cutLen
    m.m.posOff =  wiGa * m.m.cutLen
    return m
endProcedure scanWinReset

/*--- open reader and start scanning --------------------------------*/
scanWinOpen: procedure expose m.
parse arg m, lx
    call scanOpen m
    m.m.atEnd = 0
    if lx = '' then
        m.m.lineX = 1
    else
        m.m.lineX = lx
    m.m.pos = 1
    m.m.src = ''
    call jOpen m.m.rdr, m.j.cRead
    call scanWinRead m
    return m
endProcedure scanWinOpen

scanWinClose: procedure expose m.
    m.m.atEnd = 'still closed'
    call jClose m.m.rdr
    return
endProcedure scanWinClose
/*--- move the source window: cut left side and append at right side
      return number of characters cut at left ------------------------*/
scanWinRead: procedure expose m.
parse arg m
    dlt = 0
    if m.m.atEnd then
        return 0
    if m.m.pos >= m.m.posLim then do     /*  cut left side */
        dlt = m.m.pos - (m.m.pos // m.m.cutLen + m.m.posOff)
        m.m.src = substr(m.m.src, dlt+1)
        m.m.pos = m.m.pos - dlt
        m.m.lineX = m.m.lineX + dlt % m.m.cutLen
        end
    do while length(m.m.src) < m.m.winTot /* read and fill to len */
        if \ jRead(m.m.rdr, m'.'one) then do
            m.m.atEnd = 1
            return dlt
            end
        m.m.src = m.m.src || substr(m.m.one, m.m.cutPos, m.m.cutLen)
        end
    call assert 'length(m.m.src) = m.m.winTot'
    return dlt
endProcedure scanWinRead

/*--- return position of next line start -----------------------------*/
scanWinNLPos: procedure expose m.
parse arg m
    return m.m.pos + m.m.cutLen - ((m.m.pos - 1) // m.m.cutLen)

/*--- scan over spaces and comments ----------------------------------*/
scanWinSpaceNL: procedure expose m.
parse arg m
    res = 0
    do forever
        r1 = 0
        if scanVerify(m, ' ') then do
            r1 = 1
            end
        else if m.m.scanComment \== '' ,
             & abbrev(substr(m.m.src, m.m.pos), m.m.scanComment) then do
            np = scanWinNlPos(m)
            r1 = length(m.m.scanComment) <= np - m.m.pos
            if r1 then
                m.m.pos = np
            end
        if r1 then
            res = 1
        else if scanWinRead(m) = 0 then
            return res
        end
endProcedure scanWinSpaceNl

/*--- return current position in input ------------------------------*/
scanWinPos: procedure expose m.
parse arg m
    if scanAtEnd(m) then
        return 'E'
    else
        ps = m.m.pos - 1
    return (m.m.lineX + (ps % m.m.cutLen)) (ps // m.m.cutLen + 1)
endProcedure scanWinPos

/*--- return a description of the current scan position --------------*/
scanWinInfo: procedure expose m.
parse arg m
    p = scanWinPos(m)
    if p == 'E' then do
        res = 'atEnd after'
        p = m.m.lineX - 1 + length(m.m.src) % m.m.cutLen
        end
    else do
        res = 'pos' word(p, 2) 'in'
        p = word(p, 1)
        end
    return '\n'res 'line' p':' strip(substr(m.m.src,
          , 1 + (p - m.m.lineX) * m.m.cutLen, m.m.cutLen), 't')
endProcedure scanWinInfo
/* copy scanWin end   *************************************************/
/* copy scanSql begin *************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanSql: procedure expose m.
parse arg inRdr
    return scanSqlReset(scanWin(inRdr), inRdr)

scanSqlReset: procedure expose m.
parse arg m, r, scanWin
    if scanWin \== 0 then
        call scanWinOpts m, 5, 2, 1, 72
    else
        m.m.rdr = r
    return scanOpts(m, , '0123456789_' , '--')

scanSqlSeekId: procedure expose m.
parse arg m, lx, cmd, opts
    if opts = '' then
        opts = word
    if adrEdit("cursor =" max(trunc(lx), 1), 12) = 12 then
        return -1
    do while adrEdit("seek" cmd opts, 4) = 0 /* find each command*/
        call adrEdit "(fx) = cursor"
        if m.debug then do
            call adrEdit "(LI) = LINE" fx
            call debug 'scanSqlSeekId after' lx 'found' cmd 'at' fx li
            end
        call editReadReset m.m.rdr, fx
        call scanWinOpen es, fx
        do while word(scanPos(m), 1) <= fx & scanSqlClass(m)
            if m.m.sqlClass = 'i' & m.m.val == cmd then
                return fx
            end
        end
    return -1
endProcedure scanSqlSeekId
/*--- scan a sql token put class in m.sqlclass:
      'i': ordinary identifier   e.g. Name
      'd': delimited identifier  e.g. "Delimited"
      'q': qualified identifier  e.g. abc."efg"
      'u': integer units         e.g. 8G
      'n': number                e.g. -234 or .2e3
      's': string                e.g. 'abc''ef'
      '' : at end
         : any other character   e.g. ;
      ----------------------------------------------------------------*/
scanSqlClass: procedure expose m.
parse arg m, retSpace
    m.m.val = ''
    if scanSpaceNl(m) & retSpace = 1 then do
        m.m.sqlClass = 'b'
        return 1
        end
    c2 = scanLook(m ,2)
    if scanString(m, "' x' X'") then do
        m.m.sqlClass = 's'
        if \abbrev(m.m.tok, "'") then
            m.m.val = x2c(m.m.val)
        end
    else if scanSqlQuId(m) then do
        if m.m.val.0 > 1 then
            m.m.sqlClass = 'q'
        else if abbrev(m.m.tok, '"') then
            m.m.sqlClass = 'd'
        else
            m.m.sqlClass = 'i'
        end
    else if scanSqlNum(m, 0, 1)  then
        m.m.sqlClass = 'n'
    else if scanChar(m, 1) then
        m.m.sqlClass = m.m.tok
    else if scanAtEnd(m) then do
        m.m.sqlClass = ''
        return 0
        end
    else
        call scanErr m, 'cannot scan sql'
    return 1
endProcedure scanSqlClass

scanSqlSkipBrackets: procedure expose m.
parse arg m, br
    if br \== '' then
        nop
    else if scanLit(m, '(') then
        br = 1
    else
        return 0
    do while scanSqlClass(m) & m.m.sqlClass \== ';'
        if m.m.sqlClass = '('        then br = br + 1
        else if m.m.sqlClass \== ')' then iterate
        else if br > 1              then br = br - 1
        else                             return 1
        end
    call scanErr m, '; or eof, but' br 'closing ) expected'
endProcedure skipBrackets
/*--- scan an ordinary sql identifier e.g. abc, ef_12 ----------------*/
scanSqlId: procedure expose m.
parse arg m
    if \ scanName(m) then
        return 0
    m.m.val = translate(m.m.tok)
    return 1
endProcedure scanSqlId

/*--- scan a delimited or ordinay sql identifier ---------------------*/
scanSqlDeId: procedure expose m.
parse arg m
    if scanSqlId(m) then
        return 1
    if \ scanString(m, '"') then
        return 0
    m.m.val = strip(m.m.val, 't')
    return 1
endProcedure scanSqlDeId

/*--- scan a qualified sql identifier --------------------------------*/
scanSqlQuId: procedure expose m.
parse arg m
    res = ''
    rto = ''
    do qx=1
        if \ scanSqlDeId(m) then do
            if qx <> 1 then
                call scanErr m, 'id expected after .'
            return 0
            end
        m.m.val.qx = m.m.val
        res = res'.'m.m.val
        rto = rto'.'m.m.tok
        if \ scanLit(scanSkip(m), '.') then
            leave
        call scanSpaceNl m
        end
    m.m.val.0 = qx
    m.m.val = substr(res, 2)
    m.m.tok = substr(rto, 2)
    return 1
endProcedure scanSqlQuId

/*--- scan a sql number ----------------------------------------------*/
scanSqlNum: procedure expose m.
parse arg m, checkEnd, noSp
    si = ''
    if noSp == 1 then
        call err 'deimplement ???? wk'
    if noSp \== 1 then do
        if scanLit(m, '+', '-') then do
            si = m.m.tok
            call scanSpaceNl m
            ch = scanLook(m, 2)
            if left(ch, 1) == '.' then
                ch = substr(ch, 2)
            if pos(left(ch, 1), '0123456789') < 1 then do
                call scanBack m, si
                m.m.val = ''
                return 0
                end
            end
        end
    res = scanNum(m, checkEnd)
    m.m.val = si || m.m.val
    return res

endProcedure scanSqlNum

/*--- scan a sql number with a unit which may follow without space ---*/
scanSqlNumUnit: procedure expose m.
parse arg m, both, units
    if \ scanSqlNum(m, 0) then
        return 0
    nu = m.m.val
    sp = scanSpaceNl(m)
    if scanSqlId(m) then do
        if units == '' | wordpos(m.m.val, units) > 0 then
            nu = nu m.m.val
        else if both | \ sp then
            call scanErr m, 'scanSqlNumUnit after' nu 'bad unit' m.m.val
        else
            call scanBack m, m.m.tok
        end
    else if both then
        call scanErr m, 'scanSqlNumUnit no unit after' nu
    else if \sp & pos(scanLook(m, 1), m.m.scanNameR) > 0 then
        call scanErr m, 'scanSqlNumUnit bad number end after' nu
    m.m.val = nu
    return 1
endProcedure scanSqlNumUnit

/* copy scanSql end   *************************************************/
/* copy scanUtil begin *************************************************
    scan db2 utility input statements using scan and a reader
**********************************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanUtilReader: procedure expose m.
parse arg m, inRdr
    call scanReader m, inRdr
    call scanOptions sc, , , '--'
    call scanUtilReset m
    return m
endProcedure scanUtilReader

scanUtilSql: procedure expose m.
parse arg inRdr
    m = scanSql(inRdr)
    call scanUtilReset m
    return m
endProcedure scanUtilReader

scanUtilReset: procedure expose m.
parse arg m
    m.m.utilBrackets = 0
    m.scanUtil =  'BACKUP CATENFM CATMAINT CHECK' ,
                  'COPY COPYTOCOPY DIAGNOSE EXEC LISTDEF LOAD' ,
                  'MERGECOPY MODIFY OPTIONS QUIESCE REBUILD' ,
                  'RECOVER REORG REPAIR REPORT RESTORE' ,
                  'RUNSTATS STOSPACE TEMPLATE UNLOAD'
    return
endProcedure scanUtilReset
/*--- scan next token and put its class in m.sc.utilClass:
      'u' a utility name
      'n' a name
      '"' a quoted name
      "'" an apostroph'd string
      '.' a .
      ',' a ,
      'v' a value
      ''  at end
      ---------------------------------------------------------------*/
scanUtil: procedure expose m.
parse arg sc
    m.sc.utilSpace = scanSpaceNl(sc)
    ty = '?'
    if scanLit(sc, '(') then do
        m.sc.utilBrackets = m.sc.utilBrackets + 1
        end
    else if scanLIT(sc, ')') then do
        m.sc.utilBrackets = m.sc.utilBrackets - 1
        if m.sc.utilBrackets < 0 then
           call scanErr sc, 'unmatched closing bracket )'
        end
    else if scanLit(sc, ',') then do
        end
    else if scanLit(sc, '.') then do
        end
    else if scanString(sc, "'") then do
        end
    else if scanString(sc, '"') then do
        end
    else if scanName(sc) then do
        m.sc.val = translate(m.sc.tok)
        if m.sc.utilBrackets > 0 then
            ty = 'n'
        else if 0 < wordPos(m.sc.val, m.scanUtil) then
            ty = 'u'
        else
            ty = 'n'
        end
    else if scanVerify(sc, ' (),''"', 'm') then do
        ty = 'v'
        m.sc.val = translate(m.sc.tok)
        end
    else if \scanAtEnd(sc) then do
            call scanErr sc, 'scanUtil stopped before end'
        end
    else do
        /* say 'scanUtil return atEnd' */
        ty = ''
        m.sc.val = ''
        end
    if ty == '?' then
        m.sc.utilClass = left(m.sc.tok, 1)
    else
        m.sc.utilClass = ty
    return m.sc.utilClass
endProcedure scanUtil

/*--- scan a value or a bracketed list of values ---------------------*/
scanUtilValue: procedure expose m.
parse arg sc, remApo, nl
    if remApo = '' | rempApo = 0 then
        remApo = "nv"
    else if rempApo = 1 then
        remApo = "nv'"
    if '(' \== scanUtil(sc) then
         return scanUtilValueOne(sc, remApo)
    v = ''
    brx = m.sc.utilBrackets
    oLine = word(scanPos(sc), 1)
    do forever
        call scanUtil sc
        one = scanUtilValueOne(sc, remApo)
        if one == '' then
           call scanErr sc, 'eof in brackets'
        else if brx > m.sc.utilBrackets then
           return v
        nLine = word(scanPos(sc), 1)
        if \ m.sc.utilSpace then
            v = v || one
        else if nl \== '' & oLine <> nLine then
            v = v || nl || one
        else
            v = v' 'one
        oLine = nLine
        end
endProcedure scanUtilValue

scanUtilValueOne: procedure expose m.
parse arg sc, valTy
    if m.sc.utilClass == '' then
        return ''
    else if m.sc.utilClass == 'u' then
        call scanErr sc, 'util in scanUtilValueOne'
    if pos(m.sc.utilClass, valTy) > 0 then
        return m.sc.val
    else
        return m.sc.tok
endProcedure scanUtilValueOne
/* copy scanUtil end **************************************************/
/* copy env begin ******************************************************
***********************************************************************/
env: procedure expose m.
     m = oBasicNew("Env")
     m.m.toClose = ''
     m.m.in = ''
     m.m.out = ''
     m.m.ios.0 = 0
     return m
endProcedure env

envClose: procedure expose m.
parse arg m, finishLazy
    isLazy = m.m.out == 'ENV.lazyNoOut'
    if finishLazy \== '' then do
        if \ isLazy & finishLazy == 1 then
            call err 'not lazy'
        call oMutate m, 'Env'
        m.e.out = 'ENV.wasLazy'
        end
    else if isLazy then
        return m
    do wx=1 to words(m.m.toClose)
          call jClose word(m.m.toClose, wx)
           end
    m.m.toClose = ''
    return m
endProcedure envClose

envAddIO: procedure expose m.
parse arg m, opt spec
    opt = jOpt(opt)
    k = left(opt, 1)
    if k == m.j.cApp then
        k = m.j.cWri
    else if pos(k, m.j.cRead || m.j.cWri) < 1 then
        call err 'envAddIO bad opt' opt
    do kx=1 to m.m.ios.0 while m.m.ios.kx \== k
        end
    if kx > m.m.ios.0 then
        call mCut mAdd(m'.IOS', k), 0
    call mAdd m'.IOS.'kx, opt spec
    return m
endProcedure envAddIO

envLazy: procedure expose m.
parse arg e
    m.e.jReading = 0
    m.e.jWriting = 0
    m.e.lazyRdr = jClose(m.e.out)
    m.e.out = 'ENV.lazyNoOut'
    call oMutate e, 'EnvLazy'
    return e
endProcedure envLazy

/*--- return openOption and reader for opt rdr or jIn ---------------*/
envOptRdr: procedure  expose m.
parse arg opt rdr
    if opt = '' then
        return m.j.cRead || m.j.cNoOC || m.j.cObj m.j.jIn
    else if rdr = '' then
        return m.j.cRead catMake(m.j.cRead opt)
    else
        return opt catMake(opt rdr)
endProcedure envOptRdr

/*--- write all from rdr (rsp jIn) to jOut, possibly lazy -----------*/
envWriteAll: procedure expose m.
    if arg() > 1 then call err '?????????'
    parse arg optRdr
    call jWriteAll m.j.jOut, envOptRdr(optRdr)
    return
endProcedure envWriteAll

/*--- write all from rdr (rsp jIn) to jOut, not lazy ----------------*/
envWriteNow: procedure expose m.
    if arg() > 1 then call err '?????????'
    parse arg optRdr
    call jWriteNow m.j.jOut, envOptRdr(optRdr)
    return
endProcedure envWriteNow

envRead2Buf:
    call err 'use env2Buf' /*???wkTest***/

/*--- write all from rdr (rsp jIn) to a new jBuf --------------------*/
env2Buf: procedure expose m.
    parse arg optRdr
    b = jOpen(jBuf(), m.j.cWri)
    call jWriteNow b, envOptRdr(optRdr)
    return jClose(b)
endProcedure env2Buf

envPreSuf: procedure expose m.
parse arg le, ri
    do while jIn(v)
        call jOut le || m.v || ri
        end
    return
endProcedure envPreSuf

envCatStr: procedure expose m.
parse arg mi, fo
    res = ''
    do while jIn(v)
        res = res || mi || fmt(m.v)
        end
    return substr(res, length(mi))
endProcedure envCatStr

envIsDefined: procedure expose m.
parse arg na
    return symbol('m.env.vars.na') == 'VAR'
endProcedure envIsDefined

envGet: procedure expose m.
parse arg na
    return mapGet(env.vars, na)

envRead: procedure expose m.
parse arg na
    return jIn('ENV.VARS.'na)

envHasKey: procedure expose m.
parse arg na
    return mapHasKey(env.vars, na)

envVia: procedure expose m.
parse arg na
    return mapVia(env.vars, na)

envPut: procedure expose m.
parse arg na, va
    return mapPut(env.vars, na, va)

envRemove: procedure expose m.
parse arg na
    return mapRemove(env.vars, na)

envIni: procedure expose m.
    if m.env.ini == 1 then
        return
    m.env.ini = 1
    call catIni
    call classNew "n Env u JRW"
    call classNew "n EnvLazy u Cat", "m",
        , "jOpen call jOpen m.m.lazyRdr, opt; m.m.jReading = 1",
        , "jRead call envPushEnv m;res = jRead(m.m.lazyRdr, var);",
             "call envPop; return res",
        , "jReset call envClose m, r",
        , "jClose call envClose m, 1"
    call mapReset env.vars
    call jReset oMutate("ENV.lazyNoOut", "JRWErr")
    m.env.0 = 0
    call envPush /* by default pushes jIn and jOut */
    return
endProcedure envIni

envPush: procedure expose m.
    e = env()
    do ax=1 to arg()
        call envAddIo e, arg(ax)
        end
    do ix=1 to m.e.ios.0
        if m.e.ios.ix.0 = 1 then do
            rw = catMake(m.e.ios.ix.1)
            opt = word(m.e.ios.ix.1, 1)
            end
        else do
            rw = cat()
            do fx=1 to m.e.ios.ix.0
                call catWriteAll rw, m.e.ios.ix.fx
                end
            opt = m.e.ios.ix
            end
        if pos(m.j.cNoOC, opt) < 1 then do
                  call jOpen rw, opt
            m.e.toClose = m.e.toClose rw
            end
        if m.e.ios.ix = m.j.cRead then
            m.e.in = rw
        else if m.e.ios.ix = m.j.cWri then
            m.e.out = rw
        else
            call err 'envPush bad io' m.e.ios.ix 'for' m.e.ios.ix.1
        end
    return envPushEnv(e)
endProcedure envPush

envPushEnv: procedure expose m.
parse arg e
    call mAdd env, e
    if m.e.in == '' then
        m.e.in = m.j.jIn
    else
        m.j.jIn = m.e.in
    if m.e.out == '' then
        m.e.out = m.j.jOut
    else
        m.j.jOut = m.e.out
    return e
endProcedure envPushEnv

/*--- activate the last env from stack
        and return outputbuffer from current env --------------------*/
envPop: procedure expose m.
    ex = m.env.0
    if ex <= 1 then
        call err 'envPop on empty stack' ex
    o = m.env.ex
    oo = m.o.out
    ex = ex - 1
    m.env.0 = ex
    e = m.env.ex
    m.j.jIn = m.e.in
    m.j.jOut = m.e.out
    if objClass(oo, '') == class4Name('Cat') & m.oo.RWs.0 > 0 then
        return envLazy(o)
    call envClose o
    return m.o.out
endProcedure envPop

envBarBegin: procedure expose m.
    call envPush '>%' Cat()
    return
endProcedure envBarBegin

envBar: procedure expose m.
    call envPush '<%' envPop(), '>%' Cat()
    return
endProcedure envBar

envBarLast: procedure expose m.
    call envPush '<%' envPop()
    return
endProcedure envBarLast

envBarEnd: procedure expose m.
    call envPop
    return
endProcedure envBarEnd

/*--- return the output buffer of oRunner m -------------------------*/
envRun: procedure expose m.
    parse arg m
    call envPush '>%' jBuf()
    call oRun m
    return envPop()
endProcedure envRun

/* copy env end *******************************************************/
/* copy cat  begin ****************************************************
***********************************************************************/
/*--- create a reader or writer --------------------------------------*/
catMake: procedure expose m.
parse arg opt spec
    if pos(m.j.cObj, opt) > 0 then
        return spec
    else if pos(m.j.cVar, opt) > 0 then do
        if envhasKey(spec) then
            return catMake(translate(opt, m.j.cObj, m.j.cVar) envGet(spec))
        else
            return envPut(spec, jBuf())
        end
    else if pos('&', opt) > 0 then
        return file('&'spec)
    else
        return file(spec)
endProcedure catMake

/*--- create a new cat -----------------------------------------------*/
cat: procedure expose m.
    m = oNew('Cat') /* calls catReset */
    do ax=1 to arg()
        call catWriteAll m, arg(ax)
        end
    return m
endProcedure cat

catReset: procedure expose m.
parse arg m
    m.m.RWs.0 = 0
    m.m.catWr = ''
    m.m.catRd = ''
    m.m.catIx = -9
    return m
endProcedure catReset

catClose: procedure expose m.
parse arg m
    if m.m.catIx == -9 then
        return
    if m.m.catWr \== '' then do
        call jClose m.m.catWr
        call mAdd m'.RWS', jOpt(m.j.cObj) m.m.catWr
        m.m.catWr = ''
        end
    if m.m.catRd \== '' then do
        ix = m.m.catIx
        if pos(m.j.cNoOC, word(m.m.RWs.ix, 1)) < 1 then
            call jClose m.m.catRd
        m.m.catRd = ''
        end
    m.m.catIx = -9
    return m
endProcedure catClose

catOpen: procedure expose m.
parse arg m, oo
    if pos(m.j.cRead, oo) > 0 then do
        m.m.catIx = 0
        m.m.catRd = catNextRdr(m)
        m.m.jReading = 1
        end
    else if abbrev(oo, m.j.cWri) | abbrev(oo, m.j.cApp) then do
        if abbrev(oo, m.j.cWri) then
            m.m.RWs.0 = 0
        m.m.catIx = -7
        m.m.jWriting = 1
        end
    else do
        call err 'catOpen('m',' oo') bad opt'
        end
    return m
endProcedure catOpen

/*--- return and open next reader ------------------------------------*/
catNextRdr: procedure expose m.
parse arg m
    cx = m.m.catIx
    if cx > 0 & cx <= m.m.RWs.0 ,
            & pos(m.j.cNoOC, word(m.m.RWs.cx, 1)) < 1 then
        call jClose m.m.catRd
    cx = cx + 1
    m.m.catIx = cx
    if cx > m.m.RWs.0 then
        return ''
    return jOpen(catMake(m.m.RWs.cx),
            , m.j.cRead||substr(word(m.m.RWs.cx, 1), 2))
endProcedure catNextRdr

catRead: procedure expose m.
parse arg m, var
    do while m.m.catRd \== ''
        if jRead(m.m.catRd, var) then
            return 1
        m.m.catRd = catNextRdr(m)
        end
    return 0
endProcedure catRead

catWrite: procedure expose m.
parse arg m, line
    if m.m.catWr == '' then
        m.m.catWr = jOpen(jBuf(), m.j.cWri)
    call jWrite m.m.catWr, line
    return
endProcedure catWrite

catWriteR: procedure expose m.
parse arg m, var
    if m.m.catWr == '' then
        m.m.catWr = jOpen(jBuf(), m.j.cWri)
    call jWriteR m.m.catWr, var
    return
endProcedure catWriteR

/*--- write contents of a reader to cat
          or keep it for later reading -------------------------------*/
catWriteAll: procedure expose m.
parse arg m
    if m.m.catIx >= 0 then
        call err 'catWriteAll('m',' arg(2)') but opened,',
                 'catIx='m.m.catIx
    if m.m.catWr \== '' then do
        call mAdd m'.RWS', jOpt(m.j.cObj) jClose(m.m.catWr)
        m.m.catWr = ''
        end

    do ax=2 by 1 to arg()
        if words(arg(ax)) = 1 then
            call mAdd m'.RWS', jOpt() arg(ax)
        else
            call mAdd m'.RWS', jOpt(word(arg(ax),1)) subword(arg(ax),2)
        end
    return
endProcedure catWriteAll

/*--- create a reader/writer for an external file --------------------*/
file: procedure expose m.
parse arg sp
    return oNew('File', sp)
endProcedure file

fileWriteR: procedure expose m.
parse arg m, var
     if symbol('m.class.o2c.var') == 'VAR' then do
         ty = m.class.o2c.var
         if m.ty \== 'v' then
             call err 'fileWriteR with var' var 'class' ty
         end
     call jWrite m, m.var
     return
endProcedure fileWriteR

fileChild: procedure expose m.
parse arg m, name, opt
    interpret objMet(m, 'fileChild')
endProcedure fileChild

fileRm: procedure expose m.
parse arg m
    interpret objMet(m, 'fileRm')
    return
endProcedure fileRm

filePath: procedure expose m.
parse arg m
    interpret objMet(m, 'filePath')
endProcedure filePath

fileIsFile: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsFile')
endProcedure fileIsFile

fileIsDir: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsDir')
endProcedure fileIsDir

fileMkDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileMkDir')
    return
endProcedure fileRm

fileRmDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileRmDir')
    return
endProcedure fileRm

/*--- create a reader/writer for the filelist of a directory----------*/
fileList: procedure expose m.
parse arg m, opt
    return oNew('FileList', filePath(m),  opt)
endProcedure fileList

catIni: procedure expose m.
    if m.cat.ini == 1 then
        return
    m.cat.ini = 1
    call jIni
    call classNew "n Cat u JRW", "m",
        , "jOpen  return catOpen(m, opt)",
        , "jReset return catReset(m, arg)",
        , "jClose call catClose m",
        , "jRead return catRead(m, var)",
        , "jWrite call catWrite m, line; return",
        , "jWriteR call catWriteR m, var; return",
        , "jWriteAll call catWriteAll m, optRdr; return"
    os = errOS()
    if os == 'TSO' then
        call fileTsoIni
    else if os == 'LINUX' then
        call fileLinuxIni
    else
        call err 'file not implemented for os' os
    return
endProcedure catIni
/* copy cat  end   ****************************************************/

/* copy fiLinux begin *************************************************/
/*--- send ggShCmd to shell bash,
        fail if rc <> 0 or not listed in ggRet -----------------------*/
adrSh: procedure expose m.  /* really no need for variables???? */
    parse arg ggShCmd, ggRet
    address 'bash' ggShCmd
    if rc == 0                     then return 0
    else if ggRet == '*'           then return rc
    else if wordPos(rc, ggRet) > 0 then return rc
    else
        call err 'adrSh rc' rc 'for' ggShCmd
endProcedure adrSh

fileLinuxReset: procedure expose m.
parse arg m, nm
    m.m.spec = nm
    if abbrev(nm, '&') then do
        if nm == '&in' then do
            m.m.stream = .input
            m.m.jReading = 1
            end
        else if nm == '&out' then do
            m.m.stream = .output
            m.m.jWriting = 1
            end
        else do
            call err 'bad spec' nm
            end
        end
    else do
        m.m.stream = .Stream$mc$new(nm)
        m.m.stream$mc$init(m.m.stream$mc$qualify)
        end
    return m
endProcedure fileLinuxReset

fileLinuxOpen: procedure expose m.
parse arg m, opt
    if pos(m.j.cRead, opt) > 0 then do
        res = m.m.stream$mc$open(read shareread)
        m.m.jReading = 1
        end
    else do
        if pos(opt, m.j.cApp) > 0 then
            res = m.m.stream$mc$open(write append)
        else if pos(opt, m.j.cWri) > 0 then
            res = m.m.stream$mc$open(write replace)
        else
            call err 'fileLinuxOpen('m',' opt') with bad opt'
        m.m.jWriting = 1
        end
    if res \== 'READY:' then
        call err 'fileLinuxOpen fails' res':' opt m.m.stream$mc$qualify
    return m
endProcedure fileLinuxOpen

fileLinuxClose:
parse arg m
    res = m.m.stream$mc$close
    if res \== 'READY:' then
        call err 'fileLinuxClose' res':' m.m.stream$mc$qualify
    return m
endProcedure fileLinuxClose

fileLinuxRead: procedure expose m.
parse arg m, var
    res = m.m.stream$mc$lineIn
    if res == '' then
        if m.m.stream$mc$state \== 'READY' then
            return 0
    m.var = res
    return 1
endProcedure fileLinuxRead

fileLinuxWrite: procedure expose m.
parse arg m, line
    if m.m.stream$mc$lineOut(line) then
        call err 'fileLinuxWrite'
    return
endProcedure fileLinuxWrite

fileLinuxRmDir: procedure expose m.
parse arg m, opt
    if opt == '' then
        return adrSh('rmdir' m.m.spec)
    else if opt == '-r' then
        return adrSh('rm -r' m.m.spec)
    else
        call err 'bad opt' opt 'in fileLinuxRmDir'
endProcedure fileLInuxRmDir

fileLinuxListReset: procedure expose m.
parse arg m, m.m.spec, o
    if o == 'r' then
        m.m.opt = 'S'
    else if o == '' then
        m.m.opt = ''
    else
        call err 'bad opt' o 'in fileLinuxListReset'
    m.m.rx = 'closed'
    return m
endProcedure fileLinuxListReset

fileLinuxListOpen: procedure expose m.
parse arg m
    if m.m \== value('m.'m) then
        call err 'bad m for fileLinuxList:' m
    if m.m.opt == '' then
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST')
    else
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST', m.m.opt)
    if rc \== 0 then
        call err 'rc' rc 'in sysFileTree('m.m.spec', m.'m'.LIST)'
    m.m.rx = 0
    m.m.jReading = 1
    return m
endProcedure fileLinuxListOpen

fileLinuxListRead: procedure expose m.
parse arg m, var
    x = m.m.rx + 1
    if x > m.m.list.0 then
        return 0
    m.var = substr(m.m.list.x, 43)
    m.m.rx = x
    return 1
endProcedure fileLinuxListRead

fileLinuxIni: procedure expose m.
    if m.fileLinux.ini == 1 then
        return
    m.fileLinux.ini = 1
    m.file.sep = '/'
    call jIni
    call classNew "n File u JRW", "m",
        , "jReset return fileLinuxReset(m, arg)",
        , "jOpen  return fileLinuxOpen(m, opt)",
        , "jClose call fileLinuxClose m",
        , "jRead return fileLinuxRead(m, var)",
        , "jWrite call fileLinuxWrite m, line",
        , "jWriteR call fileWriteR m, var",
        , "filePath return m.m.stream~qualify",
        , "fileIsFile return sysIsFile(m.m.stream~qualify)" ,
        , "fileIsDir return sysIsFileDirectory(m.m.stream~qualify)" ,
        , "fileChild return file(m.m.stream~qualify'/'name)",
        , "fileRm return adrSh(m.m.spec)",
        , "fileMkDir return adrSh('mkdir' m.m.stream~qualify)" ,
        , "fileRmDir return fileLinuxRmDir(m, opt)"
    call classNew "n FileList u JRW", "m",
        , "jReset return fileLinuxListReset(m, arg, arg2)",
        , "jOpen  return fileLinuxListOpen(m, opt)",
        , "jClose m.m.rx = 'closed'",
        , "jRead return fileLinuxListRead(m, var)"
    return
endProcedure fileLinuxIni
/* copy fiLinux end   *************************************************/

/* copy fileTso begin *************************************************/
fileTsoReset: procedure expose m.
parse arg m, sp
    m.m.readIx = 'c'
    if symbol('m.m.defDD') \== 'VAR' then do
        ix = mInc('FILETSO.BUF')
        m.m.defDD = 'CAT'ix
        m.m.buf = 'FILETSO.BUF'ix
        m.m.spec = sp
        end
    if sp \== '' then do
        m.m.spec = dsnSpec(sp)
        rr = translate(subword(m.m.spec, 4))
        m.m.stripT = \ (pos(':V', rr) > 0 | pos('RECFM(V', rr) > 0)
        end
    return m
endProcedure fileTsoReset

fileTsoOpen: procedure expose m.
parse arg m, opt
    buf = m.m.buf
    if pos(m.j.cRead, opt) > 0 then do
        aa = dsnAlloc(m.m.spec, 'SHR', m.m.defDD)
        /* ???wkTest fehlermeld funktioniert so nicht, ist sie noetig?
        if m.dsnAlloc.dsn <> '' then
            if sysDsn("'"m.dsnAlloc.dsn"'") <> 'OK' then
                call err 'cannot read' m.dsnAlloc.dsn':',
                               sysDsn("'"m.dsnAlloc.dsn"'")         */
        call readDDBegin word(aa, 1)
        m.m.jReading = 1
        m.buf.0 = -1
        m.m.readIx = 0
        end
    else do
        if pos(opt, m.j.cApp) > 0 then
            aa = dsnAlloc(m.m.spec, 'MOD', m.m.defDD)
        if pos(opt, m.j.cWri) > 0 then
            aa = dsnAlloc(m.m.spec, 'OLD', m.m.defDD)
        else
            call err 'fileTsoOpen('m',' opt') with bad opt'
        call writeDDbegin word(aa, 1)
        m.m.jWriting = 1
        m.buf.0 = 0
        m.m.readIx = 'w'
        end
    m.m.dd = word(aa, 1)
    m.m.free = subword(aa, 2)
    return m
endProcedure fileTsoOpen

fileTsoClose:
parse arg m
    buf = m.m.buf
    if m.m.readIx \== 'c' then do
        if m.m.readIx == 'w' then do
            if m.buf.0 > 0 then
                call writeDD m.m.dd, 'M.'BUF'.'
            call writeDDend m.m.dd
            end
        else do
            call readDDend m.m.dd
            end
        interpret m.m.free
        end
    m.buf.0 = 'closed'
    m.m.readIx = 'c'
    m.m.free  = ''
    m.m.dd    = ''
    return m
endProcedure fileTsoClose

fileTsoRead: procedure expose m.
parse arg m, var
    ix = m.m.readIx + 1
    buf = m.m.buf
    if ix > m.buf.0 then do
        res = readDD(m.m.dd, 'M.'buf'.')
        if \ res then
            return 0
        ix = 1
        end
    m.m.readIx = ix
    m.var = m.buf.ix
    return 1
endProcedure fileTsoRead

fileTsoWrite: procedure expose m.
parse arg m, var
    buf = m.m.buf
    ix = m.buf.0 + 1
    m.buf.0 = ix
    if m.m.stripT then
        m.buf.ix = strip(var, 't')
    else
        m.buf.ix = var
    if ix > 99 then do
        call writeDD m.m.dd, 'M.'buf'.'
        m.buf.0 = 0
        end
    return
endProcedure fileTsoWrite

fileTsoWriteR: procedure expose m.
parse arg m, var
    if objClass(var, m.class.classV) == m.class.classV then do
        call fileTsoWrite m, m.var
        return
        end
    call err 'fileTsoWriteR('m',' var') cannot write objects of class',
                              objClass(var)
endProcedure fileTsoWriteR

fileTsoIni: procedure expose m.
    if m.fileTso.ini == 1 then
        return
    m.fileTso.ini = 1
    m.file.sep = '.'
    m.fileTso.buf = 0
    call jIni
    um = "call err 'for tso undefined method'"
    call classNew "n File u JRW", "m",
        , "jOpen  return fileTsoOpen(m, opt)",
        , "jReset return fileTsoReset(m, arg)",
        , "jClose call fileTsoClose m",
        , "jRead return fileTsoRead(m, var)",
        , "jWrite call fileTsoWrite m, line",
        , "jWriteR call fileTsoWriteR m, var",
        , "filePath return word(m.m.spec, 1)"           ,
        , "fileIsFile" um "'fileIsFile'"      ,
        , "fileIsDir   return 1"              ,
        , "fileChild   return file(word(m.m.spec, 1)'.'name opt)",
        , "fileRm"     um "'fileRm'"          ,
        , "fileMkDir"  ,
        , "fileRmDir"  um "'fileRmDir'"
 /*     , "filePath return m.m.stream~qualify",
        , "fileIsFile return sysIsFile(m.m.stream~qualify)" ,
        , "fileIsDir return sysIsFileDirectory(m.m.stream~qualify)" ,
        , "fileChild return file(m.m.stream~qualify'/'name)",
        , "fileRm return adrSh(m.m.spec)",
        , "fileMkDir return adrSh('mkdir' m.m.stream~qualify)" ,
        , "fileRmDir return fileLinuxRmDir(m, opt)" */
    call classNew "n FileList u JRW", "m",
        , "jReset if arg2 == 'r' then m.m.dsnMask=arg'.**';",
                                "else m.m.dsnMask=arg'.*';",
        , "jOpen  call csiOpen m, m.m.dsnMask; m.m.jReading=1; return",
        , "jClose" ,
        , "jRead return csiNext(m, var)"
    return
endProcedure fileTsoIni
/* copy fileTso end   *************************************************/

/* copy sqlO   begin ***************************************************
    sql interface  mit  o und j Anbindung
***********************************************************************/
sqlOini: procedure expose m.
    if m.sqlO.ini == 1 then
        return
    m.sqlO.ini = 1
    call sqlIni
    call envIni
    call oDecMethods oNewClass("SqlType", "JRW"),
        , "jOpen  call sqlOpen substr(m, 8); m.m.jReading = 1",
        , "jClose call sqlClose substr(m, 8)",
        , "jRead  return sqlFetch(substr(m, 8), var)"
    call oDecMethods oNewClass("SqlLn", "SqlType"),
        , "jRead  return sqlFetchLn(substr(m, 8), var)"
    return
endProcedure sqlOini
/*--- fetch all rows into stem st
           from sql src using type ty and format fmt -----------------*/
sql2St: procedure expose m.
parse arg st, src, ty, fmt
    cx = 49
    call sql2Cursor cx, src, ty, fmt
    call sqlOpen cx
    do ix=1 by 1 while sqlFetch(cx, st'.'ix)
        end
    m.st.0 = ix-1
    call sqlClose cx
    return ix-1
endProcedure sql2St

/*--- prepare statement 's'cx and declare cursor 'c'cx from sql src
           use or generate type ty and format fo ---------------------*/
sql2Cursor: procedure expose m.
parse arg cx, src, ty, fo
     call sqlPreDeclare cx, src, 1  /* with describe output */
     call sqlGenType cx, ty
     m.Sql.cx.FMT.0 = 0
     m.Sql.cx.FMT = sqlGenFmt('SQL.'cx'.FMT', cx, fo)
     call assert 'm.'m.sql.cx.fmt'.0 >= m.sql.cx.d.sqlD', 'too few fmt'
     return
endProcedure sql2Cursor

/*--- return the type for sql cx -------------------------------------*/
sqlType: procedure expose m.
parse arg cx
    return oGetTypePara('SQL.TY.'cx)

/*--- fetch cursor 'c'cx into destination dst
          each column is formatted and assigned to m.dst.<colName> ---*/
sqlFetch: procedure expose m.
parse arg cx, dst
    if ^ sqlFetchInto(cx, 'descriptor :M.SQL.'cx'.D') then
        return 0
    if dst == '' then
        return 1
    fi = oFlds(sqlType(cx))
    fo = m.sql.cx.fmt
    do ix=1 to m.sql.cx.d.SQLD
        f = m.fi.ix
        if m.sql.cx.d.ix.sqlInd = 0 then
            m.dst.f = fmt(m.sql.cx.d.ix.sqlData, m.fo.ix)
        else
            m.dst.f = fmtS(m.sqlNull, m.fo.ix)
        end
    return 1
endProcedure sqlFetch

/*--- fetch cursor 'c'cx
          put the formatted and concatenated columns into m.var
          return 1 if a row fetched, 0 if not ------------------------*/
sqlFetchLn: procedure expose m.
parse arg cx, var
    st = 'SQL.'cx'.FET'
    if ^ sqlFetch(cx, st) then
        return 0
    m.var = oFldCat(sqlType(cx), st, m.sql.cx.fmt)
    return 1
endProcedure sqlFetchLn

/*--- generate the type sql cx as specified in ty
          use the information from the sqlDa -------------------------*/
sqlGenType: procedure expose m.
parse arg cx, ty
     if ty == '*' | ty = '' then do
         ff = ''
         do ix=1 to m.sql.cx.d.sqlD
             f1 = word(m.sql.cx.d.ix.sqlName, 1)
             if f1 == '' then
                 f1 = 'COL'ix
             ff = ff f1
             end
         ty = oFldOnly(ff, 'e')
         end
     call oSetTypePara 'SQL.TY.'cx, ty, 'noCall'
     return ty
endProcedure sqlGenType

/*--- generate the format ff for a sql cx as specified in sp
          use the information from the sqlDa -------------------------*/
sqlGenFmt: 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
/*--- sql o interface ------------------------------------------------*/
/*--- return a reader for the givenn sql or std input ----------------*/
sql2Obj: procedure expose m.
parse arg cx, src, ty, fo
    if ty = '' then
        ty = '*'
    if src == '' then
        src = envCatStr(' ', 'sb')
    call sql2Cursor cx, src, ty, substr(fo, 1+abbrev(fo, '~'))
    call oMutate 'SQL.TY.'cx, 'SqlType'
    return 'SQL.TY.'cx
endProcedure sql2obj

/*--- write to std output the result columns of
          the sql given in src or std input --------------------------*/
sql: procedure expose m.
parse arg cx, src, ty, fo
    call jWriteAll m.j.jOut, "r£", sql2Obj(cx, src, ty, fo)
    return
endProcedure sql

/*--- write to std output the result lines   of
          the sql given in src or std input --------------------------*/
sqlLn: procedure expose m.
parse arg cx, src, ty, fo
    if fo = '' then
        fo = '~'
    squash = abbrev(fo, '~')
    if ^ abbrev(fo, '=') then
        fo = left(fo, squash) 'sl=' substr(fo, squash+1)
    t = sql2Obj(cx, src, ty, fo)
    if squash then do
        call fmtFldSquashRw t, 'opCl'
        return
        end
    m = 'SQL.LN.'cx
    call oMutate m, 'SqlLn'
    call jWrite m.j.jOut, fmtFldTitle(m.sql.cx.fmt)
    call jWriteAll m.j.jOut, "r£", m
    return
endProcedure sqlLn
/* copy sqlO   end   **************************************************/
/* copy sql    begin ***************************************************
    sql interface
***********************************************************************/
sqlIni: procedure expose m.
    m.sqlNull = '---'
    return
endProcedure sqlIni

/*--- prepare statement 's'cx from sql src into descriptor desc ------*/
sqlPrepare: procedure expose m.
parse arg cx, src, descOut, descInp
     s = ''
     if descOut == 1 then
         s = 'into :M.SQL.'cx'.D'
     call sqlExec 'prepare s'cx s 'from :src'
     if descInp == 1 | (descInp == '' & pos('?', src) > 0) then
         call sqlExec 'describe input s'cx 'into :M.SQL.'cx'.I'
     else
         m.sql.cx.i.sqlD = 0
     return
endProcedure

/*--- prepare and declare 'c'cx from sql src -------------------------*/
sqlPreDeclare: procedure expose m.
parse arg cx, src, descOut, descInp
     call sqlPrepare cx, src, descOut, descInp
     call sqlExec 'declare c'cx 'cursor for s'cx
     return
endProcedure sqlPreDeclare

/*--- prepare, declare and open 'c'cx from sql src -------------------*/
sqlPreOpen: procedure expose m.
parse arg cx, src, descOut, descInp
     call sqlPreDeclare cx, src, descOut, descInp
     call sqlOpen cx
     return
endProcedure sqlPreOpen

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

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

/*--- fetch cursor 'c'cx into variables ggVars -----------------------*/
sqlFetchInto:
parse arg ggCx, ggVars
    if ggVars == '' then
        ggVars = 'descriptor :M.SQL.'ggCX'.D'
                        /* accept sqlCodes > 0 except 100 */
    return sqlExec('fetch c'ggCx 'into' ggVars, 0 100) <> 100
endProcedure sqlFetchInto

/*--- return sql variable list for stem st and fields the word in vars
          if withInd == 1 then with sqlIndicator variables
        sqlVars('S', 'A B') --> ':S.A, :S.B'
        sqlVars('S', 'A B', 1) --> ':S.A :S.A.SQLIND, :S.B :S.B.SQLIND'
----------------------------------------------------------------------*/
sqlVars: procedure expose m.
parse arg st, vars, withInd
    res = ''
    if st ^== '' then
        st = st'.'
    do ix=1 to words(vars)
        res = res', :'st || word(vars, ix)
        if withInd == 1 then
             res = res ':'st || word(vars, ix)'.SQLIND'
        end
    return substr(res, 3)
endProcedure sqlVars

sqlVarsNull: procedure expose m.
parse arg st, vars
    hasNulls = 0
    do ix = 1 to words(vars)
        fld = word(vars, ix)
        if m.st.fld.sqlInd < 0 then do
            m.st.fld = m.sqlNull
            hasNulls = 1
            end
        end
    return hasNulls
endProcedure sqlVarsNull

sqlDescNull: procedure expose m.
parse arg cx
    desc = 'SQL.'ggCX'.D',
    hasNulls = 0
    do ix=1 to m.desc.SQLD
        if m.desc.ix.sqlInd < 0 then do
            m.desc.ix.sqlData = m.sqlNull
            hasNulls = 1
            end
        end
    return hasNulls
endProcedure sqlDescNull

/*--- open cursor 'c'cx fetch all into variables vars and close
      st = passed stem, sx = row number
      return number of rows fetched ----------------------------------*/
sqlOpAllCl:
parse arg ggCx, st, ggVars
    do ggAx=4 to arg()
        call sqlDataSet 'SQL.'ggCx'.I', ggAx-3, arg(ggAx)
        end
    call sqlOpen ggCx
    do sx = 1 while sqlFetchInto(ggCx, ggVars)
        end
    m.st.0 = sx - 1
    call sqlClose ggCx
    return m.st.0
endProcedure sqlOpAllCl

sqlDataSet: procedure expose m.
parse arg da, ix, val
    m.da.ix.sqlData = val
    m.da.ix.sqlInd = - (arg(ix+2) == m.sqlNull)
    return
endProcedure sqlDataSet
/*--- prepare, declare open cursor 'c'cx, fetch all and close
      return number of rows fetched ----------------------------------*/
sqlPreAllCl:
parse arg ggCx, ggSrc, st, ggVars
    call sqlPreDeclare ggCx, ggSrc
    return sqlOpAllCl(ggCx, st, ggVars)
endProcedure sqlPreAllCl

/*--- execute statement 's'cx using arguments arg(2), arg(3)... ------*/
sqlExecute:
parse arg ggCx
    do ggAx=2 to arg()
        call sqlDataSet 'SQL.'ggCx'.I', ggAx-1, arg(ggAx)
        end
     call sqlExec 'execute s'ggCx 'using descriptor :M.SQL.'ggCx'.I'
     return
endProcedure

/*--- execute immediate the sql src ----------------------------------*/
sqlExImm:
parse arg ggSrc, ggRet
     return sqlExec('execute immediate :ggSrc', ggRet)
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, ggRet, ggNo
    if ggNo <> '1' then
        ggSqlStmt = 'execSql' ggSqlStmt
    address dsnRexx ggSqlStmt
    /* say 'sqlCode' sqlCode 'rc' rc 'for' ggSqlStmt ggNo */
    if rc = 0 then
        return 0
    else if ggRet = '*' | wordPos(sqlCode, ggRet) > 0 then
        return sqlCode
    else if rc < 0 then
        call err sqlmsg()
    else if sqlWarn.0 ^== ' ' | sqlCode <> 0 then
        call errSay sqlMsg(), ,'w'
    return sqlCode
endSubroutine sqlExec

/*--- connect to the db2 subsystem ggSys -----------------------------*/
sqlConnect: procedure expose m.
parse arg ggSys, ggRetCon
    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 ggSys = '-' then
        return 0
    return sqlExec("connect" ggSys, ggRetCon ,1)
endProcedure sqlConnect

/*--- diconnect from db2 ---------------------------------------------*/
sqlDisconnect: procedure expose m.
parse arg ggRet
    call sqlExec "disconnect ", ggRet, 1
    return
endProcedure sqlDisconnect

/*--- issue an sql error message -------------------------------------*/
sqlMsg: /* no procedure, to keep variables sql... */
    signal on syntax name sqlMsgOnSyntax
    ggRes = 'sqlCode' sqlCodeT(sqlCode, sqlErrMc, sqlwarn.0':' ,
       || sqlwarn.1||sqlwarn.2||sqlwarn.3||sqlwarn.4||sqlwarn.5',',
       || sqlwarn.6||sqlwarn.7||sqlwarn.8||sqlwarn.9||sqlwarn.10)
    if 0 then
      sqlMsgOnSyntax: do
        ggRes = 'sqlCode' sqlCode translate(sqlErrMc, ',', 'ff'x),
                '<<rexx sqlCodeT not found or syntax>>\nwarnings'
        do ggX=0 to 10
            if sqlWarn.ggx <> '' then
                ggRes = ggRes ggx'='sqlWarn.ggx
            end
        end
    signal off syntax
    ggRes = ggRes'\nstate' sqlState'\nstmt = ' ggSqlStmt
    ggPref = '\nwith'
    ggXX = pos(':', ggSqlStmt)+1
    do 12 while ggXX > 1
        ggYY = verify(ggSqlStmt, ' ,:+-*/&%?|()¢!', 'm', ggXX)
        if ggYY < 1 then
            ggYY = length(ggSqlStmt) + 1
        ggVar = substr(ggSqlStmt, ggXX, ggYY - ggXX)
        if ggVar <> '' then do
            ggRes = ggRes || ggPref ggVar '=' value(ggVar)
            ggPref = '\n    '
            end
        ggXX = pos(':', ggSqlStmt, ggYY+1) + 1
        end
    return  ggRes
endSubroutine sqlMsg

/*--- 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
/* copy sql    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
                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
            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.EXECall(CSRXUTIL)'" c , ggRet)
    return
endProcedure csmCopy

csmAlloc: procedure expose m.
parse upper arg dd, disp, dsn, 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 sleep begin ***************************************************/
parse arg s
if s = '' then
    call sleep 5
else
    call sleep s
return
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
/* copy sleep end *****************************************************/
/* copy csi begin    ***************************************************
     csi interface: see dfs managing catalogs appendix c
         returncode/reason see message IDC3009I
**********************************************************************/
/*--- specify dsn mask and fields to start a csi catalog search --------
      arguments:
          m       objectPointer
          dsnMask specifies the dsns with wildcards:
              %  1 character
              *  0 - n character in one level
              ** 0 - n levels
          fields a (space separated) list of field names -------------*/
csiOpen: procedure expose m.
parse arg m, dsnMask, fields
    m.m.fld.0 = words(fields)
    ffix = d2c(m.m.fld.0, 2)
    do x=1 to m.m.fld.0
        m.m.fld.x = translate(word(fields, x))
        ffix = ffix || left(m.m.fld.x, 8)
        end
    if dsnMask \== '' & pos('*', dsnMask) < 1 then
        dsnMask = dsnMask'.**'
    m.m.filt = left(dsnMask, 149) ,
             || left('Y', 3) ,        /* resume offset 149      */
             || ffix                  /* csiNumEn offset 152    */

    WORKLEN = 1024 * 64
    m.m.work = D2C(WORKLEN,4) || COPIES('00'X,WORKLEN-4)
    m.m.pos = workLen + 1
    return
endProcedure csiOpen

/*--- put the next dsn into m.o and m.o.* (for other fields)
      return 1 if next dsn exists 0 otherwise ------------------------*/
csiNext: procedure expose m.
parse arg m, o
    usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET AMOUNT OF WORK AREA USED */
    px = m.m.pos
    do forever
        if px > usedL then do
            if substr(m.m.filt, 150, 1) \== 'Y' then do
                m.m.pos = px
                m.o = ''
                return 0
                end
            reason = left('', 4)
            ADDRESS LINKPGM 'IGGCSI00  reason m.'m'.filt  m.'m'.work'
            if rc == 0 & substr(reason, 3, 2) == '0000'x then
                nop
            else if rc == 4 & substr(reason, 3, 2) == '0464'x then
                say 'data set entry with error'
            else
                call err 'call csi returns' rc,
                             'rc' c2d(substr(reason, 4,1)),
                             'reason' c2d(substr(reason, 3,1)),
                             'module' substr(reason, 1,2)
            usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET WORK AREA USED */
            numFD = C2D(SUBSTR(m.m.work,13,2)) /* no flds + 1 */
            if numFd <> m.m.fld.0 + 1 then
                call err 'csiNumFd' numFd 'not' m.m.fld.0 '+' 1
            px = 15
            iterate
            end
        eType =  substr(m.m.work, px+1, 1)
        m.o = strip(substr(m.m.work, px+2, 44), 't')
        flag = substr(m.m.work, px, 1)
        /* say 'eType' eType m.o 'flag' c2x(flag) */
        if eType == '0' then do
            if flag \== '00'x & flag ^== '40'x then
                call err 'flag' c2x(flag) 'for catalog' m.o
            px = px + 50    /* length of catalog entry  */
            iterate
            end
        else do
            if \ abbrev(x2b(c2x(flag)), '101') then
                call err 'call csi entry flag' x2b(c2x(flag)),
                             'rc' c2d(substr(m.m.work, px+49,1)),
                             'reason' c2d(substr(m.m.work, px+48,1)),
                             'module' substr(m.m.work, px+46, 2),
                             'for entry' m.o,
                             'see qw IDC3009I'
            py = px + 46
            tl = c2d(substr(m.m.work, py, 2))
            pl = py + 4
            pf = py + m.m.fld.0 * 2 + 4
            do fx = 1 to m.m.fld.0
                fi = m.m.fld.fx
                fl = c2d(substr(m.m.work, pl, 2))
                m.o.fi = substr(m.m.work, pf, fl)
                if fi = 'MGMTCLAS' then
                    m.o.fi = substr(m.o.fi, 3, c2d(left(m.o.fi ,2)))
                else if wordPos(fi, 'COMUDSIZ NOBYTTRK') > 0 then
                    m.o.fi = c2d(m.o.fi)
                pf = pf + fl
                pl = pl + 2
                end
            if py + tl <> pf then
                call err 'length mismatch for entry' m.o
            m.m.pos = pf
            return 1
            end
        end
endProcedure csiNext
/*--- if dsn is arcived return 'arcive'
      if dsn is tape return 'tape'
      otherwise return managment class ------------------------------*/
csiArcTape: procedure expose m.
parse arg vo, cl, dt, dsn
        if vo = '' then
            say err '||| no volume for dsn' dsn
        else if vo = 'ARCIVE' then
            res = 'arcive'
        else if cl <> '' then
            res = cl
        else if abbrev(vo, 'SHR') then
            res = 'SHR'
        else
            res = 'tape'
        if   res = 'arcive' then
            return res
      /*if   abbrev(res, 'ar') \= abbrev(dt, '00'x) , */
        if   abbrev(res, 'ta') \= abbrev(c2x(left(dt, 1)), '7') ,
           | (left(res, 1) >= 'A') ^= abbrev(dt, '30'x) then
           say '||| mismatch cl' cl 'vo' vo 'dt' c2x(dt) 'dsn' dsn
        return res
endProcedure csiArcTape
/* copy csi 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 jOut 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 jOut 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.
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 arg ggDD
    call adrTso 'execio 0 diskr' ggDD '(finis)'
return /* end readDDEnd */

/*--- prepare writing to DD ggDD -------------------------------------*/
writeDDBegin: procedure expose m.
    parse arg ggDD
                  /* ensure file is erased, if no records are written */
    call adrTso 'execio' 0 'diskw' ggDD '(open)'
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 arg ggDD
    call adrTso 'execio 0 diskw' ggDD '(finis)'
return /* end writeDDEnd */

/*--- 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 = '-'
    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 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('/', ds) > 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"')"
    if retRc <> '' | nn == '' then do
        alRc = adrTso(c rest, retRc)
        if alRc <> 0 then
            return ' ' alRc
        return dd 'call adrTso "free dd('dd')";'
        end
    do retry=0 by 1
        alRc = adrTso(c rest, '*')
        if alRc = 0 then
            return dd 'call adrTso "free dd('dd')";'
        if retry > 0 | nn = '' | wordPos(disp, 'OLD SHR') < 1 ,
            | sysDsn("'"m.dsnAlloc.dsn"'") ^== 'DATASET NOT FOUND' then
            call err 'tsoAlloc rc' alRc 'for' c rest
        say 'tsoAlloc rc' alRc 'for' c rest '...trying to create'
        call adrTso 'alloc dd('dd') new catalog' dsnCreateAtts(na, nn)
        call adrTso 'free  dd('dd')'
        end
endProcedure tsoAlloc

dsnCreateAtts: procedure expose m.
parse arg dsn, atts, forCsm
    bl = 32760
    if abbrev(atts, ':') then do
        rl = substr(atts, 3)
        if abbrev(atts, ':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(atts, 2, 1) 'B'
            end
        end
    if pos('(', dsn) > 0 then
        po = 'dsntype(library) dsorg(po)'
    else
        po = ''
    dsn = dsnSetMbr(dsn)
    if forCsm == 1 then
        return "dataset('"dsn"')" po,
                "recfm("space(recfm, 0)") lrecl("rl") blkSize("bl")" ,
                "mgmtclas(COM#A091) space(10, 1000) cylinder"
    else
        return "dsn('"dsn"')" po,
                "recfm("recfm") lrecl("rl") block("bl")" ,
                "mgmtclas(COM#A091) space(10, 1000) cyl"
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 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

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

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

jWriteAll: procedure expose m.
parse arg m, optRdr
    if words(optRdr) <= 1 then
        optRdr = m.j.cRead optRdr
    interpret objMet(m, 'jWriteAll')
    return
endProcedure jWriteAll

jWriteNow: procedure expose m.
parse arg m, optRdr
    if words(optRdr) <= 1 then
        optRdr = m.j.cRead optRdr
    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
    if pos(m.j.cNoOC, opt) < 1 then
        call jOpen m, opt
    do ax=2 to arg()
        call jWriteAll m, arg(ax)
        end
    if pos(m.j.cNoOC, opt) < 1 then
        call jClose m
    return m
endProcedure jCat

jWriteNowImpl: procedure expose m.
parse arg m, opt rdr
    if pos(m.j.cNoOC, opt) < 1 then
        call jOpen rdr, jOpt(opt)
    do while jRead(rdr, line)
        call jWriteR m, line
        end
    if pos(m.j.cNoOC, opt) < 1 then
        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
        call err 'still open jReset('m',' arg')' / 3
    m.m.jReading = 0
    m.m.jWriting = 0
    interpret objMet(m, 'jReset')
    return m
endProcedure jReset

jOpen: procedure expose m.
parse arg m, opt
    if pos(m.j.cNoOC, opt) > 0 then
        return m
    call objMetClaM m, 'jOpen'
    if m.m.jReading | m.m.jWriting then
        return err('already opened jOpen('m',' opt')')
    interpret ggCode
    return m
endProcedure jOpen

jClose: procedure expose m.
parse arg m
    call objMetClaM m, 'jClose'
    if m.m.jReading | m.m.jWriting then
        interpret ggCode
    m.m.jReading = 0
    m.m.jWriting = 0
    return m
endProcedure jClose

/*--- analyze an option in oOpt and oVal -----------------------------*/
jOptWkTest: wkTest ??? deimplemented procedure expose m.
parse arg src, alone, val
    m.j.oOpt = ''
    if left(src, 1) \== '-' then do
        m.j.oVal = src
        return 0
        end
    sx = 2
    if alone \== '' then do
        sx = verify(src, alone, 'n', sx)
        if sx = 0  then
            sx = length(src)+1
        end
    if length(src) < sx then
        m.j.oVal = ''
    else if val == '' then
        call err 'bad opt "'src'" should contain only "'alone'"'
    else if pos(substr(src, sx, 1), val) < 1 then
        call err 'bad opt "'src'" should contain only "'alone'"' ,
                        'and/or 1 of "'val'" with value'
    else do
        sx = sx + 1
        m.j.oVal = substr(src, sx)
        end
    m.j.oOpt = substr(src, 2, sx-2)
    return 1
endProcedure jOpt

jOpt: procedure expose m.
parse arg src .
    if abbrev(src, '>>') then
        return m.j.cApp || substr(src, 3)
    else if pos(left(src, 1), m.j.cRead||m.j.cWri||m.j.cApp) < 1 then
        return m.j.cDum || src
    else
        return src
endProcedure jOpt

jIni: procedure expose m.
    if m.j.ini == 1 then
        return
    m.j.ini = 1
    m.j.cRead = '<'
    m.j.cWri = '>'
    m.j.cApp = '}'
    m.j.cObj = '%'
    m.j.cVar = '#'
    m.j.cDum = '/'
    m.j.cNoOC = '-'
    call oIni
    am = "call err 'call of abstract method"
    call classNew 'n JRW u f JREADING v, f JWRITING v', 'm',
        , "new call jReset m, arg, arg2, arg3",
        , "jRead"   am "jRead('m',' arg')'" ,
        , "jWrite" am "jWrite('m',' line')'" ,
        , "jWriteR" am "jWriteR('m',' var')'" ,
        , "jWriteAll call jWriteNowImpl m, optRdr",
        , "jWriteNow call jWriteNowImpl m, optRdr",
        , "jReset",
        , "jOpen" am" jOpen('m',' opt')'" ,
        , "jClose"
    am = "call err 'call errObject"
    call classNew 'n JRWErr u JRW', 'm',
        , "jWriteAll" er "jWriteAll 'm', optRdr'",
        , "jWriteNow" er "jWriteNow 'm', 'optRdr'",
        , "jClose" er "jClose 'm'"
    call classNew 'n JRWSay u JRW', 'm',
        , "jWrite say line",
        , "jWriteR call classOut , var, 'jOuR: '",
        , "jOpen if pos('<', opt) > 0 then",
            "call err 'can only write JRWSay.jOpen('m',' opt')';" ,
            "else m.m.jWriting = 1"
    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.jIn = oBasicNew('JRWEof')
    m.j.jOut = jOpen(oNew('JRWSay'))
    call outDest 'i', 'call jOut msg'
    call classNew "n JBuf u JRW, f .BUF s r", "m",
        , "jOpen return jBufOpen(m, opt)",
        , "jReset return jBufReset(m, arg)",
        , "jRead return jBufRead(m, var)",
        , "jWrite a = mAdd(m'.BUF', line); drop m.class.o2c.a",
        , "jWriteR call oCopy var, m'.BUF.'mInc(m'.BUF.0')"
    return
endProcedure jIni

jIn: procedure expose m.
parse arg arg
    return jRead(m.j.jIn, arg)
endProcedur jIn

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

jOuR: procedure expose m.
parse arg arg
    call jWriteR m.j.jOut, arg
    return
endProcedure jOut

/*--- 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
    return m
endProcedure jBuf

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
    return m
endProcedure jBufReset

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

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
    call oCopy m'.BUF.'nx, var
    return 1
endProcedure jBufRead

jBufWrite: procedure expose m.
parse arg m, line
    call oCopy line, m'.BUF.'mInc(m'.BUF.0')
    return 1
endProcedure jBufRead
/* copy j end *********************************************************/
/* copy o begin ******************************************************
    an object has a class and may call its methods
***********************************************************************/
oIni: procedure expose m.
     if m.o.ini = 1 then
         return
     m.o.ini = 1
     call classIni
     call oClassAdded m.class.classV
     call mRegister 'Class', 'call oClassAdded arg'
     call classNew 'n ORun u',
         , 'm oRun call err "call of abstract method oRun"'
     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
    m.cl.oAdr = 'O.'substr(cl, 7)            /* object adresses */
    m.cl.oCnt = 0
       new = 'new'
       m.cl.oMet.new = ''
       call oAddMethod cl'.OMET', cl
       call oAddFields mCut(cl'.FLDS', 0), cl
       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
    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
     if m.cl.0 \== '' then
         do x=1 to m.cl.0
             call oAddMethod mt, m.cl.x
             end
     return
endProcedure oAddMethod

/*--- add the the fields of class cl to stem f ----------------------*/
oAddFields: procedure expose m.
parse arg f, cl, nm
    if pos(m.cl, 'rv') > 0 then do
     do fx=1 to m.f.0
             if m.f.fx == nm then
                return 0
            end
        if nm == '' then do
             call mMove f, 1, 2
             m.f.1 = ''
             end
        else do
            call mAdd f, nm
            end
           return 0
        end
    if m.cl = 'f' then
        return oAddFields(f, m.cl.class, nm'.'m.cl.name)
    if m.cl.class \== '' then
        return oAddFields(f, m.cl.class, nm)
    if m.cl.0 = '' then
        return 0
    do tx=1 to m.cl.0
        call oAddFields f, m.cl.tx, nm
        end
    return 0
endProcedure oAddFields

/*--- create an an object of the class className --------------------*/
oBasicNew: procedure expose m.
parse arg className
    cl = class4Name(className)
    m.cl.oCnt = m.cl.oCnt + 1
    m = m.cl.oAdr'.'m.cl.oCnt
    if cl == m.class.classV then
        drop m.class.o2c.m
    else
        m.class.o2c.m = cl
    return m
endProcedure oBasicNew

/*--- create an an object of the class className
        and call it's new method ------------------------------------*/
oNew: procedure expose m.
parse arg className, arg, arg2, arg3
    m = oBasicNew(className)
    interpret classMet(className, 'new')
    return m
endProcedure oNew

/*--- 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, 'CLASS.CAST.') then
        return substr(obj, 12, pos(':', obj, 12)-12)
    if arg() >= 2 then
        return arg(2)
    return err('no class found for object' obj)
endProcedure objClass

/*--- 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
         call err 'no method in classMet('na',' me')'
     return m.cl.oMet.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 do
         c =  m.class.o2c.obj
         if symbol('m.c.oMet.me') == 'VAR' then
             return m.c.oMet.me
        end
    call objMetClaM obj, me
    return 'M="'m'";'ggCode
endProcedure objMet

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

/*--- 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
     ggCode = ggCla'.OMET.oCopy'
     interpret m.ggCode
     if ggCla == m.class.classV then
         drop m.class.o2c.t
     else
         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.class.classV), m, t)
endProcedure oCopy

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

/*--- return a new instance of a subclass of Run
        with code code in method oRun -------------------------------*/
oRunner: procedure expose m.
parse arg code
    t = classNew('n ORun* u', 'm oRun' code)
    return oNew(m.t.name)
endProcedure oRunner

/*--- run method oRun of object m -----------------------------------*/
oRun: procedure expose m.
parse arg m, arg, arg2, arg3
    interpret objMet(m, 'oRun')
    return
endProcedure oRun
/* 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 done in O, which, hower, extends the class definitions

      meta
      c     choice       name class
      f     field        name class
      m        method         name            met
      n     name         name class
      r     reference         class
      s     stem              class
      u     union                  stem
      v     value

      class expression (ce) allow the following syntax

      ce = name | 'v' | 'r' ce? | ('n' | 'f' | 'c') name ce
                  | 's' ce | 'm' name code | 'u' (ce (',' ce)*)?
      'm' and 'u' extend to the end of whole ce
***********************************************************************/
classIni: procedure expose m.
    if m.class.ini == 1 then
        return
    m.class.ini = 1
    call mapIni
        /* to notify other modules (e.g. O) on every new named class */
    call mRegisterSubject 'Class',
        , 'call classAddedListener subject, listener'
    m.class.0 = 0
    m.class.tmp.0 = 0
    call mapReset 'CLASS.N2C'  /* name to class */
        /* meta meta data: description of the class datatypes */
    m.class.classV = classNew('v')
    m.class.classR = classNew('r')
    m.class.class = classNew('n class u', '\')
    call classNew 'class',
            , 'c v v' ,
            , 'c r f CLASS r class' ,
            , 'c s f CLASS r class' ,
            , 'c u s r class',
            , 'c f' classNew('u f NAME v, f CLASS r class'),
            , 'c n' classNew('u f NAME v, f CLASS r class'),
            , 'c c' classNew('u f NAME v, f CLASS r class'),
            , 'c m' classNew('u f NAME v, f MET  v')
    return
endProcedure classIni

/*--- to notify a new listener about already defined classes --------*/
classAddedListener: procedure expose m.
parse arg subject, listener
    do y = 1 to m.class.0
        if m.class.y == 'n' then
            call mNotify1 'Class', listener, 'CLASS.'y
        end
    return
endProcedure classAddedListener

/*--- return the name of a class if it exists otherwise the class ---*/
className: procedure expose m.
parse arg cl
    if m.cl = 'n' 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

/*--- get or create a class from the given class expression
        arg(2) may contain options
            '\' do not search for existing class
            '+' do not finish class
            type (1 char) type of following args
        the remaining args are type expressions and will
            be added to the first union -----------------------------*/
classNew: procedure expose m.
parse arg clEx
    if arg() <= 1 then
        if mapHasKey(class.n2c, clEx) then
            return mapGet(class.n2c, clEx)
    oldTmp = m.class.tmp.0
    ox = verify(arg(2), '\+')
    if ox < 1 then
        ox = length(arg(2)) + 1
    opts = left(arg(2), ox-1)
    pr = substr(arg(2), ox, (length(arg(2)) = ox) * 2)
    t = classNewTmp(clEx)
    if arg() > 1 then do
        u = t
        do while m.u \== 'u'
            if m.u.class == '' then
                call err 'no union found' clEx
            u = m.u.class
            end
        do ax = 2 + (opts \== '' | pr \== '') to arg()
            call mAdd u, classNew(pr || arg(ax))
            end
        end
    p = classPermanent(t, pos('\', opts) < 1)
    if arg() <= 1 then
        call mapAdd class.n2c, clEx, p
    if p == t & pos('+', opts) < 1 then
        call mNotify 'Class', p
    m.class.tmp.0 = oldTmp
    return p
endProcedure classNew

/*--- create a temporary class
        with type ty, name nm and class expression ce ---------------*/
classNewTmp: procedure expose m.
parse arg ty nm ce
    if length(ty) > 1 then do
        if nm \== '' then
            call err 'class' ty 'should stand alone:' ty nm ce
        return class4Name(ty)
        end
    t = mAdd(class.tmp, ty)
    m.t.name = ''
    m.t.class = ''
    m.t.met  = ''
    m.t.0 = ''
    if pos(ty, 'v') > 0 then do
        if nm \== '' then
            call err 'basicClass' ty 'end of Exp expected:' ty nm ce
        end
    else if ty = 'u' then do
        fx = 0
        m.t.0 = 0
        ce = nm ce
        ux = 0
        do until fx = 0
            tx = pos(',', ce, fx+1)
            if tx > fx then
                sub = strip(substr(ce, fx+1, tx-fx-1))
            else
                sub = strip(substr(ce, fx+1))
            if sub \== '' then do
                ux = ux + 1
                m.t.ux = classNewTmp(sub)
                end
            fx = tx
            end
        m.t.0 = ux
        end
    else if nm == '' & ty \== 'r' then do
        call err 'basicClass' ty 'name or class Exp expected:' ty nm ce
        end
    else do
        if pos(ty, 'sr') > 0 then do
            if nm \== '' then
                m.t.class = classNewTmp(nm ce)
            end
        else do
            if pos(ty, 'cfmn') < 1 then
                call err 'unsupported basicClass' ty 'in' ty nm ce
            m.t.name = nm
            if ty = 'm' then
                m.t.met = ce
            else if ce = '' then
                call err 'basicClass' ty 'class Exp expected:' ty nm ce
            else
                m.t.class = classNewTmp(ce)
            end
        end
    return t
endProcedure classNewTmp

/*--- return the permanent class for the given temporary class
        an existing one if possible otherwise a newly created -------*/
classPermanent: procedure expose m.
parse arg t, srch
    if \ abbrev(t, 'CLASS.TMP.') then
        return t
    if m.t.class \== '' then
        m.t.class = classPermanent(m.t.class, srch)
    if m.t.0 \== '' then do
        do tx=1 to m.t.0
            m.t.tx = classPermanent(m.t.tx, srch)
            end
        end
                      /* search equal permanent class */
    do vx=1 to m.class.0 * srch
        p = class'.'vx
        if m.p.search then
               if classEqual(t, p, 1) then
                   return p
           end
    p = mAdd(class, m.t)
     m.p.name = m.t.name
    m.p.class = m.t.class
    m.p.met = m.t.met
    m.p.search = srch
    if m.t.0 > 0 then
        call mAddSt mCut(p, 0), t
    else
        m.p.0 = m.t.0
    if mapHasKey(class.n2c, p) then
        call err 'class' p 'already defined as className'
    else
        call mapAdd class.n2c, p, p
    if m.p = 'n' then do
        if right(m.p.name, 1) == '*' then
            m.p.name = left(m.p.name, length(m.p.name)-1) ,
                || substr(p, length('class.x'))
        if mapHasKey(class.n2c, m.p.name) then
            call err 'class' m.p.name 'already defined'
        else
            call mapAdd class.n2c, m.p.name, p
        if srch then
            call mNotify 'Class', p
        end
    return p
endProcedure classPermanent

/*--- return true iff the two classes are equal
        (up to the name pattern if lPat == 1) -----------------------*/
classEqual: procedure expose m.
parse arg l, r, lPat
        if m.l \== m.r | m.l.class \== m.r.class | m.l.0 \= m.r.0,
                 | m.l.met \== m.r.met then
            return 0
        if m.l.name \== m.r.name then
            if lPat \== 1 | right(m.l.name, 1) \== '*' ,
                    | \ abbrev(m.r.name,
                    , left(m.l.name, length(m.l.name)-1)) then
                return 0
        if m.l.0 == '' then
            return 1
        do sx=1 to m.l.0
            if m.l.sx \== m.r.sx then
                return 0
            end
        return 1
endProcedure classEqual

/*--- recursively ouput (with out:) the object a with class t -------*/
classOut: procedure expose m.
parse arg t, a, pr, p1
    return classOutDone(t, a, pr, p1)
     return x
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 = '' then do
        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 m.t == 'v' then
        return out(p1'=' m.a)
    if m.t == 'n' then
        return classOutDone(m.t.class, a, pr, p1':'m.t.name)
    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
            call 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.t1 == 'v'
        call out p1'union' || 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 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 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
    return
endProcedure mapIni

mapInline: procedure expose m.
parse arg pName
    if mapHasKey(map.inlineName, pName) then
        return mapGet(map.inlineName, pName)
    if m.map.inlineSearch == 1 then
        call mapReset map.inlineName, map.inline
    inData = 0
    name = ''
    do lx=m.map.inlineSearch to sourceline()
        if inData then do
            if abbrev(sourceline(lx), stop) then do
                inData = 0
                if pName = name then
                    leave
                end
            else do
                call mAdd act, strip(sourceline(lx), 't')
                end
            end
        else if abbrev(sourceline(lx), '/*<<') then do
            parse value sourceline(lx) with '/*<<' name '<<' stop
            name = strip(name)
            stop = strip(stop)
            if stop == '' then
                stop = name
            if words(stop) <> 1 | words(name) <> 1 then
                call err 'bad inline data' strip(sourceline(lx))
            if mapHasKey(map.inline, name) then
                call err 'duplicate inline data name' name ,
                    'line' lx strip(sourceline(lx), 't')
            act = mapAdd(map.inlineName, name,
                    , mCut('MAP.INLINE.' || (m.map.inline.0 + 1), 0))
            inData = 1
            end
        end
    if inData then
        call err 'inline Data' name 'at' m.map.inlineSearch,
            'has no end before eof'
    m.map.inlineSearch = lx + 1
    if name = pName then
        return act
    if arg() > 1 then
        return arg(2)
    call err 'no inline data named' pName
endProcedure mapInline

/*--- create a new map ----------------------------------------------*/
mapNew: procedure expose m.
parse arg opt
    return mapReset('MAP.'mInc('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
        call 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.a.vv
    drop m.a.ky
    return val
endProcedure mapRemove

/*--- remove all entries ---------------------------------------------*/
mapClear: procedure expose m.
parse arg a
    st = mapKeys(a)
    do kx=1 to m.st.0
        k = m.st.kx
        if length(k) > 200 then do
            k = left(k, 201)
            if symbol('m.a.k') == 'VAR' then/* ist noch hier */
                call mapClear m.a.k
            end
        drop m.a.k m.st.kx
        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 pA, pKy, fun
    a = pA
    ky = pKy
    do forever
        if length(ky) <= 200 then do
            if symbol('m.a.ky') \== 'VAR' then
                leave
            if fun == 'a' then
                call err 'duplicate key' pKy 'in map' pA
            return a'.'ky
            end
        k1 = left(ky, 201)
        if symbol('m.a.k1') \== 'VAR' then
            leave
        a = m.a.k1
        ky = substr(ky, 202)
        end
    if fun == '' then
        return ''
    opt = left('K', m.map.keys.pA \== '')
    if opt == 'K' then
        call mAdd m.map.Keys.pA, pKy
    do while length(ky) > 200
        k1 = left(ky, 201)
        n = mapNew(opt)
        m.a.k1 = n
        if a \== pA & opt == 'K' then
            call mAdd m.map.keys.a, ky
        a = n
        ky = substr(ky, 202)
        end
    return a'.'ky
endProcedure mapValAdr
/* copy map 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
***********************************************************************/
/*--- increase m.a and return it (fail if undefined) -----------------*/
mInc: procedure expose m.
    parse arg a
    m.a = m.a + 1
    return m.a
endProcedure mInc

/*--- 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

/*--- 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

/*--- 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 all elements of a stem together ----------------------------*/
mCat: procedure expose m.
parse arg st, mid
    if m.st.0 < 1 then
        return ''
    res = m.st.1
    do x=2 to m.st.0
        res = res || mid || m.st.x
        end
    return res
endProcedure mCat

/*--- notify all listeners of subject subj with argument arg --------*/
mNotify: procedure expose m.
parse arg subj, arg
    if symbol('m.m.subLis.subj') \== 'VAR' then
        call err 'subject' subj 'not registered'
    do lx=1 to m.m.subLis.subj.0
        call mNotify1 subj, lx, arg
        end
    return
endProcedure mNotify

/*--- notify the listener listener of subject subj with arg ---------*/
mNotify1: procedure expose m.
parse arg subject, listener, arg
    interpret m.m.subLis.subject.listener
    return
endProcedure mNotify1

/*--- notify subject subject about a newly registered listener
        or a new subject about previously registered listener -------*/
mNotifySubject: procedure expose m.
parse arg subject, listener
    interpret m.m.subLis.subject
    return
endProcedure mNotifySubject

/*--- register a new subject with the code for mNotifySubject -------*/
mRegisterSubject: procedure expose m.
parse arg subj, addListener
    if symbol('m.m.subLis.subj') == 'VAR' then
        call err 'subject' subj 'already registered'
    m.m.subLis.subj = addListener
    if symbol('m.m.subLis.subj.0') \== 'VAR' then do
         m.m.subLis.subj.0 = 0
         end
    else do lx=1 to m.m.subLis.subj.0
        call mNotifySubject subj, lx
        end
    return
endProcedure registerSubject

/*--- register a listener to subject subj with the code for mNotify -*/
mRegister: procedure expose m.
parse arg subj, notify
    if symbol('m.m.subLis.subj.0') \== 'VAR' then
         m.m.subLis.subj.0 = 0
    call mAdd 'M.SUBLIS.'subj, notify
    if symbol('m.m.subLis.subj') == 'VAR' then
         call mNotifySubject subj, m.m.subLis.subj.0
    return
endProcedure mRegister

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 || '.'
    return
endProcedure mIni
/* copy m end *********************************************************/
/* copy stringUt begin  ***********************************************/
/*--- return the count of occurrences of needle in heyStack ----------*/
posCount: procedure
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 stringUt end   ***********************************************/
/* copy err begin ******************************************************
    messages, errorhandling,help
***********************************************************************/
/* configure err -----------------------------------------------------*/
errReset: procedure expose m.
parse arg m.err.opt, ha
    if pos('I', translate(m.err.opt)) > 0 then
        if sysVar('sysISPF') = 'ACTIVE' then
            call adrIsp 'control errors return'
    if ha == '' then
        drop m.err.handler
    else
        m.err.handler = ha
    return
endSubroutine errReset

/*--- error routine: abend with message ------------------------------*/
err:
    parse arg ggTxt, ggOpt
    drop err handler opt
    if ggOpt == '' & symbol('m.err.handler') == 'VAR' then
        interpret value('m.err.handler')
    call outDest
    call errSay ggTxt, 'e'
    if ggOpt == '' & symbol('m.err.opt') == 'VAR' then
        ggOpt = value('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

/*--- 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 outLn(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

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

/*--- 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 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 a message to the current destination -------------------*/
out: procedure expose m.
parse arg msg
    if symbol('m.err.out') \== 'VAR' then
        call outDest
    interpret m.err.out
    return 0
endProcedure out

/*--- output all lines (separated by '\n') of all args --------------*/
outLn: 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 outLn

/*--- set and return some frequent destinations for out -------------*/
outDest: procedure expose m.
parse arg ty, a
    if ty == '' | symbol('m.err.out') \== 'VAR' then
        m.err.out = 'say msg'
    if ty == 's' then
        m.err.out = 'st='quote(a)';sx=m.st.0+1;m.st.0=sx;m.st.sx=msg'
    else if ty == 'i' then
        m.err.out = a
    else if \ abbrev('=', ty) then
        call err 'bad type in outDes('ty',' a')'
    return m.err.out
endProcedure outDest

/*--- 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
/* copy err end   *****************************************************/

}¢--- A540769.WK.REXX(WSH1003) cre=2010-02-12 mod=2010-12-03-12.57.18 A540769 ---
/* rexx ****************************************************************
     edit macro
     wsh old -> new syntax
--- history ------------------------------------------------------------
12. 3.10 w.keller neu
***********************************************************************/
/*--- main code wsh --------------------------------------------------*/
    call errReset 'hI'
    call adrEdit 'macro (mArgs)'
    say 'wshQ macro('mArgs')'
    call adrEdit "c '$)' '}' all"        , 4
    call adrEdit "c '$(' '$-{' all"      , 4
    call adrEdit "c '$@¢' '$@=¢' all"    , 4
    call adrEdit "c '$@{' '$@¢' all"     , 4
    call adrEdit "c '$}' '$!' all"       , 4
    exit
/* 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 jOut 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 jOut 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

/*--- 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 = ''
        oldTrap = outtrap()
        call outtrap m.adrTsoAl.
        res = dsnAlloc(spec, pDi, pDD, '*')
        if oldTrap = '' then
            call outtrap off
        else
            call outtrap oldTrap append
        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"')"
    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

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
    bl = 32760
    if abbrev(atts, ':') then do
        rl = substr(atts, 3)
        if abbrev(atts, ':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(atts, 2, 1) 'B'
            end
        end
    if pos('(', dsn) > 0 then
        po = 'dsntype(library) dsorg(po)'
    else
        po = ''
    dsn = dsnSetMbr(dsn)
    if forCsm == 1 then
        return "dataset('"dsn"')" po,
                "recfm("space(recfm, 0)") lrecl("rl") blkSize("bl")" ,
                "mgmtclas(COM#A091) space(10, 1000) cylinder"
    else
        return "dsn('"dsn"')" po,
                "recfm("recfm") lrecl("rl") block("bl")" ,
                "mgmtclas(COM#A091) space(10, 1000) cyl"
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 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 outPush
    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

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 = ';'
    call out 'err cleanup begin' cl
    call errInterpret cl
    call out '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

/*--- 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

/*--- 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 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

/*--- 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

/*--- if function ----------------------------------------------------*/
if: procedure expose m.
parse arg co, ifTrue, ifFalse
    if co then
        return ifTrue
    else
        return ifFalse
endProcedure if
/* copy err end   *****************************************************/
/* copy out begin ******************************************************
    out interface with simplistic implementation
***********************************************************************/
outIni: procedure expose m.
parse arg msg
    if m.out.ini == 1 then
        return
    m.out.ini = 1
    m.out.dst = ''
    return
endProcedure outIni

/*--- output a message to the current destination -------------------*/
out: procedure expose m.
parse arg msg
    if m.out.ini \== 1 then
        call outIni
    if m.out.dst == '' then do
        say msg
        end
    else do
        st = m.out.dst
        sx = m.st.0 + 1
        m.st.0 = sx
        m.st.sx = msg
        end
    return 0
endProcedure out

/*--- push an out destination ---------------------------------------*/
outPush: procedure expose m.
parse arg m.out.dst
    return
endProcedure outPush
/* copy out end   *****************************************************/
}¢--- A540769.WK.REXX(WSH30) cre=2014-07-10 mod=2015-02-18-12.41.26 A540769 ----
/* rexx ****************************************************************
  wsh: walter's rexx shell                                   version 2.2
  interfaces:                                                   17. 6.14
      edit macro: for adhoc evaluation or programming
              either block selection: q or qq and b or a
              oder mit Directives ($#...) im Text
      wsh i:  tso interpreter
      batch:  input in dd wsh
      docu:   http://chw20025641/host/db2wiki/pmwiki.php?n=Main.Wsh
      syntax: http://chw20025641/host/db2wiki/pmwiki.php?n=Main.WshSyn
|||achtung $@.sqlRdr() funktioniert nicht nur $@..¢sqlRdr() $!
|||    sqlSel schreib !$#out |||||
|||    einheitliches sql select/rdr syntax in wsh (mit ftab oder ohne|)
|||sql select aus rz2 muss wie csmExRx erfolgen (via WSH) ||||

--- history ------------------------------------------------------------
17.06.14 f: %tS %tT und %tN mit MicroSekunden
*********/ /*** end of help ********************************************
16.06.14 csmCopy auch für LoadModule usw.
30.05.14 fix sql4obj fuer rcm profex
14.04.14 class vor obj, mit lazy
19.03.14 ii = installation Info
 9.01.14 walter: redesign formatting (fmt eliminiert), csm.div.p0.exec
 3.12.13 walter: db2 interface radikal geputzt
 3.10.13 walter: uCount fuer TSO <-> unitCount fuer Csm
23. 9.13 walter: ws2 syntax
 6. 2.13 w.keller csmCopy aendert mgmtClas für neue lokale Files
23. 1.13 w.keller sqlErrHandler sowie sqlRx und sql
11. 6.12 w.keller sqlUpdComLoop
23. 5.12 w.keller fix sqlStmt: drop accepts -204
31. 3.12 w.keller sql Query interface incl. sql über CSM
10. 2.12 w.keller div catTb* und eLong
 2. 6.11 w.keller sql error with current location and dsnTiar
 2. 5.11 w.keller sqlStmt etc..
16. 3.11 w.keller basic new r '' ==> r m.class_O
28. 2.11 w.keller compFile by exprBlock, fix $@do forever, |
 7. 2.11 w.keller cleanup block / with sqlPush....
 2. 2.11 w.keller simplified syntax, ast for blocks, ? for help
19. 6.10 w.keller open/close stacked mit jUsers, framed eliminiert
19. 5.10 w.keller class wird direkt erzeugt ohne temporary
18. 4.10 w.keller scanUtilInto ==> testCase noch einbauen
17. 4.10 w.keller oMutate in fileTsoList eingebaut
14. 1.10 w.keller batch interface no longer dies on openIfNotYet
                  CSM.RZ1.P0.EXEC korrigiert
***********************************************************************/

/*--- main code wsh --------------------------------------------------*/
    call errReset 'hI'
    m.mylib = A540769.WK.REXX
    call wshLog
    m.astSay = 0
    parse arg spec
    isEdit = 0
    if spec = '' & m.err.ispf then do /* z/OS edit macro */
        isEdit = adrEdit('macro (spec) NOPROCESS', '*') == 0
        if isEdit then do
            call adrEdit '(d) = dataset'
            call adrEdit '(m) = member'
            m.editDsn = dsnSetMbr(d, m)
            if spec = '' & abbrev(m.editDsn, 'A540769.WK.REXX(WS') then
                spec = 't'
            end
        end
    if spec = '?' then
        return help()
    if translate(word(spec, 1)) == 'T' then
        return wshTst(subword(spec, 2))
    f1 = spec
    rest = ''
    inp = ''
    out = ''
    call wshIni
    if m.err.os == 'TSO' then do
        if isEdit then do
            parse value wshEditBegin(spec) with inp out
            end
        else if sysvar('sysEnv') = 'FORE' then do
            end
        else do
            inp = file('dd(wsh)')
            useOut = listDsi('OUT FILE')
            if \ (useOut = 16 & sysReason = 2) then
                out = file('dd(out)')
            end
        end
    else if m.err.os == 'LINUX' then do
        inp = file('&in')
        out = file('&out')
        end
    else
        call err 'implement wsh for os' m.err.os
    m.wshInfo = 'compile'
    call compRun spec, inp, out, wshInfo
    if isEdit then
        call wshEditEnd
exit 0

wshIni: procedure expose m.
    call compIni
    call sqlIni
    call scanWinIni
    return
endProcedure wshIni

/*--- test hook ----------------------------------------------------*/
wshHook_T: procedure expose m.
parse arg cmp
    rest = strip(scanLook(m.cmp.scan))
    call jClose m.cmp.scan
    return wshTst(rest)
endProcedure wshHook_t

wshLog: procedure expose m.
parse arg msg, st
    lNm = 'dsn.wsh.log'
    lN2 = lNm'.R' || ( random() // 19)
    f1 = dsnAlloc('dd(log) mod' lNm '::f', , , '*')
    if datatype(f1, 'n') then do
        lNm = lNm'.R' || ( random() // 19)
        f1 = dsnAlloc('dd(log) old' lNm '::f', , , '*')
        if datatype(f1, 'n') then do
            say 'could not allocate log' lNm lN2
            return
            end
        end
    parse source . . s3 .
    o.1 = 'wshLog' m.myLib'('s3')' sysvar(sysnode) ,
            'job='mvsvar('symdef', 'jobname') ,
             'user='userid() date('o') time()
    if msg <> '' then
        o.2 = left(msg, 80)
    ox = 1 + (msg <> '')
    if st <> '' then do sx=1 to m.st.0
        ox = ox+1
        o.ox = left(m.st.sx, 80)
        end
    call writedd log, o., ox
    call tsoClose log
    call tsoFree log
    return
endProcedure wshLog

wshTst: procedure expose m.
parse arg rest
    if rest = '' then do /* default */
        call csmcopy 'CMN.DIV.P0.DB2J.#000197.LLB' ,
                   , 'RZ1/A540769.TST.LXB'
        return 0
        call csmcopy 'RZ1/A540769.TST.PS' ,
                   , 'RZ4/A540769.TST.PO3(EINS)'
        return 0
        call csmcopy 'RZ1/A540769.TST.LCTL(BBB)',
                   , 'RZ4/A540769.TST.PS'
        return 0
        call csmcopy 'A540769.WK.LLB' ,
                   , 'RZ1/A540769.TST.LLB'
        call csmCopL 'RZ4/A540769.WK.JCL(QZ*)',
                   , 'RZ1/A540769.TST.yCL'
        return 0
        call tstfTst
        call sqlConnect DBAF
        call catColCom 'cmnBatch', 'DSN_PGROUP_TABLE',
                     , 'cmnBatch', 'DSN_PGROUP_TABLE_new'
        call sqlDisConnect DBAF
        return 0
        end
    c = ''
    do wx=1 to words(rest)
        c = c 'call tst'word(rest, wx)';'
        end
    if wx > 2 then
        c = c 'call tstTotal;'
    say 'wsh interpreting' c
    interpret c
    exit 0
endProcedure wshTst

/*--- i hook: interpret user input: rexx, expr, data or shell -------*/
wshHook_I: procedure expose m.
parse arg cmp
    inp = strip(scanLook(m.cmp.scan))
    call jClose m.cmp.scan
    mode = '*'
    do forever
        if pos(left(inp, 1), '/;:*@.-=') > 0 then
            parse var inp mode 2 inp
        if mode == '/' then
            exit 0
        mode = translate(mode, ';', ':')
        if inp <> '' then do
            say 'as' mode 'interpreting' inp
            if mode = ';' then
                interpret inp
            else if mode = '*' then
                interpret 'say' inp
            else do
                call errReset 'h', 'say "******* intercepting error";',
                    'call errSay ggTxt; return "";'
                call oRun compile(comp(jBuf(inp)), mode)
                call errReset 'h'
                end
            end
        say 'enter' mode 'expression, / for end, ; or * for Rexx' ,
                                                 '@ . - =  for wsh'
        parse pull inp
        end
endProcedure wshInter

/*--- sql hook -----------------------------------------------------*/
wshHook_S: procedure expose m.
parse arg cmp
    s = m.cmp.scan
    ki = '='
    call scanVerify s, m.m.chSpa
    if scanVerify(s, m.comp.chKind) then
        ki = left(m.s.tok, 1)
    call scanChar s
    rest = m.s.tok
    call scanNl s
    dbSy = word(rest, 1)
    if abbrev(dbSy, '-') | \ (length(dbSy) = 4 ,
                   | (length(dbsy) = 8 & substr(dbSy,4,1) == '/')) then
        dbSy = ''
    else
        rest = subWord(rest, 2)
    call sqlConnect dbSy
    r = oRunner(compAst2Rx(cmp, ';', compUnit(cmp, ki, '$#')))
    call sqlStmtsOpt r, rest
    call sqlDisConnect
    return ''
endProcedure wshHook_s


wshEditBegin: procedure expose m.
parse arg spec
    dst = ''
    li = ''
    m.wsh.editHdr = 0
    pc = adrEdit("process dest range Q", 0 4 8 12 16)
    if pc = 16 then
        call err 'bad range must be q'
    if pc = 0 | pc = 8 then do
        call adrEdit "(rFi) = lineNum .zFrange"
        call adrEdit "(rLa) = lineNum .zLrange"
    /*  say 'range' rFi '-' rLa */
        end
    else do
        rFi = ''
    /*  say 'no range' */
        end
    if pc = 0 | pc = 4 then do
        call adrEdit "(dst) = lineNum .zDest"
    /*  say 'dest' dst */
        dst = dst + 1
        end
    else do
    /*  say 'no dest' */
        if adrEdit("find first '$#out' 1", 4) = 0 then do
            call adrEdit "(dst) = cursor"
    /*      say '$#out' dst   */
            call adrEdit "(li) = line" dst
            m.wsh.editHdr = 1
            end
        end
    m.wsh.editDst = dst
    m.wsh.editOut = ''
    if dst \== '' then do
        m.wsh.editOut = jOpen(jBufTxt(), '>')
        if m.wsh.editHdr then
            call jWrite m.wsh.editOut, left(li, 50) date('s') time()
        end
    if rFi == '' then do
        call adrEdit "(zLa) = lineNum .zl"
        if adrEdit("find first '$#' 1", 4) = 0 then do
            call adrEdit "(rFi) = cursor"
            call adrEdit "(li) = line" rFi
            if abbrev(li, '$#out') | abbrev(li, '$#end') then
                rFi = 1
            if rFi < dst & dst \== '' then
                rLa = dst-1
            else
                rLa = zLa
            end
        else do
            rFi = 1
            rLa = zLa
            end
        end
    /*  say 'range' c1 'rc' pc':' rFi '-' rLa   'after' dst */

    m.wsh.editIn = jOpen(jBuf(), m.j.cWri)
    do lx=rFi to rLa
        call adrEdit "(li) = line" lx
        call jWrite m.wsh.editIn, li
        end
    call errReset 'h',
             , 'return wshEditErrH(ggTxt, ' rFi',' rLa')'
    return jClose(m.wsh.editIn) m.wsh.editOut
endProcedure wshEditBegin

wshEditEnd: procedure expose m.
    call errReset 'h'
    if m.wsh.editOut == '' then
        return 0
    call jClose(m.wsh.editOut)
    lab = wshEditInsLinSt(m.wsh.editDst, 0, , m.wsh.editOut'.BUF')
    call wshEditLocate max(1, m.wsh.editDst-7)
    return 1
endProcedure wshEditEnd

wshEditLocate: procedure
parse arg ln
    call adrEdit '(la) = linenum .zl'
    call adrEdit 'locate '  max(1, min(ln, la - 37))
    return
endProcedure wshEditLocate

wshEditErrH: procedure expose m.
parse arg ggTxt, rFi, rLa
    call errCleanup
    call errReset 'h'
    call errMsg ' }'ggTxt
    call mMove err, 1, 2
    isScan = 0
    if wordPos("pos", m.err.4) > 0 ,
        & pos(" in line ", m.err.4) > 0 then do
        parse var m.err.4 "pos " pos .     " in line " lin":"
        if pos = '' then do
            parse var m.err.4 " line " lin":"
            pos = 0
            end
        isScan = lin \== ''
        end
    m.err.1 = '***' m.wshInfo 'error ***'
    if m.wshInfo=='compile' & isScan then do
        do sx=1 to m.err.0
            call out m.err.sx
            end
        lab = rFi + lin
        if pos \= '' then
            lab = wshEditInsLin(lab, 'msgline', right('*',pos))
        lab = wshEditInsLinSt((rFi+lin),0, 'msgline', err)
        call wshEditLocate rFi+lin-25
        end
    else do
        if m.wsh.editOut \== '' then do
            do sx=1 to m.err.0
                call jWrite m.wsh.editOut, m.err.sx
                end
            lab = wshEditInsLinSt(m.wsh.editDst, 0, ,
                , m.wsh.editOut'.BUF')
            call wshEditInsLinSt m.wsh.editDst, m.wsh.editHdr,
                , msgline, err
            call wshEditLocate max(1, m.wsh.editDst-7)
            end
        else do
            do sx=1 to m.err.0
                say m.err.sx
                end
            end
        end
    call errCleanup
    exit
endSubroutine wshEditErrH

wshEditInsLinCmd: procedure
parse arg wh
    if dataType(wh, 'n')  then do
        if adrEdit("label" wh "= .a", 0 8 12) \= 12 then
            return 'line_before .a ='
        else
            return 'line_after .zl ='
        end
    else if left(wh, 1) == '.' then
        return 'line_before' wh '='
    else
        return wh
endProcedure wshEditInsLinCmd

wshEditInsLin: procedure
parse arg wh, type
    cmd = wshEditInsLinCmd(wh)
    do ax=3 to arg()
        li = strip(arg(ax), 't')
        if li == '' then
            iterate
        if translate(type) = 'MSGLINE' then do while length(li) > 72
            sx = lastPos(' ', li, 72)
            if sx < 10 then
                sx = 72
            one = left(li, sx)
            li = '    'strip(substr(li, sx))
            call adrEdit cmd type "(one)"
            end
        call adrEdit cmd type "(li)", 0 4
        end
    return cmd
endProcedure wshEditInsLin

wshEditInsLinSt: procedure expose m.
parse arg wh, pl, type, st
    if wh == '' then do
        do ox=1 to m.st.0
            say m.st.ox
            end
        return ''
        end
    wh = wh + pl
    cmd = wshEditInsLinCmd(wh)
    do ax=1 to m.st.0
        call wshEditInsLin cmd, type, m.st.ax
        end
    return cmd
endProcedure wshEditInsLinSt


/*** end wsh, begin all copies ****************************************/
/* copy time begin -----------------------------------------------------
 11.05.23 wk: numeric digits transparent: in jeder Procedure drin
              time2jul, tst externalisiert
----------------------------------------------------------------------*/
/*--- read timeZoneOffset and leapSeconds registers
        and set variables for uniq ----------------------------------*/
timeReadCvt: procedure expose m.
parse arg debug
    numeric digits 15
    /* offsets documented in z/OS Data Areas  Vol.1 */
    cvtOH      = '00000010'          /* cvt control block Address */
    cvtext2O   = x2d('00000560') /* offset  to extension 2    */
    cvtldtoO   = x2d('00000038') /* offset to timezone    */
    cvtlsoO    = x2d('00000050') /* offset to leapSeconds */

    /* CVT CB        address               + extention2   */
    cvtExt2A       = C2D(STORAGE(cvtOH,4)) + cvtext2O
    /* cvtLdto timeZone              address +offset      */
    m.timeZone     = c2d(STORAGE(d2x(cvtext2A+cvtldtoO, 8), 8))
    m.timeStckUnit = 1e-6 / 256 / 16
    /* cvtLso LeapSecs               address +offset      */
    m.timeLeap     = C2D(STORAGE(d2x(cvtext2A+cvtlsoO, 8), 8))
    m.timeUQDigits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345678'
                         /* find lrsn of day 0 */
    m.timeUQZero = timeGmt2Lrsn('2004-12-31-00.00.22.000000')
                         /* 0 out last 6 bits  */
    m.timeUQZero = b2x(overlay('000000', x2b(m.timeUQZero), 43))
    if debug == 1 then do
      say 'stckUnit          =' m.timeStckUnit
      say 'timeLeap          =' d2x(m.timeLeap,16) '=' m.timeLeap ,
                   '=' format(m.timeLeap * m.timeStckUnit, 9,3) 'secs'
      say 'timeZone          =' d2x(m.timeZone,16) '=' m.timeZone,
                   '=' format(m.timeZone  * m.timeStckUnit, 6,3) 'secs'
      say "cvtext2_adr       =" d2x(cvtExt2A, 8)
      say 'timeUQZero        =' m.timeUQZero
      say 'timeUQDigis       =' ,
                    length(m.timeUQDigits) 'digits' m.timeUQDigits
    end
    m.timeReadCvt = 1
    return
endSubroutine timeReadCvt

timestampParse:
    parse arg yyyy'-'mo'-'dd'-'hh'.'mm'.'ss'.'ffffff
    return

/*--- GMT timestamp yyyy-mm.... -> stck value char(8)
         BLSUXTID is described in z/OS V1R7.0 MVS IPCS Customization
         BLSUXTID format : mo/dd/yyyy hh:mm:ss.ffffff ---------------*/
timeGmt2Stck: procedure expose m.
    parse arg tst
    call timestampParse tst
    tDate = mo'/'dd'/'yyyy' 'hh':'mm':'ss'.'ffffff
    ACC=left('', 8, '00'x)
    ADDRESS LINKPGM "BLSUXTID TDATE ACC"
    RETURN acc
endProcedure timeGmt2Stck

/*--- GMT timestamp yyyy-mm.... -> stck value in hex(16) ------------*/
timeGmt2LRSN: procedure expose m.
    return c2x(left(timeGmt2Stck(arg(1)), 6))
endProcedure timeGmt2LRSN

/*--- LZT (Locale Zurich Tst -> stck value in hex(16) ---------------*/
timeLZT2LRSN: procedure expose m.
parse arg tst
    numeric digits 23
    if m.timeReadCvt \== 1 then
        call timeReadCvt
    return left(d2x(c2d(timeGmt2Stck(tst)) ,
                     - m.timeZone + m.timeLeap, 16), 12)
endProcedure timeLZT2LRSN
/*--- conversion from Stck Clock Value to GMT Timestamp
        BLSUXTOD is described in z/OS V1R7.0 MVS IPCS Customization
        input -> + leapseconds -> output ----------------------------*/
timeStck2Gmt: PROCEDURE expose m.
parse arg stck
  stck = left(stck, 8, '00'x)
  TDATE = COPIES('0' , 26)
  ADDRESS LINKPGM "BLSUXTOD stck TDATE"
  /* return format   : mo/dd/yyyy hh:mm:ss.ffffff */
  /* Timestamp format: yyyy-mm-dd-hh.mm.ss.ffffff */
  parse var TDATE mo '/' dd '/' yyyy hh ':' mm ':' ss '.' ffffff
RETURN yyyy'-'mo'-'dd'-'hh'.'mm'.'ss'.'ffffff
endProcedure timeStck2Gmt

/*--- conversion from Lrsn Clock Value to GMT Timestamp -------------*/
timeLrsn2Gmt:
    return timeStck2Gmt(x2c(arg(1)))
endProcedure timeLrsn2Gmt

/*--- conversion from Lrsn Clock Value to LZT Local Zurich Time -----*/
timeLrsn2LZT: procedure expose m.
parse arg lrsn
    numeric digits 23
    if m.timeReadCvt \== 1 then
        call timeReadCvt
    return timeStck2Gmt(d2c(x2d(left(lrsn, 16, 0)) ,
                           + m.timeZone-m.timeLeap))
endProcedure timeLrsn2LZT

/*--- timestamp to julian --------------------------------------------*/
time2jul: procedure expose m.
parse arg yyyy '-' mm '-' dd '-'
        /* date function cannot convert to julian, only from julian
            ==> guess a julian <= the correct and
                try the next values
        */
    j = trunc((mm-1) * 29.5) + dd
    yy = right(yyyy, 2)
    do j=j by 1
        j = right(j, 3, 0)

        d = date('s', yy || j, 'j')
        if substr(d, 3) = yy || mm || dd then
            return yy || j
        end
    return
endProcedure time2jul
/* convert a uniq variable to lrsn ************************************/
timeUniq2lrsn: procedure expose m.
parse arg uniq
    numeric digits 15
    if m.timeReadCvt \== 1 then
        call timeReadCvt
    uniq = left(uniq, 8, 'A')
    d42 = d2x(q2i(uniq, m.timeUQDigits))
    d48 = b2x('00'x2b(d42)'000000')
    lrsn = right(d2x(x2d(d48) + x2d(m.timeUQZero)), 12, 0)
    return lrsn
endProcedure uniq2lrsn
/*--- translate a number in q-system to decimal
       arg digits givs the digits corresponding to 012.. in the q sysem
       q = length(digits) --------------------------------------------*/
q2i: procedure expose m.
parse arg v, digits
    b = length(digits)
    i = 0
    do x = 1 to length(v)
        q = substr(v, x, 1)
        r = pos(q, digits)
        if r < 1 then
            call err 'bad digit' q 'in' v 'valid digits' digits
        i = i * b + r - 1
        end
    return i
endProcedure q2i
/*--- translate a decimal number to q-system - inverse of q2i --------*/
i2q: procedure expose m.
parse arg i, digits
    if i = 0 then
        return left(digits, 1)
    b = length(digits)
    v = ''
    do while i > 0
        v = substr(digits, 1 + (i // b), 1) || v
        i = i % b
        end
    return v
endProcedure i2q
/* copy time end -----------------------------------------------------*/
/* copy sort begin ****************************************************/
sort: procedure expose m.
parse arg i, o, cmp
    if cmp == '' then
        m.sort.comparator = "cmp = m.l.l0 <<= m.r.r0"
    else if length(cmp) < 6 then
        m.sort.comparator = "cmp = m.l.l0" cmp "m.r.r0"
    else if pos(';', cmp) < 1 then
        m.sort.comparator = "aLe = l'.'l0; aRi = r'.'r0; cmp =" cmp
    else
        m.sort.comparator = "aLe = l'.'l0; aRi = r'.'r0;" cmp
    call sort1 i, 1, m.i.0, o, 1, sort.work, 1
    m.o.0 = m.i.0
    return
endProcedure sort

sort1: procedure expose m.
parse arg i, i0, le, o, o0, w, w0
    if le <= 3 then do
        if le = 3 then do
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, w, w0
            call sortMerge i, i0+2, i0+3, w, w0, w0+2, o, o0
            end
        else if le = 2 then
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, o, o0
        else if le = 1 then
            m.o.o0 = m.i.i0
        return
        end
    h = (le + 1) % 2
    call sort1 i, i0,   h,    o, o0+le-h, w, w0
    call sort1 i, i0+h, le-h, w, w0,      o, o0
    call sortMerge o, o0+le-h, o0+le, w, w0, w0+le-h, o, o0
    return
endProcedure sort1

sortMerge: procedure expose m.
parse arg l, l0, le, r, r0, re, o, o0
    do while l0 < le & r0 < re
        interpret m.sort.comparator
        if cmp then do
            m.o.o0 = m.l.l0
            l0 = l0 + 1
            end
        else do
            m.o.o0 = m.r.r0
            r0 = r0 + 1
            end
        o0 = o0 + 1
        end
    do while l0 < le
        m.o.o0 = m.l.l0
        l0 = l0 + 1
        o0 = o0 + 1
        end
    do while r0 < re
        m.o.o0 = m.r.r0
        r0 = r0 + 1
        o0 = o0 + 1
        end
    return
endProcedure sortMerge
/* copy sort end   ****************************************************/
/* copy match begin ***************************************************/
/*--- 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 comp begin *****************************************************
    the shell compiler
         syntax and semantics of the shell language see javaDoc
***********************************************************************/
/***** initialisation *************************************************/
/*--- module initialisation ------------------------------------------*/
compIni: procedure expose m.
    if m.compIni = 1 then
        return
    m.compIni = 1
    call pipeIni
    call scanReadIni
    cc = classNew('n Compiler u')
    call mNewArea 'COMP.AST', '='

    m.comp.chOp   = '.-<@|?%^'
    m.comp.chKind = '.-=#@:'
    m.comp.chKiNO = '=#:'
    call mPut 'COMP.INFO..', "object"
    call mPut 'COMP.INFO.-', "string"
    call mPut 'COMP.INFO.=', "skeleton"
    call mPut 'COMP.INFO.#', "text"
    call mPut 'COMP.INFO.@', "rexxShell"
    call mPut 'COMP.INFO.:', "pureShell"

    m.comp.chDol = '$'
    m.comp.chSpa = ' 'x2c('09')
    call mPut 'COMP.EXTYPE.b', m.comp.chDol'{}'       /* braces */
    call mPut 'COMP.EXTYPE.d', m.comp.chDol            /* data */
    call mPut 'COMP.EXTYPE.s', m.comp.chDol            /* strip */
    call mPut 'COMP.EXTYPE.w', m.comp.chDol||m.comp.chSpa /* word */

    m.comp.idChars = m.ut_alfNum'@_'
    m.comp.wCatC = 'compile'
    m.comp.wCatO = 'out'
    m.comp.wCatS = 'do withNew with for forWith ct proc table'
    return
endProcedure compIni

compReset: procedure expose m.
parse arg m
    m.m.scan = scanRead(,,'0123456789')
    return m
endProcedure compReset

/*--- constructor of Compiler ----------------------------------------*/
comp: procedure expose m.
parse arg src
    nn = oNew('Compiler')
    m.nn.cmpRdr = j2rdr(src)
    return nn
endProcedure comp

/**** user interface **************************************************/
/*--- compile and run ------------------------------------------------*/
compRun: procedure expose m.
parse arg spec, inO, ouO, infoA
    cmp = comp(inO)
    r = compile(cmp, spec)
    if infoA \== '' then
        m.infoA = 'run'
    if ouO \== '' then
        call pipe '+F', ouO
    call oRun r
    if ouO \== '' then
        call pipe '-'
    return 0
endProcedure compRun

/*--- compile the source and return an oRunner with the code ---------*/
compile: procedure expose m.
parse arg m, spec
    call compReset m
    s = m.m.scan
    if spec = '' then
        spec = '@'
    else
        spec = strip(spec, 'l')
    if m.m.cmpRdr == '' then do
        call scanSrc s, spec
        end
    else do
        call scanReadReset s, m.m.cmpRdr
        call jOpen s, m.j.cRead, spec
        end
    m.m.out = ''
    m.m.end = ''
    res = compAst(m, '¢')
    eOld = m.err.count
    do forever
        one = ''
        if pos(scanLook(s, 1), m.comp.chKind'*') > 0 then do
            call scanChar s, 1
            ki = m.s.tok
            one = compileOne(m, ki)
            end
        else if \ scanName(s) then do
            call scanErr s, 'kind or hook expected after $#'
            end
        else if m.s.tok == 'out' then do
            m.m.out = scanPos(s)
            leave
            end
        else if m.s.tok == 'end' then do
            if m.m.end = '' then
                m.m.end = scanPos(s)
            one = compileOne(m)
            end
        else if m.s.tok == 'version' then do
            call scanSpace s
            if \ scanWord(s) | m.s.tok \== '2.0' then
                call scanErr s, 'only version 2.0 is supported'
            call scanNl s, 1
            end
        else do
            one = compileOne(m, , m.s.tok)
            end
        if one \== '' then
            call mAdd res, one
        if scanEnd(s) then
            leave
        if \ scanLit(s, '$#') then
            return scanErr(s, m.comp.info.ki "expected: compile",
                 ki "stopped before end of input")
        end
    if m.m.cmpRdr \== '' then
        call jClose s
    if m.err.count <> eOld then
        return ''
    if m.astSay then
        call compAstSay res, 0
    return oRunner(compAst2Rx(m, ';', res))
endProcedure compile

/*--- compile or use hook for one part from spec or input -----------*/
compileOne: procedure expose m.
parse arg m, ki, hook
    s = m.m.scan
    call compSpComment m
    if ki == '*' | m.m.end \== '' then do
        do until scanLook(s, 2) == '$#' | scanEnd(s)
            call scanNl s, 1
            end
        return ''
        end
    if ki \== '' then do
        call scanNl s
        return compUnit(m, ki, '$#')
        end
    say 'interpreting hook' hook':' strip(scanLook(s))
    interpret return 'wshHook_'hook'(m)'
endProcedure compileOne

/*--- parse the whole syntax of a unit ------------------------------*/
compUnit: procedure expose m.
parse arg m, ki, stopper
    s = m.m.scan
    if pos(ki, m.comp.chKind) < 1 then
        return scanErr(s, 'bad kind' ki 'in compUnit(...'stopper')')
    else if pos(ki, '.-=@:') > 0 then do
     a = compAst(m, '¢')
        do forever
         one = compPipe(m, ki)
         if one \== '' then
          call mAdd a, one
         if \ scanLit(m.m.scan, '$;') then
                return a
         end
        end
    else if ki == '#' then do
        res = compAST(m, '¢')
        call scanChar s
        if verify(m.s.tok, m.comp.chSpa) > 0 then
            call mAdd res, compAstAddOp(m,
                , compAst(m, '=', strip(m.s.tok, 't')), '$')
        do while scanNL(s, 1) & \ abbrev(m.s.src, stopper)
            call mAdd res, compAstAddOp(m,
                , compAst(m, '=', strip(m.s.src, 't')), '$')
            end
        return res
        end
    call err 'bad kind' ki 'in compUnit'
endProcedure compUnit

/*--- compile an expression, type d=data, b=block w=word, s=strip ---*/
compExpr: procedure expose m.
parse arg m, type, ki
    if symbol('m.comp.exType.type') \== 'VAR' then
        call err s, 'bad type' type 'in compExpr'
    s = m.m.scan
    if length(ki) \== 1 | pos(ki, '.-=@') < 1 then
        return scanErr(s, 'bad kind' ki 'in compExpr')

    res = compAST(m, translate(ki, '-', '='))
    astKi = translate(ki, 'os=c', '.-=@')
    laPrim = 0
    gotTxt = 0
    m.res.containsC = 0
    if pos(type, 'sb') > 0 then
        m.res.containsC = compSpComment(m) >= 2
    do forever
        if scanVerify(s, m.comp.exType.type, 'm') then do
            one = compAST(m, astKi, m.s.tok)
            if verify(m.s.tok, m.comp.chSpa) > 0 then
                gotTxt = 1
            end
        else do
            old = scanPos(s)
            if \ scanLit(s, m.comp.chDol) then
                leave
            one = compPrimary(m, ki)
            if one = '' then do
                call scanSetPos s, old
                leave
                end
            laPrim = m.res.0 + 1
            end
        call mAdd res, one
        if compComment(m) then
            m.res.containsC = 1
        end
    if pos(type, 'bs') > 0 then do
        do rx=m.res.0 by -1 to laPrim+1
            one = m.res.rx
            m.one.text = strip(m.one.text, 't')
            if length(m.one.text) <> 0 then
                leave
            call mFree one
            end
        m.res.0 = rx
        end
    m.res.containsD = laPrim > 0 | gotTxt
    return compAstFree0(res, '')
endProcedure compExpr

/*--- compile a primary and return code ------------------------------*/
compPrimary: procedure expose m.
parse arg m, ki, compOk
    s = m.m.scan
    ops = ''
    if scanLit(s, '.', '-') then
        ops = m.s.tok || compOpKi(m, m.s.tok)
    getOps = ops
    if getOps == '' then
        getOps = substr('-.', 1+(ki == '.'), 1)
    if scanString(s) then
        return compASTAddOp(m, compAST(m, '=', m.s.val), getOps)
    r = compVar(m, 'c')
    if r \== '' then do
        if m.r.var \== 'c' then
             return compASTAddOp(m, compAst(m,'&',m.r.var, r), getOps)
        o2 = ops
        do while pos(right(o2, 1) ,'.@') > 0
         o2 = left(o2, length(o2)-1)
            end
        if right(o2, 1) == '^' then
         o2 = left(o2, length(o2)-1)
        else if compOk \== 1 then
            call scanErr s, 'compile can only be used by ^'
           return compASTAddOp(m, compAst(m, 'M'), o2)
        end
    if ops == '' then
     return ''
     one = compBlock(m, ops)
    if one \== '' then
        return one
    return scanErr(s, 'primary not finished')
endProcedure compPrimary

/*--- scan an operator chain and a kind ------------------------------*/
compOpKi: procedure expose m.
parse arg m, defKi
    s = m.m.scan
    call scanVerify s, m.comp.chOp || m.comp.chKiNO
    op = m.s.tok
    kx = verify(op, m.comp.chKiNO, 'm')
    if kx = length(op) & op \== '' then
        return op
    else if kx \== 0 then
        call scanErr s, 'kind' substr(op, kx, 1) 'not after ops'
    else if pos(right(op, 1), m.comp.chKind'^%') > 0 then
        return op
    else if defKi == '<' | right(op, 1) == '<' then
        return op'='
    else if defKi \== '' then
        return op || defKi
    else
     call scanErr s, 'no kind after ops' op
endProcedure compOpKi

/*--- block or expression --------------------------------------------*/
compExprBlock: procedure expose m.
parse arg m, ki
    s = m.m.scan
    old = scanPos(s)
    pk = compOpKi(m, ki)
    kiFi = left('<', ki =='<')
    res = compBlock(m, kiFi || pk)
    if res \== '' then do
        if length(pk) \== 1 | ki == '<' then
            return res
        else
            return compAstAddOp(m, res, translate(pk, '--', '=#'))
        end
    if pk \== '' then
        lk = right(pk, 1)
    else
        lk = translate(ki, '.', '@')
    res = compExpr(m, 's', lk)
    if res \== '' then
        return compASTAddOp(m, res, kiFi || left(pk, length(pk)-1))
    call scanSetPos s, old
    return res
endProcedure compExprBlock

/*--- compile a pipe and return code ---------------------------------*/
compPipe: procedure expose m.
parse arg m, ki
    s = m.m.scan
    res = compAST(m, 'P', ' ', '', '')
    do forever
        one = compExprStmts(m, ki)
        if one \== '' then do
            if m.res.0 > 2 then  do;call compAstSay res, 0
                call scanErr s, '$| before statements needed' ; end
            call mAdd res, one
            end
        pre = left(m.comp.chDol, scanLit(s, m.comp.chDol))
        if scanLit(s, '<') then do
            if m.res.2 == '' then
                m.res.2 = compAst(m, '.')
            else
                call mAdd m.res.2, compAst(m, 'o', ', ')
            call mAdd m.res.2, compExprBlock(m, '<')
            m.res.text = m.res.text'f'
            end
        else if scanLit(s, '>>', '>') then do
            if m.res.1 <> '' then
                call scanErr s, 'duplicate output'
            m.res.text = if(m.s.tok == '>', 'F', 'A') ,
                ||substr(m.res.text, 2)
            m.res.1 = compExprBlock(m, '<')
            end
        else if scanLit(s, '|') then do
            if m.res.0 < 3 then
                call scanErr s, 'stmts expected before |'
            call compSpNlComment m
            call mAdd res, compCheckNE(m, compExprStmts(m, ki),
                , 'stmts or expressions after | expected')
            end
        else do
            call scanBack s, pre
            if m.res.0 > 3 | m.res.1 \== '' | m.res.2 \== '' then
                return res
            one = if(m.res.0 = 3, m.res.3)
            call mFree res
            return one
            end
        end
endProcedure compPipe

/*--- compile expressions and stmts ---------------------------------*/
compExprStmts: procedure expose m.
parse arg m, ki
    s = m.m.scan
    res = compAst(m, '¢')
       nlLe = 0 /* sophisticated logic using left and right NLs*/
    tb = ''
    do forever
        if tb \== '' then do
            call compSpComment m
            px = m.s.pos
            e1 = compExpr(m, 'w', '=')
            if e1 \== '' then do
                aa = compAst(m, '¢')
                fx = 0
                do until e1 == ''
                    do fx=fx+1 to m.tb.0 until px < m.ff.end
                        ff = m.tb.fx
                        end
                    if fx > m.tb.0 then
                        call scanErr s, 'right of all table fields'
                    if m.s.pos <= m.ff.pos then
                        call scanErr s, 'before table field' m.ff.name
                    call mAdd aa, compAst(m, 'A', ,
                        , compAst(m, '=', m.ff.name), e1)
                    call compSpComment m
                    px = m.s.pos
                    e1 = compExpr(m, 'w', '=')
                    end
                call mAdd res, compAst(m, 'F', 'with',
                    , compAst(m, 'o', "oNew('"m.tb.class"')"),
                    , aa, compAst(m, '*', '$.'))
                end
            nlRi = scanNL(s)
            end
        else if ki == ':' then do
            call compSpNlComment m, '*'
            nlRi = 0
            end
        else if ki == '@' then do
            call compSpNlComment m
            one = compExpr(m, 's', ki)
            if one == '' then
                nlRi = 0
            else if m.one.0 < 1 then
                call scanErr s, 'assert not empty' m.one.0
            else do
                do forever /* scan all continued rexx lines */
                    nlRi = 1
                    la = m.one.0
                    la = m.one.la
                    if m.la.kind \== 'c' then
                        leave
                    m.la.text = strip(m.la.text, 't')
                    if right(m.la.text, 1) \== ',' then
                        leave
                    m.la.text = strip(left(m.la.text,
                            , length(m.la.text)-1), 't')' '
                    call compSpNlComment m
                    cont = compExpr(m, 's', '@')
                    if cont == '' | m.cont.kind \== m.one.kind then
                        call scanErr s, 'bad rexx continuation'
                    call mAddSt one, cont
                    call mFree cont
                    end
                call mAdd res, one
                end
            end
        else do
            do cc=0 while compComment(m)
                end
            one = compExpr(m, 'd', ki)
            nlRi = scanNL(s)
            if one == '' then do
                if nlLe & nlRi & cc < 1 then
                    call mAdd res,compAstAddOp(m,compAst(m,'='),'$')
                end
            else if m.one.containsD | (nlLe & nlRi,
                      & \ (cc > 0 | m.one.containsC)) then do
                call mAdd res, compAstAddOp(m, one, '$')
                end
            else do
                call mFree one
                end
            end
        nlLe = nlRi
        if \ nlRi then do
            one = compStmt(m)
            if one = '' then
                return compAstFree0(res)
            if m.one.kind == 'T' then
                tb = one
            else
                call mAdd res, one
            end
        end
endProcedure compExprStmts

/*--- compile a single statement -------------------------------------*/
compStmt: procedure expose m.
parse arg m
    s = m.m.scan
    res = compAss(m)
    if res \== '' then
        return res
    pre = ''
    old = scanPos(s)
    if scanLit(s,m.comp.chDol'$',m.comp.chDol'@',m.comp.chDol,'@') then
        pre = m.s.tok
    if pre == m.comp.chDol'$' then
        return  compAstAddOp(m, compCheckNN(m, compExprBlock(m,'='),
            , 'block or expression expected after $$'), '$')
    wCat = compName(m, 'sv')
    fu = m.s.tok

    if right(pre, 1) == '@' & wCat \== 's' then do
        if wCat == 'v' then
         return compAstAddOp(m, compAst(m, '=', fu), '%.&')
        if compSpNlComment(m) then
                return compAstAddOp(m,
                , compCheckNE(m, compExpr(m, 's','@'),
                , 'block/primary/expr after $@'), ';')

        ops = compOpKi(m, '@')
        one = compBlock(m, ops)
        if one \== '' then
            return one
        one = compPrimary(m, right(ops, 1))
        if one \== '' then
            return compAstAddOp(m, one, ops)
        call scanSetPos s, old
        return ''
        end

    if wordPos(fu, 'for forWith with withNew') > 0 then do
        res = compAst(m, 'F', fu)
        call compSpComment m
        if fu \== 'with' then do
            b = compVar(m, left('o', fu == 'withNew'))
            end
        else do
            b = compAss(m, '.', 'o')
            if b == '' then
                b = compCheckNE(m, compExpr(m, 's', '.'),
                , "assignment or expression after with")
            end
        call compSpComment m
        st = compCheckNN(m, compStmt(m), "var? statement after" fu)
        if b = '' then do
            b = compBlockName(m, st)
            if b == '' then
                call scanErr s, "variable or named block after" fu
            b = compAst(m, '=', b)
            end
        call mAdd res, b, st
        if fu == 'withNew' then do
            vars = substr(compAstAssignedVars(m.res.2), 3)
            m.res.class = classNew('n* CompTable u' vars)
            m.res.1 = compAst(m, 'A', ,m.res.1, compAst(m, 'o',
                , "oNew('"m.res.class"')"))
            end
        if abbrev(fu, 'with') then do
            r1 = m.res.1
            if m.r1.kind == 'A' then do
                a1 = m.r1.1
                if m.a1.var == 'o' then do
                    call mAdd res, compAstAddOp(m, m.r1.2, '$.')
                    m.res.1 = m.r1.2
                    call mFree a1
                    end
                end
            end
        return res
        end

    if fu == 'do' then do
        call compSpComment m
        pre = compExpr(m, 's', '@')
        res = compAst(m, 'D', , pre)
        p1 = m.pre.1
        if pre \== '' then do
            txt = ''
            do px=1 to m.pre.0
                pC = m.pre.px
                if m.pC.kind \== 'c' then
                    leave
                txt = txt m.pC.text
                cx = pos('=', txt)
                if cx > 0 then do
                    m.res.text = strip(left(txt, cx-1))
                    leave
                    end
                end
            end
        call compSpComment m
        call mAdd res, compCheckNN(m, compStmt(m), 'stmt after do')
        return res
        end

    if fu == 'ct' then do
        call compSpComment m
        return compAst(m, 'C', , compCheckNN(m, compStmt(m),
            , 'ct statement'));
        end
    if fu == 'proc' then do
           call compSpComment m
        nm = ''
        if compName(m, 'v') == 'v' then do
            nm = m.s.tok
            call compSpComment m
            end
        st = compCheckNN(m, compStmt(m), 'proc statement')
        if nm == '' then do
            nm = compBlockName(m, st)
            if nm == '' then
                call scanErr s, 'var or namedBlock expected after proc'
            end
        return compAst(m, 'B', '', compAst(m, '=', nm), st)
        end
    if fu == 'table' then do
        call compSpComment m
        if scanNl(s) then
            call compSpComment m
        res = compAst(m, 'T', 'c')
        flds = ''
        do while compName(m, 'v') == 'v'
            f1 = compAst(m, 'T')
            m.f1.end = m.s.pos
            m.f1.pos = m.s.pos - length(m.s.tok)
            m.f1.name = m.s.tok
            m.f1.text = 'f' m.f1.name m.f1.pos m.f1.end
            call mAdd res, f1
            flds = flds', f' m.s.tok 'v'
            call compSpComment m
            end
        if \ scanNl(s) then
            call scanErr s, 'name or nl after table expected'
        if m.res.0 < 1 then
            call scanErr s, 'no names in table'
        m.res.class = classNew('n* CompTable u' substr(flds, 3))
        m.res.text = 'c' cl
        return res
        end
    call scanBack s, pre || fu
    return ''
endProcedure compStmt

compBlockName: procedure expose m.
parse arg m, a
    a1 = m.a.1
    if m.a.kind == '¢' then
         return m.a.text
    else if m.a.kind == '*' & m.a1.kind == '¢' then
        return m.a1.text
    return ''
endProcedure compBlockName

compVar: procedure expose m.
parse arg m, vk
    s = m.m.scan
    ty = compName(m, 'v' || vk)
    if ty \== '' then do
        r = compAst(m, '=', m.s.tok)
        m.r.var = ty
        return r
        end
    if \ scanLit(s, '{') then
        return ''
    call scanLit s, '?', '>'
    f = m.s.tok
    r = compCheckNE(m, compExpr(m, 'b', '='), 'var name')
    if \scanLit(s, '}') then
        call scanErr s, 'closing } missing after {'
    m.r.var = f
    return r
endProcedure compVar

compAss: procedure expose m.
parse arg m, ki, vk
    s = m.m.scan
    call scanLit s, m.comp.chDol'=', '='
    pr = m.s.tok
    if pr \== '' then
        call compSpComment m
    v = compVar(m, vk)
    if v \== '' then do
        call compSpComment m
        if \ scanLit(s, '=') then
            call scanErr s, '= expected in assignment after' pr 'var'
        end
    else if pr == '' then
        return ''
    else
        oldInfo = scanInfo(s)
    eb = compCheckNE(m, compExprBlock(m, if(ki=='', '=', ki)),
        , 'block or expression in assignment after' pr)
    if v == '' then do
        v = compBlockName(m, eb)
        if v == '' then
            call scanErr s, 'var or namedBlock expected',
                    'in assignment after' pr, oldInfo
        else
            v = compAst(m, '=', v)
        end
    return compAst(m, 'A', , v, eb)
endProcedure compAss

/*--- block deals with the correct kind and operators
      the content is parsed by compUnit ------------------------------*/
compBlock: procedure expose m.
parse arg m, ops
orOps = ops
    s = m.m.scan
    if \ scanLit(s, '¢', '/') then
        return ''
    start = m.s.tok
    if pos(right(ops, 1), '%^') > 0 then
        ops = ops'='
    if ops == '' | pos(right(ops, 1), m.comp.chKind) < 1 then
        return scanErr(s, 'bad kind' ops 'for block') /0
    ki = right(ops, 1)
    ops = left(ops, length(ops)-1)
    isCall = pos(right(ops, 1), '%^') > 0
    starter = start
    if start == '¢' then
        stopper = m.comp.chDol'!'
    else do
        call scanVerify s, '/', 'm'
        starter = '/'m.s.tok'/'
        stopper = m.comp.chDol || starter
        if \scanLit(s, '/') then
            call scanErr s, 'ending / after stopper' stopper 'expected'
        end
    if \ isCall then do
        res = compUnit(m, ki, stopper)
        end
    else do
        call compSpComment m
        res = compPrimary(m, '.', right(ops, 1) == '^')
        if m.res.kind == 'M' then
            ops = left(ops, length(ops)-1)
        else
            res = compAst(m, right(ops, 1), , res)
        args = compExpr(m, 's', ki)
        if args \== '' then
            call mAdd res, args
        end
    if \ scanLit(s, stopper, substr(stopper, 2)) then
           call scanErr s, 'ending' stopper 'expected after' starter
    if abbrev(starter, '/') then
        m.res.text = substr(starter, 2, length(starter)-2)
    return compAstAddOp(m, res, ops)
endProcedure compBlock

/**** lexicals ********************************************************/
/*--- skip a comment. return 0 if there is none ----------------------*/
compComment: procedure expose m.
parse arg m
    s = m.m.scan
    got = 0
    do forever
        if scanLit(s, m.comp.chDol'**') then
            m.s.pos = 1 + length(m.s.src) /* before next nl */
        else if scanLit(s, m.comp.chDol'*+') then
            call scanNL s, 1
        else if scanLit(s, m.comp.chDol'*(') then do
            do forever
                if scanVerify(s, m.comp.chDol, 'm') then iterate
                if scanNL(s) then iterate
                if compComment(m) then iterate
                if \ scanLit(s, m.comp.chDol) then
                    call scanErr s, 'source end in comment'
                if scanLit(s, '*)') then
                    return 1
                if scanLit(s, m.comp.chDol) then iterate
                if scanString(s) then iterate
                end
            end
        else
            return got
        got = 1
        end
endProcedure compComment

/*--- skip spaces and comments ---------------------------------------*/
compSpComment: procedure expose m.
parse arg m, xtra
    s = m.m.scan
    got = 0
    do forever
        if scanVerify(s, m.comp.chSpa) then
            got = bitOr(got, 1)
        else if compComment(m) then
            got = bitOr(got, 2)
        else if xtra == '' then
            return got
        else if \ scanLit(s, xtra) then
            return got
        else do
            got = bitOr(got, 4)
            m.s.pos = 1+length(m.s.src)
            end
        end
endProcedure compSpComment

/*--- skip spaces, NLs and comments ----------------------------------*/
compSpNlComment: procedure expose m.
parse arg m, xtra
    found = 0
    do forever
        if compSpComment(m, xtra) < 1 then
            if \ scanNL(m.m.scan) then
             return found
        found = 1
        end
endProcedure compSpNlComment
/*--- scan a name in one of the categories
        v=var, c=compile, s=stmt ----------------------------------*/
compName: procedure expose m.
parse arg m, cats
    s = m.m.scan
    if \ scanName(s) then
        return ''
    if wordPos(m.s.tok, m.comp.wCatS) > 0 then do
        if pos('s', cats) > 0 then
            return 's'
        end
    else if wordPos(m.s.tok, m.comp.wCatC) > 0 then do
        if pos('c', cats) > 0 then
            return 'c'
        end
    else if wordPos(m.s.tok, m.comp.wCatO) > 0 then do
        if pos('o', cats) > 0 then
            return 'o'
        end
    else if pos('v', cats) > 0 then do
        return 'v'
        end
    call scanBack s, m.s.tok
    return ''
endProcedure compName


compSpNlComment: procedure expose m.
/**** small helper routines ******************************************/
/*--- if va == null then issue an error with msg --------------------*/
compCheckNN: procedure expose m.
parse arg m, va, msg
    if va == '' then
        call scanErr m.m.scan, msg 'expected'
    return va
endProcedure compCheckNN

/*--- return true iff expression is empty ---------------------------*/
compIsEmpty: procedure expose m.
parse arg m, a
    if a == '' then
        return 1
    do while a \== '' & m.a.kind == '*'
        a = m.a.1
        end
    return a == '' | (m.a.kind \== '¢' & m.a.0 < 1)
endProcedure compIsEmpty

/*--- if va == null or empty then issue an error with msg -----------*/
compCheckNE: procedure expose m.
parse arg m, ex, msg
    e1 = left(ex, 1)
    if compIsEmpty(m, ex) then
        call scanErr m.m.scan, msg 'expected'
    return ex
endProcedure compCheckNE

/**** AST = Astract Syntax Graph ***************************************
          goal is to migrate to migrate to old codeGenerator to AST

------- atoms, no children
  =  string constant
----------- rexx fragments
  c  rexxStatement
  s  string
  o  object
  r  run
  f  file

------- containers
  ¢  block          ==>* c-.
  @  code = 1 stmt  ==>*
  -  string
  .  object
  *  operand chain  ==>1

------- molecules
  &  variable access ==>1 =-
  A  assignment     ==> 2
  B  proc           ==> 2
  C  ct             ==> 1
  D  do                ==> 2
  F  for + with     ==> 2
  P  Pipe           ==>* 1=input 2=output
  T  Table
  M  compile
  %  RunOut         ==> 1,2 (Run, arguments)
  ^  RunRet         ==> 1,2 (Run, arguments)
  I??  Input          ==>* .

***********************************************************************/
/*--- create a new AST -----------------------------------------------*/
compAST: procedure expose m.
parse arg m, ki, txt
    n = mNew('COMP.AST')
    if length(ki) <> 1 then
        call err 'compAST bad kind' ki / 0
    m.n.kind = ki
    m.n.text = txt
    if pos(ki, '¢@-.*&ABCDFPTM%^') > 0 then do
        do cx=1 to arg()-3
            m.n.cx = arg(cx+3)
            end
        m.n.0 = cx-1
        if txt \== '' & pos(ki, '&*FPT') < 1 then
            return err('kind' ki 'text='txt'|')/0
        end
    else if pos(ki, '=csorf') > 0  then do
        m.n.0 = 'kind'ki
        end
    else do
        call err "compAst kind '"ki"' not supported"
        1/0
        end
    return n
endProcedure compAST

/*--- free AST if empty ----------------------------------------------*/
compASTFree0: procedure expose m.
parse arg a, ret
    if m.a.0 > 0 then
        return a
    call mFree a
    return ret
endProcedure compAstFree0

/*--- add operandands to an AST -------------------------------------*/
compASTAddOp: procedure expose m.
parse arg m, a, ops
    do while right(ops, 1) == m.a.kind
        ops = left(ops, length(ops)-1)
        end
    if ops == '' then
        return a
    ops = translate(ops, '-', '=')
    if m.a.kind \== '*' then
        return compAst(m, '*', ops, a)
    m.a.text = ops || m.a.text
    return a
endProcedure compAstAddOp

/*--- return the kind of an AST --------------------------------------*/
compASTKind: procedure expose m.
parse arg m, a
    m.m.astOps = ''
    if \ abbrev(a, 'COMP.AST.') then
        return left(a, 1)
    c = a
    do while m.c.kind == 'ops'
        if m.c.text \== '' then
            return left(m.c.text, 1)
        c = m.c.1
        end
    if a == c then
        return '?'
    return compAstKind(m, c)
endProcedure compASTKind

compAstAssignedVars: procedure expose m.
parse arg a
    res = ''
    if m.a.kind == 'F' then
        return ''
    if m.a.kind == 'A' then do
        a1 = m.a.1
        if m.a1.kind == '=' & m.a1.var == 'v' then do
            if words(m.a1.text) \= 1 then
                call astErr a1 'bad var'
            a2 = m.a.2
            say m.a2.kind m.a2.text
            if pos(m.a2.kind, '-=s') > 0 ,
                | (m.a2.kind == '*' & abbrev(m.a2.text, '-')) then
                res = ', f' m.a1.text 'v'
            else if pos(m.a2.kind, '.<@o') > 0 ,
                | (m.a2.kind == '*' & abbrev(m.a2.text, '.<@')) then
                res = ', f' m.a1.text 'r'
            else
                call astErr a2, 'string or object????'
            end
        end
    if datatype(m.a.0, 'n') then
        do ax=1 to m.a.0
            res = res || compAstAssignedVars(m.a.ax)
            end
    return res
endProcedure compAstAssignedVars

compAstSay: procedure expose m.
parse arg a, lv
    if \ abbrev(a, 'COMP.AST.') then do
        if a \== '' then
            return err('bad ast' a)
        say left('', 19)': * empty ast'
        return
        end
    say lefPad(left('', lv) m.a.kind, 10) ,
        || rigPad(if(dataType(m.a.0, 'n'), m.a.0), 3),
        '@'rigPad(substr(a, 10), 4)':' m.a.text'|'
    if dataType(m.a.0, 'n') then do cx=1 to m.a.0
        call compAstSay m.a.cx, lv+1
        end
    return
endProcedure compAstSay

compAstErr: procedure expose m.
parse arg a, txt
    call errSay txt
    call compAstSay a, 0
    return err(txt)
endProcedure compAstErr

compAstOnlyOut: procedure expose m.
parse arg a, rec
    do ax=1 to m.a.0
        a1 = m.a.ax
        if m.a1.kind == '*' then
            if abbrev(m.a1.text, '$') then
                iterate
        if m.a1.kind = '¢' & rec \== 0 then
            if compAstOnlyOut(a1) then
                iterate
        return 0
        end
    return 1
endProcedure compAstOnlyOut

/*--- return the code for an AST with operand chain trg --------------*/
compCode2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if pos(o1, ';') > 0 then
        return compCode2rx(m, oR, strip(f))
    if pos(o1, '-.<|?@') > 0 then
        return compRun2rx(m, ops, quote(oRunner(f)))

    call err 'compCode2rx bad ops' ops
endProcedure compCode2rx

compCon2rx: procedure expose m.
parse arg m, ops, f, a
    do ox=length(ops) by -1 to 1 while pos(substr(ops,ox,1), '.-')>0
        end
    if substr(ops, ox+1, 1) == '.' then
        f = s2o(f)
    if length(f) < 20 then
        v = quote(f, "'")
    else if a \== '' & m.a.text == f then
        v = 'm.'a'.text'
    else
        v =    'm.'compAst(m, '=', f)'.text'
    if substr(ops, ox+1, 1) == '.' then
        return compObj2rx(m, left(ops, ox), v)
    else
        return compString2rx(m, left(ops, ox), v)
endProcedure compCon2rx

compString2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '-' then
        return compString2rx(m, oR, f)
    if o1 == '.' then
        return compObj2rx(m, oR, 's2o('f')')
    if pos(o1, '$') > 0 tOnlyOut then
        return compCode2rx(m, oR, 'call out' f';')
    if o1 == '&' then do
        o2 = substr('1'ops, length(ops), 1)
        if o2 \== '.' & o2 \== '<' then
            return compString2rx(m, oR, 'vGet('f')')
        else
            return compObj2rx(m, oR, 'vGetO('f')')
        end
    if o1 == '<' then
        return compFile2rx(m, oR, 'file('f')')
    call err 'compString2rx bad ops' ops
endProcedure compString2rx

compObj2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '.' then
        return compObj2rx(m, oR, f)
    if o1 == '-' then
        return compString2rx(m, oR, 'o2string('f')')
    if o1 == '$' then
        return compCode2rx(m, oR, 'call outO' f';')
    if o1 == '<' then
        return compFile2rx(m, oR, 'o2file('f')')
    if o1 == '@' then
        return compRun2Rx(m, oR, f)
    if pos(o1, ';%^') > 0 then
        return compRun2rx(m, ops, f)
    call err 'compObj2rx bad ops' ops
endProcedure compObj2rx

compRun2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '@' then
        return compRun2Rx(m, oR, f)
    if o1 == ';' | o1 == '%' then /*??? immer % verwenden ???*/
        return compCode2Rx(m, oR, 'call oRun' f';')
    return compObj2rx(m, ops, f)
endProcedure compObj2rx

compFile2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if pos(o1, '<.@') > 0 then
        return compFile2rx(m, oR, f)
    if o1 == '|' | o1 == '?' then
        return compObj2Rx(m, oR, 'jSingle('f ||if(o1=='?', ", ''")')')
    return compRun2rx(m, ops, f)
endProcedure compFile2rx

compAst2rx: procedure expose m.
parse arg m, ops, a
    ki = m.a.kind

    if ki == 'c' then
        return compCode2Rx(m, ops, m.a.text)
    if ki == 's' then
        return compString2rx(m, ops, m.a.text)
    if ki == 'o' then
        return compObj2Rx(m, ops, m.a.text)
    if ki == '=' then
        return compCon2Rx(m, ops, m.a.text, a)
    if ki == '*' then
        return compAst2Rx(m, ops || m.a.text, m.a.1)
    o1 = right(ops, 1)
    oR = left(ops, max(0, length(ops)-1))
    if ki == '-' then
        if m.a.0 = 1 then
            return compAst2rx(m, ops, m.a.1)
        else
            return compString2rx(m, ops, compCatRexxAll(m, a,,, ' || '))
    if ki == '.' then
        return compObj2Rx(m, ops, compCatRexxAll(m, a,,, ' || '))
    if ki == '@' then
        return compCode2Rx(m, ops, compCatRexxAll(m, a,,,' || ')';')
    if ki == '¢' then do
        a1 = m.a.1
        if m.a.0 == 1 & m.a1.kind == '¢' then
            return compAst2Rx(m, ops, a1)
        if o1 == '-' then do
            if \ compAstOnlyOut(a) then
                return compAst2Rx(m, ops';', a)
            res = ''
            do ax=1 to m.a.0
                a1 = m.a.ax
                if m.a1.kind == '*' & abbrev(m.a1.text, '$') then
                    b = compAst2rx(m, overlay('-',m.a1.text,1), m.a1.1)
                else if m.a1.kind == '¢' then
                    b = compAst2rx(m, '-', a1)
                else
                    call compAstErr a, 'onlyOut but' ax'='a1 ,
                        'kind='m.a1.kind 'text='m.a1.text
                res = compCatRexx(res, b, ' ')
                end
                    /* ???? only necessary if part of expression ????*/
            return compString2rx(m, oR, '('strip(res)')')
            end
        if o1 == '<' then
         return compAst2Rx(m, ops'@', a)
        if o1 == '.' then
            return compAst2Rx(m, oR'|', a)
        if o1 == '|' | o1 == '?' then
            if m.a.0 = 1 & compAstOnlyOut(a, 0) then do
                a1 = m.a.1
                if m.a1.kind \== '*' | \ abbrev(m.a1.text, '$') then
                    call compAstErr a, 'onlyOut but',
                        '1='a1 'kind='m.a1.kind 'text='m.a1.text
                return compAst2Rx(m, oR'.'substr(m.a1.text, 2), m.a1.1)
                end
            else
                return compFile2Rx(m, ops, compAst2rx(m, '<;', a))
        if pos(o1, '@;') > 0 then do
            if m.a.0 = 1 then  /*???pipe und code können multi stmts */
               return compCode2Rx(m, ops, 'do;' ,
                    compAst2rx(m,';',m.a.1) 'end;')
            res = ''
            do ax=1 to m.a.0
                res = res compAst2rx(m, ';', m.a.ax)
                end
            if res = '' then
                return compCode2Rx(m, ops, 'nop;')
            return compCode2Rx(m, ops, 'do;'res 'end;')
            end
        end
    if ki == '&' then do
        nm = compAst2Rx(m, '-', m.a.1)
        if m.a.text=='' | m.a.text=='v' then
            return compString2rx(m, ops'&', nm)
        else if m.a.text == '?' then
         return compString2rx(m, ops, 'vIsDefined('nm')')
        else if m.a.text == '>' then
         return compString2rx(m, ops, 'vReadO('nm')')
        else
         call compAstErr a, 'bad text' m.a.text 'in ast &'
        end
    if ki == '%' | ki == '^' then do
        c1 = compAst2Rx(m, '.', m.a.1)
        if m.a.0 = 1 then
            return compRun2Rx(m, ops, c1)
        args = compAst2Rx(m, '', m.a.2)
        if ki == '%' then
            return compCode2Rx(m, ops, 'call oRun' c1',' args';')
        call err 'implement'
        end
    if ki == 'A' then do /* assignment */
        nm = compAst2Rx(m, '-', m.a.1)
        vl = m.a.2
     if m.vl.kind == '=' | m.vl.kind == '-' ,
            | (m.vl.kind == '*' & right(m.vl.text, 1) == '-') then
         return compCode2Rx(m, ops,
                , 'call vPut' nm',' compAst2Rx(m, '-', vl)';')
        else
         return compCode2Rx(m, ops,
                , 'call vPutO' nm',' compAst2Rx(m, '.', vl)';')
        end
    if ki == 'B' then do /* proc */
        call vPutO utInter('return' compAst2Rx(m, '-', m.a.1)),
            , oRunner(compAst2Rx(m ,';', m.a.2))
        return ''
        end
    if ki == 'C' then do /* ct */
     call utInter compAst2Rx(m, ';', m.a.1)
        return ''
        end
    if ki == 'D' then do /* do */
        res = 'do' compAst2rx(m, '', m.a.1)';'
        if m.a.text \== '' then
            res = res "call vPut '"m.a.text"'," m.a.text";"
        return compCode2Rx(m, ops, res compAst2Rx(m, ';', m.a.2),
             "end;")
        end
    if ki == 'F' then do /* for... */
        a1 = m.a.1
         st = compAst2Rx(m, ';', m.a.2)
        if abbrev(m.a.text, 'with') then do
            if m.a1.kind \== 'A' then do
                v = compAst2Rx(m, '.', a1)
                end
             else do
                v = compAst2Rx(m, ,a1)
                if \ abbrev(v, 'call vPutO ') | right(v, 1) \==';' then
                    call scanErr s, 'bad vPutO' v
                v = 'vPutO('substr(v, 12, length(v)-12)')'
                end
            if m.a.0 <= 2 then
                return 'call vWith "+",' v';' st 'call vWith "-";'
            a3 = m.a.3
            if m.a3.kind == '*' then
                return 'call vWith "+",' v';' st,
                    compObj2Rx(m, m.a3.text, "vWith('-')")
            end
        v = compAst2Rx(m, '-', m.a.1)
        if m.a.text == 'forWith' then
            st = 'call vWith "=", vGetO('v');' st
        if abbrev(m.a.text, 'for') then
            st = 'do while vReadO('v');' st 'end;'
        if m.a.text == 'forWith' then
            st = 'call vWith "+";' st 'call vWith "-";'
        return compCode2Rx(m, ops, st)
        end
    if ki == 'P' then do /* pipe */
        if ((left(m.a.text, 1) == ' ') \== (m.a.1 == '')) ,
         | ((substr(m.a.text, 2) == '') \== (m.a.2 == '')) ,
         | (m.a.0 <= 3 & m.a.text == '') then
            call compAstErr a, 'bad/trivial astPipe'
        res = ''
        do ax=3 to m.a.0
            a1 = ''
            if ax < m.a.0 then /* handle output */
                t1 = 'N'
            else if m.a.1 == '' then
                t1 = 'P'
            else do
                t1 = left(m.a.text, 1)
                a1 = compAst2Rx(m, '.', m.a.1)
                end
            if ax == 3 then do /* handle input */
                t1 = '+'t1 || substr(m.a.text, 2)
                if m.a.2 \== '' then
                    a1 = a1',' compAst2Rx(m, '.', m.a.2)
                end
            else
                t1 = t1'|'
            res = res "call pipe '"t1"'," a1";" ,
                   compAst2Rx(m, ';', m.a.ax)
            end
        return compCode2Rx(m, ops, res "call pipe '-';")
        end
    if ki == 'R' then do /* Run with args */
        res = 'call oRun' compAst2Rx(m, , m.a.1)
        if m.a.0 > 1 then
            res = res',' compAst2Rx(m, , m.a.2)
        return compRun2rx(m, ops, res';')
        end
    if ki == 'M' then do
        if m.a.0 = 0 then
            args = ''
        else
            args = ',' compAst2Rx(m, , m.a.1)
        return compRun2rx(m, ops, 'compile(comp(j2Buf())' args')')
        end
    call compAstErr a, 'comAst2rx bad ops' ops 'kind' ki
endProcedure compAst2rx

compCatRexxAll: procedure expose m.
parse arg m, a, ops, mi, sep
    res = ''
    do ax=1 to m.a.0
        res = compCatRexx(res, compAst2rx(m, ops, m.a.ax), mi , sep)
        end
    return strip(res)
endProcedure compCatRexxAll

/*--- cat two rexx parts, avoid strange effects--------------------*/
compCatRexx: procedure expose m.
parse arg le, ri, mi, sep
    if mi \== '' then
        return le || mi || ri
    lr = right(le, 1)
    rl = left(ri, 1)
    if (lr == "'" | lr == '"') then do
        if rl == lr then                /* "a","b" -> "ab" */
            return left(le, length(le)-1) || substr(ri, 2)
        else if  rl == '(' then            /* "a",( -> "a" || ( */
            return le||sep||ri            /* avoid function call    */
        end
    else if pos(lr, m.comp.idChars) > 0 then
        if pos(rl, m.comp.idChars'(') > 0 then
            return le || sep || ri        /* a,b -> a || b */
    return le || ri
endProcedure compCatRexx
/* copy comp end ******************************************************/
/* copy scanSB begin ***************************************************
     Achtung: inc generiert SB aus scanSB, Aenderungen nur in scanSB|
ScanSB: basic scan
    scanLook(m,len) : returns next len chars, pos is not moved
    scanChar(m,len) : scans next len chars
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanWord(m,u)  : scan a space delimited word or a string,
                          if u=1 then uppercase non-strings
variable interface
    scanSrc(m, source) starts scanning a single line
    scanEnd(m)     : returns whether we reached end of input
    scanErr(m, txt): error with current scan location

    m is an address, to store our state
    if a scan function succeeds, the scan posititon is moved

returns: true if scanned, false otherwise
         m.m.tok  ==> last token
         m.m.val  ==> last value for scanString/Word
         m.m.pos ==> scan position
         m.m.src ==> scan source
***********************************************************************/
/*--- return the next len characters until end of src ----------------*/
scanLook: procedure expose m.
parse arg m, len
    if len == '' then
        return substr(m.m.src, m.m.pos)
    else
        return substr(m.m.src, m.m.pos,
                     , min(len, 1 + length(m.m.src) - m.m.pos))
endProcedure scanLook

/*--- scan len chararcters, atmost to end of src ---------------------*/
scanChar: procedure expose m.
parse arg m, len
    m.m.tok = scanLook(m, len)
    m.m.pos = m.m.pos + length(m.m.tok)
    return m.m.tok \== ''
endProcedure scanChar

/*--- scan the literal lit ------------------------------------------*/
scanLit: procedure expose m.
parse arg m
    do ax=2 to arg()
        if abbrev(substr(m.m.src, m.m.pos), arg(ax)) then do
            m.m.tok = arg(ax)
            m.m.pos = m.m.pos + length(arg(ax))
            return 1
            end
        end
    m.m.tok = ''
    return 0
endProcedure scanLit

/*--- scan with verify, vOpt is passed to verify ---------------------*/
scanVerify: procedure expose m.
parse arg m, alpha, vOpt, onlyIfMatch
    if arg() > 3 then
        call err 'deimplement onlyIfMatch???'
    if vOpt == '' then   /* empty string does not take default */
        nx = verify(m.m.src, alpha, , m.m.pos)
    else
        nx = verify(m.m.src, alpha, vOpt, m.m.pos)
    if nx = 0 then
        if onlyIfMatch == 1 then
            nx = m.m.pos
        else
            nx = length(m.m.src) + 1
    m.m.tok = substr(m.m.src, m.m.pos, nx - m.m.pos)
    m.m.pos = nx
    return m.m.tok \== ''
endProcedure scanVerify

/*--- scan while in charset ------------------------------------------*/
scanWhile: procedure expose m.
parse arg m, chSet
    return scanVerify(m, chSet, 'n')

/*--- scan until in charset ------------------------------------------*/
scanUntil: procedure expose m.
parse arg m, chSet
    return scanVerify(m, chSet, 'm')

/*--- scan until (and over) string End -------------------------------*/
scanStrEnd: procedure expose m.
parse arg m, sep
    px = m.m.pos
    do forever
        px = pos(sep, m.m.src, px)
        if px = 0 then do
            m.m.tok = ''
            return 0
            end
        px = px + length(sep)
        if \ abbrev(substr(m.m.src, px), sep) then do
            m.m.tok = substr(m.m.src, m.m.pos, px-m.m.pos)
            m.m.pos = px
            return 1
            end
        px = px + length(sep)
        end
endProcedure scanStrEnd

/*--- scan a string with quote char qu -------------------------------*/
scanString: procedure expose m.
parse arg m, prefs
    if prefs = '' then do
        call scanLit m, "'", '"'
        end
    else do
        do px=1 to words(prefs) until scanLit(m, word(prefs, px))
            end
        end
    if m.m.tok == '' then
        return 0
    t1 = m.m.tok
    qu = right(t1, 1)
    if \ scanStrEnd(m, qu) then do
        m.m.pos = m.m.pos - length(t1)
        return scanErr(m, 'ending Apostroph('qu') missing')
        end
    m.m.val = repAll(left(m.m.tok, length(m.m.tok)-1), qu||qu, qu)
    m.m.tok = t1 || m.m.tok
    return 1
endProcedure scanString

/*--- scan a word and put value into *.val
           a word is either delimited by space or stopper
                     or a string (with single or double quotes -------*/
scanWord: procedure expose m.
parse arg m, stopper, ucWord
    if scanString(m) then
        return 1
    if stopper == '' then
        stopper = ' '
    if \scanUntil(m, stopper) then
        return 0
    m.m.val = m.m.tok
    if ucWord == 1 then
        m.m.val = translate(m.m.val)
    return 1
endProcedure scanWord

/*--- skip, scan and return next word --------------------------------*/
scanSkWord: procedure expose m.
parse arg m, stopper, ucWord, eMsg
    if scanWord(scanSkip(m), stopper, ucWord) then
        return m.m.val
    else if eMsg == '' then
        return ''
    else
        call scanErr m, eMsg 'expected'
endProcedure scanSkWord

/*--- go back the current token --------------------------------------*/
scanBack: procedure expose m.
parse arg m, tok
    if m.m.pos <= length(tok) then
        call scanErr sc, 'cannot back "'tok'" length'/0
    cx = m.m.pos - length(tok)
    if substr(m.m.src, cx, length(tok)) \== tok then
        return scanErr(m, 'cannot back "'tok'" value')/0
    m.m.pos = cx
    return
endProcedure scanBack

/*--- set new src - allow scanning without open ----------------------*/
scanSBSrc: procedure expose m.
parse arg m, m.m.src
    return scanSBOpen(m)
endProcedure scanSBSrc

/*--- start scanning with a new single src ---------------------------*/
scanSBOpen: procedure expose m.
parse arg m
    m.m.pos = 1
    m.m.tok = ''
    return m
endProcedure scanSBOpen

/*--- start scanning with a new single src ---------------------------*/
scanSBClose: procedure expose m.
parse arg m
    m.m.pos = length(m.m.src) + 1
    m.m.tok = '--- closed ---'
    return m
endProcedure scanSBClose

scanSBSpace: procedure expose m.
parse arg m
    nx = verify(m.m.src, ' ', , m.m.pos)
    if nx = 0 then
        nx = length(m.m.src) + 1
    res = nx <> m.m.pos
    m.m.tok = left(' ', res)
    m.m.pos = nx
    return res
endProcedure scanSBSpace

/*--- skip over space, nl and comments and return m -----------------*/
scanSkip: procedure expose m.
parse arg m
    call scanSpace m
    return m
endProcedure scanSkip

scanErr: procedure expose m.
parse arg m, txt
    if arg() < 3 then
        return err('s}'txt'\n'scanInfo(m))
    else
        return err('scanErr' txt'\n'arg(3))
endProcedure scanErr

scanSBInfo: procedure expose m.
parse arg m
    return 'last token' m.m.tok 'scanPosition' ,
        strip(substr(m.m.src, m.m.pos, 40), 't') ,
        || '\npos' m.m.Pos 'in string' strip(m.m.src, 't')
endProcedure scanSBInfo

/*--- return position in simple format -------------------------------*/
scanSBPos: procedure expose m.
parse arg m
    return 'singleSrc' m.m.pos
    return if(m.m.pos > length(m.m.src), 'E', 'singleSrc' m.m.pos)

/*--- set position to position in arg to------------------------------*/
scanSetPos: procedure expose m.
parse arg m, to
    cur = scanPos(m)
    wc = words(cur)
    if wc <> words(to) ,
        | subWord(cur, 1, wc-1) <> subWord(to, 1, wc-1) then
        call scanErr m 'cannot back from' cur 'to' to
    m.m.pos = word(to, wc)
    return

/*--- return true if at end of src -----------------------------------*/
scanSBEnd: procedure expose m.
parse arg m
    return m.m.pos > length(m.m.src)
/* copy scanSB end ****************************************************/
/* copy scan begin ****************************************************
Scan: scan an input: with multiple lines
    ==> all of scanSB
    scanEnd(m)     : returns whether we reached end of input
    scanLit(m,lit) : scan Literal lit if present or return 0
    scanChar(m,n)  : scan next n characters
    scanName(m)    : scan a name
    ScanNat(m)     : scan a natural number (without sign)
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanKeyValue(m): scan a key = value clause (with spaces)
    scanWord(m,u)  : scan a space delimited word or a string,
                          if u=1 then uppercase non-strings
    scanErr(m, txt): error with current scan location

    m is an address, to store our state
    if a scan function succeeds, the scan posititon is moved

returns: true if scanned, false otherwise
         m.m.tok  ==> last token
         m.m.val  ==> last value for scanString/Word/KeyValue
         m.key    ==> key for scanKeyValue
         m.m.pos ==> scan position
         m.m.src ==> scan source
***********************************************************************/
scanIni: procedure expose m.
    if m.scan.ini == 1 then
        return
    m.scan.ini = 1
    call jIni
    ts = classNew('n ScanRes u f TOK v, f VAL v, f KEY v, f TYPE v')
    call classNew 'n ScanSB u JRWO', 'm',
        , 'scanEnd  return 1',
        , 'scanNL   m.m.tok = ""; return 0',
        , 'scanCom  m.m.tok = ""; return 0',
        , 'scanInfo return scanSBInfo(m)' ,
        , 'jReset call scanSbSrc m, arg;' ,
                  'call scanOpts m, arg2, arg3, arg(4)',
        , "jOpen call scanSBOpen scanOC(m, opt, 'ScanSBR')" ,
        , "jClose call scanSBClose scanOC(m, , 'ScanSB')",
        , 'scanPos return scanSBPos(m)'
    call classNew 'n ScanSBR u ScanSB', 'm',
        , "jReadO if scanType(m) == '' then return '';" ,
                    " else return oClaCopy('"ts"', m, '')"
    return
endProcedure scanIni
/*--- check open opt is read and mutate ------------------------------*/
scanOC: procedure expose m.
parse arg m, opt, cla
    if \ abbrev(m.j.cRead, opt) then
        call err 'scanOpen opt must be' m.j.cRead 'not' opt
    return oMutatName(m, cla)
endProcedure scanOC
/*--- start scanning with a new single src ---------------------------*/
scanSrc: procedure expose m.
parse arg m, src
    return scanSbSrc(oMutatName(m, 'ScanSB'), src)

scanOpen: procedure expose m.
parse arg m
    opt = ''
    interpret objMet(m, 'jOpen')
    return m
endProcedure scanOpen

scanClose: procedure expose m.
parse arg m
    interpret objMet(m, 'jClose')
    return m
endProcedure scanOpen

scanInfo: procedure expose m.
parse arg m
    interpret objMet(m, 'scanInfo')

/*--- return true if at end of src -----------------------------------*/
scanEnd: procedure expose m.
parse arg m
    if m.m.pos <= length(m.m.src) then
        return 0
    interpret objMet(m, 'scanEnd')

/*--- scan over white space, nl, comments ...-------------------------*/
scanSpace: procedure expose m.
parse arg m
    fnd = 0
    do while scanSBSpace(m) | scanCom(m) | scanNl(m)
        fnd = 1
        end
    m.m.tok = left(' ', fnd)
    return fnd
endProcedure scanSpace

/*--- scan next line -------------------------------------------------*/
scanNL: procedure expose m.
parse arg m, unCond
    interpret objMet(m, 'scanNL')

/*--- scan one comment -----------------------------------------------*/
scanCom: procedure expose m.
parse arg m
    interpret objMet(m, 'scanCom')

scanPos: procedure expose m.
parse arg m
    interpret objMet(m, 'scanPos')
endProcedure scanPos

scanOpts: procedure expose m.
parse arg m, m.m.scanName1, namePlus, m.m.scanComment
    if m.m.scanName1 == '' then
        m.m.scanName1 = m.ut_alfa
    if namePlus == '' then
        m.m.scanNameR = m.m.scanName1 || '0123456789'
    else
        m.m.scanNameR = m.m.scanName1 || namePlus
    return m
endProcedure scanOpts

/*--- begin scanning a single line -----------------------------------*/
scanSrc: procedure expose m.
parse arg m, m.m.src
    return scanOpen(m)
endProcedure scanSrc

/*--- scan a Name, first char in *.scanName1, rest in *.scanNameR ----*/
scanName: procedure expose m.
parse arg m
    if pos(substr(m.m.src, m.m.pos, 1),
                 , m.m.scanName1) <= 0 then do
        m.m.tok = ''
        return 0
        end
    return scanVerify(m, m.m.scanNameR)
endProcedure scanName

/*--- scan a natural number (no sign, decpoint ...) ------------------*/
scanNat: procedure expose m.
parse arg m, chEn
    if \ scanVerify(m, '0123456789') then
        return 0
    if chEn \== 0 then
        if pos(scanLook(m , 1), m.m.scanNameR) > 0 then
            call scanErr m, 'illegal number end after' m.m.tok
    return 1
endProcedure ScanNat

/*--- scan an integer (optional sign, no decpoint ...) ---------------*/
scanInt: procedure expose m.
parse arg m, chEn
    call scanLit m, '+', '-'
    si = m.m.tok
    if \ scanNat(m, chEn) then do
        m.m.pos = m.m.pos - si
        return 0
        end
    m.m.tok = si || m.m.tok
    return 1
endProcedure scanInt

/*--- scan a number (optional sign, decpoint, exponent) ------------*/
scanNum: procedure expose m.
parse arg m, chEn
    sx = m.m.pos
    call scanLit m, '+', '-'
    po = scanLit(m, '.')
    if \ scanNat(m, 0) then do
        m.m.pos = sx
        return 0
        end
    if \ po then
        if scanLit(m, '.') then
            call scanNat m, 0
        if scanLit(m, 'e', 'E') then
            if \ scanInt(m, 0) then
                call scanErr m, 'exponent expected after' ,
                             substr(m.m.src, sx, m.m.pos-sx)
    m.m.tok = substr(m.m.src, sx, m.m.pos-sx)
    m.m.val = translate(m.m.tok)
    if chEn \== 0 then
        if pos(scanLook(m , 1), m.m.scanNameR) > 0 then
            call scanErr m, 'illegal number end after' m.m.tok
    return 1
endProcedure scanNum

scanType: procedure expose m.
parse arg m, opt
    m.m.tok = ''
    if scanName(m) then
        m.m.type = 'n'
    else if scanNum(m) then
        m.m.type = 0
    else if scanString(m) then
        m.m.type = left(m.m.tok, 1)
    else if scanSpace(m) then
        m.m.type = 's'
    else do
        call scanChar m, 1
        m.m.type = m.m.tok
        end
    return m.m.type
endProcedure scanType

/*--- scan a key = word phrase
          put key into m.key and word into m.m.val -------*/
scanKeyValue: procedure expose m.
parse arg m, def
    if \ scanName(m) then
        return 0
    m.m.key = m.m.tok
    if \ scanLit(scanSkip(m), '=') then do
        m.m.val = def
        m.m.tok = ' no='
        end
    else if \scanWord(scanSkip(m)) then
        return scanErr(m, 'word expected after' m.m.key '=')
    return 1
endProcedure scanKeyValue

/*--- emit an error with current scan pos ----------------------------*/
/* copy scan end   ****************************************************/
/* copy scanRead begin ************************************************/
scanReadIni: procedure expose m.
    if m.scanRead.ini = 1 then
        return
    m.scanRead.ini = 1
    call scanIni
 /* ts = classNew('n Scan u f TOK v, f VAL v, f KEY v, f TYPE v') */
    call classNew 'n ScanRead u ScanSB', 'm',
        , 'scanEnd return m.m.atEnd' ,
        , 'scanNL return scanReadNL(m, unCond)',
        , 'scanCom return scanReadCom(m)',
        , 'scanInfo return scanReadInfo(m)',
        , 'scanPos return scanReadPos(m)',
        , "jOpen   call scanReadOpen scanOC(m, opt, 'ScanReadR')",
                    ", ,arg()>=3, arg(3)" ,
        , "jClose  call scanReadClose scanOc(m, , 'ScanRead')"
    call classNew 'n ScanReadR u ScanRead', 'm',
        , 'jReadO' classMet(class4Name('ScanSBR'), 'jReadO')
    call classNew "n EditRead u JRW", "m",
        , "jRead  return editRead(m, var)",
        , "jOpen" ,
        , "jReset m.m.linex = arg - 1"
    return
endProcedure scanReadIni

/*--- begin scanning the lines of a reader ---------------------------*/
scanRead: procedure expose m.
parse arg rdr, n1, np, co
    return scanReadReset(oNew('ScanRead'), rdr, n1, np, co)

scanReadReset: procedure expose m.
parse arg m, m.m.rdr, n1, np, co
    call oMutatName m, 'ScanRead'
    call scanOpts m, n1, np, co
    return m
endProcedure scanReadReset

scanReadOpen: procedure expose m.
parse arg m, o, hasLine0, line0
    m.m.atEnd = 0
    m.m.lineX = 0
    m.m.val = ''
    m.m.key = ''
    call jOpen m.m.rdr, '<'
    if hasLine0 then do
        m.m.pos = 1
        m.m.src = line0
        end
    else
        call scanReadNL m, 1
    return m
endProcedure scanReadOpen

scanReadClose: procedure expose m.
parse arg m
    call jClose m.m.rdr
    m.m.atEnd = 'closed'
    return scanSBClose(m)
endProcedure scanReadClose

/*--- scan over next newLine
        if unCond \== 1 only if we are already at endOfLine
         return true if we scanned a NL ------------------------------*/
scanReadNL: procedure expose m.
parse arg m, unCond
    if unCond \== 1 then
        if m.m.pos <= length(m.m.src) then
            return 0
    if m.m.atEnd then
        return 0
    m.m.atEnd = \ jRead(m.m.rdr, m'.SRC')
    if m.m.atEnd then do
        m.m.pos = 1 + length(m.m.src)
        return 0
        end
    m.m.pos = 1
    m.m.lineX = m.m.lineX + 1
    return 1
endProcedure scanReadNl

scanReadCom: procedure expose m.
parse arg m
    m.m.tok = ''
    if m.m.scanComment == '' then
        return 0
    if \ abbrev(substr(m.m.src, m.m.pos), m.m.scanComment) then
        return 0
    m.m.pos = 1 + length(m.m.src)
    m.m.tok = ' '
    return 1
endProcedure scanReadCom

scanReadPos: procedure expose m.
parse arg m, msg
    return m.m.lineX m.m.pos
endProcedure scanReadPos

scanReadInfo: procedure expose m.
parse arg m
    msg = 'last token' m.m.tok 'scanPosition' ,
        strip(substr(m.m.src, m.m.pos, 40), 't')
    if scanEnd(m) then
        msg = msg'\natEnd after'
    else
        msg = msg'\npos' m.m.pos 'in'
    return msg 'line' m.m.lineX':' strip(m.m.src, 't')
endProcedure scanReadInfo

/*--- use scan sqlEdit macro --> temporarily here --------------------*/
/*--- read next line from edit data ----------------------------------*/
editRead: procedure expose m.
parse arg m, var
    m.m.lineX = m.m.lineX + 1
    if adrEdit('(ll) = line' m.m.lineX, 12) \= 0 then
        return 0
    m.var = ll
    return 1
endProcedure editRead
/*--- search loop in edit macro --------------------------------------*/
scanSqlSeekId: procedure expose m.
parse arg m, lx, cmd, opts
    if opts = '' then
        opts = word
                     /* line 1 col 0, otherwise first word is skipped*/
    if adrEdit("cursor =" max(trunc(lx), 1) 0, 12) = 12 then
        return -1
    do while adrEdit("seek" cmd opts, 4) = 0 /* find each command*/
        call adrEdit "(fx) = cursor"
        if m.debug then do
            call adrEdit "(LI) = LINE" fx
            call debug 'scanSqlSeekId after' lx 'found' cmd 'at' fx li
            end
        call jReset m.m.rdr, fx
        call jOpen m, '<'
        m.m.lineX = fx
        do while word(scanPos(m), 1) <= fx & scanSqlClass(m)
            if m.m.sqlClass = 'i' & m.m.val == cmd then
                return fx
            end
        call jClose m
        end
    return -1
endProcedure scanSqlSeekId
/* copy scanRead end **************************************************/
/* copy scanWin begin *************************************************
     scan the the concatenation of the lines of a reader
         any token my be split over several line
         except the end-of-line-comment-token
***********************************************************************/
scanWinIni: procedure expose m.
    if m.scanWin.ini = 1 then
        return
    m.scanWin.ini = 1
    call scanReadIni
    call classNew 'n ScanWin u ScanSB', 'm',
        , 'jReset call scanWinReset m, arg, arg2, arg3',
        , "jOpen call scanWinOpen scanOC(m, opt, 'ScanWinR'), arg(3)",
        , "jClose call scanReadClose scanOC(m, , 'ScanWin')",
        , 'scanNL return scanWinNl(m, unCond)',
        , 'scanCom return scanWinCom(m)',
        , 'scanInfo return scanWinInfo(m)',
        , 'scanPos  return scanWinPos(m)'
    call classNew 'n ScanWinR u ScanWin', 'm',
        , 'jReadO' classMet(class4Name('ScanSBR'), 'jReadO')
    return
endProcedure scanWinIni

/*--- instanciate a new window scanner, open rdr ---------------------*/
scanWin: procedure expose m.
parse arg rdr, wiSz, wiBa, cuPo, cuLe
    return scanWinOpts(oNew('ScanWin', rdr), wiSz, wiBa, cuPo, cuLe)

/*--- set the reader and attributes of window scanner m -------------*/
scanWinReset: procedure expose m.
parse arg m, m.m.rdr, wiSz, wiGa, cuPo, cuLe
    m.m.atEnd = 'closed after reset'
    return scanWinOpts(scanOpts(m), wiSz, wiGa, cuPo, cuLe)
endProcedure scanWinReset

/*--- set the attributes of window scanner m ------------------------*/
scanWinOpts: procedure expose m.
parse arg m, wiSz, wiGa, cuPo, cuLe
    wiSz = word(wiSz 5, 1)
    wiGa = word(wiGa 1, 1)
    m.m.cutPos = word(cuPo 1, 1)
    m.m.cutLen = word(cuLe 72, 1)
    m.m.winTot = (wiSz * 2 + wiGa) * m.m.cutLen
    m.m.posLim = (wiSz     + wiGa) * m.m.cutLen
    m.m.posOff =  wiGa * m.m.cutLen
    return m
endProcedure scanWinOpts

/*--- open reader and start scanning --------------------------------*/
scanWinOpen: procedure expose m.
parse arg m, lx
    m.m.atEnd = 0
    if lx = '' then
        m.m.lineX = 1
    else
        m.m.lineX = lx
    call scanSBOpen m
    m.m.val = ''
    m.m.key = ''
    m.m.pos = 1
    m.m.src = ''
    call jOpen m.m.rdr, m.j.cRead
    call scanWinRead m
    return m
endProcedure scanWinOpen

/*--- move the source window: cut left side and append at right side
      return number of characters cut at left ------------------------*/
scanWinRead: procedure expose m.
parse arg m
    dlt = 0
    if m.m.atEnd then
        return 0
    if m.m.pos >= m.m.posLim then do     /*  cut left side */
        dlt = m.m.pos - (m.m.pos // m.m.cutLen + m.m.posOff)
        m.m.src = substr(m.m.src, dlt+1)
        m.m.pos = m.m.pos - dlt
        m.m.lineX = m.m.lineX + dlt % m.m.cutLen
        end
    do while length(m.m.src) < m.m.winTot /* read and fill to len */
        if \ jRead(m.m.rdr, m'.'one) then do
            m.m.atEnd = 1
            return dlt
            end
        m.m.src = m.m.src || substr(m.m.one, m.m.cutPos, m.m.cutLen)
        end
    call assert 'length(m.m.src) = m.m.winTot', 'm.m.winTot m.m.src'
    return dlt
endProcedure scanWinRead

/*--- return position of next line start -----------------------------*/
scanWinNLPos: procedure expose m.
parse arg m
    return m.m.pos + m.m.cutLen - ((m.m.pos - 1) // m.m.cutLen)

/*--- scan comment ---------------------------------------------------*/
scanWinCom: procedure expose m.
parse arg m
    call scanWinRead m
    if scanLit(m, '/*') then do
        ex = pos('*/', m.m.src, m.m.pos+2)
        if ex <= m.m.pos then
            return scanErr(m, '*/ missing after /*')
        m.m.pos = ex+2
        call scanWinRead m
        end
    else do
        cl = length(m.m.scanComment)
        np = scanWinNlPos(m)
        if \ ( cl>0 & m.m.pos+cl <= np & m.m.scanComment ,
                == substr(m.m.src, m.m.pos, cl)) then do
            m.m.tok = ''
            return 0
            end
        m.m.pos = np
        end
    m.m.tok = ' '
    return 1
endProcedure scanWinCom

/*--- scan nl --------------------------------------------------------*/
scanWinNL: procedure expose m.
parse arg m, unCond
    call scanWinRead m
    if unCond \== 1 then
       return 0
    np = scanWinNLPos(m)
    if np \= m.m.pos then
        return 0
    m.m.pos = np
    return 1
endProcedure scanWinNl

/*--- return current position in input ------------------------------*/
scanWinPos: procedure expose m.
parse arg m
    if scanEnd(m) then
        return 'E'
    ps = m.m.pos - 1
    return (m.m.lineX + (ps % m.m.cutLen)) (ps // m.m.cutLen + 1)
endProcedure scanWinPos

/*--- return a description of the current scan position --------------*/
scanWinInfo: procedure expose m.
parse arg m
    p = scanWinPos(m)
    if p == 'E' then do
        res = 'atEnd after'
        p = m.m.lineX - 1 + length(m.m.src) % m.m.cutLen
        end
    else do
        res = 'pos' word(p, 2) 'in'
        p = word(p, 1)
        end
    return 'last token' m.m.tok 'scanPosition' ,
          strip(left(substr(m.m.src, m.m.pos), 40), 't') ,
        || '\n'res 'line' p':' strip(substr(m.m.src,
          , 1 + (p - m.m.lineX) * m.m.cutLen, m.m.cutLen), 't')
endProcedure scanWinInfo
/* copy scanWin end   *************************************************/
/* copy scanSql begin *************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanSql: procedure expose m.
parse arg inRdr
    return scanSqlReset(scanWin(inRdr), inRdr)

scanSqlReset: procedure expose m.
parse arg m, r, scanWin
    if scanWin \== 0 then
        call scanWinOpts m, 5, 2, 1, 72
    m.m.rdr = r
    return scanOpts(m, , '0123456789_' , '--')
endProcedure scanSqlReset

/*--- scan a sql token put class in m.sqlclass:
      'i': ordinary identifier   e.g. Name
      'd': delimited identifier  e.g. "Delimited"
      'q': qualified identifier  e.g. abc."efg"
      'u': integer units         e.g. 8G
      'n': number                e.g. -234 or .2e3
      's': string                e.g. 'abc''ef'
      '' : at end
         : any other character   e.g. ;
      ----------------------------------------------------------------*/
scanSqlClass: procedure expose m.
parse arg m, retSpace
    m.m.val = ''
    if scanSpace(m) & retSpace = 1 then do
        m.m.sqlClass = 'b'
        return 1
        end
    c2 = scanLook(m ,2)
    if scanString(m, "' x' X'") then do
        m.m.sqlClass = 's'
        if \abbrev(m.m.tok, "'") then
            m.m.val = x2c(m.m.val)
        end
    else if scanSqlQuId(m) then do
        if m.m.val.0 > 1 then
            m.m.sqlClass = 'q'
        else if abbrev(m.m.tok, '"') then
            m.m.sqlClass = 'd'
        else
            m.m.sqlClass = 'i'
        end
    else if scanSqlNum(m, 0)  then
        m.m.sqlClass = 'n'
    else if scanChar(m, 1) then
        m.m.sqlClass = m.m.tok
    else if scanEnd(m) then do
        m.m.sqlClass = ''
        return 0
        end
    else
        call scanErr m, 'cannot scan sql'
    return 1
endProcedure scanSqlClass

scanSqlSkipBrackets: procedure expose m.
parse arg m, br
    if br \== '' then
        nop
    else if scanLit(m, '(') then
        br = 1
    else
        return 0
    do while scanSqlClass(m) & m.m.sqlClass \== ';'
        if m.m.sqlClass = '('        then br = br + 1
        else if m.m.sqlClass \== ')' then iterate
        else if br > 1              then br = br - 1
        else                             return 1
        end
    call scanErr m, '; or eof, but' br 'closing ) expected'
endProcedure skipBrackets
/*--- scan an ordinary sql identifier e.g. abc, ef_12 ----------------*/
scanSqlId: procedure expose m.
parse arg m
    if \ scanName(m) then
        return 0
    m.m.val = translate(m.m.tok)
    return 1
endProcedure scanSqlId

/*--- scan a delimited or ordinay sql identifier ---------------------*/
scanSqlDeId: procedure expose m.
parse arg m
    if scanSqlId(m) then
        return 1
    if \ scanString(m, '"') then
        return 0
    m.m.val = strip(m.m.val, 't')
    return 1
endProcedure scanSqlDeId

/*--- scan a qualified sql identifier --------------------------------*/
scanSqlQuId: procedure expose m.
parse arg m
    res = ''
    rto = ''
    do qx=1
        if \ scanSqlDeId(m) then do
            if qx <> 1 then
                call scanErr m, 'id expected after .'
            return 0
            end
        m.m.val.qx = m.m.val
        res = res'.'m.m.val
        rto = rto'.'m.m.tok
        if \ scanLit(scanSkip(m), '.') then
            leave
        call scanSpace m
        end
    m.m.val.0 = qx
    m.m.val = substr(res, 2)
    m.m.tok = substr(rto, 2)
    return 1
endProcedure scanSqlQuId

/*--- scan a sql number ----------------------------------------------*/
scanSqlNum: procedure expose m.
parse arg m, checkEnd, noSp
    si = ''
    if noSp == 1 then
        call err 'deimplement noSp, use scanNum instead'
    if scanLit(m, '+', '-') then do
        si = m.m.tok
        call scanSpace m
        ch = scanLook(m, 2)
        if left(ch, 1) == '.' then
            ch = substr(ch, 2)
        if pos(left(ch, 1), '0123456789') < 1 then do
            call scanBack m, si
            m.m.val = ''
            return 0
            end
        end
    res = scanNum(m, checkEnd)
    m.m.val = si || m.m.val
    return res

endProcedure scanSqlNum

/*--- scan a sql number with a unit which may follow without space ---*/
scanSqlNumUnit: procedure expose m.
parse arg m, both, units
    if \ scanSqlNum(m, 0) then
        return 0
    nu = m.m.val
    sp = scanSpace(m)
    if scanSqlId(m) then do
        if units == '' | wordpos(m.m.val, units) > 0 then
            nu = nu m.m.val
        else if both | \ sp then
            call scanErr m, 'scanSqlNumUnit after' nu 'bad unit' m.m.val
        else
            call scanBack m, m.m.tok
        end
    else if both then
        call scanErr m, 'scanSqlNumUnit no unit after' nu
    else if \sp & pos(scanLook(m, 1), m.m.scanNameR) > 0 then
        call scanErr m, 'scanSqlNumUnit bad number end after' nu
    m.m.val = nu
    return 1
endProcedure scanSqlNumUnit

scan2Trgs: procedure expose m.
parse arg m, t1, t2
    cx = m.m.pos - 1
    do forever
        cx = verify(m.m.src, t1 || t2, 'm', cx + 1)
        if cx = 0 then do
            m.m.pos = length(m.m.src) + 1
            return ''
            end
        if pos(substr(m.m.src, cx, 1), t1) > 0 then do
            m.m.pos = cx
            return substr(m.m.src, cx, 1)
            end
        do ax=4 to arg()
            if arg(ax) == substr(m.m.src, cx, length(arg(ax))) then do
                m.m.pos = cx
                return arg(ax)
                end
            end
        end
endProcedure scan2Trgs

scanSql2Stop: procedure expose m.
parse arg m, sta, stop
    sta = substr(sta, 2)
    c1 = left(sta, 1)
    if c1 == 't' then do
        bx = m.m.pos
        c1 = scan2Trgs(m, '"'''stop, '-/', '--', '/*')
        if bx < m.m.pos then
            return 't'sta
        m.m.pos = m.m.pos + length(c1)
        c1 = left(c1, 1)
        sta = c1 || sta
        end
    if c1 == '/' then do
        bx = m.m.pos
        c1 = scan2Trgs(m, '"''', '-*', '--', '*/')
        if bx < m.m.pos then
            return '+'sta
        m.m.pos = m.m.pos + length(c1)
        if c1 == '*/' then
            return sta
        c1 = left(c1, 1)
        sta = c1 || sta
        end
    if abbrev(sta, "'") | abbrev(sta, '"') then do
         if scanStrEnd(m, c1) then
             return sta
         m.m.pos = 1 + length(m.m.src)
         return '+'sta
         end
    if pos(c1,  '-'stop) > 0 then do
        if c1 == '-' then
            m.m.pos = length(m.m.src) + 1
        return sta
        end
    if \ abbrev(sta, '/') then
        call err 'bad sta2' sta 'for scanSql2Stop'
    call err implement
        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
endProcedure scanSql2Stop

/* copy scanSql end   *************************************************/
/* copy scanUtil begin *************************************************
    scan db2 utility input statements using scan and a reader
**********************************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanUtilReader: procedure expose m.
parse arg m, inRdr
    call scanReader m, inRdr
    call scanOptions sc, , , '--'
    call scanUtilReset m
    return m
endProcedure scanUtilReader

scanUtilSql: procedure expose m.
parse arg inRdr
    m = scanSql(inRdr)
    call scanUtilReset m
    return m
endProcedure scanUtilReader

scanUtilReset: procedure expose m.
parse arg m
    m.m.utilBrackets = 0
    m.scanUtil =  'BACKUP CATENFM CATMAINT CHECK' ,
                  'COPY COPYTOCOPY DIAGNOSE EXEC LISTDEF LOAD' ,
                  'MERGECOPY MODIFY OPTIONS QUIESCE REBUILD' ,
                  'RECOVER REORG REPAIR REPORT RESTORE' ,
                  'RUNSTATS STOSPACE TEMPLATE UNLOAD'
    return m
endProcedure scanUtilReset
/*--- scan next token and put its class in m.sc.utilClass:
      'u' a utility name
      'n' a name
      '"' a quoted name
      "'" an apostroph'd string
      '.' a .
      ',' a ,
      'v' a value
      ''  at end
      ---------------------------------------------------------------*/
scanUtil: procedure expose m.
parse arg sc
    m.sc.utilSpace = scanSpace(sc)
    ty = '?'
    if scanLit(sc, '(') then do
        m.sc.utilBrackets = m.sc.utilBrackets + 1
        end
    else if scanLIT(sc, ')') then do
        m.sc.utilBrackets = m.sc.utilBrackets - 1
        if m.sc.utilBrackets < 0 then
           call scanErr sc, 'unmatched closing bracket )'
        end
    else if scanLit(sc, ',') then do
        end
    else if scanLit(sc, '.') then do
        end
    else if scanString(sc, "'") then do
        end
    else if scanString(sc, '"') then do
        end
    else if scanName(sc) then do
        m.sc.val = translate(m.sc.tok)
        if m.sc.utilBrackets > 0 then
            ty = 'n'
        else if 0 < wordPos(m.sc.val, m.scanUtil) then
            ty = 'u'
        else
            ty = 'n'
        end
    else if scanVerify(sc, ' (),''"', 'm') then do
        ty = 'v'
        m.sc.val = translate(m.sc.tok)
        end
    else if \scanEnd(sc) then do
            call scanErr sc, 'scanUtil stopped before end'
        end
    else do
        ty = ''
        m.sc.val = ''
        end
    if ty == '?' then
        m.sc.utilClass = left(m.sc.tok, 1)
    else
        m.sc.utilClass = ty
    return m.sc.utilClass
endProcedure scanUtil

/*--- scan a value or a bracketed list of values ---------------------*/
scanUtilValue: procedure expose m.
parse arg sc, remApo, nl
    if remApo = '' | rempApo = 0 then
        remApo = "nv"
    else if rempApo = 1 then
        remApo = "nv'"
    if '(' \== scanUtil(sc) then
         return scanUtilValueOne(sc, remApo)
    v = ''
    brx = m.sc.utilBrackets
    oLine = word(scanPos(sc), 1)
    do forever
        call scanUtil sc
        one = scanUtilValueOne(sc, remApo)
        if one == '' then
           call scanErr sc, 'eof in brackets'
        else if brx > m.sc.utilBrackets then
           return v
        nLine = word(scanPos(sc), 1)
        if \ m.sc.utilSpace then
            v = v || one
        else if nl \== '' & oLine <> nLine then
            v = v || nl || one
        else
            v = v' 'one
        oLine = nLine
        end
endProcedure scanUtilValue

scanUtilValueOne: procedure expose m.
parse arg sc, valTy
    if m.sc.utilClass == '' then
        return ''
    else if m.sc.utilClass == 'u' then
        call scanErr sc, 'util in scanUtilValueOne'
    if pos(m.sc.utilClass, valTy) > 0 then
        return m.sc.val
    else
        return m.sc.tok
endProcedure scanUtilValueOne

/*--- skip over nested brackets --------------------------------------*/
scanUtilSkipBrackets: procedure expose m.
parse arg m, br, doCat
    if br \== '' then
        lim = m.m.utilBrackets - br
    else if scanLit(m, '(') then do
        lim = m.m.utilBrackets
        m.m.utilBrackets = lim + 1
        end
    else
        return 0
    doCat = doCat == 1
    res = ''
    do while scanUtil(m) \== ''
        if m.m.utilBrackets <= lim then do
            if doCat then
                m.m.val = res
            return 1
            end
        if doCat then
            res = res m.m.tok
        end
    return scanErr(m, 'eof with' m.m.utilBrackets 'open (')
endProcedure skipBrackets

/*--- analyze a punch file write intoField to stdOut -----------------*/
scanUtilInto: procedure expose m.
parse arg m
    if m.m.utilBrackets \== 0 then
        call scanErr m, 'scanUtilInto with brackets' m.m.utilBrackets
  /*sc = scanUtilReader(m.j.in)
    call jOpen sc, 'r'
 */ do forever
        cl = scanUtil(m)
        if cl == '' then
            return 0
        if cl = 'n' & m.m.tok == 'INTO' then
            leave
        end
    if scanUtil(m) \== 'n' | m.m.tok \== 'TABLE' then
        call scanErr m, 'bad into table '
    if \ scanSqlQuId(scanSkip(m)) then
        call scanErr m, 'table name expected'
    if m.m.utilBrackets \== 0 then
        call scanErr m, 'into table in brackets' m.m.utilBrackets
    m.m.tb = m.m.val
    m.m.part = ''
    m.m.when = ''
    do forever
        cl = scanUtil(m)
        if cl == '' then
            call scanErr m, 'eof after into'
        if cl == 'n' & m.m.tok == 'PART' then do
            if scanUtil(m) == 'v' then
                m.m.part = m.m.val
            else
                call scanErr m, 'bad part'
            end
        else if cl == 'n' & wordPos(m.m.val, 'WHEN WORKDDN') > 0 then do
            call scanUtilSkipBrackets m
            end
        else if cl == '(' then do
           leave
           end
        end
    oX =  m.m.lineX
    oL =  overlay('', m.m.src, 1, m.m.pos-2)
    do while m.m.utilBrackets > 0
        call scanUtil m
        if oX \== m.m.lineX then do
            call out strip(oL, 't')
            oX =  m.m.lineX
            oL =  m.m.src
            end
        end
    call out left(oL, m.m.pos)
 /* call jClose sc
 */ return 1
endProcedure scanUtilInto
/* copy scanUtil end **************************************************/
/* copy pipe begin *****************************************************
***********************************************************************/
pipeIni: procedure expose m.
    if m.pipe.ini == 1 then
        return
    m.pipe.ini = 1
    call catIni
    call mapReset v
    m.v_with.0 = 0
    m.v_withMap = ''
    m.v_with.0.map = ''
    m.pipe.0 = 1
    m.pipe.1.in  = m.j.in
    m.pipe.1.out = m.j.out
    call pipe '+'
    return
endProcedure pipeIni

/*-------------------------------
  +-       push pop frame
  PYNFA    ouput: Parent saY Newcat File, Appendtofile
  psf|     input: parent string file oldOut
  old          --> new
  pipeBegin    --> pipe '+N'
  pipeBeLa f   --> pipe '+F'
  pipeLast     --> pipe 'P|'
  pipeLast f   --> pipe 'F|', f
  pipeEnd      --> pipe '-'
--------------------------------*/
pipe: procedure expose m.
parse arg opts, aO, aI
    ox = 1; oc = substr(opts, ox, 1)
    ax = m.pipe.0
    px = ax -1
    if oc == '-' then do
        if px < 2 then
            call err 'pipe pop empty'
        call jClose m.pipe.ax.out
        call jClose m.pipe.ax.in
        ax = px
        m.pipe.0 = ax
        px = ax-1
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if oc == '+' then do
        px = ax
        ax = ax+ 1
        m.pipe.0 = ax
        m.pipe.ax.in  = jOpen(m.pipe.px.in, '<')
        m.pipe.ax.out = jOpen(m.pipe.px.out, '>')
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    oOut = m.pipe.ax.out
    if pos(oc, 'NYPFA') > 0 then do
        call jClose oOut
        if oc == 'Y' then
            m.pipe.ax.out = jOpen(m.pipe.1.out, '>')
        else if oc == 'P' then
            m.pipe.ax.out = jOpen(m.pipe.px.out, '>')
        else if oc == 'N' then
            m.pipe.ax.out = jOpen(Cat(), '>')
        else if oc == 'F' then
            m.pipe.ax.out = jOpen(o2file(aO), '>')
        else if oc == 'A' then
            m.pipe.ax.out = jOpen(o2file(aO), '>>')
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if pos(oc, 's|fp') > 0 then do
        call jClose m.pipe.ax.in
        if oc == 'p' then
            m.pipe.ax.in = jOpen(m.pipe.px.in, '<')
        else if oc == '|' then
            m.pipe.ax.in = jOpen(oOut, '<')
        else if oc == 'f' then do
            if arg() <= 3 then
                m.pipe.ax.in = jOpen(o2file(aI), '<')
            else do
                ct = jOpen(Cat(), '>')
                do lx = 3 to arg()
                    call jWriteAll ct, arg(lx)
                    end
                m.pipe.ax.in = jOpen(jclose(ct), '<')
                end
            end
        else if arg() <= 3 then
            m.pipe.ax.in = jOpen(jBuf(aI), '<')
        else do
            bu = jOpen(jBuf(), '>')
            do lx = 3 to arg()
                call jWrite bu, arg(lx)
                end
            m.pipe.ax.in = jOpen(jclose(bu), '<')
            end
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if oc \== ' ' then
        call err 'implement' substr(opts, ox) 'in pipe' opts
    m.j.in  = m.pipe.ax.in
    m.j.out = m.pipe.ax.out
    return
endProcedure pipe

/*--- write all from rdr (rsp in) to out, not lazy ----------------*/
pipeWriteNow: procedure expose m.
    parse arg rdr
    call jWriteNow m.j.out, if(rdr == '', m.j.in, rdr)
    return
endProcedure pipeWriteNow

/*--- write all from rdr (rsp in) to out, possibly lazy -----------*/
pipeWriteAll: procedure expose m.
parse arg rdr
    call jWriteAll m.j.out, if(rdr == '', m.j.in, rdr)
    return
endProcedure pipeWriteAll

pipePreSuf: procedure expose m.
parse arg le, ri
    do while in(v)
        call out le || m.v || ri
        end
    return
endProcedure pipePreSuf

vIsDefined: procedure expose m.
parse arg na
    return   '' \== mapValAdr(v, na)
endProcedure vIsDefined

vWith: procedure expose m.
parse arg fun, o
    if fun == '-' then do
        tBe = m.v_with.0
        tos = tBe - 1
        if tos < 0 then
            call err 'pop empty withStack'
        m.v_with.0 = tos
        m.v_withMap = m.v_with.tos.map
        return m.v_with.tBe.obj
        end
    tos = m.v_with.0
    if fun == '=' then do
        if o == '' & m.v_with.tos.obj == '' then
            return
        if o \== '' & m.v_with.tos.obj \== '' then
            if objClass(o) == objClass(m.v_with.tos.obj) then do
                m.v_with.tos.obj = o
                return
                end
        par = tos - 1
        end
    else if fun == '+' then do
        par = tos
        tos = tos + 1
        m.v_with.0 = tos
        end
    m.v_with.tos.obj = o
    if par > 0 then
        key = m.v_with.par.classes
    else
        key = ''
    if o \== '' then
        key = strip(key objClass(o))
    m.v_with.tos.classes = key
    if symbol('m.v_withManager.key') == 'VAR' then do
        m.v_with.tos.map = m.v_withManager.key
        m.v_withMap = m.v_withManager.key
        return
        end
    m = mapNew()
    m.v_with.tos.map = m
    m.v_withMap = m
    m.v_withManager.key = m
    do kx=1 to words(key)
        c1 = word(key, kx)
        call vWithAdd m, kx, classMet(c1, 'oFlds')
        call vWithAdd m, kx, classMet(c1, 'stms')
        end
    return
endProcedure vWith

vWithAdd: procedure expose m.
parse arg m, kx, ff
    do fx=1 to m.ff.0
        n1 = m.ff.fx
        dx = pos('.', n1)
        if dx > 1 then
            n1 = left(n1, dx-1)
        else if dx = 1 | n1 = '' then
            iterate
        call mPut m'.'n1, kx
        end
    return
endProcedure vWithAdd

vGet: procedure expose m.
parse arg na
    parse value vAdr(na) with flag 2 adr
    if flag == 'm' then
        return o2string(mapGet(v, adr))
    else if flag == 's' then
        return m.adr
    else if flag == 'o' then
        return o2String(m.adr)
    return err('vGet flag' flag)
endProcedure vGet

vGetO: procedure expose m.
parse arg na
    parse value vAdr(na) with flag 2 adr
    if flag == 'm' then
        return mapGet(v, adr)
    else if flag == 's' then
        return s2o(m.adr)
    else if flag == 'o' then
        return m.adr
    return err('vGetO flag' flag)
endProcedure vGetO

vPut: procedure expose m.
parse arg na, val
    parse value vAdr(na) with flag 2 adr
    if flag == 'm' then
        call mapPut v, adr, s2o(val)
    else if flag == 's' then
        m.adr = val
    else if flag == 'o' then
        m.adr = s2o(val)
    else
        return err('vPut flag' flag)
    return val
endProcedure vPut

vPutO: procedure expose m.
parse arg na, val
    parse value vAdr(na) with flag 2 adr
    if flag == 'm' then
        call mapPut v, adr, val
    else if flag == 's' then
        m.adr = o2String(val)
    else if flag == 'o' then
        m.adr = val
    else
        return err('vPutO flag' flag)
    return val
endProcedure vPut

/*--- find the final address
      return f || a with address a and
             f = m -> mapGet(a), o -> obect m.a, s -> string m.a  ---*/
vAdr: procedure expose m.
parse arg na
    cx = pos('&', na)
    if cx = 1 | na = '' then
        return err('vAdr bad name' na)
    if m.v_withMap \== '' then do
        mp = m.v_withMap
        cy = verify(na, '.&', 'm')
        if cy < 1 then
            w2 = na
        else
            w2 = left(na, cy - 1)
        if symbol('m.mp.w2') == 'VAR' then do
            wx = m.mp.w2
            return vAdrObj(m.v_with.wx.obj, '.'na)
            end
        end
    if cx < 1 then
        oNm = na
    else
        oNm = left(na, cx-1)
    if cx < 1 then
        return 'm'na
    return vAdrObj(mapGet(v, oNm), '.'substr(na, cx + 1))
endProcedure vAdr

/*--- vAdr for object and field --------------------------------------*/
vAdrObj: procedure expose m.
parse arg o, f
    if left(f, 1) == '.' then
        cx = 2
    else if left(f, 1) == '&' then
        cx = 1
    else
        return err('vAdrObj bad f='f)
    do forever
        if o = '' then
            return err('vAdrObj null at' left(f, cx-1) 'in name' f)
        cy = pos('&', f, cx)
        if cy < cx then
            leave
        if cy == cx then
            a = o
        else
            a = o'.'substr(f, cx, cy-cx)
        if symbol('m.a') \== 'VAR' then
            return err('vAdrObj undef' a 'at' left(f, cy-1) 'in name' f)
        o = m.a
        cx = cy+1
        end
    cl = objClass(o, '')
    if cl == '' then
        return err('vAdrObj undef' o 'in name' f)
    return vAdrClass(cl, o, '.'substr(f, cx))
endProcedure vAdrObj

/*--- vAdr for class, object and field -------------------------------*/
vAdrClass: procedure expose m.
parse arg cl, o, f
    if pos('&', f) > 0 then
        return vAdrObj(o, f)
    else if left(f, 1) == '.' then
        f2 = substr(f, 2)
    else
        return err('vAdrClass bad f='f)
    f2c = classMet(cl, 'f2c')
    if symbol('m.f2c.f2') \== 'VAR' then
        return vAdrStem(cl, o, f)
    if f2 \== '' then
        o = o'.'f2
    if m.f2c.f2 == m.class_V then
        return 's'o
    else
        return 'o'o
endProcedure vAdrClass

/*--- vAdr for stems of class, object and field ---------------------*/
vAdrStem: procedure expose m.
parse arg cl, o, f
    if left(f, 1) \== '.' & left(f, 1) \== '&' then
        return err('vAdrStem bad f='f)
    cy = 1
    do until cy < 1
        cx = cy+1
        cy = pos('.', f, cx)
        if cy == 0 then do
            if verify(f, '0123456789', 'n', cx) >= cx then
                call err 'vAdrStem no stem cl' className(cl),
                  || ', o' o', f' f
            cz = length(f) + 1
            end
        else do
            if verify(f, '0123456789', 'n', cx) \== cy then
                iterate
            cz = cy
            end
        end
    st = substr(f, 2, max(0, cx-3))
    sd = left('.', st \== '')st
    sx = substr(f, cx, cz-cx)
    if symbol('m.cl.s2c.st') \== 'VAR' then
        return err('vAdrStem not a stem cl='className(cl) 'st='st 'o='o)
    if sx \== 0 then
        return vAdrClass(m.cl.s2c.st, o || sd'.'sx, '.'substr(f, cz))
    if cy == 0 then
        return 's'o || f
    else
        return err('vAdrStem.0 not at end cl='cl 'f='f 'o='o)
endProcedure vAdrStem

vRead: procedure expose m.
parse arg na
    return in("V."na)

vReadO: procedure expose m.
parse arg na
    res = inO()
    if res == '' then
        return 0
    call vPutO na, res
    return 1
endProcedure vReadO

vHasKey: procedure expose m.
parse arg na
    return mapHasKey(v, na)

vRemove: procedure expose m.
parse arg na
    return mapRemove(v, na)
/* copy pipe end ******************************************************/
/* copy cat  begin ****************************************************
***********************************************************************/
/*--- create a new cat -----------------------------------------------*/
cat: procedure expose m.
    m = oNew('Cat') /* calls catReset */
    do ax=1 to arg()
        call catWriteAll m, arg(ax)
        end
    return m
endProcedure cat

catReset: procedure expose m.
parse arg m
    m.m.RWs.0 = 0
    m.m.catWr = ''
    m.m.catRd = ''
    m.m.catIx = -9e9
    m.m.catKeepOpen = ''
    return m
endProcedure catReset

catClose: procedure expose m.
parse arg m
    if m.m.catWr \== '' then do
        call jClose m.m.catWr
        call mAdd m'.RWS', m.m.catWr
        m.m.catWr = ''
        end
    if m.m.catRd \== '' then do
        call jClose m.m.catRd
        m.m.catRd = ''
        end
    m.m.catIx = -9e9
    return m
endProcedure catClose

catOpen: procedure expose m.
parse arg m, oo
    if oo == m.j.cRead then do
        m.m.catIx = 0
        call catNextRdr m
        m.m.jReading = 1
        end
    else if oo == m.j.cWri | oo == m.j.cApp then do
        if oo == m.j.cWri then
            m.m.RWs.0 = 0
        m.m.catIx = -9e9
        m.m.jWriting = 1
        end
    else do
        call err 'catOpen('m',' oo') bad opt'
        end
    return m
endProcedure catOpen

/*--- return and open next reader ------------------------------------*/
catNextRdr: procedure expose m.
parse arg m
    if m.m.catRd \== '' then
        call jClose m.m.catRd
    cx = m.m.catIx + 1
    m.m.catIx = cx
    if cx > m.m.RWs.0 then do
        m.m.catRd = ''
        return 0
        end
    m.m.catRd = m.m.RWs.cx
    if cx = word(m.m.catKeepOpen, 1) then
        m.m.catKeepOpen = subWord(m.catKeepOpen, 2)
    else
        call jOpen m.m.catRd , m.j.cRead
    return 1
endProcedure catNextRdr

catReadO: procedure expose m.
parse arg m
    do while m.m.catRd \== ''
        res = jReadO(m.m.catRd)
        if res \== '' then
            return res
        call catNextRdr m
        end
    return ''
endProcedure catReadO

catWrite: procedure expose m.
parse arg m, line
    if m.m.catWr == '' then
        m.m.catWr = jOpen(jBuf(), m.j.cWri)
    call jWrite m.m.catWr, line
    return
endProcedure catWrite

catWriteO: procedure expose m.
parse arg m, var
    if m.m.catWr == '' then
        m.m.catWr = jOpen(jBuf(), m.j.cWri)
    call jWriteO m.m.catWr, var
    return
endProcedure catWriteO

/*--- write contents of a reader to cat
          or keep it for later reading -------------------------------*/
catWriteAll: procedure expose m.
parse arg m
    if m.m.catWr \== '' then do
        call mAdd m'.RWS', jClose(m.m.catWr)
        m.m.catWr = ''
        end
    do ax=2 by 1 to arg()
        r = o2File(arg(ax))
        call mAdd m'.RWS', r
        if m.r.jReading then do
            m.m.catKeepOpen = m.m.rws.0 m.m.catKeepOpen
            call jOpen r, m.j.cRead
            end
        end
    return
endProcedure catWriteAll

/*--- create a reader/WriteO for an external file --------------------*/
file: procedure expose m.
parse arg str
    return oNew('File', str)
endProcedure file

fileChild: procedure expose m.
parse arg m, name, opt
    interpret objMet(m, 'fileChild')
endProcedure fileChild

fileRm: procedure expose m.
parse arg m
    interpret objMet(m, 'fileRm')
    return
endProcedure fileRm

filePath: procedure expose m.
parse arg m
    interpret objMet(m, 'filePath')
endProcedure filePath

fileIsFile: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsFile')
endProcedure fileIsFile

fileIsDir: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsDir')
endProcedure fileIsDir

fileMkDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileMkDir')
    return
endProcedure fileRm

fileRmDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileRmDir')
    return
endProcedure fileRm

/*--- create a reader/WriteO for the filelist of a directory----------*/
fileList: procedure expose m.
parse arg m, opt
    str = oIfStr(m, '')
    if str == '' then
        return oNew('FileList', filePath(m),  opt)
    else
        return oNew('FileList', dsn2Jcl(str),  opt)
endProcedure fileList

catIni: procedure expose m.
    if m.cat.ini == 1 then
        return
    m.cat.ini = 1
    call errIni
    call jIni
    call classNew "n Cat u JRWO", "m",
        , "jOpen  call catOpen m, opt",
        , "jReset call catReset m, arg",
        , "jClose call catClose m",
        , "jReadO return catReadO(m)",
        , "jWrite call catWrite m, line; return",
        , "jWriteO call catWriteO m, var; return",
        , "jWriteAll call catWriteAll m, rdr; return"

    if m.err.os == 'TSO' then
        call fileTsoIni
    else if m.err.os == 'LINUX' then
        call fileLinuxIni
    else
        call err 'file not implemented for os' m.err.os
    return
endProcedure catIni
/* copy cat  end   ****************************************************/
/* copy mail begin ***************************************************/
mailHead: procedure expose m.
parse arg m, subj, rec, snd
    m.m.1 = 'SUBJECT   ' subj
    m.m.2 = 'RECPLIST  ' rec
    m.m.3 = 'SENDERID  ' if(snd=='', userid(), snd)
    m.m.4 = 'SENDERIDCC N'
    m.m.5 = 'DOCTEXT'
    m.m.0 = 5
    return m
endProcedure mailHead

mailSend: procedure expose m.
parse arg m, dsn
    call mAdd m, 'DOCTEXTEND'
    if dsn == '' then
        call dsnAlloc 'dd(iemapi) new  ::v255'
    else
        call dsnAlloc 'dd(iemapi) shr' dsn
    call writeDD iemapi, 'M.'m'.'
    call tsoClose iemapi
    call dsnAlloc 'dd(iemlog) sysout(*)'
    call adrTso "call *(os3550)", '*'
    if rc \== 0 then
        say 'error os3550 rc='rc 'mail not sent|||||'
    call tsoFree iemlog iemapi
    return 0
endProcedure mailSend
/* copy mail end *****************************************************/
/* copy fiLinux begin *************************************************/
/*--- send ggShCmd to shell bash,
        fail if rc <> 0 or not listed in ggRet -----------------------*/
adrSh: procedure expose m.  /* really no need for variables???? */
    parse arg ggShCmd, ggRet
    address 'bash' ggShCmd
    if rc == 0                     then return 0
    else if ggRet == '*'           then return rc
    else if wordPos(rc, ggRet) > 0 then return rc
    else
        call err 'adrSh rc' rc 'for' ggShCmd
endProcedure adrSh

fileLinuxReset: procedure expose m.
parse arg m, nm
    m.m.spec = nm
    if abbrev(nm, '&') then do
        if nm == '&in' then do
            m.m.stream = .input
            m.m.jReading = 1
            end
        else if nm == '&out' then do
            m.m.stream = .output
            m.m.jWriting = 1
            end
        else do
            call err 'bad spec' nm
            end
        end
    else do
        m.m.stream = .Stream%%new(nm)
        m.m.stream%%init(m.m.stream%%qualify)
        end
    return m
endProcedure fileLinuxReset

fileLinuxOpen: procedure expose m.
parse arg m, opt
    if opt == m.j.cRead then do
        res = m.m.stream%%open(read shareread)
        m.m.jReading = 1
        end
    else do
        if opt == m.j.cApp then
            res = m.m.stream%%open(write append)
        else if opt == m.j.cWri then
            res = m.m.stream%%open(write replace)
        else
            call err 'fileLinuxOpen('m',' opt') with bad opt'
        m.m.jWriting = 1
        end
    if res \== 'READY:' then
        call err 'fileLinuxOpen fails' res':' opt ,
        "'"m.m.stream%%qualify"'"
    return m
endProcedure fileLinuxOpen

fileLinuxClose:
parse arg m
    res = m.m.stream%%close
    if res \== 'READY:' then
        call err 'fileLinuxClose' res':' m.m.stream%%qualify
    return m
endProcedure fileLinuxClose

fileLinuxRead: procedure expose m.
parse arg m, var
    res = m.m.stream%%lineIn
    if res == '' then
        if m.m.stream%%state \== 'READY' then
            return 0
    m.var = res
    m.o.o2c.var = m.class_V
    return 1
endProcedure fileLinuxRead

fileLinuxWrite: procedure expose m.
parse arg m, line
    if m.m.stream%%lineOut(line) then
        call err 'fileLinuxWrite'
    return
endProcedure fileLinuxWrite

fileLinuxRmDir: procedure expose m.
parse arg m, opt
    if opt == '' then
        return adrSh('rmdir' m.m.spec)
    else if opt == '-r' then
        return adrSh('rm -r' m.m.spec)
    else
        call err 'bad opt' opt 'in fileLinuxRmDir'
endProcedure fileLInuxRmDir

fileLinuxListReset: procedure expose m.
parse arg m, m.m.spec, o
    if o == 'r' then
        m.m.opt = 'S'
    else if o == '' then
        m.m.opt = ''
    else
        call err 'bad opt' o 'in fileLinuxListReset'
    m.m.rx = 'closed'
    return m
endProcedure fileLinuxListReset

fileLinuxListOpen: procedure expose m.
parse arg m
    if m \== translate(m) then
        call err 'bad m for fileLinuxList:' m
    if m.m.opt == '' then
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST')
    else
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST', m.m.opt)
    if rc \== 0 then
        call err 'rc' rc 'in sysFileTree('m.m.spec', m.'m'.LIST)'
    m.m.rx = 0
    m.m.jReading = 1
    return m
endProcedure fileLinuxListOpen

fileLinuxListRead: procedure expose m.
parse arg m, var
    x = m.m.rx + 1
    if x > m.m.list.0 then
        return 0
    m.var = substr(m.m.list.x, 43)
    m.m.rx = x
    call oMutate var, m.class_V
    return 1
endProcedure fileLinuxListRead

fileLinuxIni: procedure expose m.
    if m.fileLinux.ini == 1 then
        return
    m.fileLinux.ini = 1
    m.file.sep = '/'
    call jIni
    call classNew "n File u JRW", "m",
        , "jReset call fileLinuxReset m, arg",
        , "jOpen  call fileLinuxOpen m, opt",
        , "jClose call fileLinuxClose m",
        , "jRead return fileLinuxRead(m, var)",
        , "jWrite call fileLinuxWrite m, line",
        , "jWriteO call jWrite m, o2String(var)",
        , "filePath return m.m.stream%%qualify",
        , "fileIsFile return sysIsFile(m.m.stream%%qualify)" ,
        , "fileIsDir return sysIsFileDirectory(m.m.stream%%qualify)" ,
        , "fileChild return file(m.m.stream%%qualify'/'name)",
        , "fileRm return adrSh(m.m.spec)",
        , "fileMkDir return adrSh('mkdir' m.m.stream%%qualify)" ,
        , "fileRmDir return fileLinuxRmDir(m, opt)"
    call classNew "n FileList u JRW", "m",
        , "jReset call fileLinuxListReset m, arg, arg2",
        , "jOpen  call fileLinuxListOpen m, opt",
        , "jClose m.m.rx = 'closed'",
        , "jRead return fileLinuxListRead(m, var)"
    return
endProcedure fileLinuxIni
/* copy fiLinux end   *************************************************/
/* copy fileTso begin *************************************************/
fileTsoReset: procedure expose m.
parse arg m, sp
    m.m.readIx = 'c'
    if symbol('m.m.defDD') \== 'VAR' then do
        m.m.defDD = 'CAT*'
        m.fileTso.buf = m.fileTso.buf + 1
        m.m.buf = 'FILETSO.BUF'm.fileTso.buf
        m.m.spec = sp
        end
    if sp \== '' then do
        m.m.spec = dsnSpec(sp)
        rr = translate(subword(m.m.spec, 4))
        m.m.stripT = \ (pos(':V', rr) > 0 | pos('RECFM(V', rr) > 0)
        end
    return m
endProcedure fileTsoReset

fileTsoOpen: procedure expose m.
parse arg m, opt
    buf = m.m.buf
    if opt == m.j.cRead then do
        aa = dsnAlloc(m.m.spec, 'SHR', m.m.defDD)
        aDD = word(aa, 1)
        aDsn = m.tso_dsn.aDD
        if aDsn <> '' then
            if pos('(', aDsn) > 0 & pos('/', aDsn) < 1 then
                if sysDsn("'"m.tso_dsn.aDD"'") <> 'OK' then
                    call err 'cannot read' m.tso_dsn.aDD':',
                               sysDsn("'"m.tso_dsn.aDD"'")
        call tsoOpen word(aa, 1), 'R'
        m.m.jReading = 1
        m.buf.0 = -1
        m.m.readIx = 0
        end
    else do
        if opt == m.j.cApp then
            aa = dsnAlloc(m.m.spec, 'MOD', m.m.defDD)
        else if opt == m.j.cWri then
            aa = dsnAlloc(m.m.spec, 'OLD', m.m.defDD)
        else
            call err 'fileTsoOpen('m',' opt') with bad opt'
        call tsoOpen word(aa, 1), 'W'
        m.m.jWriting = 1
        m.buf.0 = 0
        m.m.readIx = 'w'
        end
    parse var aa m.m.dd m.m.free
    m.m.dsn = mGet('TSO_DSN.'m.m.dd)
    return m
endProcedure fileTsoOpen

fileTsoClose: procedure expose m.
parse arg m
    buf = m.m.buf
    if m.m.readIx \== 'c' then do
        if m.m.readIx == 'w' & m.buf.0 > 0 then
            call writeDD m.m.dd, 'M.'BUF'.'
        call tsoClose m.m.dd
        call tsoFree  m.m.free
        end
    m.buf.0 = 'closed'
    m.m.readIx = 'c'
    m.m.free  = ''
    m.m.dd    = ''
    return m
endProcedure fileTsoClose

fileTsoRead: procedure expose m.
parse arg m, var
    ix = m.m.readIx + 1
    buf = m.m.buf
    if ix > m.buf.0 then do
        res = readDD(m.m.dd, 'M.'buf'.')
        if \ res then
            return 0
        ix = 1
        end
    m.m.readIx = ix
    m.var = m.buf.ix
    call oMutate var, m.class_V
    return 1
endProcedure fileTsoRead

fileTsoWrite: procedure expose m.
parse arg m, var
    buf = m.m.buf
    ix = m.buf.0 + 1
    m.buf.0 = ix
    if m.m.stripT then
        m.buf.ix = strip(var, 't')
    else
        m.buf.ix = var
    if ix > 99 then do
        call writeDD m.m.dd, 'M.'buf'.'
        m.buf.0 = 0
        end
    return
endProcedure fileTsoWrite

fileTsoWriteO: procedure expose m.
parse arg m, var
    if objClass(var, m.class_V) == m.class_V then do
        call fileTsoWrite m, m.var
        return
        end
    call err 'fileTsoWriteO('m',' var') cannot write objects of class',
                              objClass(var)
endProcedure fileTsoWriteO

fSub: procedure expose m.
    return file('.sysout(T) writer(intRdr)')
endProcedure fSub

fEdit: procedure expose m.
parse arg spec, vw
    if spec == '' then
        spec = 'new ::f'
    else if abbrev(spec, '::') then
        spec = 'new' spec
    else if abbrev(spec, ':') then
        spec = 'new' ':'spec
    f  = oNew('FileEdit', spec)
    m.f.editType = if(abbrev(translate(vw), 'V'), 'view', 'edit')
    return f
endProcedure fEdit

fileTsoEditClose: procedure expose m.
parse arg m
    dsn = m.m.dsn
    if dsn \== '' then do
        call fileTsoClose m
        call adrIsp m.m.editType "dataset('"dsn"')", 4
        return
        end
    fr = m.m.free
    dd = m.m.dd
    m.m.free = ''
    call fileTsoClose m
    call adrIsp "LMINIT DATAID(lmmId) ddName("dd") ENQ(SHRW)"
    eRc = adrIsp(m.m.editType "dataid("lmmId")", '*')
    lRc = adrIsp("LMFree DATAID("lmmId")", '*')
    call tsoFree fr
    if (eRc \== 0 & eRc \== 4) | lRc \== 0 then
        call err m.m.editType 'rc' eRc', lmFree rc' lRc
    return
endProcedure fileTsoEditClose

fileTsoIni: procedure expose m.
    if m.fileTso.ini == 1 then
        return
    m.fileTso.ini = 1
    m.file.sep = '.'
    m.fileTso.buf = 0
    call jIni
    um = "call err 'for tso undefined method'"
    call classNew "n File u JRW", "m",
        , "jOpen  call fileTsoOpen m, opt",
        , "jReset call fileTsoReset m, arg",
        , "jClose call fileTsoClose m",
        , "jRead return fileTsoRead(m, var)",
        , "jWrite call fileTsoWrite m, line",
        , "jWriteO call fileTsoWriteO m, var",
        , "filePath return word(m.m.spec, 1)"           ,
        , "fileIsFile" um "'fileIsFile'"      ,
        , "fileIsDir   return 1"              ,
        , "fileChild   return file(word(m.m.spec, 1)'.'name opt)",
        , "fileRm"     um "'fileRm'"          ,
        , "fileMkDir"  ,
        , "fileRmDir"  um "'fileRmDir'"
 /*     , "filePath return m.m.stream%%qualify",
        , "fileIsFile return sysIsFile(m.m.stream%%qualify)" ,
        , "fileIsDir return sysIsFileDirectory(m.m.stream%%qualify)" ,
        , "fileChild return file(m.m.stream%%qualify'/'name)",
        , "fileRm return adrSh(m.m.spec)",
        , "fileMkDir return adrSh('mkdir' m.m.stream%%qualify)" ,
        , "fileRmDir return fileLinuxRmDir(m, opt)" */
    call classNew "n FileList u JRW", "m",
        , "jReset if arg2 == 'r' then m.m.dsnMask=arg'.**';",
                                "else m.m.dsnMask=arg'.*';",
        , "jOpen  call csiOpen m, m.m.dsnMask",
        , "jClose" ,
        , "jRead return csiNext(m, var)"
    call classNew "n FileEdit u File", "m",
        , "jClose call fileTsoEditClose m"
    return
endProcedure fileTsoIni
/* copy fileTso end   *************************************************/
/* copy mat begin *****************************************************/
sqrt: procedure expose m.
parse arg n
    if n < 2 then
        return n
     k = 1
     g = n
     do while k+1 < g
         m = (g + k) % 2
         if m * m <= n then
             k = m
         else
             g = m
         end
     return k
endProcedure sqrt

isPrime: procedure expose m.
parse arg n
    if n < 2 then
        return 0
    if n // 2 = 0 then
        return n = 2
    do q=3 by 2 to sqrt(n)
        if n // q = 0 then
            return 0
        end
    return 1
endProcedure isPrime

nxPrime: procedure expose m.
parse arg n
    do i = n + (\ (n // 2)) by 2
        if isPrime(i) then
            return i
        end
endProcedure nxPrime

permut: procedure expose m.
parse arg m, p
    m.m.1 = 1
    do i=2 while p > 0
        j = i - (p // i)
        m.m.i = m.m.j
        m.m.j = i
        p = p % i
        end
    m.m.0 = i-1
    return i-1
endProcedure permut
/* copy mat end   *****************************************************/
/* copy sqlDiv begin **************************************************/
/*--- generate the format m for a sql cx as specified in sp
          use the information from the sqlDa -------------------------*/
sqlFTabReset: procedure expose m.
parse arg ff, cx, tBef, tAft, m.ff.maxChar, m.ff.blobMax, m.ff.maxDec
    if m.ff.maxChar == '' then
        m.ff.maxChar = 32
    if m.ff.blobMax == '' then
        m.ff.blobMax = 200
    bf = '%-'max(m.ff.blobMax, 4)'C'
    m.ff.flds = ''
    m.ff.sqlX = cx
    call fTabReset ff, tBef, tAft, '-'
    m.ff.sql2fmt.384 = '%-10C' /* date    */
    m.ff.sql2fmt.388 = '%-8C'  /* time    */
    m.ff.sql2fmt.392 = '%-26C' /* timestamp */
    m.ff.sql2fmt.400 = 'c'     /* graphic string */
    m.ff.sql2fmt.404 = bf      /* BLOB           */
    m.ff.sql2fmt.408 = bf      /* CLOB           */
    m.ff.sql2fmt.412 = bf      /* DBCLOB         */
    m.ff.sql2fmt.448 = 'c'     /* varchar        */
    m.ff.sql2fmt.452 = 'c'     /* char           */
    m.ff.sql2fmt.452 = 'c'     /* long varchar   */
    m.ff.sql2fmt.460 = 'c'     /* null term. string */
    m.ff.sql2fmt.464 = 'c'     /* graphic varchar   */
    m.ff.sql2fmt.468 = 'c'     /* graphic char      */
    m.ff.sql2fmt.472 = 'c'     /* long graphic varchar   */
    m.ff.sql2fmt.480 = '%-7e'  /* float                  */
    m.ff.sql2fmt.484 = 'd'     /* packed decimal         */
    m.ff.sql2fmt.492 = '%20i'  /* bigInt                 */
    m.ff.sql2fmt.496 = '%11i'  /* int                    */
    m.ff.sql2fmt.500 = '%6i'   /* smallInt               */
    m.ff.sql2fmt.904 = '%-34H' /* rowID 17 Byte Binary   */
    return
endProcedure sqlFTabReset
/*--- set a defaultFormat for type tx in fTab ff ---------------------*/
sqlFTabDef: procedure expose m.
parse arg ff, tx, m.ff.sql2fmt.tx
return ff

sqlFTabAdd: procedure expose m.
parse arg m, c1 aDone, f1, l1
    if symbol('m.m.set.c1') == 'VAR' then do
        sx = m.m.set.c1
        if word(m.m.set.sx, 1) == c1 & sx <= m.m.set.0 then do
            parse var m.m.set.sx c1 aDone
            f1 = m.m.set.sx.fmt
            l1 = m.m.set.sx.label
            end
        end
    cx = m.m.sqlX
    kx = sqlCol2kx(cx, c1)
    if kx == '' then
        call err 'colName not found' c1
    do tx=2 to arg()-3
        if arg(tx+3) \== '' then
            call fTabAddTit m, tx, arg(tx+3)
        end
    if f1 \== '' then do
        if right(f1, 1) \== ' ' then
            f1 = f1' '
        return fTabAdd(m, c1 aDone, f1, l1)
        end
    ty = m.sql.cx.d.kx.sqlType
    le = m.sql.cx.d.kx.sqlLen
    withNulls = ty // 2
    ty = ty - withNulls
    if symbol('m.m.sql2fmt.ty') <> 'VAR' then
        call err 'sqlType' ty 'col' c1 'not supported'
    f2 = m.m.sql2fmt.ty
    if f2 == 'c' then
        f2 = '%-'min(le, m.m.maxChar)'C'
    else if f2 == 'd' then do
        trace ?r
        pr =  le % 256
        de =  le // 256
        f2 = '%'pr'.'de'i'
        end
    if \ abbrev(f2, '%') then
        call err 'sqlType' ty 'col' c1 'bad format' f2
    return fTabAdd(m, c1 aDone, f2' ', l1)
endProcedure sqlFTabAdd

sqlFTabOthers: procedure expose m.
parse arg m, doNot
    cx = m.m.sqlX
    do kx=1 to m.sql.cx.d.sqlD
        c1 = m.sql.cx.col.kx
        wx = wordPos(c1, m.m.cols)
        if (wx < 1 | m.m.wx.done \== 1) & wordPos(c1, doNot) < 1 then
            call sqlFTabAdd m, m.sql.cx.col.kx
        end
    return
endProcedure sqlFTabOthers

sqlFTab: procedure expose m.
parse arg m
    call fTabBegin m
    do while sqlRxFetch(m.m.sqlX, 'sqlFTab')
        call out f(m.m.fmt, 'sqlFTab')
        end
    return fTabEnd(m)
endProcedure sqlFTab

sqlFTabCol: procedure expose m.
parse arg m
    do rx=1 while sqlRxFetch(m.m.sqlX, 'sqlFTab')
        call out left('--- row' rx '', 100, '-')
        call fTabCol m, 'sqlFTab'
        end
    call out left('--- end of' (rx-1) 'rows ', 100, '-')
    return
endProcedure sqlFTabCol

sqlCatTb: procedure expose m.
parse arg ty gOnly, wh, ord, fTab, paPlus
    tb = tkrTable(, ty)
    if gOnly == 1 then
        edFun = ''
    else
        edFun = tkrTable(, ty, 'e')
    cx = 1
    ft = 'ft'm.tb.alias
    call sqlFTabReset ft, cx, 'c 1', '1 c', 12, if(fTab, , 2000)
    call sqlFTabDef      ft, 492, '%7e'
    call FTabSet         ft, 'CONTOKEN'  , '%-16H'
    call FTabSet         ft, 'DCONTOKEN'  , '%-16H'
    call FTabSet         ft, 'DBNAME'    , '%-8C', 'db'
    call FTabSet         ft, 'DSNAME'    , '%-44C'
    call FTabSet         ft, 'DSNUM'     , '%5i'
    call FTabSet         ft, 'PARTITION' ,'%5i' , 'part'
    call FTabSet         ft, 'PIT_RBA'   , '%-12H'
    call FTabSet         ft, 'RBA1'      , '%-12H'
    call FTabSet         ft, 'RBA2'      , '%-12H'
    call FTabSet         ft, 'START_RBA' ,'%-12H'
    call FTabSet         ft, 'TSNAME'    , '%-8C', 'ts'
    call FTabSet         ft, 'VERSION'   , '%-28C'
    if edFun \== '' then do
        interpret 'sq =' edFun'(ft, tb, wh, ord)'
        end
    else do
        cl = sqlColList(m.tb.table, m.ft.blobMax)
        sq = 'select' cl tkrTable( , tb, 'f') wh ,
             'order by' if(ord=='', m.tb.order, ord)
        call sqlQuery cx, sq
        call sqlFTabOthers ft
        call sqlCatTbVl ft, tb
        end
    if fTab then
        call sqlFTab ft
    else
        call sqlFTabCol ft
    call sqlClose cx
    call sqlCatTbTrailer space(m.TKR.path paPlus, 1), sq
    return 0
endProcedure sqlCatTb

sqlCatTbVlsep:
    return '+++'

sqlCatTbVl: procedure expose m.
parse arg ft, tb, sep
    if sep == '' then
        sep = sqlCatTbVLsep()
    if m.tb.vlKey == '' then
        return
    ky = m.tb.vlKey
    ff = ''
    tt = ''
    do kx=1 to m.ky.0
        tt = tt || sep || m.ky.kx.col
        ff = ff || sep'@'m.ky.kx.col'%S'
        end
    call fTabAdd ft, substr(tt,length(sep)+1), substr(ff,length(sep)+1)
    return
endProcedure sqlCatTbVl

sqlCatTbTrailer: procedure expose m.
parse arg pa, sq
    ox = lastPos(' order by ', sq)
    if ox < 1 then
        call err 'order by not found in' sq
    ord = substr(sq, ox+10)
    sq = left(sq, ox-1)
    sqUp = translate(sq)
    call out ''
    call out 'dbSys:' m.sql_dbSys
    call out 'path:' pa
    int = ''
    iNx = '  '
    br = ''
    cx = 1
    stops = '(select from where'
    do while cx < length(sq)
        nx = -1
        do sx=1 to words(stops)
            n2 = pos(word(stops, sx), sq, cx+1)
            if n2 > cx & (nx < 1 | n2 < nx) then
                nx = n2
            end
        if nx < 0 then
            leave
        call out int || substr(sq, cx, nx-cx)
        int = iNx
        if substr(sq, nx, 3) = '(se' then do
            iNx = iNx'  '
            br = left(br, length(int))')'
            end
        cx = nx
        end
    ll =  strip(substr(sq, cx))
    bq = strip(br)
    do while bq <> ''
        if right(bq, 1) \== ')' | right(ll, 1) \== ')' then
           call err 'missing ) bq:' bq', ll:' ll
        ll = strip(left(ll, length(ll) - 1))
        bq = strip(left(bq, length(bq) - 1))
        end
    call out int || ll
    if br <> '' then
        call out br
    if ord <> '' then
        call out '  order by' ord
    return
endProcedure sqlCatTbTrailer

sqlCatIxKeys: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select ikK.colSeq, ikK.colName, ikK.ordering, ikK.period' ,
             ', ik.creator, ik.name, ik.tbCreator, ik.tbName, ikC.*'  ,
          tkrTable(, tb ,'f') wh,
          'order by' if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, CREATOR, '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME   , '%-16C','index'
    call sqlFTabAdd      ft, colSeq  , '%5i',  'coSeq'
    call sqlFTabAdd      ft, colName, '%-16C', 'column'
    call sqlFTabAdd      ft, ordering
    call sqlFTabAdd      ft, period
    call sqlFTabAdd      ft, COLNO
    call sqlFTabAdd      ft, COLTYPE
    call sqlFTabAdd      ft, LENGTH
    call sqlFTabAdd      ft, SCALE
    call sqlFTabAdd      ft, NULLS
    call sqlFTabOthers ft, 'COL9 COL10 COL11 COL47'
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatIxKeys

sqlCatIXStats: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select *' tkrTable( , tb, 'f') wh ,
         'order by' if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, CREATOR, '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME   ,       , 'index'
    call sqlFTabAdd      ft, INSTANCE   , '%1i' , 'i'
    call sqlFTabAdd      ft, PARTITION , , 'part'
    call sqlFTabOthers ft
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatIXStats

sqlCatTables: procedure expose m.
parse arg ft, tb, wh, ord
    al = m.tb.alias
    sq = 'select' al'.*, tsX.type tsType, tsX.partitions',
            ', tsX.pgSize, tsX.dsSize' ,
            ', timestamp(rba1 || x''0000'') rba1Tst' ,
            ', timestamp(rba2 || x''0000'') rba2Tst' ,
          'from' m.tb.table 'left join sysibm.sysTablespace tsX',
            'on' al'.dbName = tsx.dbName and' al'.tsName = tsX.name',
            'where' m.tb.cond wh ,
            'order by'  if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, creator   , '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME      , '%-24C', 'table'
    call sqlFTabAdd      ft, type
    call sqlFTabAdd      ft, dbNAME    , '%-8C', 'db'
    call sqlFTabAdd      ft, tsNAME    , '%-8C', 'ts'
    call sqlFTabAdd      ft, tsType
    call sqlFTabAdd      ft, partitions,       , 'parts'
    call sqlFTabAdd      ft, pgSize
    call sqlFTabAdd      ft, dsSize
    call sqlFTabOthers ft, 'RBA1 RBA1TST RBA2 RBA2TST'
    call sqlFTabAdd      ft, rba1      , '%-12H'
    call sqlFTabAdd      ft, rba1Tst   ,       , 'rba1Timestamp:GMT'
    call sqlFTabAdd      ft, rba2      , '%-12H'
    call sqlFTabAdd      ft, rba2Tst   ,       , 'rba2Timestamp:GMT'
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatTables

sqlCatTSStats: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select' m.tb.alias'.*' ,
           tkrTable( , tb, 'f') wh ,
           'order by' if(ord == '', m.tb.order , ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, DBNAME, '%-8C', 'db'
    call sqlFTabAdd      ft, NAME   , '%-8C', 'ts'
    call sqlFTabAdd      ft, INSTANCE   , '%1i' , 'i'
    call sqlFTabAdd      ft, PARTITION , , 'part'
    call sqlFTabAdd      ft, NACTIVE   , , 'nActive'
    call sqlFTabAdd      ft, NPAGES    , , 'nPages'
    call sqlFTabAdd      ft, SPACE       , , 'spaceKB'
    call sqlFTabAdd      ft, TOTALROWS   , , 'totRows'
    call sqlFTabAdd      ft, DATASIZE         , , 'dataSz'
    call sqlFTabAdd      ft, LOADRLASTTIME    , , 'loadRLasttime'
    call sqlFTabAdd      ft, REORGLASTTIME    , , 'reorgLasttime'
    call sqlFTabAdd      ft, REORGINSERTS     , , 'inserts'
    call sqlFTabAdd      ft, REORGDELETES     , , 'deletes'
    call sqlFTabAdd      ft, REORGUPDATES     , , 'updates'
    call sqlFTabAdd      ft, REORGUNCLUSTINS  , , 'unClIns'
    call sqlFTabAdd      ft, REORGDISORGLOB   , , 'disorgL'
    call sqlFTabAdd      ft, REORGMASSDELETE  , , 'massDel'
    call sqlFTabAdd      ft, REORGNEARINDREF  , , 'nearInd'
    call sqlFTabAdd      ft, REORGFARINDREF   , , 'farInd'
    call sqlFTabAdd      ft, REORGCLUSTERSENS , , 'cluSens'
    call sqlFTabAdd      ft, REORGSCANACCESS  , , 'scanAcc'
    call sqlFTabAdd      ft, REORGHASHACCESS  , , 'hashAcc'
    call sqlFTabAdd      ft, STATSLASTTIME    , , 'statsLasttime'
    call sqlFTabAdd      ft, STATSINSERTS     , , 'inserts'
    call sqlFTabAdd      ft, STATSDELETES     , , 'deletes'
    call sqlFTabAdd      ft, STATSUPDATES     , , 'updates'
    call sqlFTabAdd      ft, STATSMASSDELETE  , , 'massDel'
    call sqlFTabAdd      ft, COPYLASTTIME     , , 'copyLasttime'
    call sqlFTabAdd      ft, COPYUPDATETIME   , , 'copyUpdatetime'
    call sqlFTabAdd      ft, COPYUPDATELRSN   , '%-12H', 'updateLRSN'
    call sqlFTabAdd      ft, COPYUPDATEDPAGES , , 'updaPgs'
    call sqlFTabAdd      ft, COPYCHANGES      , , 'changes'
    call sqlFTabOthers ft
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatTSStats

sql4obj: procedure expose m.
parse arg m, tb
    call out 'insert into' tb '--' className(objClass(m))
    line = ''
    ff = oFlds(m)
    pr = '   ('
    do fx=1 to m.ff.0
        call sql4ObjOut m.ff.fx
        end
    call sql4ObjOut , 1
    call out '   ) values '
    pr = '   ('
    do fx=1 to m.ff.0
        f1 = m || left('.', m.ff.fx  \== '')m.ff.fx
        v = m.f1   /* no strip T, gives errors in RCM profile | */
        if dataType(v, n) then
            call sql4ObjOut v
        else do qx=1 until v == ''
            vx = verify(v, m.ut_alfPrint)
            if vx = 0 then do
                l1 = min(60, length(v))
                w = quote(left(v, l1), "'")
                end
            else if vx > 29 then do
                l1 = min(60, vx-1)
                w = quote(left(v, l1), "'")
                end
            else do
                l1 = min(29, length(v))
                w = 'x'quote(c2x(left(v, l1)), "'")
                end
            if qx == 1 then
                call sql4ObjOut w
            else do
                if qx = 2 then
                    call sql4ObjOut , 1
                call out '   ||' w
                end
            v = substr(v, l1+1)
            end
        end
    call sql4ObjOut , 1
    call out '   ) ; '
    return
endProcedure
sql4objOut:
parse arg t1, force
    if (force == 1 & line \== '') | length(line t1) > 65 then do
        call out pr  substr(line, 3)
        pr = '   ,'
        line = ''
        end
    if force \== 1 then
        line = line',' t1
    return
endProcedure sql4objOut
/*--- -dis db interface ---------------------------------------------*/
/*--- do one -dis db... and insert it into stem  --------------------*/
sqlDisDb: procedure expose m.
parse upper arg o, cc
    do cx=1
        mid = strip(left(m.cc.cx, 10))
        if words(mid) > 1 then
            call err 'bad msgId' mid 'line:' m.cc.cx
        if mid == '' | wordPos(mid, 'DSNT360I DSNT361I DSNT365I') ,
                > 0 then
            iterate
        if mid == 'DSN9022I' then
            if cx = m.cc.0 then
                return m.o.0
            else
                call err 'not at end' cx':' m.cc.cx
        if mid \== 'DSNT362I' then
            call err 'DSNT362I expected not line:' m.cc.cx
        dx = pos('DATABASE =', m.cc.cx)
        sx = pos('STATUS ='  , m.cc.cx)
        if dx < 1 | sx <= dx then
            call err 'bad DSNT362I line' cx':' m.cc.cx
        db = word(substr(m.cc.cx, dx+10), 1)
        sta = strip(substr(m.cc.cx, sx+8))
        call sqlDisDbAdd o, db, ,0, 0, 'DB', sta
        do cx=cx+1 while abbrev(m.cc.cx, '   ')
            end
        if abbrev(m.cc.cx, 'DSNT397I ') then do
            cx = cx + 1
            if \ abbrev(space(m.cc.cx, 1),
                 , 'NAME TYPE PART STATUS ') then
                call err 'NAME TYPE PART STATUS mismatch' cx m.cc.cx
            txNa = pos('NAME', m.cc.cx)
            txTy = pos('TYPE', m.cc.cx)
            txPa = pos('PART', m.cc.cx)
            txSt = pos('STAT', m.cc.cx)
            txEn = verify(m.cc.cx, ' ', 'n', txSt+6)
            if 0 then say 'title' txNa txTy txPa txSt txEn cx m.cc.cx
            cx=cx+1
            do forever
                do while abbrev(m.cc.cx, '----')
                    cx = cx + 1
                    end
                if abbrev(m.cc.cx, '*') then
                    leave
                parse var m.cc.cx sp =(txTy)  ty . =(txPa)  paFr . ,
                                       =(txSt) sta   =(txEn)
                sp = strip(sp)
                if words(sp) \= 1 | wordPos(ty, 'TS IX') < 0 then
                    call err 'bad name or type' cx':'m.cc.cx
                if paFr == '' | paFr == 'L*' then
                    paFr = 0
                else if abbrev(paFr, 'D') | abbrev(paFr, 'L') then
                    paFr = substr(paFr, 2)
                if \ datatype(paFr, 'n') then
                    call err 'part not numeric' cx':'m.cc.cx
                paTo = paFr
                cw = cx
                cx = cx + 1
                if abbrev(m.cc.cx, '    -THRU ') then do
                    parse var m.cc.cx =(txPa)  paTo . =(txSt)
                    if \ datatype(paTo, 'n') then
                        call err '-thru part not numeric' cx':'m.cc.cx
                    cx = cx + 1
                    end
                call sqlDisDbAdd o, db, sp, paFr, paTo, ty, sta
                end
            end
        if m.cc.cx = '******** NO SPACES FOUND' then
            cx = cx + 1
        if abbrev(m.cc.cx, '******* DISPLAY OF DATABASE ') ,
          & word(m.cc.cx,5) == db then
            if word(m.cc.cx,6) == 'ENDED' then
                iterate
            else if word(m.cc.cx,6) == 'TERMINATED' then
                call err 'db display overflow' cx':' m.cc.cx
        call err 'database' db 'ENDED mismatch' cx':' m.cc.cx
        end
endProcedure sqlDbDis

/*--- insert one tuple into tDbState ---------------------------------*/
sqlDisDbAdd: procedure expose m.
    if arg(7) == '' | arg(7) == 'RW' then
         return
parse arg o
    m.o.0 = m.o.0 + 1
    q = o'.'m.o.0
parse arg , m.q.db, m.q.sp, m.q.paFr, m.q.paTo, m.q.ty, m.q.sta
 /* say added q m.q.db'.'m.q.sp':'m.q.paFr'-'m.q.paTo m.q.ty':'m.q.sta*/
    ky = m.q.db'.'m.q.sp
    if symbol('m.o.ky') \== 'VAR' then
        m.o.ky = m.o.0
    return
endProceedure sqlDisDbAdd

/*--- get index in o for db sp part ----------------------------------*/
sqlDisDbIndex: procedure expose m.
parse arg st, d, s, pa
     if symbol('m.st.d.s') \== 'VAR' then
         return 0
     ix = m.st.d.s
     if ix > m.st.0 | d \== m.st.ix.db | s \== m.st.ix.sp then
         return 0
     if pa == '' then
         return ix
     do ix=ix to m.st.0 while d == m.st.ix.db & s == m.st.ix.sp
         if pa < m.st.ix.paFr then
             return 0
         else if pa <= m.st.ix.paTo then
             return ix
         end
     return 0
endProcedure sqlDisDbIndex

/*--- dsn Command, return true if continuation needed ----------------*/
sqlDsnCont: procedure expose m.
parse arg cc, ssid, cmd
    say '???dsnCont' cmd
    cont = sqlDsn(cc, ssid, cmd, 12) <> 0
    if cont then do
        cz = m.cc.0
        cy = cz - 1
        if \ abbrev(m.cc.cy, DSNT311I) ,
                | \ abbrev(m.cc.cz, 'DSN9023I') then
            call err 'sqlDsn rc=12 for' cmd 'out='cz ,
                     '\n'cy'='m.cc.cy'\n'cz'='m.cc.cz
        m.cc.0 = cz-2
        end
    return cont
endProcedure sqlDsnCont
/* copy sqlDiv end   **************************************************/
/* copy db2Cat begin **************************************************/
catTbLastCol: procedure expose m.
parse upper arg cr, tb
    return sql2one( ,
          "select strip(char(colcount)) || ' ' || strip(c.name) one"  ,
              "from sysibm.sysTables t left join sysibm.sysColumns c" ,
                  "on c.tbCreator = t.creator and c.tbName = t.name"  ,
                       "and c.colNo = t.colCount"                     ,
               "where t.creator = '"cr"' and t.name = '"tb"'", ,'')
endProcedure catTbLastCol

catTbCols: procedure expose m.
parse upper arg cr, tb
    if sql2St("select strip(name) name "     ,
          "from sysibm.sysColumns " ,
          "where tbcreator = '"cr"' and tbname='"tb"'",
          "order by colNo", ggSt) < 1 then
        return ''
    res = m.ggst.1.name
    do cx=2 to m.ggst.0
        res = res m.ggst.cx.name
        end
    return res
endProcedure catTbCols

catTbColsTrunc: procedure expose m.
parse upper arg cr, tb, maxL
    if sql2St("select strip(name) name, colType, length, length2"     ,
          "from sysibm.sysColumns " ,
          "where tbcreator = '"cr"' and tbname='"tb"'",
          "order by colNo", ggSt) < 1 then
        return ''
    res = ''
    do cx=1 to m.ggst.0
        ty = m.ggSt.cx.colType
        if pos('LOB', ty) > 0 then
            res = res', substr('m.ggSt.cx.name', 1,' ,
                 min(maxL, m.ggSt.cx.length2)') 'm.ggSt.cx.name
        else if pos('CHAR', ty) > 0 & m.ggSt.cx.length > maxL then
            res = res', substr('m.ggSt.cx.name', 1,' maxL')',
                 m.ggSt.cx.name
        else
            res = res',' m.ggSt.cx.name
        end
    return substr(res, 3)
endProcedure catTbColsTrunc

catIxKeys: procedure expose m.
parse upper arg cr, ix
    sql = "select colSeq, colName, ordering"                          ,
              "from sysibm.sysKeys"                                   ,
               "where ixCreator = '"cr"' and ixName = '"ix"'" ,
               "order by colSeq"
    call sqlQuery 1, sql, '?sq col ord'
    res = ''
    drop d
    do kx=1 while sqlFetch(1, d)
        if m.d.sq \= kx then
            call err 'expected' kx 'but got colSeq' m.d.sq ,
                     'in index' cr'.'ix'.'m.d.col
        res = res || strip(m.d.col) || translate(m.d.ord, '<>?', 'ADR')
        end
    call sqlClose 1
    return res
endProcedure catIxKeys

catColCom: procedure expose m.
parse upper arg fCr, fTb, tCr, tTb
    sql = "select t.name, t.colType, t.nulls, t.""DEFAULT"""        ,
                    ", coalesce(f.nulls, 'new')"                    ,
              "from sysibm.sysColumns t"                            ,
                "left join sysibm.sysColumns f"                     ,
                  "on f.tbCreator = '"fCr"' and f.tbName = '"fTb"'" ,
                    "and f.name = t.name"                           ,
              "where t.tbCreator = '"tCr"' and t.tbName = '"tTb"'"  ,
              "order by t.colNo"
    call sqlQuery 1, sql, 'na ty nu de nn'
    pr = ' '
    do kx=1 while sqlFetch(1)
        /* say kx m..na m..ty m..nu m..de 'nn' m..nn */
        if pos('CHAR', m..ty) > 0 then
            dv = "''"
        else if pos('INT' ,m..ty) > 0 ,
                | wordPos(m..ty, 'REAL FLOAT') > 0 then
            dv = 0
        else if m..ty == 'TIMESTMP' then
            dv = '0001-01-01-00.00.00'
        else if pos('LOB', m..ty) > 0 then
            dv = m..ty"('')"
        else
            dv = '???'
        if m..nu = 'Y' then
            dv = 'case when 1=0 then' dv 'else null end'
        r = '???'
        if m..ty = 'ROWID' then do
            r = '--'
            end
        else if m..nn == 'new' then do
            if m..de = 'Y' then
                r = '--'
            else if m..nu == 'N' then
                r = dv
            else
                r = 'case when 1=0 then' dv 'else null end'
            end
        else do
            if m..nu = 'Y' | (m..nu = m..nn) then
                r = ''
            else
                r = 'coalesce('m..na',' dv')'
            end
        if abbrev(r, '--') then do
            r = ' ' r
            end
        else do
            r = pr r
            pr = ','
            end
        if pos('???', r) > 0 then
            call err 'no default for type' m..ty 'in' tCr'.'tTb'.'m..na
        call out r m..na
        end
    call sqlClose 1
    return
endProcedure catColCom
/* copy db2Cat end   **************************************************/
/* copy sqlO   begin ***************************************************
    sql interface  mit  o und j Anbindung
***********************************************************************/
sqlIni: procedure expose m.
    if m.sqlO_ini == 1 then
        return
    call sqlRxIni
    m.sqlO_ini = 1
    call jIni
    call scanIni
    m.sqlO.cursors  = left('', 200)
    call classNew 'n SqlRdr u JRWO', 'm',
        , "jReset m.m.src = arg; m.m.type = arg2;",
        , "jOpen  call sqlRdrOpen m, opt",
        , "jClose call sqlRdrClose m",
        , "jReadO return sqlRdrReadO(m)"
    call classNew 'n SqlResRdr u JRWO', 'm',
        , "jReset m.m.cursor = arg; m.m.type = arg2;",
        , "jOpen  call sqlRdrO2 m" ,
        , "jClose call sqlClose m.m.cursor" ,
        , "jReadO return sqlRdrReadO(m)"
    call classNew 'n SqlRxConnection u', 'm',
        , "sqlQuery  return sqlRxQuery(cx, src, feVa, 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, feVa, 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, feVa, retOK)",
        , "sqlFetch  return sqlCsmFetch(cx, dst, retOk)",
        , "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, feVa, retOk)",
        , "sqlFetch  return sqlCsmFetch(m.cx.cursor, dst, retOk)",
        , "sqlClose  return 0",
        , "sqlUpdate call err 'implement sqlCsmUpdate'"   ,
        , "sqlCall   call err 'implement sqlCsmCall'"
    return 0
endProcedure sqlIni

/*--- connect to DB2 dsnRexx or csm ----------------------------------*/
sqlConnect: procedure expose m.
parse upper arg sys, retOk
    call sqlIni
    if sys == '' then
        sys = sqlDefaultSys()
    if pos('/', sys) <= 0 then do
        call  sqlRxConnect sys
        m.sql_connection = oNew('SqlRxConnection')
        end
    else do
        parse var sys m.sql_csmHost '/' m.sql_dbSys
        m.sql_connection = oNew('SqlCsmConnection')
        end
    return 0
endProcedure sqlConnect

/*--- disconnect from db2 --------------------------------------------*/
sqlDisconnect: procedure expose m.
parse arg retOk
    if m.sql_csmHost == '' then
        call sqlRxDisconnect
    else
        m.sql_csmHost = ''
    m.sql_dbSys = ''
    m.sql_connection = 'notConnected'
    return 0
endProcedure sqlDisonnect

/*--- execute a query from sql, with one resultset -------------------*/
sqlQuery: procedure expose m.
parse arg cx, src, feVa, retOk
    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')
endProcedue sqlUpdate

/*-- 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')
endProcedure sqlCall

/*--- return a free cursor -------------------------------------------*/
sqlGetCursor: procedure expose m.
parse arg rng
    if rng == '' then
        return sqlGetCursorRng(rng, 10, 48)
    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

/*--- execute stmts with options -------------------------------------*/
sqlStmtsOpt: procedure expose m.
parse arg src, opts
    upper opts
    sub = ''
    o = ''
    retOk = ''
    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
            retOk = retOk w
        else
            call err 'bad opt' w 'in opts' opts 'not -sql? -o or subsys'
        end
    return sqlStmts(src, strip(retOk), strip(o))
endProcedure sqlStmtsOpt

/*** execute sql's in a stream (separated by ;)
       opt: 'o' ==> write objects, otherwise fTabAuto
            'sql72' ==> spufi formatting (window 72) else linebreaks */
sqlStmts: procedure expose m.
parse arg sqlSrc, retOk, opt
   dlm = ';'
   isStr = oStrOrObj(sqlSrc, 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 scanSrc 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
       if translate(left(s1, 10)) == 'TERMINATOR' then do
            dlm = strip(substr(s1, 11))
            if length(dlm) \== 1 then
                call scanErr sqlStmts, 'bad terminator' dlm
            iterate
            end
       call outSt(splitNl(sqlTmp, sqlStmt(s1, retOk, opt)))
       end
   if \ isStr then
       call jClose fi
   return 0
endProcedure sqlStmts

/*--- execute a single sql statement --------------------------------*/
sqlStmt: procedure expose m.
parse arg src, retOk, opt
    cx = sqlGetCursor()
    r1 = sqlExecute(cx, src, retOK)
    res = m.sql_HaHi || sqlMsgLine(r1, m.sql.cx.updateCount, src)
    if m.sql.cx.resultSet \== '' then do
        rdr = jReset(oMutate('SQL_RDR.'cx, 'SqlResRdr'), cx)
        if pos('o', opt) > 0 then
            call pipeWriteAll rdr
        else
            call fTabAuto fTabReset(sql_StmtFmt, 1), rdr
        res = sqlMsgLine(m.rdr.rowCount 'rows fetched', , src)
        end
    call sqlFreeCursor cx
    return res
endProcedure sqlStmt

/*--- execute the given sql plus a commit
         until no more rows are updated -----------------------------*/
sqlUpdComLoop: procedure expose m.
parse arg src, retOk, opt
    src = inp2Str(src)
    crs = sqlGetCursor()
    upds = 0
    if retOk == '' then
        retOk = 100
    do coms=0
        cd = sqlExecute(crs, src, retOk)
        if m.sql.crs.updateCount < 1 then do
            call sqlFreeCursor(crs)
            return sqlMsgLine( , upds, src, coms 'commits')
            end
        upds = upds + m.sql.crs.updateCount
        call sqlCommit
        if coms // 20 = 19 then
            say sqlMsgLine(time(), upds, src, (coms+1) 'commits')
        end
endProcedure sqlUpdComLoop

/*--- sql call statement ---------------------------------------------*/
sqlStmtCall: procedure expose m.
parse arg src, retOk, 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.ut_alfDot) 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 fTabAuto 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

/*--- prepare and open cursor
      generate type and fetchList ------------------------------------*/
sqlRdr: procedure expose m.
parse arg src, type
     return oNew('SqlRdr', inp2str(src, '-sql'), type)
endProcedure sqlRdr

sqlRdrOpen: procedure expose m.
parse arg m, opt
    if opt\== m.j.cRead then
        call err 'opt not' m.j.cRead 'sqlRdrOpen('m',' opt')'
    m.m.cursor = sqlGetCursor()
    if m.m.type == '' then
        ff = ''
    else do
        ff = mCat(classMet(m.m.type, 'oFlds'), ' ')
        if abbrev(ff, '. ') then
            call err 'sqlRdrOpen implement value for type' m.m.type
        ff = space(translate(ff, ' ', '.'), 1)
        end
    call sqlQuery m.m.cursor, m.m.src, ff
    call sqlRdrO2 m
    return
endProcedure sqlRdrOpen

sqlRdrO2: procedure expose m.
parse arg m
    if m.m.type = '' then do
        ff = mCat('SQL.'m.m.cursor'.COL', ' v, f ')
        m.m.type = classNew('n* SQL u f' ff 'v')
        end
    m.m.rowCount = 0
    return
endProcedure sqlRdrO2

/*--- close sql Cursor -----------------------------------------------*/
sqlRdrClose: procedure expose m.
parse arg m, v
    call sqlClose m.m.cursor
    call sqlFreeCursor m.m.cursor
    m.m.cursor = ''
    return m
endProcedure sqlRdrClose

/*--- read next from cursor, return as object ------------------------*/
sqlRdrReadO: procedure expose m.
parse arg m
    v = oNew(m.m.type)
    if \ sqlFetch(m.m.cursor, v) then
        return mFree(v)
    m.m.rowCount = m.m.rowCount + 1
    return v
endProcedure sqlRdrReadO

/*--- select and write all to stdOut ---------------------------------*/
sqlSel: procedure expose m.
parse arg src, type
     s = sqlRdr(src, type)
     call pipeWriteAll s
     return m.s.rowCount
endProcedure sqlSel
/* copy sqlO   end   **************************************************/
/* copy sqlCsm begin **************************************************/
/*--- send an sql to csm and handle sqlCode --------------------------*/
sqlCsmExe:
parse arg cx, ggSqlStmt, ggRetOk
    sql_HOST =  m.sql_csmhost
    SQL_DB2SSID = m.sql_dbSys
    sql_query = ggSqlStmt
    address tso "CSMAPPC START PGM(CSMASQL)"
    if \ (rc = 0 |  rc = 4) then
        return 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\nsql =' ggSqlStmt
        else
            call errSay 'w}'sqlMsg(sqlCA2rx(sqlCa))
    return sqlCode
endProcedure sqlCsmExe

/*--- execute a query from sql, with one resultset -------------------*/
sqlCsmQuery: procedure expose m.
parse arg cx, sqlSrc, feVa, retOk, dst
    res = sqlCsmExe(cx, sqlSrc, 100 retOk)
    if res < 0 then
        return res
    if dst == '' then
        dst = 'SQL.'cx'.CSMDATA'
    m.dst.0 = 0
    m.dst.laIx = 0
    st = 'SQL.'cx'.COL'
    call sqlRemVars st
    feVa = translate(feVa, ' ', '?')
    if feVa = '' then do kx=1 to sqlD
        call sqlAddVar st, SQLDA_REXXNAME.kx, 1
        end
    else if abbrev(feVa, ':')  then
        return err('fetchVars with : for csm:' feVa)
    else if words(feVa) <> sqlD then
        return err('csm sqlD='sqlD', but' words(feVa) 'vars:' feVa)
    else do kx=1 to words(feVa)
        call sqlAddVar st, word(feVa, kx), 1
        end
    do kx=1 to sqlD
        rxNa = SQLDA_REXXNAME.kx
        cn = m.st.kx
        do rx=1 to sqlRow#
            if substr(sqlIndicator.rx, kx ,1) == 'ff'x then
                m.dst.rx.cn = m.sqlNull
            else
                m.dst.rx.cn = value(rxNa'.'rx)
            end
        end
    m.dst.0 = sqlRow#
    return 0
endProcedure sqlCsmQuery

sqlCsmFetch: procedure expose m.
parse arg cx, dst
    src = 'SQL.'cx'.CSMDATA'
    rx = m.src.laIx + 1
    if rx > m.src.0 then
        return 0
    m.src.laIx = 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 sqlRx  begin ***************************************************
       Achtung: inc generiert sql aus sqlRx, Aenderungen nur in sqlRx|
    sql interface
***********************************************************************/
/*--- initialize sqlRx -----------------------------------------------*/
sqlRxIni: procedure expose m.
    if m.sqlRx_ini == 1 then
        return
    m.sqlRx_ini = 1
    m.sqlNull = '---'
    m.sqlInd = 'sqlInd'
    m.sql_defCurs= 49
    m.sqlCAMsg = 0
    m.sqlSuMsg = 2
    m.sql_dbSys = ''
    m.sql_csmhost = ''
    isInProd = wordPos(sysvar(sysNode), 'RZ2') > 0
    m.sqlRetOK = 'dne' copies('rod', \ isInProd)
    return 0
endProcedure sqlRxIni

/*--- get default db2System ------------------------------------------*/
sqlDefaultSys: procedure expose m.
parse arg
    if sysvar(sysnode) == 'RZ1' then
        return 'DBAF'
    else if sysvar(sysnode) == 'RZ4' then
        return 'DP4G'
    else
        call err 'no default subsys for' sysvar(sysnode)
endProcedure sqlDefaultSys

/*--- 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
    if sys == '' then
        sys = sqlDefaultSys()
    m.sql_dbSys = sys
    ggSqlStmt =  'connect' sys
    address dsnRexx ggSqlStmt
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, ggRetOk, ggSqlStmt)
endProcedure sqlRxConnect

/*--- diconnect from db2 ---------------------------------------------*/
sqlRxDisconnect: procedure expose m.
parse arg retOk
    if m.sql_dbSys == '' then
        return 0
    ggSqlStmt =  'disconnect'
    m.sql_dbSys = ''
    m.sql_csmHost = ''
    address dsnRexx ggSqlStmt
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, retOk, ggSqlStmt)
endProcedure sqlRxDisconnect

/*--- reset sql cursor 'c'cx fields ----------------------------------*/
sqlReset: procedure expose m.
parse arg cx
     m.sql.cx.updateCount = ''
     m.sql.cx.resultSet   = ''
     m.sql.cx.d.sqlD = 'noSqlDA'
     m.sql.cx.i.sqlD = 'noDescInp'
     m.sql.cx.fetchVars = ''
     m.sql.cx.type  = ''
     call sqlRemVars 'SQL.'cx'.COL'
     return
endProcedue sqlReset

/*--- execute a query from sql, with one resultset -------------------*/
sqlRxQuery: procedure expose m.
parse arg cx, src, feVa, retOk
     call sqlReset cx
     src = inp2str(src, '%,%c ')
     s1 = ''
     if pos(left(feVa, 1), '?:') < 1 then
         s1 = 'into :M.SQL.'cx'.D'
     res = sqlExec('prepare s'cx s1 'from :src', retOk)
     if res < 0 then
         return res
     call sqlRxFetchVars cx, feVa
     call sqlExec 'declare c'cx 'cursor for s'cx
     res = sqlExec('open c'cx, retOk)
     if res < 0 then
         return res
     m.sql.cx.updateCount = sqlErrd.3
     m.sql.cx.resultSet = cx
     return res
endProcedure sqlRxQuery

/*--- prepare a query from sql, with one resultset -------------------*/
sqlQueryPrep: procedure expose m.
parse arg cx, src, feVa, retOk
     call sqlReset cx
     src = inp2str(src, '%,%c ')
     s1 = ''
     if feVa == '' | feVa = 'd' then
         s1 = 'into :M.SQL.'cx'.D'
     res = sqlExec('prepare s'cx s1 'from :src', retOk)
     if res < 0 then
         return res
     call sqlRxFetchVars cx, feVa
     call sqlExec 'declare c'cx 'cursor for s'cx
     return sqlExec('describe input s'cx 'into :M.SQL.'cx'.I')
endProcedure sqlQueryPrep

sqlQueryArgs: procedure expose m.
parse arg cx retOk
    do ix=1 to arg()-1
        call sqlDASet cx , 'I', ix, arg(ix+1)
        end
     res = sqlExec('open c'cx 'using descriptor :M.SQL.'cx'.I', retOk)
     if res < 0 then
         return res
     m.sql.cx.updateCount = sqlErrd.3
     m.sql.cx.resultSet = cx
     return res
endProcedure sqlQueryArgs

/*--- fetch next row to m.dst.* at end return false ------------------*/
sqlRxFetch: procedure expose m.
parse arg cx, dst, retOk
    fetCode = sqlExec('fetch c'cx 'into' m.sql.cx.fetchVars, 100 retOk)
    if fetCode == 100 then
        return 0
    if fetCode < 0 then
        return fetCode
    interpret m.sql.cx.fetchCode
    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 sqlExec('execute immediate :src', retOk)
        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, retOk)
        end
    if fun == 'DECLARE'  then do
        if 'GLOBAL' == translate(word(substr(src, bx), 2)) then
            return sqlExec('execute immediate :src', retOk)
        end
    res = sqlExec(src, retOk)
    if wordPos(fun, 'DELETE INSERT UPDATE') > 0 then
        m.sql.cx.updateCount = sqlErrd.3
    return res
endProcedure sqlRxUpdate

/*-- prepare an update -----------------------------------------------*/
sqlUpdPrep: procedure expose m.
parse arg cx, src, retOk
    res = sqlExec('prepare s'cx 'from :src', retOk)
    return sqlExec('describe input s'cx 'into :M.SQL.'cx'.I')
endProcedure sqlUpdPrep

/*-- execute a prepared update with the given arguments --------------*/
sqlUpdArgs: procedure expose m.
parse arg cx retOk
    do ix=1 to arg()-1
        call sqlDASet cx , 'I', ix, arg(ix+1)
        end
    res = sqlExec('execute s'cx 'using descriptor :M.SQL.'cx'.I',
                  , retOk)
    m.sql.cx.updateCount = sqlErrd.3
    return res
endProcedure sqlRxUpdArgs

/*-- execute a query, update or call ---------------------------------*/
sqlExecute: procedure expose m.
parse arg cx, src, retOk
    src = inp2Str(src, '-sql')
    f = translate(word(src, 1))
    bx = pos('(', f)
    if bx > 0 then
        f = left(f, max(1, bx-1))
    m.sql.cx.fun = f
    if f == 'SELECT' | f == 'WITH' | f == '(' 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

/*-- fetch all rows to stem and close --------------------------------*/
sqlFetch2St: procedure expose m.
parse arg cx, dst, retOK
    do sx=1 while sqlFetch(cx, dst'.'sx)
       end
    res = sx-1
    m.dst.0 = sx-1
    call sqlClose cx
    return m.dst.0
endProcedure sqlFetch2St

/*-- execute a query, copy result to stem ----------------------------*/
sql2St: procedure expose m.
parse arg src, dst, feVa, retOK
    cx = m.sql_defCurs
    res = sqlQuery(cx, src, feVa, retOk)
    return sqlFetch2St(cx, dst, retOk)
endProcedure sql2St

/*-- return first column of the only row and close -------------------*/
sqlFetch2One: procedure expose m.
parse arg cx, dst, retNone
    f1 = sqlFetch(cx, dst)
    if f1 then
        f2 = sqlFetch(cx, dst)
    call sqlClose cx
    if \ f1 then
        if retNone \== '' then
            return substr(retNone, 2)
        else
            call err 'sqlFetch2One: no row returned'
    else if f2 then
        call err 'sqlFetch2One: more than 1 row'
    c1 = m.sql.cx.col.1
    return m.dst.c1
endProcedure sqlFetch2One

/*-- fxecute a query and return first row of the only colun
           if > 1 row fail, if 0 rows return arg(3) or fail ----------*/
sql2One: procedure expose m.
parse arg src, dst, feVa, retOk, retNone
    cx = m.sql_defCurs
    call sqlQuery cx, src, feVa, retOk
    return sqlFetch2One(cx, dst, retNone)
endProcedure sql2One

/*--- describe table and return sqlDA --------------------------------*/
sqlDescribeTable: procedure expose m.
parse upper arg tb, force
    if force == 1 | \ datatype(m.sql.table.tb.sqlD, 'n') then
         call sqlExec 'describe table :tb into :M.SQL.TABLE.'tb
    return 'SQL.TABLE.'tb
endProcedure sqlDescribeTable

/*--- return select column list for table tb
      omit Blobs (blobMax < 0) or substr(blob, 1, blobMax) ----------*/
sqlColList: procedure expose m.
parse arg tb al, blobMax
    sd = sqlDescribeTable(tb)
    bs = ''
    lst = ''
    if al \== '' & right(al, 1) \== '.' then
        al = al'.'
    do sx=1 to m.sd.sqld
        if wordPos(m.sd.sx.sqlType, '404 405 408 409 412 413') < 1 then
            lst = lst',' al || m.sd.sx.sqlName
        else do
            bs = bs m.sd.sx.sqlName
            if blobMax >= 0 then
                lst = lst', length('al || m.sd.sx.sqlName')' ,
                                          m.sd.sx.sqlName'Len' ,
                     || ', substr('al  || m.sd.sx.sqlName ,
                     || ', 1,' blobMax')' m.sd.sx.sqlName
            end
        end
    m.sd.colList = substr(lst, 3)
    m.sd.blobs = strip(bs)
    return substr(lst, 3)
endProcedure sqlColList

/*--- use describe output to generate column names,
        ''         use names from ca (rexxified)
        nms+       use names, check ca for null values
        ?('?'?nm)+ use names, check for null if preceeded by ?
        :...       use string as is
                fetchVariables and sqlNull names ---------------------*/
sqlRxFetchVars: procedure expose m.
parse arg cx, src, cd
    m.sql.cx.fetchCode = cd
    st = 'SQL.'cx'.COL'
    call sqlRemVars st
    if abbrev(src, ':') then do
        m.sql.cx.fetchVars = src
        return
        end
    m.sql.cx.fetchVars = ''
    vrs = ''
    sNu = ''
    if abbrev(src, '?') then do
        r = substr(src, 2)
        do wx=1 to words(src)
            cn = word(src, wx)
            if abbrev(cn, '?') then
                call sqlRexxAddVar substr(cn, 2), 0, 1
            else
                call sqlRexxAddVar cn, 0, 0
            end
        end
    else if src <> '' then do kx=1 to words(src)
        cn = word(src, kx)
        call sqlRexxAddVar cn, 0, m.sql.cx.d.kx.sqlType // 2
        end
    else do kx=1 to m.sql.cx.d.sqlD
        call sqlRexxAddVar m.sql.cx.d.kx.sqlName, 1 ,
                       , m.sql.cx.d.kx.sqlType // 2
        end
    m.sql.cx.fetchVars = substr(vrs, 3)
    m.sql.cx.fetchCode = sNu cd
    return
endProcedure sqlRxFetchVars

sqlRexxAddVar:
parse arg nm, nicify, hasNulls
     nm = sqlAddVar(st, nm, nicify)
     if \ hasNulls then
          vrs = vrs', :m.dst.'nm
     else do
         vrs = vrs', :m.dst.'nm ':m.dst.'nm'.sqlInd'
         sNu = sNu 'if m.dst.'nm'.sqlInd < 0 then' ,
                    'm.dst.'nm '= m.sqlNull;'
         end
    return
endSubroutine sqlRexxAddVar

sqlCol2kx: procedure expose m.
parse arg cx, nm
    if symbol('M.SQL.CX.COL.NM') \== 'VAR' then
        return ''
    kx = m.sql.cx.col.nm
    if m.sql.cx.col.kx \== nm then
        call err 'sqlCol2kx' nm '==>' kx 'but' m.sql.cx.col.kx
    return kx
endProcedure sqlCol2kx

sqlRemVars: procedure expose m.
parse arg st
    if symbol('m.st.0') == 'VAR' then do
        do sx=1 to m.st.0
            nm = m.st.sx
            drop m.st.nm m.st.sx
            end
        end
    m.st.0 = 0
    return
endProcedure sqlRemVars

sqlAddVar: procedure expose m.
parse arg st, sNa, nicify
    sx = m.st.0 + 1
    if 1 | nicify then do
        cx = verifId(sNa)
        if cx > 0 then /* avoid bad characters for classNew| */
            sNa = left(sNa, cx-1)
        upper sNa
        if sNa == '' | symbol('m.st.sNa') == 'VAR' then
            sNa = 'COL'sx
        end
    m.st.0 = sx
    m.st.sx = sNa
    m.st.sNa = sx
    return sNa
endProcedure sqlAddVar

/*--- 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

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

/*--- execute sql thru the dsnRexx interface -------------------------*/
sqlExec: /* no procedure, to keep variables sql... */
    parse arg ggSqlStmt, ggRetOk
    m.sql_HaHi = ''
    address dsnRexx 'execSql' ggSqlStmt
    /* say 'sqlCode' sqlCode 'rc' rc 'for' ggSqlStmt ggNo */
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, ggRetOk, ggSqlStmt)
endProcedure sqlExec

sqlErrorHandler: procedure expose m. ,
                   sqlCode sqlErrMc sqlErrP sqlErrD. sqlWarn. sqlState
parse arg drC, retOk, verb rest
    if drC == 0 then
        return 'return 0'
    if wordPos(drC, '1 -1') < 0 then
        return "call err 'dsnRexx rc" drC"' sqlmsg()"
    if pos('-', retOK) < 1 then
        retOK = retOk m.sqlRetOk
    if pos('*', retOK) > 0 | wordPos(sqlCode, retOK) > 0 then do
        if sqlCode < 0 & pos('say', retOK) > 0 then
            return "call outSt errMsg(' }'sqlMsg()); return" sqlCode
        else
            return "return" sqlCode
        end
    upper verb
    if verb == 'DROP' then do
        if (sqlCode == -204 | sqlCode == -458) ,
                       & wordPos('dne', retok) > 0 then
            return 'return' sqlCode
        if sqlCode = -672 & wordPos('rod', retok) > 1 then do
            hahi = m.sql_HaHi ,
                 || sqlMsgLine(sqlCode, 'tb='sqlErrMc ,verb rest)'\n'
            call sqlExec 'alter table' SqlErrMc ,
                    'drop restrict on drop'
            hahi = hahi || m.sql_HaHi ,
                        || sqlMsgLine(sqlCode, , ggSqlStmt)'\n'
            call sqlExec verb rest
            m.sql_HaHi = hahi
            return 'return' sqlCode
            end
        end
    if drC < 0 then
         return "call err sqlmsg(); return" sqlCode
    if (sqlCode <> 0 | sqlWarn.0 ^==' ') & pos('w',retOK) < 1 then
        return "call outSt errMsg(' }'sqlMsg()); return" sqlCode
    return 'return' sqlCode
endProcedure sqlErrorHandler

sqlMsgLine: procedure expose m. sqlErrD.
parse arg res, cnt, src, plus
    verb = translate(word(src, 1))
    if datatype(res, 'n') then
        res = 'sqlCode' res
    if cnt \== '' then do
        res = res',' cnt
        vx = wordPos(translate(word(src,1)), 'DELETE INSERT UPDATE')
        if datatype(cnt, 'n') then
            if vx > 0 then
               res = res 'rows' word('deleted inserted updated', vx)
            else if cnt <> 0 then
                res = res 'rows updated'
        end
    if plus \== '' then
        res = res',' plus
    if abbrev(res, ', ') then
        res = substr(res, 3)
    if src \== '' then do
        ll = 75 - length(res)
        aa = strip(src)
        if length(aa) > ll then
            aa = space(aa, 1)
        if length(aa) > ll then
           aa = left(aa,  ll-3)'...'
        res = res':' aa
        end
    return res
endProcedure sqlMsgLine

/*--- 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

/*--- 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
    ggFrom = 'ggSqlStmt'
    ggW1 = translate(word(ggSqlStmt, 1))
    ggW2 = translate(word(ggSqlStmt, 2))
    if ggW1 == 'PREPARE' then
        ggFrom = sqlHostVarFind(ggSt, 'FROM')
    else if ggW1 ggW2 == 'EXECUTE IMMEDIATE' then
        ggFrom = sqlHostVarFind(ggSt, 1)
    ggPos = 0
    if datatype(sqlErrd.5, 'n') & sqlErrd.5 > 0 then do
        ggPos = sqlErrd.5
        ggRes = ggRes || sqlMsgSrcPos(value(ggFrom), sqlErrd.5)
        end
    if ggFrom == 'ggSqlStmt' then do
        ggRes = ggRes'\nsql =' sqlShorten(ggSqlStmt, 2000, ggPos)
        end
    else do
        ggRes = ggRes'\nsql =' sqlShorten(value(ggFrom), 2000, ggPos)
        ggRes = ggRes'\nstmt =' sqlShorten(ggSqlStmt, 2000)
        end
    ggPref = '\nwith'
    do ggXX=1 to m.ggSt.0
        if ggFrom = m.ggVa.ggXX then
            iterate
        ggRes = ggRes || ggPref m.ggBe.ggXX ':'m.ggVa.ggXX ,
                      '=' sqlShorten(value(m.ggVa.ggXX), 210)
        ggPref = '\n    '
        end
    if m.sqlSuMsg == 1 | (m.sqlSuMsg == 2 & m.sql_csmhost \== '') then
        ggRes = ggRes'\nsubsys =' m.sql_dbSys ,
             || ', host =' m.sql_csmhost
    return  ggRes
endSubroutine sqlMsg

sqlShorten: procedure expose m.
parse arg txt, maxL, pos
    if length(txt) <= maxL then
        return txt
    if \ datatype(pos, 'n') | pos < 1 then
        pos = 1
    ex = pos + min(60, maxL%7)
    if ex <= maxL - 4 then
        return left(txt, maxL-4) '...'
    if ex >= length(txt) then
        return left(txt, 67) '...\n'substr(txt, length(txt)-maxL+72)
    else
        return left(txt, 67) '...\n'substr(txt, ex-maxL+76, maxL-75) ,
                       '...'
endProcedure sqlShorten
/*--- 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 nop
    else if rc = 4 then say 'warn linkPgm dsnTiar rc' rc 'sqlCa' ca
    else                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: procedure expose m. ,
                   sqlCode sqlErrMc sqlErrP sqlErrD. sqlWarn. sqlState
    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 = 1
    do cnt
        cx = pos(':', src, cx) + 1
        if cx < 2 then
           leave
        if pos(substr(src, cx, 1), m.ut_rxN1) > 0 then
            iterate
        ex = verify(src, m.ut_rxDot, 'n', cx)
        if ex < 1 then
            m.st.var.sx = substr(src, cx)
        else
            m.st.var.sx = substr(src, cx, ex - cx)
        if m.st.var.sx == '' | length(m.st.var.sx) > 100 then
            iterate
                       /* 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.ut_alfa) > 0
            end
        if bB < bE & bB >= 0 then
            m.st.bef.sx = substr(src, bB+1, bE-bB)
        else
            m.st.bef.sx = ''
        sx = sx + 1
        end
    m.st.0 = sx-1
    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 sqlRx  end   **************************************************/
/* copy csi begin    ***************************************************
     csi interface: see dfs managing catalogs chapt. 11
         returncode/reason see message IDC3009I
**********************************************************************/
/*--- specify dsn mask and fields to start a csi catalog search --------
      arguments:
          m       objectPointer
          dsnMask specifies the dsns with wildcards:
              %  1 character
              *  0 - n character in one level
              ** 0 - n levels
          fields a (space separated) list of field names -------------*/
csiOpen: procedure expose m.
parse arg m, dsnMask, fields
    m.m.fld.0 = words(fields)
    ffix = d2c(m.m.fld.0, 2)
    do x=1 to m.m.fld.0
        m.m.fld.x = translate(word(fields, x))
        ffix = ffix || left(m.m.fld.x, 8)
        end
    if dsnMask \== '' & right(dsnMask, 1) \== ' ' ,
          & pos('*', dsnMask) < 1 & length(dsnMask) < 42 then
        dsnMask = dsnMask'.**'
    m.m.filt = left(dsnMask, 149) ,
             || left('Y', 3) ,        /* resume offset 149      */
             || ffix                  /* csiNumEn offset 152    */

    WORKLEN = 1024 * 64
    m.m.work = D2C(WORKLEN,4) || COPIES('00'X,WORKLEN-4)
    m.m.pos = workLen + 1
    return
endProcedure csiOpen

/*--- put the next dsn into m.o and m.o.* (for other fields)
      return 1 if next dsn exists 0 otherwise ------------------------*/
csiNext: procedure expose m.
parse arg m, o
    usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET AMOUNT OF WORK AREA USED */
    px = m.m.pos
    do forever
        if px > usedL then do
            if substr(m.m.filt, 150, 1) \== 'Y' then do
                m.m.pos = px
                m.o = ''
                return 0
                end
            reason = left('', 4)
            ADDRESS LINKPGM 'IGGCSI00  reason m.'m'.filt  m.'m'.work'
            if rc == 0 & substr(reason, 3, 2) == '0000'x then
                nop
            else if rc == 4 & substr(reason, 3, 2) == '0464'x then
                say 'data set entry with error'
            else
                call err 'call csi returns' rc,
                             'rc' c2d(substr(reason, 4,1)),
                             'reason' c2d(substr(reason, 3,1)),
                             'module' substr(reason, 1,2)
            usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET WORK AREA USED */
            numFD = C2D(SUBSTR(m.m.work,13,2)) /* no flds + 1 */
            if numFd <> m.m.fld.0 + 1 then
                call err 'csiNumFd' numFd 'not' m.m.fld.0 '+' 1
            px = 15
            iterate
            end
        eType =  substr(m.m.work, px+1, 1)
        m.o = strip(substr(m.m.work, px+2, 44), 't')
        flag = substr(m.m.work, px, 1)
        /* say 'eType' eType m.o 'flag' c2x(flag) */
        if eType == '0' then do
            if flag \== '00'x & flag \== '40'x then
                call err 'flag' c2x(flag) 'for catalog' m.o
            px = px + 50    /* length of catalog entry  */
            iterate
            end
        else do
            if \ abbrev(x2b(c2x(flag)), '101') then
                call err 'call csi entry flag' x2b(c2x(flag)),
                             'rc' c2d(substr(m.m.work, px+49,1)),
                             'reason' c2d(substr(m.m.work, px+48,1)),
                             'module' substr(m.m.work, px+46, 2),
                             'for entry' m.o,
                             'see qw IDC3009I'
            py = px + 46
            tl = c2d(substr(m.m.work, py, 2))
            pl = py + 4
            pf = py + m.m.fld.0 * 2 + 4
            do fx = 1 to m.m.fld.0
                fi = m.m.fld.fx
                fl = c2d(substr(m.m.work, pl, 2))
                m.o.fi = substr(m.m.work, pf, fl)
                if fi = 'MGMTCLAS' then
                    m.o.fi = substr(m.o.fi, 3, c2d(left(m.o.fi ,2)))
                else if wordPos(fi, 'COMUDSIZ NOBYTTRK') > 0 then
                    m.o.fi = utc2d(m.o.fi)
                pf = pf + fl
                pl = pl + 2
                end
            if py + tl <> pf then
                call err 'length mismatch for entry' m.o
            m.m.pos = pf
            return 1
            end
        end
endProcedure csiNext
/*--- if dsn is arcived return 'arcive'
      if dsn is tape return 'tape'
      otherwise return managment class ------------------------------*/
csiArcTape: procedure expose m.
parse arg vo, cl, dt, dsn
        if vo = '' then
            say err '||| no volume for dsn' dsn
        else if vo = 'ARCIVE' then
            res = 'arcive'
        else if cl <> '' then
            res = cl
        else if abbrev(vo, 'SHR') then
            res = 'SHR'
        else
            res = 'tape'
        if   res = 'arcive' then
            return res
      /*if   abbrev(res, 'ar') \= abbrev(dt, '00'x) , */
        if   abbrev(res, 'ta') \= abbrev(c2x(left(dt, 1)), '7') ,
           | (left(res, 1) >= 'A') \= abbrev(dt, '30'x) then
           say '||| mismatch cl' cl 'vo' vo 'dt' c2x(dt) 'dsn' dsn
        return res
endProcedure csiArcTape
/* copy csi end ******************************************************/
/* copy csm begin ******************************************************
    interface to csm,
        it is integrate with adrTso, eg. dsnAlloc , 'RZ3/..' uses csm
***********************************************************************/
/*--- execute a single csmExec command ------------------------------*/
adrCsm:
    if wordPos(translate(word(arg(1), 1)), 'COPY MBRLIST') > 0 then
       ggTO = ''
    else if symbol('m.csm_timeOut') == 'VAR' then
        ggTO = 'timeout('m.csm_timeOut')'
    else
        ggTO = 'timeout(30)'
    ggStart = time()
    if adrTso('csmExec' arg(1) ggTO, '*') == 0 then
        return 0
    if pos('CSMXC43E NO CONNECTION TO S', m.tso_trap) > 0 then
               /* CSMXC43E NO CONNECTION TO SYSPLEX:RR2 AVAILABLE */
        m.csm_err = 'noConn'
    else if pos('IKJ56225I', m.tso_trap) > 0             ,
               & ( pos('ALREADY IN USE', m.tso_trap) > 0 ,
                 | pos('CATED TO ANOTH', m.tso_trap) > 0) then
               /* 5 IKJ56225I DATA SET A540769.TMP.TEXT ALREADY IN USE,
                  6 IKJ56225I DATA SET IS ALLOCATED TO ANOTHER JOB */
        m.csm_err = 'inUse'
    else
        m.csm_err = ''
    if pos('*', arg(2)) > 0 then
        return m.tso_rc
    else if wordPos(m.tso_rc, arg(2)) > 0 then
        return m.tso_rc
    else
        call err strip('csmExec' m.csm_err) 'rc='m.tso_rc ,
            '\nstmt='subWord(m.tso_stmt, 2) m.tso_trap ,
            '\nend of csmExec, time='ggStart '-' time()
endProcedure adrCsm

csmDel: procedure expose m.
parse arg rz, dsn
    if dsnGetMbr(dsn) == '' then do
        call adrCsm "allocate system("rz") dataset('"dsn"')" ,
                         "disp(del) ddname(del1)"
        call adrTso 'free dd(del1)'
        end
    else do
        rr = adrCsm("mDelete system("rz") dataset('"dsnSetMbr(dsn)"')",
                          "member("dsnGetMbr(dsn)")", 8)
        if rr <> 0 then
            if pos('CSMEX77E Member:', m.tso_trap) < 1 ,
              | pos(' not found', m.tso_trap) < 1 then
                  call err 'rc='rr 'csm mDelete' rz'/'dsn':'m.tso_trap
        end
    return
endProcedure csmDel
/*--- copy members / datasets
      Vorlage: csrxUtil ----------------------------------------------*/
csmCopy: procedure expose m.
parse arg aFr, aTo, retOk
    frDD = tsoDD('csmFr*', 'a')
    frMbr = dsnGetMbr(aFr)
    if frMbr == '*' then
        fr = dsnSetMbr(aFr)
    else
        fr = aFr
    call csmAlloc fr frDD 'shr'
    toDD = tsoDD('csmTo*', 'a')
    toMbr = dsnGetMbr(aTo)
    if toMbr\== '=' then
        to = aTo
    else
        to = dsnSteMbr(aTo, frMbr)
    call csmAlloc to toDD 'shr ::D'frDD
    if frMbr \== '' & toMbr == '' & m.tso_dsOrg.toDD == 'PO' then do
        call adrTso 'free dd('toDD')'
        to = dsnSetMbr(aTo, frMbr)
        call csmAlloc to toDD 'shr'
        end
    inDD = tsoDD('csmIn*', 'a')
    if frMbr == '' & m.tso_dsOrg.frDD == 'PO' then do
        call tsoAlloc '-' inDD 'NEW ::F'
        call adrCsm "mbrList ddName("frDD") index(' ') short"
        do ix=1 to mbr_mem#
            i.ix = ' S M='mbr_name.ix
            end
        call writeDD inDD, 'I.', mbr_mem#
        call tsoCLose inDD
        end
    else do
        call adrTso 'alloc dd('inDD') dummy'
        end
    outDD = word(dsnAlloc('dd(csmO*) new ::V137'), 1)
    cmdu = 'CSMUTIL CSM,COPYREPLACE,DD(,,,,'inDD','outDD ,
               ||  ',,'frDD','toDD'),MARC(0)'
    cRc = adrTso(cmdU, '*')
    if cRc <> 0 then do
        call readDD outDD, o.
        call tsoClose outDD
        say 'rc='cRc',' o.0 'outputlines for' cmdU
        do ox=1 to o.0
            say o.ox
            end
        end
    call tsoFree frDD toDD inDD outDD
    if cRc <> 0 then
        call err 'csmCopy rc='cRc
    return cRc
endProcedure csmCopy

csmCopLOld: procedure expose m.
       /* csrxUtil geht nicht mit to(mbr), copy geht nicht fuer Load */
parse arg aFr, aTo, retOk
    if \ dsnExists(aTo) then do
            /* alloc ourself, because csrxutil forget mgmtclas */
        call dsnAlloc 'dd(csmCopy) catalog' aTo ,
            dsnLikeAtts(dsnSetMbr(aFr), 0)
        call tsoFree csmCopy
        end
    parse value csmSysDsn(aFr) with fSys '/' fDsn
    parse value csmSysDsn(aTo) with tSys '/' tDsn
    return adrTso("exec 'CSM.DIV.P0.EXEC(CSRXUTIL)'" ,
        "'COPY" fSys"/''"fDsn"'' TO" tSys"/''"tDsn"'' REPLACE'", retOk)
endProcedure csmCopLOld

csmCopyOld: procedure expose m.
parse arg aFr, aTo, retOk
    if dsnGetMbr(csnTo) \= '' & dsnGetMbr(csnTo) \= '' then do
        if dsnGetMbr(csnFr) <> dsnGetMbr(csnTo) then
            call err 'member rename' csnFr 'to' csnTo
        csnTo = dsnSetMbr(csnTo)
        end
    fr = csmSysDsn(aFr)
    frMbr = dsnGetMbr(fr)
    frDD = tsoDD('csmFr*', 'a')
    to = csmSysDsn(aTo)
    toMbr = dsnGetMbr(to, '=')
    toDD = tsoDD('csmTo*', 'a')
    call csmAlloc dsnSetMbr(fr) frDD 'shr'
    if frMbr == '' & m.tso_dsorg.frDD == 'PO' then
        if toMbr \== '=' then
            call err 'csmCopy from' fr'(*) to ps' to
        else
            frMbr = '*'
    if frMbr == '' & (toMbr \== '' & toMbr \== '=') then
        psOrLib = 'dsorg(po) dsntype(library)'
    else if frMbr \== '' & toMbr == '' then
        psOrLib = 'dsorg(ps)'
    else
        psOrLib = ''
    call csmAlloc dsnSetMbr(to) toDD 'shr ::D'frDD psOrLib
    c = 'indd('frDD') outDD('toDD')'
    if frMbr \== '*' then do
        if frMbr \== '' then
            c = c 'member('frMbr')'
        if toMbr \== '' & toMbr \== '=' then
            c = c 'newName('toMbr')'
        call adrCsm 'copy' c
        end
    else do
        call adrCsm "mbrList ddName("frDD") index(' ') short"
        do mx=1 to mbr_mem#
            call adrCsm 'copy' c 'member('mbr_name.mx')'
            end
        end
    call tsoFree frDD toDD
    return
endProcedure csmCopyOld

csmAlloc: procedure expose m.
parse arg dsn dd disp rest ':' nn, retRc
    sys = ''
    a2 = ''
    upper dd disp
    m.tso_dsn.dd = csmSysDsn(dsn)
    parse var m.tso_dsn.dd sys '/' dsn
    if disp = '' then
        disp = 'shr'
    else if words(disp) = 2 then
        disp = word(disp, 2)
    a1 = "SYSTEM("sys") DDNAME("dd")"
    if dsn == 'INTRDR' then do
        a1 = a1 'sysout(T) writer(intRdr)'
        end
    else do
        if dsn <> '' then do
            a1 = a1 "DATASET('"dsnSetMbr(dsn)"')"
            mbr = dsnGetMbr(dsn)
            if mbr <> '' then
                a1 = a1 'MEMBER('mbr')'
            end
        if abbrev(disp, 'SYSOUT(') then
            a1 = a1 disp
        else
            a1 = a1 "DISP("disp")"
        end
    nAtts = wordPos(disp, 'NEW MOD CAT') > 0 & nn \== ''
    if nAtts then
        rest = dsnCreateAtts('-'dsn , nn) rest
    cx = pos(' UCOUNT(', ' 'translate(rest))
    if cx > 0 then do
         rest = left(rest, cx) || 'nitCnt(' || substr(rest, cx+7)
         end
    cx = pos(' RECFM(', ' 'translate(rest))
    if cx > 0 then do
        cy = pos(')', rest, cx)
        rest = left(rest, cx+5) || space(substr(rest,cx+6,cy-cx-6), 0) ,
                                || substr(rest,cy)
        end
    cx = pos(' CYLINDERS ', ' 'translate(rest)' ')
    if cx > 0 then do
        rest = delStr(rest, cx+8, 1)
        end
    cx = pos(' CYL ', ' 'translate(rest)' ')
    if cx > 0 then do
        rest = insert('inder', rest, cx+2)
        end
    if retRc <> '' | nAtts | nn == '' then do
        alRc = adrCsm('allocate' a1 rest, retRc)
        m.tso_dsorg.dd = subsys_dsOrg
        return alRc
        end
    alRc = adrCsm('allocate' a1 rest, '*')
    m.tso_dsorg.dd = subsys_dsOrg
    if alRc = 0 then
        return 0
    say 'csmAlloc rc' alRc 'for' a1 rest '...trying to create'
    call csmAlloc m.tso_dsn.dd dd 'CAT' rest ':'nn
    call adrTso 'free  dd('dd')'
    return adrCsm('allocate' a1 rest)
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

csmLikeAtts: procedure expose m.
parse upper arg rz '/' dsn
    lc = adrCsm('dslist system('rz') dsnMask('dsnSetMbr(dsn)')')
    if stemsize <> 1 then
        call err 'csmLikeAtts stemSize='stemsize 'for dsn='dsn
    if abbrev(dsOrg.1, 'PO') then
        r = 'dsorg(po) dsnType(library)'
    else
        r = 'dsorg('dsOrg.1')'
    r = r 'mgmtClas('mgmtClas.1')'                       ,
       /* 'dataClas('dataClas.1')'   */                  ,
          'recFM('strip(translate('1 2 3', recFm.1, '123'))')'  ,
          'lRecl('lRecl.1')'                         ,
          'space('tracksused.1','  tracks.1') tracks'
    /*    if \ datatype(tracksused.1, 'n') then do
              if \ datatype(tracks.1, 'n') then
                  r = r 'space('tracks.1',' tracks.1')'
              if \ datatype(tracks.1, 'n') then
                  tracks.1 = tracksUsed.1   */

    return r
endProcedure csmLikeAtts
/*--- execute a rexx (under tso) in another rz
           here we use rexx TPSYSIKJ which was written for
           jcl procedure RM@IKJ01
arguments
rz   which rz to run rexx
proc the (remote) procedure library to use
opt  options
cmd  the tso command to execute
----------------------------------------------------------------------*/
/*--- execute a rexx (under tso) in another rz
          directly (without TPSYSIKJ) --------------------------------*/
csmExRx: procedure expose m.
parse arg rz, proc, opt, cmd
    pStem = opt
    if pStem = '' then
        pStem ='CSMEXRX'
    do cx=1 to (length(cmd)-1) % 68
        cmd.cx = substr(cmd, 68*cx-67,68)'-'
        end
    cmd.cx = substr(cmd, 68*cx-67)
    cmd.0 = cx
    timeout = 11
    if 0 then do
        call adrTso  'free ed(rmtSys)'  ,'*'
        call tsoFree tsoDD(rmtsPrt, 'a')
        call adrTso  'free dd(rmtsIn)','*'
        call adrTso  'free dd(sysproc)' ,'*'
        end
    call dsnAlloc rz"/"proc "dd(rmSyPro) rmtDDn(sysProc)"
    call dsnAlloc rz"/tmp.tsin new dd(rmTsIn) rmtDdn(sysTsIn) ::f "
    call tsoOpen rmTsIn, 'w'
    call writeDD rmTsIn, cmd.
    call tsoClose rmtsin
    call dsnAlloc rz"/tmp.prt new dd(rmtsprt) rmtDdn(sysTsPrt)",
                    "::f133"
    call dsnAlloc rz"/tmp.rmt new dd(rmtSys) timeout("timeout")"
    call adrtso "csmappc start pgm(csmexec)" ,
           "parm('select tsocmd(''csmappc allocate plu(*.rmtSys)" ,
                 "tpname(sysikj) dealloc '')')",
           "timeout("timeOut")", '*'
    if rc <> 0 | appc_rc <> 0 then do
        ee = 'csm tso exec rc='rc 'appc_rc='appc_rc
        say ee
        say '  rexx rz='rz 'proc='proc 'opt='opt
        say '  cmd='cmd
        call csmappcRcSay ggTsoCmd
        call readDD 'rmTsPrt', 'M.'pStem'.'
        call tsoClose rmtsPrt
        say m.pStem.0 'tso output lines'
        do px=1 to m.pStem.0
            say ' ' strip(m.pStem.px, 't')
            end
        call err ee
        end
    if opt <> '' then do
        call readDD 'rmTsPrt', 'M.'pStem'.'
        call tsoClose rmtsPrt
        end
    call tsoFree rmSyPro rmtsPrt rmtSys rmtsIn
    return
 endProcedure csmExRx

/*--- sys the re and result variables from csmAppcRc -----------------*/
csmappcRcSay: procedure expose appc_rc appc_reason appc_msg. ,
           appc_state_c appc_state_f
 parse arg cmd
     say 'rc='appc_rc 'reason='appc_reason ,
         'state_c='appc_state_c appc_state_f
     say '  for' cmd
     do ix=1 to appc_msg.0
         say ' ' appc_msg.ix
         end
     return appc_rc
 endProcedure csmappcRcSay

/*--- execute a single csmAppc command ------------------------------*/
csmAppc:
    return adrTso('csmAppc' arg(1), arg(2))
endProcedure csmAppc
/* copy csm end *******************************************************/
/* copy timing begin *************************************************/
timing: procedure expose m.
parse arg typ, c2, txt
    e1 = time('E')
    c1 = strip(sysvar('syscpu'))
    s1 = sysvar('syssrv')
    if typ \== '' then do
        if symbol('m.timing_ela') \== 'VAR' then
            call err 'timing('typ',' c2',' txt') ohne ini'
        if symbol('m.timing.typ.ela') \== 'VAR' then do
            m.timing.typ.ela = 0
            m.timing.typ.cpu = 0
            m.timing.typ.su  = 0
            m.timing.typ.cnt = 0
            m.timing.typ.cn2 = 0
            if symbol('m.timing_types') == 'VAR' then
                m.timing_types = m.timing_types typ
            else
                m.timing_types = typ
            if symbol('m.timing_say') \== 'VAR' then
                m.timing_say = 0
            end
        m.timing.typ.ela = m.timing.typ.ela + e1 - m.timing_ela
        m.timing.typ.cpu = m.timing.typ.cpu + c1 - m.timing_cpu
        m.timing.typ.su  = m.timing.typ.su  + s1 - m.timing_su
        m.timing.typ.cnt = m.timing.typ.cnt + 1
        if c2 \== '' then
           m.timing.typ.cn2 = m.timing.typ.cn2 + c2
        end
    m.timing_ela = e1
    m.timing_cpu = c1
    m.timing_su  = s1
    if m.timing_say then
        if typ == '' then
            say left(typ, 10)right(c2, 10) 'ela='e1 'cpu='c1 'su='s1 txt
        else
            say left(typ, 10)right(m.timing.typ.cn2, 10) ,
                'ela='m.timing.typ.ela ,
                'cpu='m.timing.typ.cpu 'su='m.timing.typ.su txt
    return
endProcedure timing

timingSummary: procedure expose m.
    say 'timing summary' time()
    do tx = 1 to words(m.timing_types)
        typ = word(m.timing_types, tx)
        say left(typ, 10)right(m.timing.typ.cnt,  7)       ,
                      || right(m.timing.typ.cn2,  7)       ,
                         'cpu='right(m.timing.typ.cpu, 10) ,
                         'su='right(m.timing.typ.su, 10)
        end
    return
endProcedure timingSummary
/* copy timing end   *************************************************/
/* copy ii begin ********* Installation Info *************************/
iiini: procedure expose m.
    if m.ii_ini == 1 then
        return
    m.ii_ini = 1
    m.ii_rz = ''
    m.ii.rzC = ''
    i = 'RZ1 1 S1 DBTF T DTF DVTB V DTB DBOC C DOC' ,
        'RZ2 2 S2 DBOF F DOF DVBP P DBP DP2G Q DP2' ,
        'RR2 R R2 DBOF F DOF DVBP P DBP DP2G Q DP2' ,
        'RZ4 4 S4 DBOL O DOL DP4G U DP4' ,
        'RZX X X2 DE0G E DE0 DEVG M DEV DPXG I DPX DX0G X DX0' ,
        'RZY Y Y2 DE0G E DE0 DEVG M DEV DPYG Y DPY' ,
        'RZZ Z Z2 DE0G E DE0 DEVG M DEV DPZG N DPZ'
    m.ii_rz = ''
    m.ii_rzC = ''
    do wx=1 by 3 to words(i)
        parse value subWord(i, wx, 3) with w1 w2 w3
        if abbrev(w1, 'R') & length(w1) == 3 then do
           rz = w1
           m.ii_DbSys.rz = ''
           m.ii_rz = strip(m.ii_rz rz)
           m.ii_rzC = m.ii_rzC || w2
           call iiA1 ii_sys2rz, w3, rz
           call iiA1 ii_rz2c, rz, w2
           end
        else if abbrev(w1, 'D') & length(w1) == 4 then do
           m.ii_DbSys.rz = strip(m.ii_DbSys.rz w1)
           call iiA1 ii_db2c, w1, w2
           call iiA1 ii_mbr2db, w3, w1
           end
        else
            call err 'bad w1' w1 w2 w3
        end
    return
endProcedure iiIni

iiA1: procedure expose m.
parse arg st, key ,val
    if symbol('m.st.key') \== 'VAR' then
        m.st.key = val
    else if m.st.key \== val then
        call err 'already <> defined' st'.'key'='m.st.key 'val='val
    return
endProcedure iiA1

iiMbr2DbSys: procedure expose m.
parse upper arg mbr
    return iiLazy(ii_mbr2db, left(mbr, 3), 'member')

iiRz2C: procedure expose m.
parse upper arg rz
    return iiLazy(ii_rz2c, rz, 'rz')

iiRz2Dsn: procedure expose m.
parse upper arg rz
    return overlay('Z', rz, 2)

iiDBSys2C: procedure expose m.
parse upper arg db
    return iiLazy(ii_db2c, db, 'dbSys')

iiSys2RZ: procedure expose m.
parse upper arg sys
    return iiLazy(ii_sys2rz, left(sys, 2), 'sys')

iiLazy: procedure expose m.
parse arg st, key, txt
    if symbol('m.st.key') == 'VAR' then
        return m.st.key
    if m.ii_ini == 1 then
       return err('no' txt'='key 'in ii' st)
    call iiIni
    return iiLazy(st, key, txt)
endProcedure iiRz2C
/* copy ii end   ********* Installation Info *************************/
/* 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 tsoOpen grp, 'R'
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 tsoClose 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 m.tso_stmt, ggRet
    call outtrap m.tso_trap.
    address tso m.tso_stmt
    m.tso_rc = rc
    call outtrap off
    if m.tso_rc == 0 then
        return 0
    m.tso_trap = ''
    do ggXx=1 to min(7, m.tso_trap.0)
        m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
        end
    if m.tso_trap.0 > 7 then do
        if m.tso_trap.0 > 14 then
            m.tso_trap = m.tso_trap'\n............'
        do ggXx=max(8, m.tso_trap.0-6) to m.tso_trap.0
            m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
            end
        end
    if ggRet == '*' then
        return m.tso_rc
    else if wordPos(m.tso_rc, ggRet) > 0 then
        return m.tso_rc
    else
        call err 'adrTso rc=' m.tso_rc 'stmt='m.tso_stmt m.tso_trap
endSubroutine 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 arg(2)
     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:  tsoOpen...'R', readDD*,  tsoClose
        write: tsoOpen...'W', writeDD*, tsoClose

        readDD returns true if data read, false at eof
        do not forget that open is mandatory to write empty file|
***********************************************************************/

/*--- open dd for read (rw='R') or write (rw='W') --------------------*/
tsoOpen: procedure expose m.
parse upper arg dd, rw
    return adrTso('execio' 0 'disk'RW dd '(open)')
return /* end tsoOpen */

/*--- close dd -----------------------------------------------------*/
tsoClose: procedure expose m.
    parse upper arg dd
    return adrTso('execio 0 diskR' dd '(finis)')
endProcedure tsoClose

/*--- 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 */

/*--- 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

/*--- 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
    parse value dsnAlloc('dd('m.m.dd')' m.m.dsn) with m.m.dd m.m.free
    call tsoOpen m.m.dd, 'R'
    return m
endProcedure readDDNxBegin

/*--- return the stem of the next line, or '' at end -----------------*/
readNx: procedure expose m.
parse arg m
    m.m.cx = m.m.cx + 1
    if m.m.cx <= m.m.0 then
        return m'.'m.m.cx
    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 stem of the curr line, '' at end --------------------*/
readNxCur: procedure expose m.
parse arg m
    if m.m.cx <= m.m.0 then
        return m'.'m.m.cx
    else
        return ''
endProcedure readNxCur

/*--- 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 tsoClose m.m.dd
    call tsoFree m.m.free
    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' | w == 'CAT' then
            di = di 'CAT'
        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 pos('(', w) > 0 then
            leave
        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.tso_trap.1 = ''
        m.tso_trap.2 = ''
        m.tso_trap.3 = ''
        res = dsnAlloc(spec, pDi, pDD, '*')
        if \ datatype(res, 'n') then
            return res
        msg = m.tso_trap.1'\n'm.tso_trap.2'\n'm.tso_trap.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 dd == '-' & pDD \== '' then
        dd = pDD
    if dd == '-' then
        dd = 'DD*'
    dd = tsoDD(dd, 'a')
    m.tso_dsn.dd = ''
    if na == '-' & di == '-' & rest = '' then
        return dd
    if di = '-' then
        if pDi == '' then
            di = 'SHR'
        else
            di = pDi
    if pos('(', na) < 1 then
        nop
    else if di = 'MOD' then
        call err 'disp mod for' na
    else
        di = 'SHR'
    if lastPos('/', na, 6) > 0 then
        rx = csmAlloc(na dd di rest, retRc)
    else
        rx = tsoAlloc(na dd di rest, retRc)
    if rx = 0 then
        return dd dd
    else
        return rx
endProcedure dsnAlloc

/*--- find a free dd with prefix dd ----------------------------------*/
tsoDD: procedure expose m.
parse upper arg dd, f, noErr
    if symbol('m.tso_ddAll') \== 'VAR' then do
        call errIni
        m.tso_ddAll = ''
        end
    if f == '-' then do
        ax = wordPos(dd, m.tso_ddAll)
        if ax > 0 then
            m.tso_ddAll = delWord(m.tso_ddAll, ax, 1)
        else if noErr \== 1 then
            call err 'tsoDD dd' dd 'not used' m.tso_ddAll
        end
    else if f <> 'A' then
        call err 'tsoDD bad fun' f
    else do
        if right(dd, 1) = '*' then do
            d0 = left(dd, length(dd)-1) || m.err.screen
            dd = d0
            do dx=1 while wordPos(dd, m.tso_ddAll) > 0
                dd = d0 || dx
                end
            end
        else if pos('?', dd) > 0 then
            dd = repAll(dd, '?', m.err.screen)
        if wordPos(dd, m.tso_ddAll) < 1 then
            m.tso_ddAll = strip(m.tso_ddAll dd)
        m.tso_dsn.dd = ''
        m.tso_dsOrg.dd = ''
        end
    return dd
endProcedure tsoDD

tsoAlloc: procedure expose m.
parse arg na dd disp rest ':' nn, retRc
    dd = translate(dd)
    c = 'alloc dd('dd')' disp
    if na == '-' then
        m.tso_dsn.dd = ''
    else if na \== 'INTRDR' then do
        c = c "DSN('"na"')"
        m.tso_dsn.dd = na
        end
    else do
        c = c "sysout(*) writer(intRdr)"
        m.tso_dsn.dd = '*intRdr'
        end
    if wordPos(disp, 'NEW MOD') > 0 & nn \== '' then
        c = c dsnCreateAtts(,nn)
    if adrTso(c rest, '*') = 0 then
        return 0
    if pos('IKJ56246I', m.tso_trap) > 0 then
        if pos('TED, FILE IN USE', m.tso_trap) > 0 then do
     /* IKJ56246I DATA SET A540769.WK.REXX NOT ALLOCATED, FILE IN USE */
        say 'tsoAlloc dd' dd 'already in use:' substr(m.tso_trap, 3)
        say '.... trying to free'
        call tsoFree dd, 1
        say '.... retrying to allocate' c rest
        if adrTso(c rest, '*') = 0 then
            return 0
        end
    if nn \= '' & wordPos(disp, 'OLD SHR') > 0 ,
          & pos('IKJ56228I', m.tso_trap) > 0 ,
          & pos(' NOT IN CATALOG ', m.tso_trap) > 0 then do
       /* IKJ56228I DATA SET A540769.XY.REXX NOT IN CATALOG OR CATAL*/
        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
    if pos('*', retRc) < 1 & wordPos(m.tso_rc, retRc) < 1 then
        call err 'tsoAlloc rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
    call saySt(splitNl(err, 'tsoAlloc rc='m.tso_rc ,
                  'tsoStmt='m.tso_stmt m.tso_trap))
    return m.tso_rc
endProcedure tsoAlloc

dsnExists: procedure expose m.
parse upper arg aDsn
    parse value csmSysDsn(aDsn) with rz '/' dsn
    if rz == '*' then
        return sysDsn("'"dsn"'") == 'OK'
    else if dsnGetMbr(dsn) == '' then do
        lc = adrCsm('dslist system('rz') dsnMask('dsn') short', 4)
        if stemsize = 0 | stemSize = 1 then
            return stemSize
        call err 'csmExists stemSize='stemsize 'for dsn='aDsn
        end
    else do
        cc = adrCsm("mbrList system("rz") dataset('"dsnSetMbr(dsn)"')",
                    "member("dsnGetMbr(dsn)") index(' ') short", 8)
        if cc <> 0 then do
            if pos(' NOT IN CATALOG\', m.tso_trap) > 0 ,
              & pos('CSMSV29E DATA SET ', m.tso_trap) > 0 then
                return 0
            return err('error in csm mbrList' aDsn m.tso_trap)
            end
        if mbr_name.0 == 0 | mbr_name.0 == 1 then
            return mbr_name.0
        call err 'csmExists mbr_mem#='mbr_name.0 'for dsn='aDsn
        end
endProcedure dsnExists

dsnLikeAtts: procedure expose m.
parse upper arg dsn, isFile
     if isFile then do
         ddDsn = m.tso_dsn.dsn
         if lastPos('/', m.tso_dsn.dsn, 4) < 1 then
              return tsoLikeAtts(dsn, 1)
         dsn = m.tso_dsn.dsn
         end
     sx = lastPos('/', dsn, 4)
     if sx < 1 then
         return tsoLikeAtts(dsn, 0)
     else if abbrev(dsn, '*/') | abbrev(dsn, sysVar('SYSNODE')'/') then
         return tsoLikeAtts(substr(dsn, sx+1), 0)
     else
         return csmLikeAtts(dsn)
endProcedure dsnLikeAtts

tsoLikeAtts: procedure expose m.
parse arg dsn, isFile
    rc = listDsi("'"dsn"'" copies('FILE', isFile) "SMSINFO")
    if rc = 0 then
        r = ''
    else if rc = 4 & sysReason = 19 then do
        r = 'UCOUNT(30)' /* 7.8.13 corrected attribute */
        say 'creating' dsn 'with multi volume' mv
        end
    else if rc ^= 0 then
        call err 'listDsi rc' rc 'reason' sysReason,
                             sysMsgLvl1 sysMsgLvl2
    if right(sysDsSms, 7) == 'LIBRARY' | abbrev(sysDsSms, 'PDS') ,
                      | sysDsOrg = 'PO' then
         r = 'dsOrg(PO) DSNTYPE(LIBRARY)' r
    else
         r = "dsOrg("sysDSorg")" r
    if sysUnits = 'TRACK' then
        sysUnits = 'TRACKS'
    return r "MGMTCLAS("sysMgmtClass")",
        "DATACLAS("sysDataClass")" ,
        "RECFM("strip(translate('1 2 3', ' 'sysREcFM, '123'))")",
        "LRECL("SYSLRECL")",
        "SPACE("sysPrimary"," sysSeconds")" ,
            sysUnits || left('S', sysUnits == 'TRACK')
    /*  "blksize("sysBLkSIZE")" removed 3.4.13: let sms do the magic */
endProcedure tsoLikeAtts

tsoFree: procedure expose m.
parse arg ddList, tryClose
    do dx=1 to words(ddList)
        dd = word(ddList, dx)
        if adrTso('free dd('dd')', '*') <> 0 then do
            if pos('IKJ56861I', m.tso_trap) > 0 & tryClose == 1 then
              if pos('NOT FREED, DATA SET IS OPEN', m.tso_trap),
                    > 0 then do
                  /* IKJ56861I  FILE A1 NOT FREED, DATA SET IS OPEN */
                say 'dataset open:' substr(m.tso_trap, 3)
                say '.... trying to close'
                if adrTso('execio 0 diskR' dd '(finis)', '*') = 0 then
                   call adrTso 'free dd('dd')', '*'
                end
            if m.tso_rc \== 0 then
                call saySt(splitNl(err, 'rc='m.tso_rc ,
                            'tsoStmt='m.tso_stmt m.tso_trap))
            end
        call tsoDD dd, '-', 1
        end
    return
endProcedure tsoFree

dsnCreateAtts: procedure expose m.
parse arg dsn, atts
    res = ''
    if dsn \== '' & \ abbrev(dsn, '-') then
        res = "dataset('"dsnSetMbr(dsn)"')"
    if abbrev(atts, ':') then do
        parse var atts a1 atts
        rl = substr(a1, 3)
        if abbrev(a1, ':F') then do
            if rl = '' then
                rl = 80
            res =  res "recfm("space(f b)") lrecl("rl")"
            end
        else if abbrev(a1, ':V') then do
            if rl = '' then
                rl = 32756
            res =  res "recfm("space(v b)") lrecl("rl")"
            end
        else if abbrev(a1, ':L') then
            res = res dsnLikeAtts(rl, 0)
        else if abbrev(a1, ':D') then
            res = res dsnLikeAtts(rl, 1)
        else
            call err 'csnCreateAtt bad :' a1
        end
    aU = ' 'translate(atts)
    hasOrg = pos(' DSORG(', aU) > 0 | pos(' DSNTYPE(', aU) > 0
    hasMbr = pos('(', dsn) > 0
    if hasMbr & \ hasOrg then
        atts = atts 'dsorg(po) dsntype(library)'
    if hasOrg | hasMbr then do
        ww = DSORG DSNTYPE
        do wx=1 to words(ww)
            do forever
                cx = pos(' 'word(ww, wx)'(', ' 'translate(res))
                if cx == 0 then
                    leave
                cy = pos(')', res, cx)
                res = delstr(res, cx, cy+1-cx)
                end
            end
        end
    res = res atts
    aU = ' 'translate(res)
    if pos(' MGMTCLAS(', aU) < 1 then
        res = res 'mgmtclas(COM#A091)'
    if pos(' SPACE(', aU) < 1 then
        res = res 'space(100, 500) cylinders'
    return res
endProcedure dsnCreateAtts
/*--- check if a dataset is archive ------------------------------------
          returns 'ok'                    if dataset on disk
                  'not'                   if dataset is not catalogued
                  'arc'                   if dataset archived
                  listDsi errorMsg        otherwise ------------------*/
dsnArc: procedure expose m.
parse upper arg dsn
    lc = listDsi("'"strip(dsn)"' noRecall")
    if lc = 0 then
        return 'ok'
    else if lc=4 & sysReason = 19 then  /* multiple volumes */
        return 'ok'
    else if lc=16 & sysReason = 5 then
        return 'notCat'
    else if lc=16 & sysReason = 9 then
        return 'arc'
    else
        return 'listDsi cc='lc', sysReason='sysReason ,
                          'm2='sysMsgLvl2', m1='sysMsgLvl1
endProcedure dsnArc
/*--- 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)'
    call tsoFree word(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)'
    call tsoFree word(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
    parse value dsnAlloc(frSpec, 'SHR', 'FRDD') with frDD frFr
    parse value dsnAlloc(toSpec, 'OLD', 'TODD') with toDD toFr
    call tsoOpen frDD, 'R'
    call tsoOpen toDD, 'W'
    cnt = 0
    do while readDD(frDD, r.)
        call writeDD toDD, r.
        cnt = cnt + r.0
        end
    call tsoClose frDD
    call tsoClose toDD
    call tsoFree frFr toFr
    if ggSay == 1 | m.debug == 1 then
       say cnt 'records copied from' frSpec 'to' to toSpec
    return
endSubroutine writeDsn
/* copy adrTso end ****************************************************/
/* copy csv begin *****************************************************/
csvIni: procedure expose m.
    if m.csv.ini == 1 then
        return
    m.csv.ini = 1
    call jIni
    call classNew "n CsvRdr u JRWO, f RDR r", "m",
        , "jReset m.m.rdr = arg",
        , "jOpen call csvRdrOpen m, opt",
        , "jClose call jClose m.m.rdr; call oMutatName m, 'CsvRdr'"
    call classNew "n CsvRdrR u CsvRdr", "m",
        , "jReadO return csvRdrReadO(m)"
    call classNew "n CsvWrt u JRW, f RDR r", "m",
        , "jReset m.m.rdr = arg",
        , "jOpen call csvWrtOpen m, opt",
        , "jClose call jClose m.m.rdr; call oMutatName m, 'CsvWrt'"
    call classNew "n CsvWrtR u CsvWrt", "m",
        , "jRead return csvWrtRead(m, var)"
    return
endProcedure csvIni

/*--- create a new csvRdr --------------------------------------------*/
csvRdr: procedure expose m.
parse arg rdr
    return jReset(oNew('CsvRdr'), rdr)
endProcedure csvRdr

/*--- open csvRdr: read first line and create dataClass --------------*/
csvRdrOpen: procedure expose m.
parse arg m
    call jOpen m.m.rdr, '<'
    if jRead(m.m.rdr, m'.LINE') then do
        ff = 'f' repAll(m.m.line, ',', ' v, f ') 'v'
        m.m.class = classNew("n* CsvF u" ff)
        end
    call oMutatName m, 'CsvRdrR'
    return
endProcedure csvRdrOpen

/*--- read next line and return derived object -----------------------*/
csvRdrReadO: procedure expose m.
parse arg m
    do until m.m.line <> ''

        if \ jRead(m.m.rdr, m'.LINE') then
            return ''
        end
    var = oNew(m.m.class)
    ff = classMet(m.m.class, 'oFlds')
    s = m'.SCAN'
    call scanSrc s, m.m.line
    do fx=1
        f1 = m.ff.fx
        if scanString(s, '"') then
            m.var.f1 = m.s.val
        else do
            call scanUntil s, ','
            m.var.f1 = m.s.tok
            end
        if scanEnd(s) then
            leave
        if \ scanLit(s, ',') then
            call scanErr s, ',' expected
        end
    if fx <> m.ff.0 then
        call scanerr s, 'csv cla' m.ff.0 'fields but' cx 'in line'
    return var
endProcedure csvRdrReadO

/*--- create a new csvRdr --------------------------------------------*/
csvWrt: procedure expose m.
parse arg rdr
    return jReset(oNew('CsvWrt'), rdr)
endProcedure csvWrt

/*--- open csvRdr: read first line and create dataClass --------------*/
csvWrtOpen: procedure expose m.
parse arg m
    call jOpen m.m.rdr, '<'
    m.m.class = ''
    m.m.o1    = ''
    call oMutatName m, 'CsvWrtR'
    return
endProcedure csvWrtOpen

/*--- read next line and return derived object -----------------------*/
csvWrtRead: procedure expose m.
parse arg m, var
    if m.m.o1 == '' then
        i1 = jReadO(m.m.rdr)
    else do
        i1 = m.m.o1
        m.m.o1 = ''
        end
    if i1 == '' then
        return 0
    if m.m.class == '' then do
        m.m.class = objClass(i1)
        m.m.o1 = i1
        t = ''
        ff = oFlds(i1)
        do fx=1 to m.ff.0
            t = t','m.ff.fx
            end
        m.var = substr(t, 2)
        return 1
        end
    else do
        m.var = csv4Obj(i1, oFlds(i1), 0)
        return 1
        end
endProcedure csvWrtRead

csv4obj: procedure expose m.
parse arg o, ff, hasNull, oNull
    res = ''
    do fx=1 to m.ff.0
        of1 = o || left('.', m.ff.fx \== '') || m.ff.fx
        v1 = m.of1
        if hasNull & v1 = oNull then
            res = res','
        else if v1 = '' then
            res = res',""'
        else if pos(',', v1) > 0 | pos('"', v1) > 0 then
            res = res','quote(v1, '"')
        else
            res = res','v1
        end
    return substr(res, 2)
endProcedure csv4obj
/* copy csv end   *****************************************************/
/* copy j begin *******************************************************
    the j framework
         jReset
         jOpen
         jClose
         jRead
         jWrite
***********************************************************************/
jRead: procedure expose m.
parse arg m, var
    met = objMet(m, 'jRead')
    if m.m.jReading then
        interpret met
    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'
    met = objMet(m, 'jReadO')
    if m.m.jReading then
        interpret met
    else
        return err('jReadO('m',' var') but not opened r')
endProcedure jReadO

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

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

jWriteAll: procedure expose m.
parse arg m, rdr
    rdr = o2file(rdr)
    met = objMet(m, 'jWriteAll')
    if \ m.m.jWriting then
        return err('jWriteAll('m',' rdr') but not opened w')
    interpret met
    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')')
    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
    met = objMet(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 met
            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 met
            m.m.jWriting = 1
            end
        end
    m.m.jUsers = oUsers + 1
    return m
endProcedure jOpen

jClose: procedure expose m.
parse arg m
    met = objMet(m, 'jClose')
    oUsers = m.m.jUsers
    if oUsers = 1 then do
        interpret met
        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))
    fmt = '@cat%('fmt'%)'
    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'%.1', m.line)
        end
    call jClose m
    return res || f(fmt'%.2')
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 m.m.src == '' then
            m.m.src = ' '
        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
    sta = 'tt'
    res = ''
    do forever
        do while scanSBEnd(m)
            if \ jCatSqlNl(m) then
                return strip(res)
            end
        bx = m.m.pos
        sta = scanSql2Stop(m, sta, stop)
        s1 = left(sta, 1)
        if pos(s1, stop) > 0 then do
            if res <> '' then
                return strip(res)
            end
        else if s1 == '-' | s1 == '/' then
            res = res' '
        else if pos('/', sta) = 0 then
            res = res || substr(m.m.src, bx, m.m.pos - bx)
        end
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 classIni
    am = "call err 'call of abstract method"
    c1 = classNew('n JRW u ORun, f JREADING v, f JWRITING v', 'm',
        , "new ?r return jReset(?new2, 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")
    m.class.forceDown.c1 = c1'#new'
    c2 = classNew('n JRWDeleg u JRW', 'm',
        , "new ?r return jReset(?new1, arg)",
        , "jRead return jRead(m.m.deleg, var)" ,
        , "jReadO return jReadO(m.m.deleg)" ,
        , "jWrite  call jWrite(m.m.deleg, line)" ,
        , "jWriteO call jWrite(m.m.deleg, var)" ,
        , "jWriteAll call jWriteAll m.m.deleg, rdr",
        , "jWriteNow call jWriteNow m.m.deleg, rdr",
        , "jReset    if arg \== '' then m.m.deleg = arg;",
                                   "else call jReset m.m.deleg;",
        , "jOpen     call jOpen m.m.deleg,' opt; return m" ,
        , "jClose    call jClose m.m.deleg; return m" )
    m.class.forceDown.c2 = c2'#new'
    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)
    m.j.out = jOpen(oNew('JSay'), '>')
    m.j.errRead  = "return err('jRead('m',' var') but not opened r')"
    m.j.errReadO = "return err('jReadO('m',' var') but not opened r')"
    m.j.errWrite = "return err('jWrite('m',' line') but not opened w')"
    m.j.errWriteO= "return err('jWriteO('m',' var') but not opened w')"
    call classNew "n JBuf u JRWO, f BUF s r", "m",
        , "jOpen call jBufOpen m, opt",
        , "jClose call oMutatName m, 'JBuf'",
        , "jReset call jBufReset m, arg",
        , "jRead" m.j.errRead ,
        , "jReadO" m.j.errReadO ,
        , "jWrite" m.j.errWrite ,
        , "jWriteO" m.j.errWriteO
    call classNew "n JBufOR u JBuf", "m",
        , "jRead return jBufORead(m, var)",
        , "jReadO return jBufOReadO(m)"
    call classNew "n JBufSR u JBuf", "m",
        , "jRead return jBufSRead(m, var)",
        , "jReadO return jBufSReadO(m)"
    call classNew "n JBufOW u JBuf", "m",
        , "jWrite call jBufOWrite m, line",
        , "jWriteO call jBufOWriteO m, var"
    call classNew "n JBufSW u JBuf", "m",
        , "jWrite call jBufSWrite m, line",
        , "jWriteO call jBufSWriteO 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

/*--- 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

/*--- open for input given rdr, or stdIn if missing ------------------*/
inOpen: procedure expose m.
parse arg rdr
    if rdr == '' then
        return jOpen(m.j.in, '<')
    else
        return jOpen(o2file(rdr), '<')
endProcedure

j2Buf: procedure expose m.
    parse arg rdr
    if oStrOrObj(rdr, m.j.in) then
        return jBuf(ggStr)
    if classInheritsOf(ggCla, '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

outX: procedure expose m.
parse arg line
    if symbol('m.tst_m') \== 'VAR' then
        call jWrite m.j.out, line
    else
        call tstOut m.tst_m, line
    return 0
endProcedure out

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

outStem: procedure expose m.
parse arg st
    do sx=1 to m.st.0
       call jWrite m.j.out, m.st.sx
       end
    return 0
endProcedure out

JRWDeleg: procedure expose m.
parse arg arg
    return oNew('JRWDeleg', arg)
endProcedure JRWDeleg

/*--- 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.allS = 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.allS = 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.allS = 1
    return m
endProcedure jBufReset

jBufOpen: procedure expose m.
parse arg m, opt
    if opt == m.j.cRead then do
        m.m.readIx = 0
        if m.m.allS then
            call oMutatName m, 'JBufSR'
        else
            call oMutatName m, 'JBufOR'
        return m
        end
    if opt == m.j.cWri then do
        m.m.buf.0 = 0
        m.m.allS = 1
        end
    else if opt \== m.j.cApp then
         call err 'jBufOpen('m',' opt') with bad opt'
    if m.m.allS then
        call oMutatName m, 'JBufSW'
    else
        call oMutatName m, 'JBufOW'
    return m
endProcedure jBufOpen

jBufOWrite: procedure expose m.
parse arg m, line
    call mAdd m'.BUF', s2o(line)
    return
endProcedure jBufOWrite
jBufSWrite: procedure expose m.
parse arg m, line
    call mAdd m'.BUF', line
    return
endProcedure jBufWrite

jBufWriteStem: procedure expose m.
parse arg m, st
    ax = m.m.buf.0
    if m.m.allS 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

jBufOWriteO: procedure expose m.
parse arg m, ref
    call mAdd m'.BUF', ref
    return
endProcedure jBufOWriteO

jBufSWriteO: procedure expose m.
parse arg m, ref
    cl = objClass(ref)
    if cl = m.class_V then do
        call mAdd m'.BUF', m.ref
        return
        end
    if cl == m.class_W then do
        call mAdd m'.BUF', substr(ref, 2)
        return
        end
    do ax=1 to m.m.buf.0
        m.m.buf.ax = s2o(m.m.buf.ax)
        end
    m.m.allS = 0
    call oMutatName m, 'JBufOW'
    call mAdd m'.BUF', ref
    return
endProcedure jBufWriteO

jBufOReadO: procedure expose m.
parse arg m
    nx = m.m.readIx + 1
    if nx > m.m.buf.0 then
        return ''
    m.m.readIx = nx
    return m.m.buf.nx
endProcedure jBufOReadO

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

jBufORead: 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
    m.var = o2String(m'.BUF.'nx)
    return 1
endProcedure jBufORead

jBufSRead: 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
    m.var = m.m.buf.nx
    return 1
endProcedure jBufRead

jBufTxtWriteO: procedure expose m.
parse arg m, ref
    if m.m.allS \== 1 then
        call err '1 \== allS' m.m.allS 'in jBufTxtWriteO('m',' ref')'
    cl = objClass(ref, '?')
    if cl = m.class_V then
        call mAdd m'.BUF', m.ref
    else if cl == m.class_W 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 = oFlds(ref)
        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 = m.ff.fx
                 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

jSingle: procedure expose m.
parse arg m
    call jOpen m, '<'
    res = jReadO(m)
    two = jReadO(m)
    call jClose m
    if res == '' then
        if arg() < 2 then
             call err 'empty file in jSingle('m')'
        else
            res = arg(2)
    if two \== '' then
        call err '2 or more recs in jSingle('m')'
    return res
endProcedure jSingle
/* copy j end *********************************************************/
/* copy o begin ******************************************************
    an object is register for a class in o2c
    a class has a list of parents in cParent
    a class has a methodTable cMet with lazy initialization
             a method generator
        otherwise an existing method is simply copied
***********************************************************************/
/*--- create an an object of the class className
        and call it's new method ------------------------------------*/
oNew: procedure expose m.
parse arg cl, arg, arg2, arg3
    interpret classMet(class4name(cl), 'new')
endProcedure oNew

/*--- return the class of object obj --------------------------------*/
objClass: procedure expose m.
parse arg m
    if symbol('m.o.o2c.m') == 'VAR' then
         return m.o.o2c.m
    else if abbrev(m, m.o_escW) then
         return m.class_W
    else if arg() >= 2 then
        return arg(2)
    else
        return err('no class found for object' m)
endProcedure objClass

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

/*--- return the code of method me of object m
         set m to the address and ggClass to the class ---------------*/
objMet: procedure expose m. m ggClass
parse arg m, me
    if symbol('m.o.o2c.m') == 'VAR' then
         ggClass = m.o.o2c.m
    else if abbrev(m, m.o_escW) then
         ggClass = "w"
    else if arg() >= 3 then
        return arg(3)
    else
        return err('no class found for object' m)
    if symbol('m.ggClass.method.me') == 'VAR' then
        return m.ggClass.method.me
    else
        return classMet(ggClass, me)
endProcedure objMet

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

oClear: procedure expose m.
parse arg m
    interpret objMet(m, 'oClear')
    return m
endProcedure oClear

/*--- mutate object m to the class cl -------------------------------*/
oMutate: procedure expose m.
parse arg m, cl
    m.o.o2c.m = cl
    return m
endProcedure oMutate

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

/*--- copy object m of class cl to t --------------------------------*/
oClaCopy: procedure expose m.
parse arg cl, m, t
    interpret "drop cl;" classMet(cl, 'oCopy')
endProcedure oClaCopy

/*--- copy object m to t / create a new object if t=='' -------------*/
oCopy: procedure expose m.
parse arg m, t
    interpret objMet(m, 'oCopy')
endProcedure oCopy

/*--- return a new instance of a subclass of Run
        with code code in method oRun -------------------------------*/
oRunner: procedure expose m.
parse arg code
    return oNew(classNew('n* ORun u ORun, m oRun' code))
endProcedure oRunner

/*--- 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 of object m No Procedure:
        ??? optimize: class only run ???
         use from caller unprotected---------------------------------*/
oRunNP: procedure expose m.
    interpret objMet(arg(1), 'oRun')
    return
endProcedure oRunNP

/*--- run method oRun and return output in new JBuf ------------------*/
oRun2File: procedure expose m.
parse arg rn
    b = jBuf()
    call pipe '+F' , b
    call oRun rn
    call pipe '-'
    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 arg() = 1 then
        fmt = ' '
    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_V m.class_W) > 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_V then
        l = m.m
    else if cl == m.class_W then
        l = substr(m, 2)
    else if cl == '?' then
        l = '@'m 'class=???'
    else do
        l = '@'m 'class='className(cl)
        ff = oFlds(m)
        do fx=1 to m.ff.0 while length(l) < maxL + 3
            if m.ff.fx == '' then
                 l = l', .='m.m
            else do
                 f1 = m.ff.fx
                 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.o_escW || str
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_V then
        return = m.m
    else if cl == m.class_W 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 address (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
    m.o_escW = '!'
    call mapIni
    m.class.0 = 0
    call mapReset class_n2c  /* name to class */
    m.class_V = classNew('n v u', 'm',
                , "oCopy return oCopyV(m, t)" ,
                , "o2String return m.m",
                , "o2File return file(m.m)")
    m.class_W = classNew('n w u', 'm' ,
                , "o2String return substr(m, 2)" ,
                , "o2File return file(substr(m,2))")
    m.class_O = classNew('n o u')
    m.class_R = classNew('r')

    m.class_C = classNew('n class u')
    call classNew 'n= class u v' ,
            , 'c u u f NAME v',           /* union or class */
            , 'c f u f NAME v',           /* field          */
            , 'c s u' ,                   /* stem           */
            , 'c c u f NAME v',           /* choice         */
            , 'c r u' ,                   /* reference      */
            , 'c m u f NAME v, f MET  v'  /* method         */
    call mAdd m.class_C, classNew('s r class')
    m.class_root = classNew('n root u', 'm',
           , "new    ?l" ,
           , "new1   ?l" ,
           , "new2   ?l" ,
           , "f2c    ?l" ,
           , "s2c    ?l" ,
           , "oClear ?l" ,
           , "oFlds  ?l" ,
           , "oCopy  ?l" ,
           , "stms   ?l" ,
           , "s2c    ?l" )
    call 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)'
    return
endProcedure classIni


/*--- return the name of a class if it exists otherwise the class ---*/
className: procedure expose m.
parse arg cl
    if \ mapHasKey(class_n2c, cl) then
        return 'notAClass:' cl
    c2 = mapGet(class_n2c, cl)
    if m.c2 = 'u' & m.c2.name \= '' then
        return m.c2.name
    else
        return cl
endProcedure className

/*--- 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

classNe1: procedure expose m.
parse arg n, ty, nm, refs, io
    ky = ty','nm','space(refs, 1)','strip(io)
    if ty == 'f' & abbrev('=', nm) then do
        if words(refs) = 1 & io == '' then
            return strip(refs)
        else
            call err 'bad field name:' ky
        end
    if n == 0 | n == 1 then do
        if n then
            if symbol('m.class_k2c.ky') == 'VAR' then
                return m.class_k2c.ky
        m.class.0 = m.class.0 + 1
        n = 'CLASS.'m.class.0
        call mapAdd class_n2c, n, n
        end
    m.n = ty
    m.n.name = nm
    m.n.met = strip(io)
    m.n.0 = words(refs)
    do rx=1 to m.n.0
        m.n.rx = mapGet(class_n2c, word(refs, rx))
        end
    if right(nm, 1) == '*' then
        nm = left(nm, length(nm)-1)substr(n, 7)
    if length(ty) \== 1 | pos(ty, 'ufscrm') < 1 then
        call err 'bad type' ty': classNe1('ky')' /0
    else if nm == '' & pos(ty, 'm') > 0 then
        call err 'empty name: classNe1('ky')'
    else if nm \== '' & ty \== 'c' & verifId(nm) > 0 then
        call err 'bad name' nm': classNe1('ky')'
    else if nm \= '' & pos(ty, 'rs') > 0 then
        call err 'name for type' ty': classNe1('ky')'
    else if (pos(ty, 'fcsr') > 0 & m.n.0 \== 1) ,
          | (    ty == 'm' & m.n.0 \== 0) then
        call err m.n.0 'bad ref count in classNe1('ky')'
    return n
endProcedure classNe1

classNew: procedure expose m.
parse arg clEx 1 ty rest
    n = ''
    nm = ''
    io = ''
    refs = ''
    if wordPos(ty, 'n n? n* n=') > 0 then do
        nmTy = right(ty, 1)
        parse var rest nm ty rest
        if nmTy = '=' then do
            if \ mapHasKey(class_n2c, nm) then
                call err 'class' nm 'not defined: classNew('clEx')'
            n = mapGet(class_n2c, nm)
            end
        if ty \== 'u' then
            call err 'class name' nm 'without u: classNew('clEx')'
        if nmTy == '?' then do
            if mapHasKey(class_n2c, nm) then
                return mapGet(class_n2c, nm)
            end
        else if nmTy == '*' & arg() == 1 then do
            if mapHasKey(class_n2c, clEx) then
                return mapGet(class_n2c, clEx)
            end
        end
    else do
        nmTy = ''
        if arg() == 1 then
            if mapHasKey(class_n2c, clEx) then
                return mapGet(class_n2c, clEx)
        if length(ty) <> 1 | pos(ty, 'ufscmr') < 1 then
            return err('bad type' ty': classNew('clEx')')
        if pos(ty, 'fcm') > 0 then
            parse var rest nm rest
        if ty == 'm' then
            io = rest
        else if pos(ty, 'fsc') > 0 | (ty == 'r' & rest \== '') then
            refs = classNew(strip(rest))
        else if ty == 'r' then
            refs = m.class_O
        end
    if ty == 'u' then 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
            refs = refs 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
                refs = refs classNew(pref || arg(ax))
            end
        end
    if nmTy == '=' then
        n = classNe1(n, ty, nm, refs, io)
    else if nmTy == '*' then
        n = classNe1(0, ty, nm'*', refs, io)
    else
        n = classNe1(nmTy == '', ty, nm, refs, io)
    if arg() == 1 then
        call mapAdd class_n2c, clEx, n
    if nmTy == '*' & m.n.name == nm'*' then
        m.n.name = nm || substr(n, 6)
    if nmTy \== '' & nmTy \== '=' then
       call mapAdd class_n2c, m.n.name, n
    return n
endProcedure classNew

/*--- find the code for a met in a class ---------------------------*/
classMet: procedure expose m.
parse arg cl, met
    if symbol('m.cl.method.met') == 'VAR' then
        return m.cl.method.met
    if symbol('m.class_n2c.cl') \== 'VAR' then
        call err 'no class classMet('cl',' met')'
    if cl \== m.class_n2c.cl then
        return classMet(m.class_n2c.cl, met)
    if symbol('m.cl.metLazy.met') == 'VAR' then do
         parse var m.cl.metLazy.met l1 lRest
         m.cl.method.met = "call err 'building lazy method" cl"#"me"'"
         if l1 == '?l' then
             m.cl.method.met = classMetLazy(cl, met, lRest)
         else if l1 == '?r' then
             m.cl.method.met = classMetRec(cl, met, lRest)
         else
             call err 'implement' l1 'for lazy' className(cl)'#'met
         return m.cl.method.met
         end
    if m.cl.methods == 1 then
        return err('no method' met 'in class' className(cl))
    m.cl.methods = 1
    call classMetGen m.class_root, cl
    call classMetGen cl, cl
    return classMet(cl, met)
endProcedure classMet

classCycle: procedure expose m.
parse arg cl, pa
    if wordPos(cl, pa) < 1 then
        return pa cl
    call err implement cycle
endProcedure classCycle

classMetGen: procedure expose m.
parse arg cl, trg, pa
    pa = classCycle(cl, pa)
    if m.cl == 'm' then do
        mthd = m.cl.name
 /*     if mthd = 'jOpen' then trace ?r
        if symbol('m.trg.method.mthd') == 'VAR' then nop
        else if symbol('m.trg.metLazy.mthd') == 'VAR' then nop
        else if \ abbrev(m.cl.met, '?') then
 */     drop m.trg.method.mthd m.trg.metLazy.mthd
        if \ abbrev(m.cl.met, '?') then
            m.trg.method.mthd = m.cl.met
        else
            m.trg.metLazy.mthd = m.cl.met
        end
    else if m.cl == 'u' then do
        do cx=1 to m.cl.0
            if wordPos(m.cl.cx,
                , m.class_V m.class_W m.class_O m.class_R) < 1 then
                call classMetGen m.cl.cx, trg, pa
            end
        end
    else if pos(m.cl, 'fscr') < 1 then
        call err 'bad cla' cl m.cl
    return
endProcedure classMetGen

classFlds: procedure expose m.
parse arg cl
    return classMet(cl, 'oFlds')
endProcedure classFlds

classMetLazy: procedure expose m.
parse arg cl, met, rest
    if met = 'new1' then do
        call mNewArea cl, 'O.'substr(cl,7)
        return  "oMutate(mNew('"cl"'), '"cl"')"
        end
    if met == 'oFlds' then do
        m.cl.flds.0 = 0
        m.cl.flds_self = 0
        m.cl.stms.0 = 0
        m.cl.stms_self = 0
        call classFldAdd cl, cl
        return cl'.FLDS'
        end
    call classMet cl, 'oFlds'
    if met == 'new' then do
        return 'return' classMet(cl, 'new2')
        end
    else if met == 'new2' then do
        call classMet cl, 'oClear'
        return "classClear('"cl"'," classMet(cl, 'new1')")"
        end
    else if wordPos(met, 'f2c stms s2c') > 0 then do
        return cl'.'translate(met)
        end
    else if met == 'oClear' then do
        do fx=1 to m.cl.flds.0
            f1 = m.cl.flds.fx
            m.cl.flds_null.fx = if(m.cl.f2c.f1==m.class_W,
                            , m.o_escW, '')
            end
        m.cl.flds_null.0 = m.cl.flds.0
        return "return classClear('"cl"', m)"
        end
    else if met == 'oCopy' then do
        do sx=1 to m.cl.stms.0
            s1 = m.cl.stms.sx
            s2 = m.cl.s2c.s1
            call classMet m.cl.s2c.s1, 'oCopy'
            call classMet m.s2.1, 'oCopy'
            end
        call classMet cl, 'new1'
        return "return classCopy('"cl"', m, t)"
        end
    else
        call err 'bad method in classMetLazy('cl',' met')'
endProcedure classMetLazy

classMetRec: procedure expose m.
parse arg cl, met, rest
    gen = ''
    rx = 1
    do forever
        ry = pos('?', rest, rx)
        if ry == 0 then
            return gen || substr(rest, rx)
        gen = gen || substr(rest, rx, ry-rx)
        rx = verify(rest, m.ut_alfid, 'n', ry+1)
        if rx = 0 then
            rx = length(rest)+1
        gen = gen  || classMet(cl, substr(rest, ry+1, rx-ry-1))
        end
endProcedure classMetLazy

/*--- add the the fields of class cl to stem f ----------------------*/
classFldAdd: procedure expose m.
parse arg f, cl, nm, pa
    pa = classCycle(cl, pa)
    if cl == m.class_V | cl == m.class_W | cl == m.class_O ,
             | m.cl == 'r' then
             return classFldAdd1(f'.FLDS', f'.F2C', cl, nm,
                  , if(cl=m.class_W, m.o_escW, ''))
    if m.cl = 's' then do
        if m.cl.1 == '' then
            call err 'stem null class'
        return classFldAdd1(f'.STMS', f'.S2C', m.cl.1, nm, 0)
        end
    if m.cl = 'f' then
        return classFldAdd(f, m.cl.1, nm ,
          || left('.', m.cl.name \== '' & nm \== '') || m.cl.name, pa)
    do tx=1 to m.cl.0
        call classFldAdd f, m.cl.tx, nm, pa
        end
    return 0
endProcedure classFldAdd

classFldAdd1: procedure expose m.
parse arg fa, f2, cl, nm, null
    if symbol('m.f2.nm') == 'VAR' then
        if m.f2.nm == cl then
            return 0
        else
            return err('f2 mismatch f2='f2 Nm 'cl='cl 'old='m.f2.nm)
    m.f2.nm = cl
    if nm == '' then do
        call mMove fa, 1, 2
        m.fa.1 = ''
        call mPut fa'_SELF', 1
        end
    else do
        call mAdd fa, nm
        end
    return 0
endProcedure classFldAdd1

classInheritsOf: procedure expose m.
parse arg cl, sup    /* wkTst optimierung in classAdded */
    cl = class4name(cl)
    sup = class4name(sup)
    if m.cl.inheritsOf \== 1 then do
        m.cl.inheritsOf = 1
        call classInheritsOfAdd cl, cl'.INHERITSOF'
        end
    return m.cl.inheritsOf.sup == 1
endProcedure classInheritsOf

classInheritsOfAdd: procedure expose m.
parse arg cl, trg, pa
    pa = classCycle(cl, pa)
    m.trg.cl = 1
    call assert "m.cl == 'u'"
    do cx=1 to m.cl.0
        c1 = m.cl.cx
        if m.c1 == 'u' then
            call classInheritsOfAdd c1, trg, pa
        end
    return
endProcedure classInheritsOf

classClear: procedure expose m.
parse arg cl, m
    if m.cl.flds_self then
        m.m = m.cl.flds_null.1
    do fx=1+m.cl.flds_self to m.cl.flds.0
        f1 = m.cl.flds.fx
        m.m.f1 = m.cl.flds_null.fx
        end
    if m.cl.stms_self then
        m.m.0 = 0
    do sx=1+m.cl.stms_self to m.cl.stms.0
        s1 = m.cl.stms.sx
        m.m.s1.0 = 0
        end
    return m
endProcedure classClear

classCopy: procedure expose m.
parse arg cl, m, t
    if t == '' then
        t = oMutate(mNew(cl), cl)
    else
        call oMutate t, cl
    if m.cl.flds_self then
        m.t = m.m
    do fx=1+m.cl.flds_self to m.cl.flds.0
        f1 = m.cl.flds.fx
        m.t.f1 = m.m.f1
        end
    if m.cl.stms_self then
        call classCopyStem m.cl.stms., m, t
    do sx=1+m.cl.stms_self to m.cl.stms.0
        s1 = m.cl.stms.sx
        call classCopyStem m.cl.s2c.s1, m'.'s1, t'.'s1
        end
    return t
endProcedure classCopy

classCopyStem: procedure expose m.
parse arg cl, m, t
    m.t.0 = m.m.0
    do sx=1 to m.t.0
        call classCopy cl, m'.'sx, t'.'sx
        end
    return 0
endProcedure classCopyStem

/*--- return true if src is a rexxVariable a, m.a.c etc. -------------*/
rxIsVar: procedure expose m.
parse arg src
    if pos(left(src, 1), m.ut_rxN1) > 0 then
        return 0
    else
        return verify(src, m.ut_rxId) = 0
endProcedure rxIsVar

/*--- return true if src is a rexxConstant rerpresenting its value ---*/
rxIsConst: procedure expose m.
parse arg src, vars                                                   c
    if \ rxIsVar(src) then
        return 0
    srU = translate(src)
    if srU \== src then
        return 0
    srU = '.'srU'.'
    if pos('.GG', srU) > 0 then
        return 0
    if vars == '' then
        return 1
    upper vars
    do vx=1 to words(vars)
        if pos('.'word(vars, vx)'.', vars) > 0 then
            return 0
        end
    return 1
endProcedure rxIsConst

/*--- return rexx code m.cc or mGet('cc') ----------------------------*/
rxMGet: procedure expose m.
parse arg v1, cc, vars
    if cc == '' then
        return 'm.'v1
    else if rxIsConst(cc, vars) then
        return 'm.'v1'.'cc
    else
        return 'mGet('v1 || quote('.'cc, "'")')'
endProcedure rxMGet

/*--- oCopy for classW ----------------------------------------------*/
oCopyW: procedure expose m.
trace ?r
parse arg m, t
    if t == '' then
        return m
    m.t = o2String(m)
    return oMutate(t, m.class_V)
endProcedure oCopyW
/*--- 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_V then
       call out p1 || m.m
   else if c == m.class_W then
       call out p1 || o2String(m)
   else
       call classOutDone c, m, pr, p1
   return
endProcedure objOut

/*--- recursively output (with outX:) object a with class t ---------*/
classOut: procedure expose m.
parse arg t, a, pr, p1
    return classOutDone(if(t=='',m.class_O, 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 outX(p1'done :'className(t) '@'a)
    done.t.a = 1
    if t = m.class_O then do
        if a == '' then
            return outX(p1'obj null')
        t = objClass(a, '')
        if t = '' then
            return outX(p1'obj has no class @'m.a)
        else
            return classOutDone(t, a, pr, p1'isA')
        end

    if t == m.class_V then
        return outX(p1'=' m.a)
    if t == m.class_W == 'w' then
        return outX(p1'}' substr(a, 2))
    if m.t == 'f' then
        return classOutDone(m.t.1, a'.'m.t.name, pr, p1'.'m.t.name)
    if m.t == 'r' then do
        if m.a == '' then
            return outX(p1'refTo :'className(m.t.1) '@null@')
        else
            return classOutDone(m.t.1, 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_V
        call outX 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 outX p1'stem' m.a.0
        do ux=1 to m.a.0
            call classOutDone m.t.1, 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.1, 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 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.ut_alfDot, '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 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 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>.** and m.<mbr>_**: every rexx Module (copy) should only
               allocate these addresses to avoid address conficts
               with <mbr> the name of therexx 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
    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
    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

mNew: 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 mNew

mFree: procedure expose m.
parse arg m
    p = 'M.P2A.'left(m, lastPos('.', m)-1)
    area = m.p
    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

/*--- get m.a --------------------------------------------------------*/
mGet: procedure expose m.
parse arg a
    return m.a
endProcedure mGet

/*--- put value v into m.a -------------------------------------------*/
mPut: procedure expose m.
parse arg a, v
    m.a = v
    return v
endProcedure mPut

/*--- 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, fx , tx
    dx = m.dst.0
    if fx == '' then
        fx = 1
    if tx == '' then
        tx = m.src.0
    do sx = fx to tx
        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
    return mCatFT(st, 1, m.st.0, fmt)

mCatFT: procedure expose m.
parse arg st, fx, tx, fmt
    if tx < fx then
        return ''
    fmt = '@cat%('fmt'%)'
    res = f(fmt, m.st.fx)
    do sx=fx+1 to tx
        res = res || f(fmt'%.1', m.st.sx)
        end
    return res || f(fmt'%.2')
endProcedure mCatFT

fGenCat: procedure expose m.
parse arg s, ax
    do fx=1 until \ scanLit(s, '%,')
        f.fx = fGen(s)
        end
    if \ scanLit(s, '%)') then
        call scanErr s, 'no %) after @fGenCat%('
    if \ scanEnd(s) then
        call scanErr s, 'mGenCat not at end'
    if fx < 2 | f.2 == "''" then
        f.2 = fGen(scanSrc(f_u, '%c'))
    if fx < 3 then
        f.3 = "''"
    if fx < 4 then
        f.4 = "''"
    adr = m.s.src'%'
    if f.1 == "''" then
        m.f_gen.adr.1 = 'return' f.2
    else
        m.f_gen.adr.1 = 'return' f.1 '||' f.2
    m.f_gen.adr.2 = 'return' f.4
    if f.3 == "''" then
        return f.2
    else
        return f.3 '||' f.2
endProcedure fGenCat

mIni: procedure expose m.
    if m.m.ini == 1 then
        return
    m.m.ini = 1
    call utIni
    m.mBase64 = m.ut_alfUC || m.ut_alfLc || m.ut_digits'+-'
    m.m.area.0 = 0
    call mNewArea
    return
endProcedure mIni

/* copy m end *********************************************************/
/* copy fTab begin ****************************************************/
fTabReset: procedure expose m.
parse arg m, m.m.titBef, m.m.titAft, m.m.opt
    m.m.generated = ''
    m.m.0 = 0
    m.m.len = 0
    m.m.cols = ''
    m.m.tit.0 = words(m.m.titBef m.m.titAft) + 5
    m.m.set.0 = 0
    do tx=1 to m.m.tit.0
        m.m.tit.tx = ''
        end
    return m
endProcedure fTabReset

/* add a piece to title tx at current pos */
fTabAddTit: procedure expose m.
parse arg m, tx, t1
    m.m.generated = ''
    m.m.tit.tx = left(m.m.tit.tx, m.m.len) || t1
    return m
endProcedure fTabAddTit

/*--- set the infos for one column -----------------------------------*/
fTabSet: procedure expose m.
parse arg m, c1 aDone, f1, l1
    sx = m.m.set.0 + 1
    m.m.set.0 = sx
    m.m.set.sx = c1 aDone
    m.m.set.sx.fmt = f1
    m.m.set.sx.label = l1
    m.m.set.c1 = sx
    return
endProcedure fTabSet

fTabAdd: procedure expose m.
parse arg m, c1 aDone, f1, l1
    cx = m.m.0 + 1
    m.m.generated = ''
    m.m.0 = cx
    k1 = c1
    if c1 == '' then
        k1 = '='
    else if c1 == '=' then
        c1 = ''
    m.m.cols = m.m.cols k1
    if words(m.m.cols) <> cx then
        call err 'mismatch of column number' cx 'col' c1  / 0
    if length(aDone) > 1 | wordPos('<'aDone'>', '<> <0> <1>') < 1 then
        call err 'bad done' length(aDone) '<'aDone'> after c1' c1
    m.m.cx.col = c1
    m.m.cx.done = aDone \== 0
    if l1 == '' then
        m.m.cx.label = k1
    else
        m.m.cx.label = l1
    if cx == 1 then
        if abbrev(f1, ' ') then
           f1 = substr(f1, 2)
    px = pos('%', f1)
    ax = pos('@', f1)
    if px < 1 | (ax > 0 & ax < px) then
        m.m.cx.fmt = f1
    else
        m.m.cx.fmt = left(f1, px-1)'@.'c1 || substr(f1, px)
    call mPut 'F_TEMP'left('.', c1\=='')c1, m.m.cx.label
    t1 = f(m.m.cx.fmt, 'F_TEMP')
    if pos(strip(t1), m.m.cx.label) < 1 then
        t1 = left(left('', max(0, verify(t1, ' ') -1))m.m.cx.label,
           , length(t1))
    m.m.cx.len = length(t1)
    call fTabAddTit m, 1, t1
    do tx=2 to arg()-3
        if arg(tx+3) \== '' then
            call fTabAddTit m, tx, arg(tx+3)
        end
    m.m.len = m.m.len + length(t1)
    return m
endProcedure fTabAdd

fTabGenerate: procedure expose m.
parse arg m
    f = ''
    do kx=1 to m.m.0
        f = f || m.m.kx.fmt
        end
    do while right(f, 1) == ' ' & f \== ''
        f = left(f, length(f)-1)
        end
    m.m.fmt = fCache('%.', f)

    cSta = m.m.tit.0+3
    do cEnd=cSta until kx > m.m.0
        cycs = ''
        do cx=cSta to cEnd
            m.m.tit.cx = ''
            cycs = cycs cx
            end
        cx = cSta
        ll = 0
        do kx=1 to m.m.0 while length(m.m.tit.cx) < max(ll,1)
            m.m.tit.cx = left(m.m.tit.cx, ll)m.m.kx.col
            cx = cx + 1
            if cx > cEnd then
                cx = cSta
            ll = ll + m.m.kx.len
            end
        end
    m.m.cycles = strip(cycs)
    if pos('-', m.m.opt) > 0 then
        m.m.tit.1 = translate(lefPad(m.m.tit.1, m.m.len), '-',' ')'---'
    m.m.generated = m.m.generated't'
    return
endProcedure fTabGenerate

fTabColGen: procedure expose m.
parse arg m
    do kx=1 to m.m.0
        l = if(m.m.kx.label == m.m.kx.col, , m.m.kx.label)
        f = lefPad(l, 10) lefPad(m.m.kx.col, 18)
        if length(f) > 29 then
           if length(l || m.m.kx.col) < 29 then
               f = l || left('', 29 - length(l||m.m.kx.col))m.m.kx.col
           else
               f = lefPad(strip(l m.m.kx.col), 29)
        g = strip(m.m.kx.fmt)
        o = right(g, 1)
        if pos(o, 'dief') > 0 then
            f = f '@'m.m.kx.col'%12e @'m.m.kx.col'%18c'
        else if o = 'C' then
            f = f left(g, length(g)-1)'c'
        else
            f = f g
        m.m.kx.colFmt = f
        end
    m.m.generated = m.m.generated'c'
    return
endProcedure fTabColGen

fTab: procedure expose m.
parse arg m, rdr
    call fTabBegin m
    call fAll m.m.fmt, rdr
    return fTabEnd(m)
endProcedure fTab

fTabCol: procedure expose m.
parse arg m, i
    if pos('c', m.m.generated) < 1 then
        call fTabColGen m
    do cx=1 to m.m.0 / 0
        call out f(m.m.cx.colFmt, i)
        end
    return 0
endProcedure fTabCol

fTabBegin: procedure expose m.
parse arg m
    if pos('t', m.m.generated) < 1 then
        call fTabGenerate m
    return fTabTitles(m, m.m.titBef)

fTabEnd: procedure expose m.
parse arg m
    return fTabTitles(m, m.m.titAft)

fTabTitles: procedure expose m.
parse arg m, list
    list = repAll(list, 'c', m.m.cycles)
    do tx=1 to words(list)
        t1 = word(list, tx)
        call out m.m.tit.t1
        end
    return m
endProcedure fTabTitles

/*--- auto tables detect format from data ---------------------------*/
/*--- detect fmt from a rdr and write it formatted to stdOut --------*/
fTabAuto: procedure expose m.
parse arg m, rdr, wiTi
    if m == '' then
        m = fTabReset(f_auto, 1)
    i = j2Buf(rdr)
    if m.i.buf.0 <= 0 then
        return m
    call fTabDetect m, i'.BUF', wiTi
    return fTab(m, i)
endProcedure fTabAuto

/*--- generate format for all fields of a stem of objects -----------*/
fTabDetect: procedure expose m.
parse arg m, b, wiTi
    ff = oFlds(m.b.1)
    do fx=1 to m.ff.0
        call fTabAddDetect m, m.ff.fx, b
        end
    return
endProcedure fTabDetect

/*--- detect format for one field in stem st ------------------------*/
fTabAddDetect: procedure expose m.
parse arg m, c1 aDone, st, l1
    lMa = -1
    rMa = -1
    bMa = -1
    aDiv = 0
    nMi =  9e999
    nMa = -9e999
    eMi =  9e999
    eMa = -9e999
    eDa = 2
    dMa = -9e999
    suf = left('.', c1 \== '')c1
    do sx=1 to m.st.0
        v = mGet(m.st.sx || suf)
        lMa = max(lMa, length(strip(v, 't')))
        rMa = max(rMa, length(strip(v, 'l')))
        bMa = max(bMa, length(strip(v, 'b')))
        if \ dataType(v, 'n') then do
            if length(v) > 100 then
                aDiv = 99
            else if aDiv <=3 then
                if aDiv.v \== 1 then do
                    aDiv.v = 1
                    aDiv = aDiv + 1
                    end
            iterate
            end
        v = strip(v)
        nMi = min(nMi, v)
        nMa = max(nMa, v)
        ex = verify(v, 'eEfF', 'm')
        if ex > 0 then do
            eMa = max(eMa, substr(v, ex+1))
            eMi = min(eMi, substr(v, ex+1))
            v = left(v, ex-1)
            do while pos(left(v,1), '+-0') > 0
                v = substr(v, 2)
                end
            eDa = max(eDa, length(v) - (pos('.', v) > 0))
            end
        dx = pos('.', v)
        if dx > 0 then do
            do while right(v, 1) == 0
                v = left(v, length(v)-1)
                end
            dMa = max(dMa, length(v)-dx)
            end
        end
    if nMi > nMa | aDiv > 3 then
        newFo = '-'max(1, (lMa+0))'C'
    else if eMi <= eMa then do
        newFo = ' ' || (eDa+max(length(eMa), length(eMi))+3) ,
             || '.'||(eDa-1)'e'
        end
    else do
        be = max(length(trunc(nMi)), length(trunc(nMa)))
        if dMa <= 0 then
            newFo = max(be, bMa)'I'
        else
            newFo = max(be+1+dMa, bMa)'.'dMa'I'
        end
    call fTabAdd m, c1 aDone, ' %'newFo, l1
  /*  say c1 '????==> %'newFo */
   return newFo
endProcedure fTabAddDetect

/*--- format with units seconds ==> 3d13 oder 5.43e6 ==> 5m43 --------*/
fTime: procedure expose m.
parse arg s, signed
    return fUnits(s, 't', signed==1)
endProcedure fTime

fDec: procedure expose m.
parse arg s, signed
    return fUnits(s, 'd', signed==1)
endProcedure fDec


fUnits: procedure expose m.
parse arg s, scale, signed
    if f_units_ini \== 1 then do
        f_units_ini = 1
        call fUnitsIni 't', '?smhd', 0 100 60 60 24 100, 0.01, 2
        call fUnitsIni 'd', '?.kMGTPE', 0 copies('1000 ',8), 0.001, 3
        end
    if s >= 0 then
        res = fUnitsNN(s, scale, wi)
    else
        res = '-'fUnitsNN(abs(s), scale, wi)
    len = m.fmt.units.scale.f.length + signed
    if length(res) <= len then
       return right(res, len)
    if \ abbrev(res, '-') then
        return right(right(res, 1), len, '+')
    if length(res) = len+1 & datatype(right(res, 1), 'n') then
        return left(res, len)
    return right(right(res, 1), len, '-')
endProcedure fUnits

fUnitsNN: procedure expose m.
parse arg s, scale
    sf = 'FMT.UNITS.'scale'.F'
    sp = 'FMT.UNITS.'scale'.P'
    if m.sf \== 1 then do
        call fmtIni
        if m.sf \== 1 then
            call err 'fUnitsNN bad scale' scale
        end

    do q=3 to m.sp.0 while s >= m.sp.q
        end
    do forever
        qb = q-2
        qu = q-1
        r = format(s / m.sp.qb, ,0)
        if q > m.sf.0 then
            return r || substr(m.sf.units, qb, 1)
        if r < m.sf.q * m.sf.qu then
            return (r % m.sf.qu) || substr(m.sf.units, qu, 1) ,
                              || right(r //m.sf.qu, m.sf.width, 0)
            /* overflow because of rounding, thus 1u000: loop back */
        q = q + 1
        end
endProcedure fUnitsNN

fUnitsIni: procedure expose m.
parse arg sc, us, fact, prod, wi
    sf = 'FMT.UNITS.'sc'.F'
    sp = 'FMT.UNITS.'sc'.P'
    m.sf.0 = words(fact)
    if length(us) + 1 <> m.sf.0 then
        call err 'fUnitsIni mismatch' us '<==>' fact
    m.sf.1 = word(fact, 1)
    m.sp.1 = prod
    do wx=2 to m.sf.0
        wx1 = wx-1
        m.sf.wx = word(fact, wx)
        m.sp.wx = m.sp.wx1 * m.sf.wx
        end
    m.sp.0 = m.sf.0
    m.sf.units = us
    m.sf.width = wi
    m.sf.length= 2 * wi + 1
    m.sf = 1
    return
endProcedure fUnitsIni
/* copy fTab end   ****************************************************/
/* copy f begin *******************************************************/
/*--- format with the given format ggA1, ggA2, etc. -----------------*/
f: procedure expose m.
parse arg ggFmt, ggA1, ggA2
    if symbol('M.f_gen.ggFmt') \== 'VAR' then
        call fCache ggFmt, ggFmt
    interpret m.f_gen.ggFmt
endProcedure f

fImm: procedure expose m.
parse arg ggFmt, ggA1
    interpret m.ggFmt
endProcedure fImm

/*--- compile format fmt put in the cache with address a ------------*/
fCache: procedure expose m.
parse arg a, fmt
    if a == '%.' then do
        if symbol('m.f_gen0') == 'VAR' then
            m.f_gen0 = m.f_gen0 + 1
        else
            m.f_gen0 = 1
        a = '%.'m.f_gen0
        end
    else if symbol('M.f_gen.a') == 'VAR' then
        return a
    if symbol('m.f_s_0') \== 'VAR' | m.f_s_0 == 0 then
        m.f_s_0 = 1
    else do
        m.f_s_0 = m.f_s_0 + 1
        f_s = 'F_S_'m.f_s_0
        end
    call scanSrc f_s, fmt
    m.f_gen.a = 'return' fGen(f_s)
    m.f_s_0 = m.f_s_0 - 1
    if \ scanEnd(f_s) then
        call scanErr f_s, "bad specifier '"m.f_s.tok"'"
    return a
endProcedure fCache

/*--------------------------------------------------------------------
 Format generator    should be compatible with fPrint|
 <<<< + extension of fPrint, - in fPrint but not implemented

   %%  %@ the escaped char
   ('@' argN? '.'? field)?'%' flags? width? ('.' precision)? specifier

 specifier: is the most significant one and defines the type

 - c  Character rigPad or lefPad, prec ==> substr(..., prec)
 -  C Cut %-nC = left(v,n), %nC = right(v,n) %n.mC = substr(m, n)
 - hH Characters in hex
 - iI Signed decimal integer (padded or cut)
 - eE Scientific notation (mantissa/exponent) using e character 3.92e+2
 - S  Strip (both)
 - txy time date formatting from format x to format y
 - (,) call format generator routine with name 'fGen' || field
 Flags:
 - -  Left-justify within the given field width; Right is default
 - +  print '+' before non negative numbers
 -' ' print ' ' before non negative numbers
 - /  cut to length
----------------------------------------------------------------------*/
fGen: procedure expose m.
parse arg f_s
    ax = 0
    cd = ''
    do forever
        txt = fText(f_s)
        if txt \== '' then
            cd = cd '||' quote(txt, "'")
        if scanEnd(f_s) then
            leave
        if \ scanLit(f_s, '@') then do
            ax = ax + 1
            af = ''
            hasDot = 0
            end
        else do
            if scanWhile(f_s, '0123456789') then
                ax = m.f_s.tok
            else if ax < 1 then
                ax = 1
            hasDot = scanLit(f_s, '.')
            af = fText(f_s)
            end
        if \ scanLit(f_s, '%') then
            call scanErr f_s, 'missing %'
        call scanWhile f_s, '-+ /'
        flags = m.f_s.tok
        if scanWhile(f_s, '0123456789') then
            len = m.f_s.tok
        else
            len = 0
        if \ scanLit(f_s, '.') then
            prec  = ''
        else do
            call scanWhile f_s, '0123456789'
            prec = m.f_s.tok
            end
        call scanChar f_s, 1
        sp = m.f_s.tok
        if ax < 3 then
            aa = 'ggA'ax
        else
            aa = 'arg(' || (ax+1) || ')'
        if af \== '' | hasDot then
            aa = rxMGet(aa, af)
        if sp == 'c' then do
            if prec \== '' then
                aa = 'substr('aa',' prec')'
            if len < 1 then
                cd = cd '||' aa
            else if pos('-', flags) > 0 then
                cd = cd '|| lefPad('aa',' len')'
            else
                cd = cd '|| rigPad('aa',' len')'
            end
        else if sp == 'C' then do
            if prec \== '' then do
                cd = cd '|| substr('aa',' prec
                if len < 1 then
                    cd = cd')'
                else
                    cd = cd',' len')'
                end
            else if len < 1 then
                cd = cd '||' aa
            else if pos('-', flags) > 0 then
                cd = cd '|| left('aa',' len')'
            else
                cd = cd '|| right('aa',' len')'
            end
        else if sp == 'H' then
            cd = cd "|| fH("aa"," len',' (pos('-', flags) > 0)')'
        else if sp == 'h' then
            cd = cd "|| translate(fH("aa", '"siL"'), 'abcdef','ABCDEF')"
        else if sp == 'i' then
            cd = cd "|| fI("aa"," len", '"flags"'," word(prec 0, 1)")"
        else if sp == 'I' then
            cd = cd "|| fI("aa"," len", '/"flags"'," word(prec 0, 1)")"
        else if sp == 'E' | sp == 'e' then do
            if len = 0 then
                len = 8
            if prec = '' then
                prec = len - 6
            cd = cd "|| fE("aa"," len"," prec", '"sp"', '"flags"')"
            end
        else if sp = 'S' then
            cd = cd '|| strip('aa')'
        else if sp = 't' then do
            call scanChar f_s, 2
            cd = cd '||' fTstGen(m.f_s.tok, aa)
            end
        else if sp = '(' then do
            if af == '' | flags \== '' | len \== 0 | prec \== '' then
                call scanErr f_s, "bad call shoud be @sub%("
            interpret "cRes = fGen"af"(f_s, ax)"
            cd = cd '||' cRes
            if \ scanLit(f_s, '%)') then
                if \ scanEnd(f_s) then
                    call scanErr f_s, '%) to end call' af 'expected'
            end
        else do
            call scanBack f_s, '%'sp
            leave
            end
        end
    if cd \== '' then
        return substr(cd, 5)
    else
        return "''"
endProcedure fGen

fText: procedure expose m.
parse arg f_s
    res = ''
    do forever
        if scanUntil(f_s, '@%') then
            res = res || m.f_s.tok
        if \ scanLit(f_s, '%%', '%@') then
            return res
        res = res || substr(m.f_s.tok, 2)
        end
endProcedure fText

fAll: procedure expose m.
parse arg fmt, rdr
    i = inOpen(rdr)
    o = jReadO(i)
    do while o \== ''
        call out f(fmt, o)
        o = jReadO(i)
        end
    call jClose i
    return
endProcedure fAll

/*--- format character2hex (if not sql null) -------------------------*/
fH: procedure expose m.
parse arg v, l, leftJ
    if v \== m.sqlNull then
        v = c2x(v)
    if length(v) > l then
        return v
    else if leftJ \== 1 then
        return right(v, l)
    else
        return left(v, l)
endProcedure fH

/*--- format integer or fixPoint Decimal -----------------------------*/
fI: procedure expose m.
parse arg v, l, flags, d
    if \ datatype(v, 'n') then
        return fRigLeft(strip(v), l, flags)
    v = format(v, , d, 0)
    if pos('+', flags) > 0 then
        if \ abbrev(v, '-') then
            v = '+'v
    if length(v) > l then
        if pos('/', flags) > 0 then
            return left('', l, '*')
        else
            return v
    return fRigLefPad(v, l, flags)
endProcedure fI

/*--- format with exponent l=total output len
                           d=number of digits after . in mantissa
                           c=exponent character
                           flags: - to ouput text left justified
    differences: exponent is always printed but without +
                 overflow ==> expand exponent, *****, 0e-999 ---------*/
fE: procedure expose m.
parse arg v, l, d, c, flags
    if \ datatype(v, 'n') then
        return fRigLeft(v, l, flags)
    if pos(' ', flags) < 1 then
        if v >=  0 then
            if pos('+', flags) > 0 then
                return '+'substr(fE(v, l, d, c, ' 'flags), 2)
            else
                return substr(fE(v, l+1, d+1, c, ' 'flags), 2)
    x = format(v, 2, d, 7, 0)
    m = 2 + d + (d>0)
    call assert "length(x) == m+9", 'm x length(x)'
    if substr(x, m+1) = '' then
        return left(x, m)c || left('', l-m-1, 0)
    call assert "substr(x, m+1, 1) == 'E'","exponenent in x not at m"
    y = verify(x, '0', 'n',  m+3)
    call assert 'y>0'
    if substr(x, m+1, 2) == 'E+' then do
        if m+10-y <= l-m-1 then
             return left(x,m)c || right(x, l-m-1)
        z = l - 4 - (m+10-y)
        end
    else if substr(x, m+1, 2) == 'E-' then do
        if m+10-y <= l-m-2 then
             return left(x,m)c'-'right(x, l-m-2)
        z = l - 5 - (m+10-y)
        end
    else
        call err 'bad x' x
    if z >= -1 & max(0, z) < d then
        return fE(v, l, max(0, z), c, flags)
    else if substr(x, m+1, 2) == 'E-' then
        return left(x,1)'0'c'-'left('', l-4, 9)
    else
        return left('', l, '*')
endProcedure fE

/*--- right or left with truncation ----------------------------------*/
fRigLeft: procedure expose m
parse arg s, len, flags
    if length(s) = len then
        return s
    else if pos('-', flags) > 0 | length(s) > len then
        return left(s, len)
    else
        return right(s, len)
endProcedure fRigLefPad

/*--- right or left pad without truncation --------------------------*/
fRigLefPad: procedure expose m
parse arg s, len, flags
    if pos('-', flags) > 0 then
        if length(strip(s, 't')) >= len then
            return strip(s, 't')
        else
            return left(s, len)
    else
        if length(strip(s, 'l')) >= len then
            return strip(s, 'l')
        else
            return right(s, len)
endProcedure fRigLefPad

/*--- generate timestamp formats: from format c to format d ----------*/
fTstGen: procedure expose m.
parse arg c 2 d, s
    cd = c || d
    if symbol('m.f_tstFo.c') \== 'VAR' ,
         | symbol('m.f_tstFo.d') \== 'VAR' then do
         if m.f_tstIni == 1 then
             call err "bad timestamp from or to format '"cd"'"
        m.f_tstIni = 1
        a = 'F_TSTFO.'
                      /* Y: A = 2010, M:B=Januar,
                         H=Stunde A=0 B=10 C=20
                         jkl = julian day
                         yyyy-mm-dd-hh.mm.ss.ffffff    */
        call mPut a'S', 'yz34-56-78-hi.mn.st.abcdef'
        call mPut a's', 'yz34-56-78-hi.mn.st'
        call mPut a' '
        call mPut a'D', 'yz345678'
        call mPut a'd',   '345678'
        call mPut a't',            'hi.mn.st'
        call mPut a'T',            'hi:mn:st.abcdef'
        call mPut a'E', '78.56.yz34'
        call mPut a'e', '78.56.34'
        call mPut a'Y',      'YM78'
        call mPut a'M',    'M78himns'
        call mPut a'A',    'A8himnst'
        call mPut a'H',           'Himnst'
        call mPut a'n', 'yz345678 hi:mn:st'
        call mPut a'N', 'yz345678 hi:mn:st.abcdef'
        return fTstGen(cd, s)
        end

    if pos('(', s) > 0 | length(s) > 20 then
        return "fImm('F_GEN."fCache('%t'cd, '%t'cd)"'," s")"
    if c == ' ' then do
        if pos(d, 'SN') > 0 then
            o = "Ndate('S') time('L')"
        else if pos(d, 'sMAn ') > 0 then
            o = "ndate('S') time()"
        else if pos(d, 'DdEeY') > 0 then
            o = "Ddate('S')"
        else if pos(d, 'tH') > 0 then
            o = "ttime('')"
        else if pos(d, 'T') > 0 then
            o = "Ttime('L')"
        else
            call err 'fTstGen implement d='d
        parse var o o1 2 o2
        return "fImm('F_GEN."fCache('%t'o1 || d, '%t'o1 || d)"',"o2")"
        end
    f = m.f_tstFo.c
    if d = ' ' then
        d = 's'
    t = m.f_tstFo.d
    if f == t then
        return s
    r = "translate("quote(t, "'")"," s "," quote(f, "'")")"

    if pos('yz34', t) > 0 & pos('yz34', f) < 1 then do
        if pos('34', f) > 0 then
            r = "overlay('20'," r"," pos('yz', t)")"
        else if pos('Y', f) > 0 then
            r = fTstGY2Year(r, "substr("s"," pos('Y', f)", 1)", 4,
                , pos('yz34', t))
        else
            r = "overlay('0000'," r"," pos('yz34', t)")"
        end
    else if pos('34', t) > 0 & pos('34', f) < 1 then do
        if pos('Y', f) > 0 then
            r = fTstGY2Year(r, "substr("s"," pos('Y', f)", 1)", 2,
                   , pos('34', t))
        else
            r = "overlay('00'," r"," pos('34', t)")"
        end
    if pos('Y', t) > 0 & pos('Y', f) < 1 then do
        if pos('yz34', f) > 0 then
            r = fTstGYear2Y(r, "substr("s"," pos('yz34', f)", 4)" ,
                   , pos('Y', t))
        else if pos('34', f) > 0 then
            r = fTstGYear2Y(r, "substr("s"," pos('34', f)", 2)" ,
                   , pos('Y', t))
        else
            r = "overlay('?'," r"," pos('Y', t)")"
        end
    if pos('M', t) > 0 & pos('M', f) < 1 then do
        if pos('56', f) > 0 then
            r = fTstG02A(r, "substr("s"," pos('56', f)", 2)" ,
                   , pos('M', t))
        else
            r  = "overlay('?'," r"," pos('M', t)")"
        end
    if pos('56', t) > 0 & pos('56', f) < 1 then
        if pos('M', f) > 0 then
            r = fTstGA2N2(r, "substr("s"," pos('M', f)", 1)" , "-1",
                   , pos('56', t))
        else
            r = "overlay('01'," r"," pos('56', t)")"
    if pos('78', t) > 0 & pos('78', f) < 1 then
        r = "overlay('01'," r"," pos('78', t)")"
    if pos('A8', t) > 0 & pos('A8', f) < 1 then
        if pos('78', f) > 0 then
            r = fTstG02A(r, "substr("s"," pos('78', f)", 1)" ,
                   , pos('A8', t))
        else
            r = "overlay('A1'," r"," pos('A8', t)")"
    if pos('hi', t) > 0 & pos('hi', f) < 1 then do
        if pos('H', f) > 0 then
            r = fTstGA2N1(r, "substr("s"," pos('H', f)", 1)" , "-1",
                   , pos('hi', t))
        else
            r = "overlay('00'," r"," pos('hi', t)")"
        end
    if pos('i', t) > 0 & pos('i', f) < 1 then
        r = "overlay('0'," r"," pos('i', t)")"
    if pos('H', t) > 0 & pos('H', f) < 1 then do
        if pos('hi', f) > 0 then
            r = fTstG02A(r, "substr("s"," pos('hi', f)", 1)" ,
                   , pos('H', t))
        else
            r  = "overlay('A'," r"," pos('H', t)")"
        end
    if pos('mn', t) > 0 & pos('mn', f) < 1 then
        r = "overlay('00'," r"," pos('mn', t)")"
    if pos('st', t) > 0 then do
        if pos('st', f) < 1 then
            if pos('s', f) > 0 then
                r = "overlay('0'," r"," pos('t', t)")"
            else
                r = "overlay('00'," r"," pos('st', t)")"
        end
    else if pos('s', t) > 0 & pos('s', f) < 1 then
        r = "overlay('0'," r"," pos('s', t)")"
    if pos('abcdef', t) > 0 & pos('abcdef', f) < 1 then
        r = "overlay('000000'," r"," pos('abcdef', t)")"
    return r
endProcedure fTstGen

/*--- from Number overlay Character A=0, B=1 etc.. ------------------*/
fTstG02A: procedure expose m.
parse arg r, n, tx
     return "overlay(substr(m.ut_alfUc," n "+ 1, 1)," r"," tx")"

/*--- from year overlay Y-Character  A=2010 etc..--------------------*/
fTstGYear2Y: procedure expose m.
parse arg r, y, tx
     return fTstG02A(r, "("y "+ 10) // 20", tx)

/*--- from character A=0 overlay 1 digit number ---------------------*/
fTstGA2N1: procedure expose m.
parse arg r, y, dlt, tx
     return "overlay(right(pos("y", m.ut_alfUc)" dlt", 1)," r"," tx")"

/*--- from character A=0 overlay 2 digit number ---------------------*/
fTstGA2N2: procedure expose m.
parse arg r, y, dlt, tx
     return "overlay(right('00' || (pos("y", m.ut_alfUc)" dlt"), 2)",
                    "," r"," tx")"

/*--- from Y yearCharacter overlay 2 or 4 digit year ----------------*/
fTstGY2Year: procedure expose m.
parse arg r, y, len, tx
     if m.f_tstY2ini \== 1 then do  /* generate lookup table */
          m.f_tstY2ini = 1
          n = left(f('%t  '), 4)
          do i = n-15 to n+4   /* 20 values: from -15 years to + 4 */
              j = left(f('%tDY', i'0101'), 1)
              m.f_tstY24.j = i
              m.f_tstY22.j = right(i, 2)
           /* say j m.f_tstY22.j m.f_tstY24.j  */
              end
          end
     if wordPos(len, 2 4) < 1 then
         call err "fTstGYear2Y bad len" len
     return "overlay(mGet('F_TSTY2"len".'"y")," r"," tx")"
endProcedure fTstGY2Year

/* copy f end   *******************************************************/
/* copy err begin *** errorhandling, messages, help    ****************/
errIni: procedure expose m.
    if m.err.ini == 1 then
        return
    call utIni
    m.err.ini     = 1
    m.err.handler = ''
    m.err.cleanup = ';'
    m.err.opt     = ''
    parse source m.err.os .
    m.err.ispf    = 0
    m.err.screen  = 0
    if m.err.os \== 'LINUX' then do
        address tso 'profile MsgId'   /* brauchen wir in tsoAlloc| */
        if sysVar('sysISPF') = 'ACTIVE' then do
            m.err.ispf = 1
            address ispExec 'vget zScreen shared'
            m.err.screen = zScreen
            end
        end
    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 & m.err.ispf then
        address ispExec '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 stackHistory
    if ggOpt == '' & m.err.handler \== '' then
        interpret m.err.handler
    call errSay 'f}'ggTxt
    call errCleanup
    if ggOpt == '' then
        ggOpt = m.err.opt
    upper ggOpt
    if pos('T', ggOpt) > 0  then do
        trace ?r
        say 'trace ?r in err'
        end
    if pos('H', ggOpt) > 0 & m.err.eCat == 'f' then
        x = show + stackHistory + by + bad + arithmetic + conversion
    call errSay ' }errorhandler exiting with exit(12)'
    exit errSetRc(12)
endSubroutine err

/*--- set rc for ispf: -------------------------------------------------
    if a cmd is run by ispStart, its RC is ignored,
         but ISPF passes the value of the shared variable zIspfRc
         back as return code
----------------------------------------------------------------------*/
errSetRc: procedure expose m.
parse arg zIspfRc
    if m.err.ispf then
        address ispExec vput 'zIspfRc' shared
    return zIspfRc
endProcedure errSetRc

/*--- error routine: user message cleanup exit -----------------------*/
errAddCleanup: procedure expose m.
parse arg code
    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
    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
    errCleanup = m.err.cleanup
    if errCleanup <> ';' then do
        m.err.cleanup = ';'
        say 'err cleanup begin' errCleanup
        interpret errCleanup
        say 'err cleanup end' errCleanup
        end
    if symbol('m.tso_ddAll') == 'VAR' then
        call tsoFree m.tso_ddAll, 1
    return
endProcedure errCleanup

/*--- say an errorMessage msg with pref and linesplits --------------*/
errSay: procedure expose m.
parse arg msg
    return saySt(errMsg(msg))

/*--- prefix an errormessage with pref,
          split it into lines at \n to stem m.err -------------------*/
errMsg: procedure expose m.
parse arg msg
    m.err.eCat = 'f'
    do while substr(msg, 2, 1) == '}'
        parse var msg m.err.eCat '}' msg
        end
    res = msg
    if m.err.eCat <> '' then do
       pTxt = ',error,fatal error,input error,s-}scanErr,warning,'
 /*    pTxt = ',error,fatal error,input error,syntax error,warning,' */
       px = pos(','m.err.eCat, pTxt)
       if px < 1 then do
           m.err.eCat = 'f'
           px = pos(','m.err.eCat, pTxt)
           end
       res = substr(pTxt, px+1, pos(',', pTxt, px+2)-px-1)
       if substr(res, 3, 1) == '}' then
           parse var res 2 opt 3 br 4 res
       if opt == '-' then
           res = res msg
       else do
           parse source . . s3 .              /* current rexx */
           res = res 'in' s3':' msg
           end
       end
    return splitNl(err, res)           /* split lines at \n */
endProcedure errMsg

splitNL: procedure expose m.
parse arg st, msg
    bx = 1
    do lx=1 to 20
        ex = pos('\n', msg, bx)
        if ex < bx then
            leave
        m.st.lx = substr(msg, bx, ex-bx)
        bx = ex+2
        end
    m.st.lx = substr(msg, bx)
    m.st.0 = lx
    return st
endProcedure splitNL

/*--- say (part of) the lines of a stem ----------------------------*/
saySt: procedure expose m.
parse arg st, fx, tx
    do lx=word(fx 1, 1) to word(tx m.st.0, 1)
        say m.st.lx
        end
    return st
endProcedure saySt

/*--- out (part of) the lines of a stem ----------------------------*/
outSt: procedure expose m.
parse arg st, fx, tx
    do lx=word(fx 1, 1) to word(tx m.st.0, 1)
        call out m.st.lx
        end
    return st
endProcedure outSt

/*--- say a trace message if m.trace is set --------------------------*/
debug: procedure expose m.
parse arg msg
    if m.debug == 1 then
        say 'debug:' msg
    return
endProcedure debug

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

/*--- assert that the passed rexx expression evaluates to true -------*/
assert:
    interpret 'if ('arg(1)') == 1 then return 1'
    interpret 'call err' quote('assert failed' arg(1) ':' arg(2) '==>'),
                        arg(2)
endProcedure assert

/*--- abend with Message after displaying help -----------------------*/
errHelp: procedure expose m.
parse arg msg
    call errSay 'i}'msg
    call help 0
    call err 'i}'msg
endProcedure errHelp

/*--- display the first comment block of the source as help ----------*/
help: procedure expose m.
parse arg doClear
    if doClear \== 0 then
        address tso 'clear'
    parse source . . s3 .
    say right(' help for rexx' s3, 72, '*')
    do lx=1 while pos('/*', sourceLine(lx)) < 1
        if lx > 10 then
            return err('initial commentblock not found for help')
        end
    doInc = 1
    ho = m.err.helpOpt
    do lx=lx+1 to sourceline() while pos('*/', sourceline(lx)) = 0
        li = strip(sourceLine(lx), 't')
        cx = lastPos('{', li)
        if cx > 0 then do
            if length(ho) = 1 then
                doInc = cx = length(li) | pos(ho, li, cx+1) > 0
            li = left(li, cx-1)
            end
        if doInc then
            say li
        end
    say right(' end help for rexx' s3, 72, '*')
    return 4
endProcedure help

/* copy err end   *****************************************************/
/* copy ut begin  *****************************************************/
utIni: procedure expose m.
    if m.ut_ini == 1 then
        return
    m.ut_ini = 1
    m.ut_digits = '0123456789'
    m.ut_alfLC  = 'abcdefghijklmnopqrstuvwxyz'
    m.ut_alfUC  = translate(m.ut_alfLc)
    m.ut_Alfa   = m.ut_alfLc || m.ut_alfUC
    m.ut_alfNum = m.ut_alfa || m.ut_digits
    m.ut_alfDot = m.ut_alfNum || '.'
    m.ut_alfId  = m.ut_alfNum'_' /* avoid rexx allowed @ # $ ¬ . | ? */
    m.ut_alfIdN1 = m.ut_digits    /* not as first character */
    m.ut_rxId   = m.ut_AlfNum'@#$?' /* charset puff mit ¬*/
    m.ut_rxDot  = '.'m.ut_rxId
    m.ut_rxN1   = '.0123456789'
    m.ut_alfPrint = m.ut_alfNum'+-*/=()¢!{}<> .:,;?|''"%&#@$£\_'
    return
endProcedure utIni
/*--- 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 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 ------------------------------*/
utTime: procedure expose m.
    return time() 'ela='time('E') 'cpu='sysvar('syscpu'),
            'su='sysvar('syssrv')

/*--- 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

/*--- left without truncation ----------------------------------------*/
lefPad: procedure expose m
parse arg s, len
    if length(strip(s, 't')) >= len then
        return strip(s, 't')
    return left(s, len)
endProcedure lefPad

/*--- right without truncation ---------------------------------------*/
rigPad: procedure expose m
parse arg s, len
    if length(strip(s, 'l')) >= len then
        return strip(s, 'l')
    return right(s, len)
endProcedure rigPad

/*--- 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

/*--- translate to lowercase -----------------------------------------*/
ut2lc: procedure expose m.
parse arg s
    return translate(s, m.ut_alfLc, m.ut_alfUc)

/*--- verify an id ---------------------------------------------------*/
verifId: procedure expose m.
    parse arg src, extra
    if pos(left(src, 1), m.ut_alfIdN1) > 0 then
        return 1
    else
        return verify(src, m.ut_alfId || extra, 'n')

/*--- 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
    do ax = 2 by 2 to arg()
        src = repAl2(src, src, arg(ax), arg(ax+1))
        end
    return src
endProcedure repAll

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

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

utc2d: procedure expose m.
parse arg ch
    cx = length(ch) // 3
    if cx = 0 then
        cx = 3
    res = c2d(left(ch, cx))
    do cx=cx+1 by 3 to length(ch)
        res = res * 16777216 + c2d(substr(ch, cx, 3))
        end
    return res

utInter: procedure expose m.
    interpret arg(1)
    return
endProcedure utInter

/* copy ut end ********************************************************/
/* copy tstAll begin  *************************************************/
/*----------- neu, noch versorgen |||||-------------------------------*/
tstRts: procedure expose m.
    call wshIni
    call sqlConnect dbaf
    call sqlQuery 3, "select * from sysibm.sysTableSpaceSTats" ,
                    "where dbName = 'MF01A1A' and name = 'A150A'",
                    "order by partition  asc"
    do while sqlFetch(3, rr)
        say f('@.DBNAME%-8C.@NAME%-8C @PARTITION %4C' ,rr)
        end
    call sqlDisconnect
endProcedure tstRts

tstWiki:
    call mapReset docs, 'k'
    call addFiles docs, 'n', '/media/wkData/literature/notes'
    call addFiles docs, 'd', '/media/wkData/literature/docs'

    in = jOpen(file('wiki.old'), '<')
    out = jOpen(file('wiki.new'), '>')
    abc = '(:abc: %l%'
    do cx=1 to length(m.ut_alfLC)
        c1 = substr(m.ut_alfLC, cx, 1)
        abc = abc '¢¢#'c1 '|' c1'!!'
        end
    call jWrite out, abc ':)'
    inTxt = 0
    li = m.i
    do lx=1 while jRead(in, i)
        if 0 then
            say length(m.i) m.i
        if m.i = '' then
            iterate
        li = m.i
        do forever
            bx = pos('¢=', li)
            if bx < 1 then
                leave
            ex = pos('=!', li)
            if ex <= bx then
                call err '=! before ¢= in' lx li
            li = left(li, bx-1)substr(li,bx+2, ex-bx-2)substr(li,ex+2)
            end
        li = strip(li)
        if abbrev(li, '|') then do
            w = word(substr(li, 2), 1)
            call jWrite out, '¢¢#'w'!! {$:abc}'
            call jWrite out, '|||' substr(li, 2)
            inTxt=1
            iterate
            end
        if \ inTxt then do
            call jWrite out, li
            iterate
            end
        if \ (abbrev(li, '->') | abbrev(li, '#') ,
                | abbrev(li, '¢')) then do
            call jWrite out, '-<' li
            iterate
            end
        cx = 1
        if substr(li, cx, 2) == '->' then
            cx = verify(li, ' ', 'n', cx+2)
        hasCross = substr(li, cx, 1) == '#'
        if hasCross then
            cx = verify(li, ' ', 'n', cx+1)
        ex = verify(li, '!:\, ', 'm', cx)
        ex = ex - (substr(li, ex, 1) \== '!')
        hasBr = substr(li, cx, 1) == '¢'
        if \ hasBr then
            w = substr(li, cx, ex+1-cx)
        else if substr(li, ex, 1) == '!' then
            w = substr(li, cx+1, ex-1-cx)
        else
            call err 'br not closed' substr(w, cx+1, ex-1-cx) 'in' lx li
        hasPdf = right(w, 4) == '.pdf'
        if hasPdf then
            w = left(w, length(w)-4)
        if verify(w, '#?', 'm') > 0 then do
            w = translate(w, '__', '#?')
            say '*** changing to' w 'in' lx li
            end
        o = '-< {def+'w'}'
        o = '-< ¢¢'w'!!'
        k = translate(w)
        if k.k == 1 then
            say '*** doppelter key' k 'in:' lx left(li,80)
        k.k = 1
        dT = ''
        if mapHasKey(docs, k) then do
            parse value mapGet(docs, k) with dT dC dN
            call mapPut docs, k, dT (dC+1) dN
            do tx=1 to length(dT)
                t1 = substr(dT, tx, 1)
                o = o '¢¢Lit'translate(t1)':'word(dN, tx) '|' t1 '!!'
                end
            end
        qSeq = 'nd'
        qq = left(qSeq, 1)
        qx = 0
        do forever
            qx = pos('@'qq, li, qx+1)
            if qx < 1 then do
                qq = substr(qSeq, 1 + pos(qq, qSeq), 1)
                qx=0
                if qq = '' then
                    leave
                else
                    iterate
                end
            if pos(qq, dT) < 1 then do
                say '*** @'qq 'document not found:' lx li
                iterate
                end
            do qb = qx-1 by -1 while substr(li, qb, 1) == ' '
                end
            do qe = qx+2 by 1 while substr(li, qe, 1) == ' '
                end
            if substr(li, qb, 1) == '.' & substr(li, qe, 1) == '.' then
                li = left(li, qb)substr(li, qe+1)
            else
                li = left(li, qb) substr(li, qe)
            end
        o = o':' strip(substr(li, ex+1+(substr(li,ex+1,1)==':')))
        if 0 then say left(li, 30) '==>' left(o, 30)
        call jWrite out, o
        end
    dk = mapKeys(docs)
    do dx=1 to m.dk.0
        parse value mapGet(docs, m.dk.dx) with dT dC dN
        if dC < 1 then
            say '*** document not used:' dT dC dn
        end
    call jClose in
    call jClose out
    return
endProcedure tstWiki

addFiles: procedure expose m.
parse arg m, ty, file
    fl = jOpen(fileList(file(file)), '<')
    do while jRead(fl, fi1)
        nm = substr(m.fi1, lastPos('/', m.fi1)+1)
        k = translate(left(nm, pos('.', nm)-1))
        if \ mapHasKey(m, k) then do
            call mapAdd m, k, ty 0 nm
            end
        else do
            parse value mapGet(m, k) with dT dC dN
            call mapPut m, k, dT || ty 0 dN nm
            end
        end
    call jClose fl
    return
endProcedure addFiles

tstAll: procedure expose m.
    say 'tstAll ws2 25.2.13...............'
    call tstBase
    call tstComp
    call tstDiv
    if m.err.os = 'TSO' then do
        call tstZos
        call tstTut0
        end
    return 0
endProcedure tstAll

/****** tstZos ********************************************************/
tstZOs:
    call tstTime
    call tstII
    call sqlIni
    call tstSqlRx
    call tstSql
    call tstSqlCsm
    call scanIni
    call tstSqlC
    call tstSqlCsv
    call tstSqlRxUpd
    call tstSqlUpd
    call tstSqlUpdPre
    call tstSqlE
    call tstSqlB
    call tstSqlO1
    call tstSqlO2
    call tstSqlStmt
    call tstSqlStmts
    call tstSqlUpdComLoop
    call tstSqls1
    call tstSqlO
    call tstSqlFTab
    call tstsql4obj
    call tstTotal
    return
endProcedure tstZOs

tstWshBatch:
    call adrTso 'alloc dd(WSH) shr dsn(WK.TEXT(WSHBATCH))'
    call wshBatch
    return adrTso('free dd(WSH)')

tstLmdTiming:
parse arg lev
    say timing() lev
    call lmdBegin abc, lev
    c = 0
    do while lmdNext(abc, st.)
        c = c + st.0
        end
    call lmdEnd   abc
    say timing() lev 'with group - without reading' c
    call adrIsp 'lmdinit listid(lmdId) level('lev')'
    do c=0 while adrIsp('lmdlist listid(&lmdId) dataset(abc)', 8) = 0
        end
    call adrIsp 'lmdfree listid(&lmdId)'
    say timing() lev 'with list' c
    return
endProcedure lmdTiming

tstCsi: procedure expose m.
    if 0 then do
        call lmd 'A540769.*K'
        call tstCsiCla 'A540769.WK.REXX'
        call tstCsiCla 'A540769.AAA.DATASETS'
        call tstCsiCla 'A540769.RRR.DATASETS'
        end
    if 0 then do
        call tstCsiOpNx 'A540769.WK.*E*'
        call tstCsiOpNx 'A540769.AAA.DATASETS'
        call tstCsiOpNx 'A540769.RRR.DATASETS'
        end
    if 1 then do
        call tstCsiNxCl 'A540769.WK.**'
        call tstCsiNxCl 'DBTF.M*.**'
        call tstCsiNxCl 'DBTF.BE*.**'
        end
    return
tstCsi: procedure expose m.
 /* call lmd 'A540769.*K' */
    call tstCsi1   'A540769.WK.REXX'
    call tstCsi1   'A540769.AAA.DATASETS'
    call tstCsi1   'A540769.RRR.DATASETS'
    return

tstCsiCla:
parse arg ds
    say ds '-->' csiCla(ds)
    return

tstCsiOpNx: procedure expose m.
parse arg ds
    m = 'NUE123'
    s = 'res89'
    flds = 'devtyp volser mgmtclas comudsiz NOBYTTRK UDATASIZ HARBA'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    do while csiNext(m, s)
        say m.s     'dev' c2x(m.s.devTyp) ,
                    'vol' m.s.volSer 'cla' m.s.mgmtclas,
                    'comuDsiz' m.s.comuDsiz 'noBytTrk' m.s.noBytTrk,
                    'udatasiz' c2x(m.s.udatasiz) ,
                    'harba' c2x(m.s.harba)
        end
    return

tstCsiNxCl: procedure expose m.
parse arg ds
    m = 'ABC123'
    s = 'efg89'
    flds = 'devtyp volser mgmtclas'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    say timing() 'begin'
    do i=1 while csiNext(m, s)
        nn = csiArcTape(m.s.volser, m.s.mgmtClas, m.s.devtyp, m.s)
/*      oo = csiCla(strip(m.s))
        if oo <> nn then
            say nn '<>' oo m.s
 */     if i // 1000 = 0 then
            say timing() i nn m.s
        end
    say timing() (i-1) nn m.s
    return
endProcedure tstCsiNxCl

listCatClass: procedure expose m.
parse upper arg dsn
    rt = adrTso("listcat volume entry('"dsn"')", 4)
    /* say 'listct rc =' rt 'lines' m.tso_trap.0 */
    cl = ''
    vo = ''
    if word(m.tso_trap.1, 3) \== dsn then
        call out 'err ??? for dsn' dsn 'bad first line' m.tso_trap.1
    else if pos('NOT FOUND', m.tso_trap.1) > 0 then
        return 'notFound'
    else if word(m.tso_trap.1, 1)\== 'NONVSAM' then
        call out 'err ??? for dsn' dsn 'bad first line' m.tso_trap.1
    do tx=2 to m.tso_trap.0 while vo = '' ,
              & left(m.tso_trap.tx, 1) = ' '
     /* say m.tso_trap.tx */
        p = pos('MANAGEMENTCLASS-', m.tso_trap.tx)
        if p > 0 then
            vo = strip(word(substr(m.tso_trap.tx, p+16), 1), 'l', '-')
        p = pos('VOLSER--', m.tso_trap.tx)
        if p > 0 then
            vo = strip(word(substr(m.tso_trap.tx, p+6), 1), 'l', '-')
        p = pos('DEVCLASS--', m.tso_trap.tx)
            dt = strip(word(substr(m.tso_trap.tx, p+8), 1), 'l', '-')
        end
   /*  say 'lc' cl 'vo' vo 'dt' dt 'dsn' dsn */
    if vo = '' then
        call out '??? err no volume for dsn' dsn
    else if vo = 'ARCIVE' then
        res =  'arcive'
    else if cl = '' then
        res = 'tape'
    else
        res = cl
    if   abbrev(res, 'ar') \= abbrev(dt, "X'0") ,
       | abbrev(res, 'ta') \= abbrev(dt, "X'7") ,
       | (left(res, 1) >= 'A') \= abbrev(dt, "X'3") then
       call out 'err ??? mismatch cl' cl 'vo' vo 'dt' dt 'dsn' dsn
    return res
endProcedure listCatClass

/****** tstDiv ********************************************************/
tstDiv:
    call tstSort
    call tstMat
    call tstMatch
    call tstTotal
    return
endProcedure tstDiv


tstSort: procedure expose m.
    call tstSortComp
    call tstSortComp '<<='
    call tstSortComp 'm.aLe <<= m.aRi'
    call tstSortComp 'if 1 then cmp = m.aLe <<= m.aRi; else call err sd'
    return
endProcedure tstSort

tstSortComp: procedure expose m.
parse arg cmp
/*
$=/tstSort/
    ### start tst tstSort #############################################
    sort 29  c ACHT DREI DREIZEHN EINS ELF FUENF M.I.25 M.I.26 M.I.27 M+
    ..I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI ZWOELF 0 1 1 1 2 2+
    . 3 3 4 4
    sort 22  c ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27 M.I.29 NEUN VIERZ+
    EHN ZEHN ZWOELF 0 1 1 1 2 2 3 3 4 4
    sort 15  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1 1 2 2 3 3 4 4
    sort 8  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1
    sort 1  M.I.29
$/tstSort/ */
/*
$=/tstSortAscii/
    ### start tst tstSortAscii ########################################
    sort 29  0 1 1 1 2 2 3 3 4 4 ACHT DREI DREIZEHN EINS ELF FUENF M.I.+
    25 M.I.26 M.I.27 M.I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI Z+
    WOELF c
    sort 22  0 1 1 1 2 2 3 3 4 4 ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27+
    . M.I.29 NEUN VIERZEHN ZEHN ZWOELF c
    sort 15  0 1 1 1 2 2 3 3 4 4 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 8  0 1 1 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 1  M.I.29
$/tstSortAscii/ */
    say '### start with comparator' cmp '###'
    if m.err.os == 'LINUX' then
        call tst t, "tstSortAscii"
    else
        call tst t, "tstSort"
    call mAdd mCut(i, 0), eins, zwei, drei, vier, fuenf, sechs,
        ,sieben, acht, neun, zehn, elf, zwoelf, dreizehn, vierzehn
    call mAdd i, 1, 2, 3, 4, 4, 3, 2, 1, 0, 1,
        , 'M.I.25', 'M.I.26', 'M.I.27', 'c', 'M.I.29'

    do yy = m.i.0 by -1 to 1
        do x = 0 to yy
            m.i.0 = x
            call sort i, o, cmp
            m = ''
            la = ''
            if x <> m.o.0 then
                call err 'size mismatch' x '<>' m.o.0
            do y=1 to m.o.0
                m = m m.o.y
                if \ (la << m.o.y) then
                    call err 'sort mismatch' yy x y '\' la '<<' m.o.y
                end
            end
        if yy // 7 = 1 then
           call tstOut t, 'sort' yy m
        do x = 2 to yy
            x1 = x-1
            m.i.x1 = m.i.x
            end
        end
    call tstEnd t
    return
endProcedure tstSort
tstMatch: procedure expose m.
/*
$=/tstMatch/
    ### start tst tstMatch ############################################
    match(eins, e?n*) 1 1 2,i,s trans(E?N*) EiNs
    match(eins, eins) 1 1 0 trans(EINS) EINS
    match(e1nss, e?n*) 1 1 2,1,ss trans(?*) 1ss
    match(eiinss, e?n*) 0 0 -9
    match(einss, e?n *) 0 0 -9
    match(ein s, e?n *) 1 1 2,i,s trans(E?N *) EiN s
    match(ein abss  , ?i*b*) 1 1 3,e,n a,ss   trans(?I*B*) eIn aBss  .
    match(ein abss wie gehtsssxdirx und auch , ) 0 0 -9
    match(ies000, *000) 1 1 1,ies trans(*000) ies000
    match(xx0x0000, *000) 1 1 1,xx0x0 trans(*000) xx0x0000
    match(000x00000xx, 000*) 1 1 1,x00000xx trans(000*) 000x00000xx
    match(000xx, *0*) 1 1 2,00,xx trans(ab*cd*ef) ab00cdxxef
$/tstMatch/ */
    call tst t, "tstMatch"
    call tstOut t, matchTest1('eins', 'e?n*'                        )
    call tstOut t, matchTest1('eins', 'eins'                        )
    call tstOut t, matchTest1('e1nss', 'e?n*', '?*'                 )
    call tstOut t, matchTest1('eiinss', 'e?n*'                      )
    call tstOut t, matchTest1('einss', 'e?n *'                      )
    call tstOut t, matchTest1('ein s', 'e?n *'                      )
    call tstOut t, matchTest1('ein abss  ', '?i*b*'                 )
    call tstOut t, matchTest1('ein abss wie gehtsssxdirx und auch ' )
    call tstOut t, matchTest1('ies000', '*000'                      )
    call tstOut t, matchTest1('xx0x0000', '*000'                    )
    call tstOut t, matchTest1('000x00000xx', '000*'                 )
    call tstOut t, matchTest1('000xx', '*0*', 'ab*cd*ef'            )
    call tstEnd t
return

matchTest1:
parse arg w, m, m2
    r = 'match('w',' m')' match(w, m) match(w, m, vv) m.vv.0
    do x=1 to m.vv.0
        r = r','m.vv.x
        end
    if m2 = '' then
        m2 = translate(m)
    if m.vv.0 >= 0 then
        r = r 'trans('m2')' matchTrans(m2, vv)
    return r
endProcedure matchTest1

tstIntRdr: procedure expose m.
    i.1 = "//A540769J JOB (CP00,KE50),'DB2 REO',"
    i.2 = "//         MSGCLASS=T,TIME=1440,"
    i.3 = "//         NOTIFY=&SYSUID,REGION=0M,SCHENV=DB2"
    i.4 = "//*MAIN CLASS=LOG"
    i.5 = "//S1       EXEC PGM=IEFBR14"
    call writeDsn 'RR2/intRdr', i., 5, 1
    return
endProcedure tstIntRdr

tstII: procedure expose m.
/*
$=/tstII/
    ### start tst tstII ###############################################
    iiRz2C(RZ2)       2
    *** err: no rz=R?Y in ii II_RZ2C
    iiRz2C(R?Y)       0
    iiRz2C(RZY)       Y
    iiDbSys2C(de0G)   E
    *** err: no dbSys=D??? in ii II_DB2C
    iiDbSys2C(d???)   0
    iiDbSys2C(DBOF)   F
    iiSys2RZ(S27)     RZ2
    iiMbr2DbSys(DBP5) DVBP
    ii_rz             RZ1 RZ2 RR2 RZ4 RZX RZY RZZ
    ii_dbSys.rzx      DE0G DEVG DPXG DX0G
$/tstII/
*/
    call tst t, 'tstII'
    call tstOut t, 'iiRz2C(RZ2)      '  iiRz2C(RZ2)
    call tstOut t, 'iiRz2C(R?Y)      '  iiRz2C(R?Y)
    call tstOut t, 'iiRz2C(RZY)      '  iiRz2C(RZY)
    call tstOut t, 'iiDbSys2C(de0G)  '  iiDbSys2C('de0G')
    call tstOut t, 'iiDbSys2C(d???)  '  iiDbSys2C('d???')
    call tstOut t, 'iiDbSys2C(DBOF)  '  iiDbSys2C('DBOF')
    call tstOut t, 'iiSys2RZ(S27)    '  iiSys2RZ(S27)
    call tstOut t, 'iiMbr2DbSys(DBP5)'  iiMbr2DbSys(DBP5)
    call tstOut t, 'ii_rz            '  m.ii_rz
    call tstOut t, 'ii_dbSys.rzx     '  m.ii_dbSys.rzx
    call tstEnd t
    return
endProcedure tstII

tstTime: procedure
/*         Winterzeit dez 2011
$=/tstTime/
    ### start tst tstTime #############################################
    Lrsn2Lzt(C5E963363741) 2010-05-01-12.34.55.789008
    Achtung: output ist abhaengig von Winter/Sommerzeit und LeapSecs
    timeZone 7200.00000 leapSecs 25.0000000
    2jul(2011-03-31-14.35.01.234567)  11090
    Lrsn2Gmt(C5E963363741) 2010-05-01-10.35.20.789008
    Lrsn2Lzt(C5E963363741) 2010-05-01-12.34.55.789008
    gmt2Lrsn(2011-03-31-14.35.01.234567) C78D87B86E38
    lzt2Lrsn(2011-03-31-14.35.01.234567) C78D6CFDD13C
    Lrsn2Gmt(gmt2Lrsn(2011-03-31-14.35.01.234567) 2011-03-31-14.35.01.2+
    34560
    gmt2Lrsn(Lrsn2Gmt(C5E963363741) C5E963363741
    Lrsn2LZt(LZt2Lrsn(2011-03-31-14.35.01.234567) 2011-03-31-14.35.01.2+
    34560
    LZt2Stc(Lrsn2LZt(C5E963363741)( C5E963363741
$/tstTime/
*/
    call jIni
    call tst t, 'tstTime'
    t1 = '2011-03-31-14.35.01.234567'
    s1 = 'C5E963363741'
    call out 'Lrsn2Lzt('s1')' timeLrsn2Lzt(s1)
    call out ,
     'Achtung: output ist abhaengig von Winter/Sommerzeit und LeapSecs'
    call out 'timeZone' m.timeZone * m.timeStckUnit ,
             'leapSecs' m.timeLeap * m.timeStckUnit
    call timeReadCvt 1
    call out '2jul('t1') ' time2jul(t1)
    call out 'Lrsn2Gmt('s1')' timeLrsn2Gmt(s1)
    call out 'Lrsn2Lzt('s1')' timeLrsn2Lzt(s1)
    call out 'gmt2Lrsn('t1')' timeGmt2Lrsn(t1)
    call out 'lzt2Lrsn('t1')' timeLzt2Lrsn(t1)
    call out 'Lrsn2Gmt(gmt2Lrsn('t1')' timeLrsn2Gmt(timeGmt2Lrsn(t1))
    call out 'gmt2Lrsn(Lrsn2Gmt('s1')' timeGmt2Lrsn(timeLrsn2Gmt(s1))
    call out 'Lrsn2LZt(LZt2Lrsn('t1')' timeLrsn2LZt(timeLZt2Lrsn(t1))
    call out 'LZt2Stc(Lrsn2LZt('s1')(' timeLZt2Lrsn(timeLrsn2LZt(s1))
    call tstEnd t
    return
endProcedure tstTime
tstMat: procedure expose m.
/*
$=/tstMat/
    ### start tst tstMat ##############################################
    .   0 sqrt  0 isPrime 0 nxPrime    3 permut 1 > 1 2 3 4 5
    .   1 sqrt  1 isPrime 0 nxPrime    3 permut 2 > 2 1 3 4 5
    .   2 sqrt  1 isPrime 1 nxPrime    3 permut 3 > 1 3 2 4 5
    .   3 sqrt  1 isPrime 1 nxPrime    3 permut 3 > 2 3 1 4 5
    .   4 sqrt  2 isPrime 0 nxPrime    5 permut 3 > 3 2 1 4 5
    .   5 sqrt  2 isPrime 1 nxPrime    5 permut 3 > 3 1 2 4 5
    .   6 sqrt  2 isPrime 0 nxPrime    7 permut 4 > 1 2 4 3 5
    .   7 sqrt  2 isPrime 1 nxPrime    7 permut 4 > 2 1 4 3 5
    .   8 sqrt  2 isPrime 0 nxPrime   11 permut 4 > 1 3 4 2 5
    .   9 sqrt  3 isPrime 0 nxPrime   11 permut 4 > 2 3 4 1 5
    .  10 sqrt  3 isPrime 0 nxPrime   11 permut 4 > 3 2 4 1 5
    .  11 sqrt  3 isPrime 1 nxPrime   11 permut 4 > 3 1 4 2 5
    .  12 sqrt  3 isPrime 0 nxPrime   13 permut 4 > 1 4 3 2 5
    .  13 sqrt  3 isPrime 1 nxPrime   13 permut 4 > 2 4 3 1 5
    .  14 sqrt  3 isPrime 0 nxPrime   17 permut 4 > 1 4 2 3 5
    .  15 sqrt  3 isPrime 0 nxPrime   17 permut 4 > 2 4 1 3 5
    .  16 sqrt  4 isPrime 0 nxPrime   17 permut 4 > 3 4 1 2 5
    .  17 sqrt  4 isPrime 1 nxPrime   17 permut 4 > 3 4 2 1 5
    .  18 sqrt  4 isPrime 0 nxPrime   19 permut 4 > 4 2 3 1 5
$/tstMat/
$/tstMat/
*/
    call tst t, 'tstMat'
    q = 'tst_Mat'
    do qx=1 to 20
        m.q.qx = qx
        end
    do i=0 to 18
        call permut q, i
        call tstOut t, right(i,4) 'sqrt' right(sqrt(i), 2) ,
        'isPrime' isPrime(i) 'nxPrime' right(nxPrime(i), 4) ,
            'permut' m.q.0 '>' m.q.1 m.q.2 m.q.3 m.q.4 m.q.5
        end
    call tstEnd t
    return
endProcedure tstMat

/****** tstSql ********************************************************/
tstSqlUtils: procedure expose m.
    call sqlConnect 'DBAF'
    id = 'A540769.dsnUtils'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "TEMPLATE TCOPYD",
            "DSN('&SSID..&JO..&US..P&PART(2)..D&DATE(3)..T&TIME.')",
            "DATACLAS(NULL12) MGMTCLAS(COM#A011) STORCLAS(FAR$N)",
            "SPACE (150,3750) TRK UNCNT 59;",
         "listdef abc include tablespace DA540769.A002* partlevel;",
         "listdef mf  include tablespace MF01A1A.A110A partlevel;",
         "copy list abc copyddn(tcopyd) shrlevel change;"
    st = translate(st)
    call sqlExec "call SYSPROC.DSNUTILS ( :id, :rst,",
                   ":st,:retcode, :ANY" copies(',:e,:e,:z',12) ")"
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.DSNUTILS'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    exit
endProcedure tstSqlUtils

tstSqlStored: procedure expose m.
    call sqlConnect 'DBAF'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "abc"
    call sqlExec "call SYSPROC.COMMAND ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.COMMAND'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStored

tstSqlStoredWK: procedure expose m.
    call sqlConnect 'DBAF'
 /* st = "direct wkUtiSub"
    rst = 'NO'
    say 'before call st='st 'rst='rst
    call sqlExec "call A540769.WKUTILSUB ( :st, :rst)"
    say 'after call st='st 'rst='rst
 */ rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    prc = 'DB2UTIL.DB2UTIL'
    st = "DA540769.A2*" /*  DA540769.A1*" */
    say  "call" prc "("st", ...)"
    call sqlExec "call" prc "(:st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE' prc
 /* say 'results' results  */
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say ''
    say '***** utility output'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say '***** end output'
    call sqlDisconnect
    return 0
endProcedure tstSqlStoredWK

tstSqlStoredSQL1: procedure expose m.
    call sqlConnect 'DBIA'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "DA540769.A2* DA540769.A1*"
    call sqlExec "call A540769.WKSQL1 ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE A540769.WKUTIL'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStoredSQL1

tstSqlTriggerTiming:
    parse upper arg tb ni
    m.noInsert = ni == 0
    cnt = 200000
    if tb = '' then
        TB = 'GDB9998.TWK511TRI'
    call sqlConnect dbaf
    say timing() 'noInsert' m.noInsert 'tb' tb
    call sql2St 'select max(pri) MX from' tb, cc
    if m.cc.1.mx == m.sqlNull then
        m.cc.1.mx = 0
    von = m.cc.1.mx + 1
    bis = m.cc.1.mx + cnt
    say m.cc.0 'max' m.cc.1.mx 'von' von 'bis' bis 'count' cnt tb
    if right(tb, 2) = 'A1' then do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long, par)' ,
                   'values (?, ?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    else do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long)' ,
                   'values (?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    /*  call sqlLn 5,,, 'select * from' tb 'where pri >=' von */
    call sqlCommit
    say timing()
    call sqlDisconnect
    return
endProcedure tstSqlTriggerTiming

tstSqlRx: procedure expose m.
/*
$=/tstSqlRx/
    ### start tst tstSqlRx ############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s7 into :M.SQL.7.D from :src
    .    e 3: with into :M.SQL.7.D = M.SQL.7.D
    fetchA 1 ab=m.abcdef.123.AB=abc ef=efg
    fetchA 0 ab=m.abcdef.123.AB=abc ef=efg
    fetchB 1 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchB 0 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
    fetchBT 1 SYSTABLES
    fetchBT 0 SYSTABLES
    fetchBi 1 SYSINDEXES
    fetchBi 0 SYSINDEXES
$/tstSqlRx/ */
    call tst t, "tstSqlRx"
    call sqlRxConnect
    cx = 7
    call sqlRxQuery cx, 'select * from sysdummy'
    call sqlRxQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlRxFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    call sqlRxClose cx
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlRxQuery cx, sql, '?ab cd ?ef ?gh'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlRxFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    call sqlRxClose cx
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlRxQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlRxFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    call sqlRxClose cx
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call sqlQueryPrep cx, "select name" ,
                        "from sysibm.sysTables" ,
                        "where creator = 'SYSIBM' and name = ?",':m.nm'
    call sqlQueryArgs cx, 'SYSTABLES'
    call out 'fetchBT' sqlRxFetch(cx) m.nm
    call out 'fetchBT' sqlRxFetch(cx) m.nm
    call sqlRxClose cx
    call sqlQueryArgs cx, 'SYSINDEXES'
    call out 'fetchBi' sqlRxFetch(cx) m.nm
    call out 'fetchBi' sqlRxFetch(cx) m.nm
    call tstEnd t
    call sqlRxDisconnect
    return
endProcedure tstSqlRx

tstSql: procedure expose m.
/*
$=/tstSql/
    ### start tst tstSql ##############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s7 into :M.SQL.7.D from :src
    .    e 3: with into :M.SQL.7.D = M.SQL.7.D
    fetchA 1 ab=m.abcdef.123.AB=abc ef=efg
    fetchA 0 ab=m.abcdef.123.AB=abc ef=efg
    fetchB 1 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchB 0 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
    sql2St 1 st.0=1
    sql2St:1 a=a b=2 c=--- d=d
    sql2One a
    sql2One a=a b=2 c=--- d=d
    fetchBT 1 SYSTABLES
    fetchBT 0 SYSTABLES
    fetchBi 1 SYSINDEXES
    fetchBi 0 SYSINDEXES
$/tstSql/ */
    call tst t, "tstSql"
    call sqlConnect
    cx = 7
    call sqlQuery cx, 'select * from sysdummy'
    call sqlQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    call sqlClose cx
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlQuery cx, sql, '?ab cd ?ef ?gh'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    call sqlClose cx
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    call sqlClose cx
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call out 'sql2St' sql2St(sql, st) 'st.0='m.st.0
    do i=1 to m.st.0
        call out 'sql2St:'i ,
            'a='m.st.i.a 'b='m.st.i.b 'c='m.st.i.c 'd='m.st.i.d
        end
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    drop m.st.a m.st.b m.st.c m.st.d m.st.0
    call out 'sql2One' sql2One(sql, st)
    call out 'sql2One' ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
    drop m.st.a m.st.b m.st.c m.st.d m.st.0
    call sqlQueryPrep cx, "select name" ,
                        "from sysibm.sysTables" ,
                        "where creator = 'SYSIBM' and name = ?",':m.nm'
    call sqlQueryArgs cx, 'SYSTABLES'
    call out 'fetchBT' sqlFetch(cx) m.nm
    call out 'fetchBT' sqlFetch(cx) m.nm
    call sqlClose cx
    call sqlQueryArgs cx, 'SYSINDEXES'
    call out 'fetchBi' sqlFetch(cx) m.nm
    call out 'fetchBi' sqlFetch(cx) m.nm
    call tstEnd t
    call sqlDisconnect
    return
endProcedure tstSql

tstSqlCsm: procedure expose m.
/*
$=/tstSqlCsm/
    ### start tst tstSqlCsm ###########################################
    *** err: SQLCODE = -204: S100447.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: subsys = DE0G, host = RZZ
    *** err: fetchVars with : for csm: :m.dst.ab, :m.dst.ef
    fetchA 0 ab=m.abcdef.123.AB=M.abcdef.123.AB ef=M.abcdef.123.EF
    fetchA 0 ab=m.abcdef.123.AB=M.abcdef.123.AB ef=M.abcdef.123.EF
    fetchB 1 ab=a cd=2 ef=--- ind=M.abc.Def.123.EF.SQLIND gh=d ind=M.ab+
    c.Def.123.GH.SQLIND
    fetchB 0 ab=a cd=2 ef=--- ind=M.abc.Def.123.EF.SQLIND gh=d ind=M.ab+
    c.Def.123.GH.SQLIND
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
$/tstSqlCsm/ */
    call tst t, "tstSqlCsm"
    call sqlConnect 'RZZ/DE0G'
    cx = 7
    call sqlCsmQuery cx, 'select * from sysdummy'
    call sqlCsmQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlCsmFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlCsmQuery cx, sql, '?ab cd ?ef ?gh'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlCsmFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlCsmQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlCsmFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call tstEnd t
    call sqlDisconnect
    return
endProcedure tstsqlCsm

tstSqlCSV: procedure expose m.
/*
$=/tstSqlCSV/
    ### start tst tstSqlCSV ###########################################
    NAME,CREATOR,MITCOM,MITQUO,MITNU,COL6
    SYSTABLES,SYSIBM  ,"a,b","a""b",1,8
    SYSTABLESPACE,SYSIBM  ,"a,b","a""b",---,8
    SYSTABLESPACESTATS,SYSIBM,"a,b","a""b",---,6
$/tstSqlCSV/ */
    call csvIni
    call scanReadIni
    call sqlConnect
    call tst t, "tstSqlCSV"
    r = csvWrt(sqlRdr("select name, creator, 'a,b' mitCom",
         ", 'a""b' mitQuo" ,
         ", case when name='SYSTABLES' then 1 else null end mitNu" ,
         ",length(creator)" ,
              "from sysibm.sysTables" ,
              "where creator = 'SYSIBM' and name like 'SYSTABLES%'",
              "fetch first 3 rows only"))
    call pipeWriteAll r
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlCsv

tstSqlB: procedure expose m.
/*
$=/tstSqlB/
    ### start tst tstSqlB #############################################
    #jIn 1# select strip(name) "tb", strip(creator) cr
    #jIn 2# , case when name = 'SYSTABLES' then 1 else null end
    #jIn 3# from sysibm.sysTables
    #jIn 4# where creator = 'SYSIBM' and name like 'SYSTABLES%'
    #jIn 5# .
    #jIn 6# order by name
    #jIn 7# fetch first 3 rows only
    #jIn eof 8#
    dest1.fet: SYSTABLES SYSIBM 1
    dest2.fet: SYSTABLESPACE SYSIBM ---
    dest3.fet: SYSTABLESPACESTATS SYSIBM ---
$/tstSqlB/ */
    call tst t, "tstSqlB"
    cx = 9
    call sqlConnect
    call jIni
    call mAdd mCut(t'.IN', 0),
      , 'select strip(name) "tb", strip(creator) cr' ,
      ,     ", case when name = 'SYSTABLES' then 1 else null end" ,
      ,   "from sysibm.sysTables" ,
      ,   "where creator = 'SYSIBM' and name like 'SYSTABLES%'", ,
      ,   "order by name",
      ,   "fetch first 3 rows only"
     call sqlQuery cx
     do qx=1 while sqlFetch(cx, 'dest'qx'.fet')
         dst = 'dest'qx'.fet'
         call out dst':' m.dst.tb m.dst.cr m.dst.col3
         drop m.dst.tb m.dst.cr m.dst.col3
         end
     call sqlClose cx
     call sqlDisconnect
     call tstEnd t
     return
endProcedure tstSqlB

tstSqlFTab: procedure expose m.
/*
$=/tstSqlFTab/
    ### start tst tstSqlFTab ##########################################
    UPDATESTATSTIME----------------NACTIVE------NPAGES-EXTENT-LOADRLAST+
    TIME--------------REORGLASTTIME--------------REORGINSERT-REORGDELET+
    E-REORGUPDATE-REORGUNCLUS-REORGDISORG-REORGMASSDE-REORGNEARIN-REORG+
    FARIND-STATSLASTTIME--------------STATSINSERT-STATSDELETE-STATSUPDA+
    TE-STATSMASSDE-COPYLASTTIME---------------COPYUPDATED-COPYCHANGES-C+
    OPYUP-COPYUPDATETIME-------------I---DBID---PSID-PARTIT-INSTAN---SP+
    ACE-TOTALRO-DATASIZ-UNCOMPR-DBNAME-------NAME---------REORGCL-REORG+
    SC-REORGHA-HASHLASTUS-DRI-L-STATS01----
    --- modified
    allg vorher                     others vorher
    db-------ts---------part---inst-UPDATESTATSTIME----------------NACT+
    IVE------NPAGES-EXTENT-LOADRLASTTIME--------------REORGLASTTIME----+
    ----------REORGINSERT-REORGDELETE-REORGUPDATE-REORGUNCLUS-REORGDISO+
    RG-REORGMASSDE-REORGNEARIN-REORGFARIND-STATSLASTTIME--------------S+
    TATSINSERT-STATSDELETE-STATSUPDATE-STATSMASSDE-COPYLASTTIME--------+
    -------COPYUPDATED-COPYCHANGES-COPYUP-COPYUPDATETIME-------------I-+
    --DBID---PSID---SPACE-TOTALRO-DATASIZ-UNCOMPR-REORGCL-REORGSC-REORG+
    HA-HASHLASTUS-DRI-L-STATS01----
    db-------ts---------part---inst-UPDATESTATSTIME----------------NACT+
    IVE------NPAGES-EXTENT-LOADRLASTTIME--------------REORGLASTTIME----+
    ----------REORGINSERT-REORGDELETE-REORGUPDATE-REORGUNCLUS-REORGDISO+
    RG-REORGMASSDE-REORGNEARIN-REORGFARIND-STATSLASTTIME--------------S+
    TATSINSERT-STATSDELETE-STATSUPDATE-STATSMASSDE-COPYLASTTIME--------+
    -------COPYUPDATED-COPYCHANGES-COPYUP-COPYUPDATETIME-------------I-+
    --DBID---PSID---SPACE-TOTALRO-DATASIZ-UNCOMPR-REORGCL-REORGSC-REORG+
    HA-HASHLASTUS-DRI-L-STATS01----
    allg nachher                    others nachher
    DBNAME                   INSTANCE                                  +
    .    NPAGES                                        REORGLASTTIME   +
    .                                   REORGUPDATES                   +
    .     REORGMASSDELETE                     STATSLASTTIME            +
    .                          STATSUPDATES                            +
    .           COPYUPDATEDPAGES               COPYUPDATETIME          +
    .            PSID                   DATASIZE                REORGSC+
    ANACCESS            DRIVETYPE
    .         NAME                   UPDATESTATSTIME                   +
    .                 EXTENTS                                          +
    .            REORGINSERTS                        REORGUNCLUSTINS   +
    .                  REORGNEARINDREF                                 +
    .   STATSINSERTS                        STATSMASSDELETE            +
    .                        COPYCHANGES                               +
    .    IBMREQD         SPACE                   UNCOMPRESSEDDATASIZE  +
    .  REORGHASHACCESS        LPFACILITY
    .                  PARTITION                                NACTIVE+
    .                        LOADRLASTTIME                             +
    .                        REORGDELETES                        REORGD+
    ISORGLOB                      REORGFARINDREF                       +
    .              STATSDELETES                        COPYLASTTIME    +
    .                                   COPYUPDATELRSN                 +
    .     DBID                  TOTALROWS               REORGCLUSTERSEN+
    S        HASHLASTUSED     STATS01
$/tstSqlFTab/
*/
    call tst t, 'tstSqlFTab'
    call sqlConnect
    call sqlQuery 17, 'select * from sysibm.sysTablespaceStats' ,
                "where dbName = 'xxxDB06' and name = 'SYSTSTAB'"
    call sqlFTabReset abc, 17, 1,     ,  12
    call sqlFTabDef      abc, 492, '%7e'
    call sqlFTabOthers abc
    call sqlfTab abc
    call sqlClose 17
    call out '--- modified'
    call sqlQuery 17, 'select * from sysibm.sysTablespaceStats' ,
                "where dbName = 'xxxDB06' and name = 'SYSTSTAB'"
    call sqlFTabReset abc, 17, 2 1, 1 3 'c', 12
    call sqlFTabDef      abc, 492, '%7e'
    call sqlFTabAdd      abc, DBNAME, '%-8C', 'db', 'allg vorher'  ,
                                                  , 'allg nachher'
    call sqlFTabAdd      abc, NAME  , '%-8C', 'ts'
    call sqlFTabAdd      abc, PARTITION , , 'part'
    call sqlFTabAdd      abc, INSTANCE  , , 'inst'
    call fTabAddTit      abc, 2,                    'others vorher'
    call fTabAddTit      abc, 3,                    'others nachher'
    call sqlFTabOthers abc
    call sqlFTab abc
    call sqlClose 17
    call tstEnd t
    return
endProcedure tstSqlFTab

tstSql4Obj: procedure expose m.
/*
$=/tstSql4Obj/
    ### start tst tstSql4Obj ##########################################
    tstR: @tstWriteoV2 isA :tstClass-1 = -11
    tstR:  .a2i = -11
    tstR:  .b3b = b3
    tstR:  .D4 = D4-11+D4++++.
    tstR:  .fl5 = -111.1
    tstR:  .ex6 = -.111e-11
    insert into cr.insTb -- tstClass-1
    .   ( , a2i, b3b, D4, fl5, ex6
    .   ) values .
    .   ( -11, -11, 'b3', 'D4-11+D4++++', -111.1, -.111e-11
    .   ) ; .
    insert into cr.insTbHex -- tstClass-1
    .   ( , a2i, b3b, D4, fl5, ex6
    .   ) values .
    .   ( -11, -11, 'b3', x'C40760F1F14EC4F44E4E4E4E', -111.1, -.111e-1+
    1
    .   ) ; .
    tstR: @tstWriteoV4 isA :tstClass-2
    tstR:  .c = c83
    tstR:  .a2i = 83
    tstR:  .b3b = b3b8
    tstR:  .D4 = D483+D4++++++++++++++++++++++++++++++++++++++++++++++++
    .++++++++++++++++++++++++++++++.
    tstR:  .fl5 = .183
    tstR:  .ex6 = .11183e-8
    insert into cr.insTb -- tstClass-2
    .   ( c, a2i, b3b, D4, fl5, ex6
    .   ) values .
    .   ( 'c83', 83, 'b3b8'
    .   , 'D483+D4++++++++++++++++++++++++++++++++++++++++++++++++++++++
    '
    .   || '++++++++++++++++++++++++'
    .   , .183, .11183e-8
    .   ) ; .
    insert into cr.insTbHex -- tstClass-2
    .   ( c, a2i, b3b, D4, fl5, ex6
    .   ) values .
    .   ( 'c83', 83, 'b3b8'
    .   , x'C407F8F34EC4F44E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E'
    .   || '++++++++++++++++++++++++++++++++'
    .   || x'314E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E'
    .   , .183, .11183e-8
    .   ) ; .
$/tstSql4Obj/
*/
    call pipeIni
    call tst t, 'tstSql4Obj'
    call pipe '+N'
    call tstDataClassOut '. c3 a2i i b3b c5 D4 c23 fl5 f8n2 ex6 e9n3',
        , -11, -11
    call tstDataClassOut 'c c3 a2i i b3b c5 D4 c93 fl5 f8n2 ex6 e9n3',
        , 83, 83
    call pipe 'P|'
    do cx=1
        i = inO()
        if i == '' then
            leave
        call mAdd t'.'trans, className(objClass(i)) 'tstClass-'cx
        call outO i
        call sql4Obj i, 'cr.insTb'
        m.i.d4 = overlay('07'x, m.i.d4, 2)
        if length(m.i.d4) >= 62 then
            m.i.d4 = overlay('31'x, m.i.d4, 62)
        call sql4Obj i, 'cr.insTbHex'
        end
    call pipe '-'
    call tstEnd t
    return
endProcedure tstSql4Obj
tstSqlC: procedure expose m.
call sqlIni
/*
$=/tstSqlCRx/
    ### start tst tstSqlCRx ###########################################
    *** err: SQLCODE = -104: ILLEGAL SYMBOL "?". SOME SYMBOLS THAT MIGH+
    T
    .    e 1:     BE LEGAL ARE: . <IDENTIFIER> JOIN INNER LEFT RIGHT FU+
    LL CROSS ,
    .    e 2:     HAVING GROUP
    .    e 3: src select * from sysibm?sysDummy1
    .    e 4:   >    >>>pos 21 of 30>>>
    .    e 5: sql = select * from sysibm?sysDummy1
    .    e 6: stmt = prepare s9 into :M.SQL.9.D from :src
    .    e 7: with into :M.SQL.9.D = M.SQL.9.D
    *** err: SQLCODE = -204: NONONO.SYSDUMMY1 IS AN UNDEFINED NAME
    .    e 1: sql = select * from nonono.sysDummy1
    .    e 2: stmt = prepare s9 into :M.SQL.9.D from :src
    .    e 3: with into :M.SQL.9.D = M.SQL.9.D
    sys  ==> server CHSKA000DP4G    .
    fetched a1=abc, i2=12, c3=---
    .  I1 C2  .
    .   1 eins
    2222 zwei
$/tstSqlCRx/
$=/tstSqlCCsm/
    ### start tst tstSqlCCsm ##########################################
    *** err: SQLCODE = -104: ILLEGAL SYMBOL "?". SOME SYMBOLS THAT MIGH+
    T
    .    e 1:     BE LEGAL ARE: . <IDENTIFIER> JOIN INNER LEFT RIGHT FU+
    LL CROSS ,
    .    e 2:     HAVING GROUP
    .    e 3: src select * from sysibm?sysDummy1
    .    e 4:   >    >>>pos 21 of 30>>>
    .    e 5: sql = select * from sysibm?sysDummy1
    .    e 6: subsys = DE0G, host = RZZ
    *** err: SQLCODE = -204: NONONO.SYSDUMMY1 IS AN UNDEFINED NAME
    .    e 1: sql = select * from nonono.sysDummy1
    .    e 2: subsys = DE0G, host = RZZ
    sys RZZ/DE0G ==> server CHROI00ZDE0G    .
    fetched a1=abc, i2=12, c3=---
    .  I1 C2  .
    .   1 eins
    2222 zwei
$/tstSqlCCsm/ */
    sqlBuf = jBuf("select 1 i1, 'eins' c2 from sysibm.sysDummy1",
      , "union all select 2222 i1, 'zwei' c2 from sysibm.sysDummy1")
    do tx=1 to 2
        if tx = 1 then do
            call tst t, "tstSqlCRx"
            sys = ''
            end
        else do
            call tst t, "tstSqlCCsm"
            sys =  'RZZ/DE0G'
            end
        call sqlConnect sys
        cx = 9
        call sqlQuery cx, 'select * from sysibm?sysDummy1'
        call sqlQuery cx, 'select * from nonono.sysDummy1'
        call sqlQuery cx, "select 'abc' a1, 12 i2, current server srv",
                     ", case when 1=0 then 1 else null end c3",
                 "from sysibm.sysDummy1"
        do while sqlFetch(cx, dst)
            call out 'sys' sys '==> server' m.dst.srv
            call out 'fetched a1='m.dst.a1', i2='m.dst.i2', c3='m.dst.c3
            end
        call fTabAuto , sqlRdr(sqlBuf)
        call sqlDisconnect
        call tstEnd t
        end
    return
endProcedure tstSqlC

tstSqlUpd: procedure expose m.
/*
$=/tstSqlUpd/
    ### start tst tstSqlUpd ###########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table (update session.dgtt  set c2 = 'u' +
    || c2)
    stmt = prepare s9 into :M.SQL.9.D from :src
    with into :M.SQL.9.D = M.SQL.9.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstSqlUpd/ */
    call tst t, "tstSqlUpd"
    cx = 9
    qx = 3
    call sqlConnect
    call sqlUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call sqlUpdate,"insert into session.dgtt" ,
                    "values(1, 'eins', '2012-04-01 06.07.08')"
    call sqlUpdate,"insert into session.dgtt" ,
                    "values(2, 'zwei', '2012-02-29 15:44:33.22')"
    call out 'insert updC' m.sql..updateCount
    call sqlUpdate,"insert into session.dgtt" ,
                      "select i1+10, 'zehn+'||strip(c2), t3+10 days",
                           "from session.dgtt"
    call out 'insert select updC' m.sql..updateCount
    call sqlQuery cx, 'select d.*' ,
               ', case when mod(i1,2) = 1 then 1 else null end grad' ,
               'from session.dgtt d'
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlClose cx
    call sqlQuery cx, "select * from final table (update session.dgtt",
                   " set c2 = 'u' || c2)"
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSqlUpd
tstSqlUpdPre: procedure expose m.
/*
$=/tstSqlUpdPre/
    ### start tst tstSqlUpdPre ########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table ( update session.dgtt set c2 = ? ||+
    . c2)
    stmt = prepare s5 into :M.SQL.5.D from :src
    with into :M.SQL.5.D = M.SQL.5.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstSqlUpdPre/ */
    call tst t, "tstSqlUpdPre"
    cx = 5
    qx = 3
    call sqlConnect
    call sqlUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call sqlUpdPrep 5, "insert into session.dgtt values (?, ?, ?)"
    call sqlUpdArgs 5, 1, 'eins', '2012-04-01 06.07.08'
    call sqlUpdArgs 5, 2, 'zwei', '2012-02-29 15:44:33.22'
    call out 'insert updC' m.sql.5.updateCount
    call sqlUpdPrep 5,"insert into session.dgtt" ,
                      "select i1+?, 'zehn+'||strip(c2), t3+? days",
                           "from session.dgtt"
    call sqlUpdArgs 5, 10, 10
    call out 'insert select updC' m.sql.5.updateCount
    call sqlQueryPrep cx, 'select d.*' ,
               ', case when mod(i1,2) = ? then 0+? else null end grad' ,
               'from session.dgtt d'
    call sqlQueryArgs cx, 1, 1
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlClose cx
    call sqlQueryPrep cx, "select * from final table (" ,
              "update session.dgtt set c2 = ? || c2)"
    call sqlQueryArgs cx, "u"
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSqlUpdPre
tstsqlRxUpd: procedure expose m.
/*
$=/tstsqlRxUpd/
    ### start tst tstsqlRxUpd #########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table (update session.dgtt set c2 = 'u' +
    || c2)
    stmt = prepare s9 into :M.SQL.9.D from :src
    with into :M.SQL.9.D = M.SQL.9.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstsqlRxUpd/ */
    call tst t, "tstsqlRxUpd"
    cx = 9
    qx = 3
    call sqlRxConnect
    call sqlRxUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call sqlRxUpdate,"insert into session.dgtt" ,
                    "values(1, 'eins', '2012-04-01 06.07.08')"
    call sqlRxUpdate,"insert into session.dgtt" ,
                    "values(2, 'zwei', '2012-02-29 15:44:33.22')"
    call out 'insert updC' m.sql..updateCount
    call sqlRxUpdate,"insert into session.dgtt" ,
                      "select i1+10, 'zehn+'||strip(c2), t3+10 days",
                           "from session.dgtt"
    call out 'insert select updC' m.sql..updateCount
    call sqlRxQuery cx, 'select d.*' ,
               ', case when mod(i1,2) = 1 then 1 else null end grad' ,
               'from session.dgtt d'
    do qx=qx+1 while sqlRxFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlRxClose cx
    call sqlRxQuery cx, "select * from final table",
                 "(update session.dgtt set c2 = 'u' || c2)"

    do qx=qx+1 while sqlRxFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlRxClose cx
    call sqlRxDisconnect
    call tstEnd t
    return
endProcedure tstsqlRxUpd

tstSqlE: procedure expose m.
/*
$=/tstSqlE/
    ### start tst tstSqlE #############################################
    *** err: SQLCODE = -713: THE REPLACEMENT VALUE FOR CURRENT SCHEMA I+
    S
    .    e 1:     INVALID
    .    e 2: sql = set current schema = 'sysibm'
    .    e 3: stmt = execute immediate :src
    -713 set schema ''
    0 set schema
    0 select
    fetch=1 SYSIBM
$/tstSqlE/
*/
    call sqlConnect
    call tst t, "tstSqlE"
    call tstOut t, sqlExecute(3, "set current schema = 'sysibm'") ,
                                 "set schema ''"
    call tstOut t, sqlExecute(3, " set current schema = sysibm ") ,
                                 "set schema"
    call tstOut t, sqlExecute(3, " select current schema c"      ,
                                      "from sysibm.sysDummy1") 'select'
    call tstOut t, 'fetch='sqlFetch(3, aa) m.aa.c
    call sqlClose 3
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlE
tstSqlO: procedure expose m.
/*
$=/tstSqlO/
    ### start tst tstSqlO #############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s10 into :M.SQL.10.D from :src
    .    e 3: with into :M.SQL.10.D = M.SQL.10.D
    REQD=Y col=123 case=--- .sqlInd:-1 col5=anonym geburri=1956-04-01+
    -06.00.00.000000
$/tstSqlO/
*/
    call sqlConnect
    call tst t, "tstSqlO"
    call sqlStmt 'set current schema = A540769';
    call sqlStmt 'select * from sysdummy';
    r = sqlRdr( ,
          "select d.*, 123, timestamp('01.04.1956','06:00:00')" ,
                             '"geburri walter",',
                    'case when 1=0 then 1 else null end caseNull,',
                    "'anonym'" ,
               'from sysibm.sysdummy1 d')
    call jOpen r, '<'
    do while assNN('o', jReadO(r))

        call out 'REQD='m.o.IBMREQD 'col='m.o.col2,
                  'case='m.o.CASENULL '.sqlInd:'m.o.caseNull.sqlInd,
                  'col5='m.o.col5,
                  'geburri='m.o.GEBURRI
        end
    call jClose r
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlO

tstSqlUpdComLoop: procedure expose m.
/*
$=/tstSqlUpdComLoop/
    ### start tst tstSqlUpdComLoop ####################################
    sqlCode 0: declare global temporary table session.dgtt (i1 int) on +
    commit ....
    sqlCode 0, 123 rows inserted: insert into session.dgtt select row_n+
    umber()....
    CNT
    123
    1 rows fetched: select count(*) cnt from session.dgtt
    123 rows deleted, 10 commits: delete from session.dgtt d where i1 i+
    n (sele....
    C
    0
    1 rows fetched: select count(*) cnt from session.dgtt
$/tstSqlUpdComLoop/ */
    call tst t, "tstSqlUpdComLoop"
    call sqlConnect
    call out sqlStmt("declare global temporary table session.dgtt",
                           "(i1 int) on commit preserve rows")
    call out sqlStmt("insert into session.dgtt",
       "select row_number() over() from sysibm.sysTables",
           "fetch first 123 rows only")
    call out sqlStmt("select count(*) cnt from session.dgtt")
    call out sqlUpdComLoop("delete from session.dgtt d where i1 in",
       "(select i1 from session.dgtt fetch first 13 rows only)")
    call out sqlStmt("select count(*) cnt from session.dgtt")
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlUpdComLoop

tstSqlO1: procedure expose m.
/*
$=/tstSqlO1/
    ### start tst tstSqlO1 ############################################
    tstR: @tstWriteoV2 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART
    tstR: @tstWriteoV3 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART_HIST
    tstR: @tstWriteoV4 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLES
    tstR: @tstWriteoV5 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLESPACE
    --- writeAll
    tstR: @tstWriteoV6 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART
    tstR: @tstWriteoV7 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART_HIST
    tstR: @tstWriteoV8 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLES
    tstR: @tstWriteoV9 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLESPACE
$/tstSqlO1/
*/
    call sqlConnect
    call tst t, "tstSqlO1"
    qr = sqlRdr("select strip(creator) cr, strip(name) tb",
                     "from sysibm.sysTables",
                     "where creator='SYSIBM' and name like 'SYSTABL%'",
                     "order by 2 fetch first 4 rows only")
    call jOpen qr, m.j.cRead
    call mAdd t.trans, className(m.qr.type) '<tstSqlO1Type>'
    do while assNN('ABC', jReadO(qr))
        if m.qr.rowCount = 1 then do
            cx = m.qr.cursor
            end
        call outO abc
        end
    call jClose qr
    call out '--- writeAll'
    call pipeWriteAll qr
    call tstEnd t
    return 0
endProcedure tstSqlO1

tstSqlO2: procedure expose m.
/*
$=/tstSqlO2/
    ### start tst tstSqlO2 ############################################
    CR     TB                RR
    SYSIBM SYSTABLEPART       1
    SYSIBM SYSTABLEPART_HIST  4
    SYSIBM SYSTABLES          9
    SYSIBM SYSTABLESPACE     16
$/tstSqlO2/
*/
    call sqlConnect
    call tst t, "tstSqlO2"
    call pipe '+N'
    call out    "select strip(creator) cr, strip(name) tb,"
    call out         "(row_number()over())*(row_number()over()) rr"
    call out         "from sysibm.sysTables"
    call out         "where creator='SYSIBM' and name like 'SYSTABL%'"
    call out         "order by 2 fetch first 4 rows only"
    call pipe 'N|'
    call sqlSel
    call pipe 'P|'
    call fTabAuto fTabReset(abc, 1)
    call pipe '-'
    call tstEnd t
    return 0
endProcedure tstSqlO2
tstSqlS1: procedure expose m.
/*
$=/tstSqlS1/
    ### start tst tstSqlS1 ############################################
    select c, a from sysibm.sysDummy1
    tstR: @tstWriteoV2 isA :<cla sql c a>
    tstR:  .C = 1
    tstR:  .A = a
    select ... where 1=0
    tstR: @ obj null
$/tstSqlS1/
*/
    call sqlIni
    call tst t, "tstSqlS1"
    call sqlConnect
    s1 = jSingle( ,
        sqlRdr("select count(*) c, 'a' a from sysibm.sysdummy1"))
    call mAdd t.trans, className(objClass(s1)) '<cla sql c a>'
    call out 'select c, a from sysibm.sysDummy1'
    call tstWriteO t, s1
    call out 'select ... where 1=0'
    call tstWriteO t, jSingle( ,
        sqlRdr("select*from sysibm.sysdummy1 where 1=0"), '')
    call tstEnd t
    return
endProcedure tstSqlS1
tstSqlStmt: procedure expose m.
/*
$=/tstSqlStmt/
    ### start tst tstSqlStmt ##########################################
    *** err: SQLCODE = -713: THE REPLACEMENT VALUE FOR CURRENT SCHEMA I+
    S
    .    e 1:     INVALID
    .    e 2: sql = set current schema = 'sysibm'
    .    e 3: stmt = execute immediate :src
    sqlCode -713: set current schema = 'sysibm'
    sqlCode 0: set current schema =  sysibm
    tstR: @tstWriteoV2 isA :<sql?sc>
    tstR:  .C = SYSIBM
    1 rows fetched: select current schema c  from sysDummy1
    tstR: @tstWriteoV3 isA :<sql?sc>
    tstR:  .C = SYSIBM
    1 rows fetched: (select current schema c from sysDummy1)
$/tstSqlStmt/
*/
    call sqlConnect
    call tst t, "tstSqlStmt"
    cn = className(classNew('n* SQL u f C v'))
    call mAdd t.trans, cn '<sql?sc>'
    call tstOut t, sqlStmt("set current schema = 'sysibm'")
    call tstOut t, sqlStmt("  set current schema =  sysibm ")
    call tstOut t, sqlStmt("   select current schema c  from sysDummy1",
                           , ,'o')
    call tstOut t, sqlStmt("  (select current schema c from sysDummy1)",
                           , ,'o')
    call tstEnd t
    return
endProcedure tstSqlStmt
tstSqlStmts: procedure expose m.
/*
$=/tstSqlStmts/
   ### start tst tstSqlStmts #########################################
   *** err: SQLCODE = -104: ILLEGAL SYMBOL "BLABLA". SOME SYMBOLS THAT
   .    e 1:     MIGHT BE LEGAL ARE: <ERR_STMT> <WNG_STMT> GET SQL SAV+
   EPOINT HOLD
   .    e 2:     FREE ASSOCIATE
   .    e 3: src blabla
   .    e 4:   > <<<pos 1 of 6<<<
   .    e 5: sql = blabla
   sqlCode -104: blabla
   sqlCode 0: set current schema=  sysIbm
   C
   1
   1 rows fetched: select count(*) "c" from sysDummy1  with    ur
   C
   1
   1 rows fetched: select count(*) "c" from sysDummy1  with    ur
   #jIn 1# set current -- sdf
   #jIn 2# schema = s100447;
   sqlCode 0: set current  schema = s100447
   #jIn eof 3#
$/tstSqlStmts/ */
    call sqlConnect
    call scanReadIni
    call scanWinIni
    call tst t, "tstSqlStmts"
    call sqlStmts "blabla ;;set current schema=  sysIbm "
    b = jBuf('select count(*) "c" from sysDummy1 --com' ,
             ,'with  /* comm */ ur;')
    call sqlStmts b
    call sqlStmts b, , '-sql72'
    call mAdd mCut(t'.IN', 0), 'set current -- sdf', 'schema = s100447;'
    call sqlStmts
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlStmts

tstCatTb:
/*
$=/tstCatTb/
    ### start tst tstCatTb ############################################
    ..
    select * from sysibm.SYSDUMMY1  .
    IBMREQD
    I .
    Y .
    I .
    IBMREQD
$/tstCatTb/
*/
    call sqlConnect
    call tst t, 'tstCatTb'
    call sqlCatTb 'sysDummy1'
    call sqlCatTb 'SYSTableSpaceStats',
             , "name = 'A403A1' and dbName = 'DA540769'"
    call tstEnd t
    return
endProcedure tstCatTb

tstSqlDisDb: procedure expose m.
    call sqlDsn di, 'DP4G', '-dis db(*) sp(*)' ,
                    'restrict advisory limit(*)', 12
    m.oo.0 = 0
    call sqlDisDb oo, di
    say 'di.0' m.di.0 '==> oo.0' m.oo.0
    trace ?r
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE)
    say 'DB2PDB6.RR2HHAGE  ==>' ix m.oo.ix.sta
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE, 3)
    say 'DB2PDB6.RR2HHAGE.3  ==>' ix m.oo.ix.sta
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE,22)
    say 'DB2PDB6.RR2HHAGE.22 ==>' ix m.oo.ix.sta
    return
endProcedure tstSqlDisDb

/****** tstComp ********************************************************
    test the wsh compiler
***********************************************************************/
tstComp: procedure expose m.
    call compIni
    call tstCompDataConst
    call tstCompDataVars
    call tstCompShell
    call tstCompPrimary
    call tstCompExpr
    call tstCompFile
    call tstCompStmt
    call tstCompDir
    call tstCompObj
    call tstCompORun
    call tstCompDataIO
    call tstCompPipe
    call tstCompPip2
    call tstCompRedir
    call tstCompComp
    call tstCompColon
    call tstCompTable
    call tstCompSyntax
    if m.err.os == 'TSO' then
        call tstCompSql
    call tstTotal
    return
endProcedure tstComp

tstComp1: procedure expose m.
parse arg ty nm cnt
    c1 = 0
    if cnt = 0 | cnt = '+' then do
        c1 = cnt
        cnt = ''
        end
    call jIni
    src = jBuf()
    call jOpen src, m.j.cWri
    do sx=2 to arg()
        call jWrite src, arg(sx)
        end
    call tstComp2 nm, ty, jClose(src), , c1, cnt
    return
endProcedure tstComp1

tstComp2: procedure expose m.
parse arg nm, spec, src, compSt
    call compIni
    call tst t, nm, compSt
    if src == '' then do
        src = jBuf()
        call tst4dp src'.BUF', mapInline(nm'Src')
        end
    m.t.moreOutOk = abbrev(strip(arg(5)), '+')
    oldErr = m.err.count
    cmp = comp(src)
    call out 'compile' spec',' (m.src.buf.0) 'lines:' m.src.buf.1
    r = compile(cmp, spec)
    noSyn = m.err.count = oldErr
    coErr = m.t.err
    say if( noSyn, "compiled", "*** syntaxed") r ":" objMet(r, 'oRun')

    cnt = 0
    do ax = 5 to max(arg(), 5) while m.t.err = coErr & noSyn
        a1 = strip(arg(ax))
        if a1 == '' & arg() >= 5 then
            iterate
        if abbrev(a1, '+') then do
            m.t.moreOutOk = 1
            a1 = strip(substr(a1, 2))
            end
        if datatype(a1, 'n') then
            cnt = a1
        else if a1 \== '' then
            call err 'tstComp2 bad arg('ax')' arg(ax)
        if cnt = 0 then do
            call mCut 'T.IN', 0
            call out "run without input"
            end
        else  do
            call mAdd mCut('T.IN', 0),
                ,"eins zwei drei", "zehn elf zwoelf?",
                , "zwanzig 21 22 23 24 ... 29|"
            do lx=4 to cnt
                call mAdd 'T.IN', left(lx "line" lx, lx+9, '-')';'
                end
            call out "run with" cnt "inputs"
            end
        m.t.inIx = 0
        call oRun r
        end
    call tstEnd t
    return
endProcedure tstComp2

tstCompDataConst: procedure expose m.
/*
$=/tstCompDataConst/
    ### start tst tstCompDataConst ####################################
    compile =, 8 lines:       Lline one, $** asdf
    run without input
    .      Lline one, .
    line two..
    line threecontinued on 4
    line five  fortsetzung
    line six   fortsetzung
$/tstCompDataConst/ */
    call tstComp1 '= tstCompDataConst',
        , '      Lline one, $** asdf',
        , 'line two.',
        , 'line three$*+ bla bla' ,
        , 'continued on 4',
        , 'line five $*( und so',
        , 'weiter $abc $? $''$*)'' $"$*)" und weiter $*) fortsetzung',
        , 'line six  $*( und $*( $** $*( so',
        , 'weiter $abc $? $*)'' $"$*)" und weiter $*) fortsetzung'

/*
$=/tstCompDataConstBefAftComm1/
    ### start tst tstCompDataConstBefAftComm1 #########################
    compile =, 3 lines:     $*(anfangs com.$*)       $*(plus$*) $** x
    run without input
    the only line;
$/tstCompDataConstBefAftComm1/ */
    call tstComp1 '= tstCompDataConstBefAftComm1',
        , '    $*(anfangs com.$*)       $*(plus$*) $** x',
        , 'the only line;',
        , '      $*(end kommentar$*)              '

/*
$=/tstCompDataConstBefAftComm2/
    ### start tst tstCompDataConstBefAftComm2 #########################
    compile =, 11 lines:     $*(anfangs com.$*)       $*(plus$*) $*+ x
    run without input
    the first non empty line;
    .      .
    befor an empty line with comments;
$/tstCompDataConstBefAftComm2/ */

    call tstComp1 '= tstCompDataConstBefAftComm2',
        , '    $*(anfangs com.$*)       $*(plus$*) $*+ x',
        , '    $*(forts Zeile com.$*)       $*(plus$*) $** x',
        , ' $*(comment 2. Zeile$*) $*(plus$*) $*+ x',
        , ' $*(forts Zeile com.$*) $*(plus$*) $** x',
        , 'the first non empty line;',
        , '      ',
        , 'befor an empty line with comments;',
        , ' $*(comment 4. Zeile$*) $*(plus$*) $*+ x',
        , ' $*(forts 4.Zeile com.$*) $*(plus$*) $** x',
        , '      $*(end kommentar$*)              $*+',
        , ' $*(forts end com.$*) $*(plus$*) $** x'
     return
endProcedure tstCompDataComm

tstCompDataVars: procedure expose m.
/*
$=/tstCompDataVars/
    ### start tst tstCompDataVars #####################################
    compile =, 5 lines:       Lline one, $** asdf
    run without input
    .      Lline one, .
    lline zwei output
    lline 3 .
    variable v1 = valueV1 ${v1}= valueV1; .
    .      $-.{""$v1} = valueV1; .
$/tstCompDataVars/ */
    call tstComp1 '= tstCompDataVars',
        , '      Lline one, $** asdf',
        , '   $$ lline zwei output',
        , 'lline 3 $=v1= valueV1 ' ,
        , 'variable v1 = $v1 $"${v1}=" ${  v1  }; ',
        , '      $"$-.{""""$v1} =" $-.{$""$"v1"}; '
    return
endProcedure tstCompDataVars

tstCompShell: procedure expose m.
/*
$=/tstCompShell3/
    ### start tst tstCompShell3 #######################################
    compile @, 8 lines: call tstOut "T",  "abc" $-¢2*3$! "efg"$-¢2*3$!"+
    hij"
    run without input
    abc 6 efg6hij
    insert into A540769x.tqt002 with n(n) as ( select 4 from sysibm.s
    insert into A540769x.tqt002 with n(n) as ( select 4 from sysibm.s  +
    .   union all .
    abc 6 efg6hij
$/tstCompShell3/ */
    call tstComp1 '@ tstCompShell3',
        , 'call tstOut "T",  "abc" $-¢2*3$! "efg"$-¢2*3$!"hij"',
        , 'ix=3' ,
        , 'call tstOut "T","insert into A540769x.tqt002" ,',
        ,     '"with n(n) as ( select" $-¢ix+1$! "from sysibm.s"',
        , 'call tstOut "T","insert into A540769x.tqt002"  ,  ',
        ,    '"with n(n) as ( select" $-¢ix+1$! "from sysibm.s" , ' ,
        ,    '"    union all "' ,
        , '$$ abc $-¢2*3$! efg$-¢2*3$!hij',
/*
$=/tstCompShell/
    ### start tst tstCompShell ########################################
    compile @, 12 lines:   $$  Lline one, $** asdf
    run without input
    Lline one,
    lline zwei output
    v1 = valueV1 ${v1}= valueV1|
    REXX OUT L5 CONTINUED L6 CONTINUED L7
    L8 ONE
    L9 TWO
    valueV1
    valueV1 valueV2
    out  valueV1 valueV2
    SCHLUSS
$/tstCompShell/ */
    call tstComp1 '@ tstCompShell',
        , '  $$  Lline one, $** asdf',
        , '   $$ lline zwei output',
        , '        $=v1= valueV1 ' ,
        , '$$       v1 = $v1 $"${v1}=" ${  v1  }| ' ,
        , 'call out rexx out l5, ' ,
        , '     continued l6 , ' ,
        , '     continued l7   ' ,
        , 'call out l8 one    ' ,
        , 'call out l9 two$=v2=valueV2  ',
        , '$$- $v1  $$- $v1 $v2   ',
        , 'call out   "out "     $v1 $v2   ',
        , '$$-   schluss    '
/*
$=/tstCompShell2/
    ### start tst tstCompShell2 #######################################
    compile @, 13 lines: $@do j=0 to 1 $@¢ $$ do j=$j
    run without input
    do j=0
    after if 0 $@¢ $!
    after if 0 $=@¢ $!
    do j=1
    if 1 then $@¢ a
    a2
    if 1 then $@=¢ b
    b2
    after if 1 $@¢ $!
    after if 1 $=@¢ $!
    end
$/tstCompShell2/ */
    call tstComp1 '@ tstCompShell2',
        , '$@do j=0 to 1 $@¢ $$ do j=$j' ,
        ,     'if $j then $@¢ ',
        ,          '$$ if $j then $"$@¢" a $$a2' ,
        ,          '$!',
        ,     'if $j then $@=¢ ',
        ,          '$$ if $j then $"$@=¢" b $$b2' ,
        ,          '$!',
        ,     'if $j then $@¢ $!' ,
        ,     '$$ after if $j $"$@¢ $!"' ,
        ,     'if $j then $@=¢ $!' ,
        ,     '$$ after if $j $"$=@¢ $!"' ,
        ,     '$!',
        , '$$ end'
    return
endProcedure tstCompShell

tstCompPrimary: procedure expose m.
    call compIni
/*
$=/tstCompPrimary/
    ### start tst tstCompPrimary ######################################
    compile =, 16 lines: Strings $"$""$""""$""" $'$''$''''$'''
    run without input
    Strings $"$""$" $'$''$'
    rexx $-¢ 3 * 5 $! = 15
    rexx $-¢ 5 * 7 $! = 35
    rexx $-// 7 * 11 $// = 77
    rexx $-/abcEf/ 11 * 13 $/abcEf/ = 143
    data  line three line four  bis hier
    shell line five line six bis hier
    var get   v1 value Eins, v1 value Eins .
    var isDef v1 1, v2 0 .
    #jIn eof 1#
    var read  >1 0 rr undefined
    #jIn eof 2#
    var read  >2 0 rr undefined
    no call abc$-¢4*5$! $-¢efg$-¢6*7$! abc20 EFG42
    brackets $-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$! 1000
    run with 3 inputs
    Strings $"$""$" $'$''$'
    rexx $-¢ 3 * 5 $! = 15
    rexx $-¢ 5 * 7 $! = 35
    rexx $-// 7 * 11 $// = 77
    rexx $-/abcEf/ 11 * 13 $/abcEf/ = 143
    data  line three line four  bis hier
    shell line five line six bis hier
    var get   v1 value Eins, v1 value Eins .
    var isDef v1 1, v2 0 .
    #jIn 1# eins zwei drei
    var read  >1 1 rr eins zwei drei
    #jIn 2# zehn elf zwoelf?
    var read  >2 1 rr zehn elf zwoelf?
    no call abc$-¢4*5$! $-¢efg$-¢6*7$! abc20 EFG42
    brackets $-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$! 1000
$/tstCompPrimary/ */
    call vRemove 'v2'
    call tstComp1 '= tstCompPrimary 3',
        , 'Strings $"$""$""""$"""' "$'$''$''''$'''",
        , 'rexx $"$-¢ 3 * 5 $! =" $-¢ 3 * 5 $!' ,
        , 'rexx $"$-¢ 5 * 7 $! =" $-¢ 5 * 7 $!' ,
        , 'rexx $"$-// 7 * 11 $// =" $-// 7 * 11 $//' ,
        , 'rexx $"$-/abcEf/ 11 * 13 $/abcEf/ ="',
            '$-/abcEf/ 11 * 13 $/abcEf/' ,
        , 'data $-=¢ line three',
        , 'line four $! bis hier'  ,
        , 'shell $-@¢ $$ line five',
        , '$$ line six $! bis hier' ,
        , '$= v1  =   value Eins  $=rr=undefined $= eins = 1 ',
        , 'var get   v1 $v1, v1 ${  v1  } ',
        , 'var isDef v1 ${? v${  eins  }  }, v2 ${?v2 } ',
        , 'var read  >1 ${> rr} rr $rr' ,
        , 'var read  >2 ${> rr} rr $rr',
        , 'no call $"abc$-¢4*5$! $-¢efg$-¢6*7$!"',
            'abc$-¢4*5$! $-¢efg$-¢6*7$!$!',
        , 'brackets $"$-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$!"',
            '$-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$!'
    return
endProcedure tstCompPrimary

tstCompExpr: procedure expose m.
    call compIni
/*
$=/tstCompExprStr/
    ### start tst tstCompExprStr ######################################
    compile -, 3 lines: $=vv=vvStr
    run without input
    vv=vvStr
    o2String($.vv)=vvStr
$/tstCompExprStr/ */
    call tstComp1 '- tstCompExprStr',
        , '$=vv=vvStr' ,
        , '"vv="$vv' ,
        , '$"o2String($.vv)="o2String($.vv)'
/*
$=/tstCompExprObj/
    ### start tst tstCompExprObj ######################################
    compile ., 5 lines: $=vv=vvStr
    run without input
    vv=
    vvStr
    s2o($.vv)=
    vvStr
$/tstCompExprObj/ */
    call tstComp1 '. tstCompExprObj',
        , '$=vv=vvStr' ,
        , '"!vv="', '$vv',
        , '$"s2o($.vv)="', 's2o($-vv)'
/*
$=/tstCompExprDat/
    ### start tst tstCompExprDat ######################################
    compile =, 4 lines: $=vv=vvDat
    run without input
    vv=vvDat
    $.vv= !vvDat
    $.-¢"abc"$!=!abc
$/tstCompExprDat/ */
    call tstComp1 '= tstCompExprDat',
        , '$=vv=vvDat' ,
        , 'vv=$vv',
        , '$"$.vv=" $.vv',
        , '$"$.-¢""abc""$!="$.-¢"abc"$!'

/*
$=/tstCompExprRun/
    ### start tst tstCompExprRun ######################################
    compile @, 3 lines: $=vv=vvRun
    run without input
    vv=vvRun
    o2string($.vv)=vvRun
$/tstCompExprRun/ */
    call tstComp1 '@ tstCompExprRun',
        , '$=vv=vvRun' ,
        , 'call out "vv="$vv',
        , 'call out $"o2string($.vv)="o2string($.vv)'
/*
$=/tstCompExprCon/
    ### start tst tstCompExprCon ######################################
    compile #, 2 lines: $$ in # drinnen
    run without input
    $$ in # drinnen
    call out "vv="$vv
$/tstCompExprCon/
$=/tstCompExprCo2/
    ### start tst tstCompExprCo2 ######################################
    compile #, 3 lines: $$ in # drinnen
    run without input
    $$ in # drinnen
    call out "vv="$vv
    nacgh $#@
$/tstCompExprCo2/
*/
    call tstComp1 '# tstCompExprCon',
        , '$$ in # drinnen' ,
        , 'call out "vv="$vv'

    call tstComp1 '# tstCompExprCo2',
        , '$$ in # drinnen' ,
        , 'call out "vv="$vv',
        , '$#@ $$ nacgh $"$#@"'

    return
endProcedure tstCompExpr

tstCompStmt: procedure expose m.
/*
$=/tstCompStmt1/
    ### start tst tstCompStmt1 ########################################
    compile @, 8 lines: $= v1 = value eins  $= v2  =- 3*5*7 .
    run without input
    data v1 value eins v2 105
    eins
    zwei
    drei
    vier
    fuenf
    elf
    . zwoelf  dreiZ  .
    . vierZ .
    fuenfZ
    lang v1 value eins v2 945
    oRun ouput 1
$/tstCompStmt1/ */
    call pipeIni
    call vPutO 'oRun', oRunner('call out "oRun ouput" (1*1)')
    call vRemove 'v2'
    call tstComp1 '@ tstCompStmt1',
        , '$= v1 = value eins  $= v2  =- 3*5*7 ',
        , '$$ data v1 $v1 v2 ${   v2  }',
        , '$$eins $@¢$$ zwei $$ drei  ',
        , '   $@¢   $!  $@//   $// $@/q r s /   $/q r s /',
             '       $@/eins/ $@¢ $$vier $! $/eins/ $! $$fuenf',
        , '$$elf $@=¢$@=¢ zwoelf  dreiZ  $!  ',
        , '   $@=¢   $! $@=¢ $@=¢ vierZ $! $! $! $$fuenfZ',
        , '$$- "lang v1" $v1 "v2" ${v2}*9',
        , '$@oRun'
/*
$=/tstCompStmt2/
    ### start tst tstCompStmt2 ########################################
    compile @, 1 lines: $@for qq $$ loop qq $qq
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    loop qq eins zwei drei
    #jIn 2# zehn elf zwoelf?
    loop qq zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    loop qq zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
$/tstCompStmt2/ */
    call tstComp1 '@ tstCompStmt2 3',
        , '$@for qq $$ loop qq $qq'

/*
$=/tstCompStmt3/
    ### start tst tstCompStmt3 ########################################
    compile @, 9 lines: $$ 1 begin run 1
    2 ct zwei
    ct 4 mit assign .
    run without input
    1 begin run 1
    3 run 3 ctV = ct 4 assign ctV|
    run 5 procCall $@prCa
    out in proc at 8
    run 6 vor call $@prCa
    out in proc at 8
    9 run end
    run with 3 inputs
    1 begin run 1
    3 run 3 ctV = ct 4 assign ctV|
    run 5 procCall $@prCa
    out in proc at 8
    run 6 vor call $@prCa
    out in proc at 8
    9 run end
$/tstCompStmt3/ */
    call tstComp1 '@ tstCompStmt3 3',
        , '$$ 1 begin run 1',
        , '$@ct $$ 2 ct zwei',
        , '$$ 3 run 3 ctV = $ctV|',
        , '$@ct $@=¢ct 4 mit assign $=ctV = ct 4 assign ctV $!',
        , '$$ run 5 procCall $"$@prCa" $@prCa',
        , '$$ run 6 vor call $"$@prCa"',
        , '$@prCa',
        , '$@proc prCa $$out in proc at 8',
        , '$$ 9 run end'

/*
$=/tstCompStmt4/
    ### start tst tstCompStmt4 ########################################
    compile @, 4 lines: $=eins=vorher
    run without input
    eins vorher
    eins aus named block eins .
$/tstCompStmt4/ */
    call tstComp1 '@ tstCompStmt4 0',
        , '$=eins=vorher' ,
        , '$$ eins $eins' ,
        , '$=/eins/aus named block eins $/eins/' ,
        , '$$ eins $eins'
/*
$=/tstCompStmtDo/
    ### start tst tstCompStmtDo #######################################
    compile @, 2 lines: ti=0$@do y=3 to 4 $@do 2 $@¢
    run without input
    y=3 ti1 z=7
    y=3 ti1 z=8
    y=3 ti2 z=7
    y=3 ti2 z=8
    y=4 ti3 z=7
    y=4 ti3 z=8
    y=4 ti4 z=7
    y=4 ti4 z=8
$/tstCompStmtDo/ */
    call tstComp1 '@ tstCompStmtDo', 'ti=0$@do y=3 to 4 $@do 2 $@¢',
     ,    'ti = ti + 1',
        '$@do $*(sdf$*) z $*(sdf$*) =7 to 8 $$ y=$y ti$-¢ti$! z=$z $!'

/*
$=/tstCompStmtDo2/
    ### start tst tstCompStmtDo2 ######################################
    compile @, 7 lines: $$ $-=/sqlSel/
    run without input
    select 1 abc select 2 abc after table .
$/tstCompStmtDo2/ */
    call tstComp1 '@ tstCompStmtDo2',
         , '$$ $-=/sqlSel/',
         ,     '$=ty = abc ',
         ,     '$@do tx=1 to 2 $@=/table/',
         ,          'select $tx $ty',
         , '$/table/',
         ,     '$=ty = abc',
         ,     'after table',
         '$/sqlSel/'
/*
$=/tstCompStmtWith/
    ### start tst tstCompStmtWith #####################################
    compile @, 3 lines: $@with $.vA $$ fEins=$FEINS fZwei=$FZWEI va&fEi+
    ns=${vA&FEINS}
    run without input
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
    fEins=2Eins fZwei=2Zwei va&fEins=1Eins
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
$/tstCompStmtWith/
*/
    cl = classNew('n? tstStmtWith u f FEINS v, f FZWEI v')
    v1 = onew(cl)
    m.v1.feins = '1Eins'
    m.v1.fzwei = '1Zwei'
    v2 = oNew(cl)
    m.v2.feins ='2Eins'
    m.v2.fzwei ='2Zwei'
    call vPutO 'vA', v1
    call vPutO 'vB', v2
    stmt = '$$ fEins=$FEINS fZwei=$FZWEI va&fEins=${vA&FEINS}'
    call tstComp1 '@ tstCompStmtWith',
         , '$@with $.vA' stmt ,
         , '$@with $vA $@¢' stmt ,
         , '$@with $vB ' stmt stmt '$!'
     return
endProcedure tstCompStmt

tstCompSyntax: procedure expose m.
    call tstCompSynPrimary
    call tstCompSynAss
    call tstCompSynRun
    return
endProcedure tstCompSyntax

tstCompSynPrimary: procedure expose m.

/*
$=/tstCompSynPri1/
    ### start tst tstCompSynPri1 ######################################
    compile @, 1 lines: a $ =
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $ =
    .    e 2: pos 3 in line 1: a $ =
    *** err: no class found for object 0
$/tstCompSynPri1/ */
    call tstComp1 '@ tstCompSynPri1 +', 'a $ ='

/*
$=/tstCompSynPri2/
    ### start tst tstCompSynPri2 ######################################
    compile @, 1 lines: a $. {
    *** err: scanErr primary not finished
    .    e 1: last token  scanPosition  {
    .    e 2: pos 5 in line 1: a $. {
$/tstCompSynPri2/ */
    call tstComp1 '@ tstCompSynPri2 +', 'a $. {'

/*
$=/tstCompSynPri3/
    ### start tst tstCompSynPri3 ######################################
    compile @, 1 lines: b $-  ¢  .
    *** err: scanErr primary not finished
    .    e 1: last token  scanPosition   ¢
    .    e 2: pos 5 in line 1: b $-  ¢
$/tstCompSynPri3/ */
    call tstComp1 '@ tstCompSynPri3 +', 'b $-  ¢  '

/*
$=/tstCompSynPri4/
    ### start tst tstCompSynPri4 ######################################
    compile @, 1 lines: a ${ $*( sdf$*) } =
    *** err: scanErr var name expected
    .    e 1: last token  scanPosition } =
    .    e 2: pos 17 in line 1: a ${ $*( sdf$*) } =
$/tstCompSynPri4/ */
    call tstComp1 '@ tstCompSynPri4 +', 'a ${ $*( sdf$*) } ='

/*
$=/tstCompSynFile/
    ### start tst tstCompSynFile ######################################
    compile @, 1 lines: $@.<$*( co1 $*) $$abc
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@.<$*( co1 $*) $$abc
    .    e 2: pos 1 in line 1: $@.<$*( co1 $*) $$abc
$/tstCompSynFile/ */
    call tstComp1 '@ tstCompSynFile +', '$@.<$*( co1 $*) $$abc'

    return
endProcedure tstCompSynPrimary

tstCompSynAss: procedure expose m.

/*
$=/tstCompSynAss1/
    ### start tst tstCompSynAss1 ######################################
    compile @, 1 lines: $=
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 3 in line 1: $=
$/tstCompSynAss1/ */
    call tstComp1 '@ tstCompSynAss1 +', '$='

/*
$=/tstCompSynAss2/
    ### start tst tstCompSynAss2 ######################################
    compile @, 2 lines: $=   .
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $=
$/tstCompSynAss2/ */
    call tstComp1 '@ tstCompSynAss2 +', '$=   ', 'eins'

/*
$=/tstCompSynAss3/
    ### start tst tstCompSynAss3 ######################################
    compile @, 2 lines: $=   $$
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition $$
    .    e 2: pos 6 in line 1: $=   $$
$/tstCompSynAss3/ */
    call tstComp1 '@ tstCompSynAss3 +', '$=   $$', 'eins'

/*
$=/tstCompSynAss4/
    ### start tst tstCompSynAss4 ######################################
    compile @, 1 lines: $=   eins
    *** err: scanErr = expected in assignment after $= var
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $=   eins
$/tstCompSynAss4/ */
    call tstComp1 '@ tstCompSynAss4 +', '$=   eins'

/*
$=/tstCompSynAss5/
    ### start tst tstCompSynAss5 ######################################
    compile @, 1 lines: $=  abc eins $$ = x
    *** err: scanErr = expected in assignment after $= var
    .    e 1: last token  scanPosition eins $$ = x
    .    e 2: pos 9 in line 1: $=  abc eins $$ = x
$/tstCompSynAss5/ */
    call tstComp1 '@ tstCompSynAss5 +', '$=  abc eins $$ = x'

/*
$=/tstCompSynAss6/
    ### start tst tstCompSynAss6 ######################################
    compile @, 1 lines: $=  abc =
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $=  abc =
$/tstCompSynAss6/ */
    call tstComp1 '@ tstCompSynAss6 +', '$=  abc ='

/*
$=/tstCompSynAss7/
    ### start tst tstCompSynAss7 ######################################
    compile @, 1 lines: $=  abc =..
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition ..
    .    e 2: pos 10 in line 1: $=  abc =..
$/tstCompSynAss7/ */
    call tstComp1 '@ tstCompSynAss7 +', '$=  abc =.'
    return
endProcedure tstCompSynAss

tstCompSynRun: procedure expose m.

/*
$=/tstCompSynRun1/
    ### start tst tstCompSynRun1 ######################################
    compile @, 1 lines: $@
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@
    .    e 2: pos 1 in line 1: $@
$/tstCompSynRun1/ */
    call tstComp1 '@ tstCompSynRun1 +', '$@'

/*
$=/tstCompSynRun2/
    ### start tst tstCompSynRun2 ######################################
    compile @, 1 lines: $@=
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@=
    .    e 2: pos 1 in line 1: $@=
$/tstCompSynRun2/ */
    call tstComp1 '@ tstCompSynRun2 +', '$@='

/*
$=/tstCompSynRun3/
    ### start tst tstCompSynRun3 ######################################
    compile @, 1 lines: $@: und
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@: und
    .    e 2: pos 1 in line 1: $@: und
    *** err: no class found for object 0
$/tstCompSynRun3/ */
    call tstComp1 '@ tstCompSynRun3 +', '$@: und'

/*
$=/tstCompSynFor4/
    ### start tst tstCompSynFor4 ######################################
    compile @, 1 lines: $@for
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $@for
$/tstCompSynFor4/ */
    call tstComp1 '@ tstCompSynFor4 +', '$@for'

/*
$=/tstCompSynFor5/
    ### start tst tstCompSynFor5 ######################################
    compile @, 2 lines: $@for
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $@for
$/tstCompSynFor5/ */
    call tstComp1 '@ tstCompSynFor5 +', '$@for', a

/*
$=/tstCompSynFor6/
    ### start tst tstCompSynFor6 ######################################
    compile @, 2 lines: a
    *** err: scanErr variable or named block after for
    .    e 1: last token  scanPosition .
    .    e 2: pos 15 in line 2:  b $@for   $$q
$/tstCompSynFor6/ */
    call tstComp1 '@ tstCompSynFor6 +', 'a', ' b $@for   $$q'

/*
$=/tstCompSynFor7/
    ### start tst tstCompSynFor7 ######################################
    compile @, 3 lines: a
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 11 in line 2:  b $@for a
$/tstCompSynFor7/ */
    call tstComp1 '@ tstCompSynFor7 +', 'a', ' b $@for a', '  $$q'

/*
$=/tstCompSynCt8/
    ### start tst tstCompSynCt8 #######################################
    compile @, 3 lines: a
    *** err: scanErr ct statement expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 8 in line 2:  b $@ct
$/tstCompSynCt8/ */
    call tstComp1 '@ tstCompSynCt8 +', 'a', ' b $@ct', '  $$q'

/*
$=/tstCompSynProc9/
    ### start tst tstCompSynProc9 #####################################
    compile @, 2 lines: a
    *** err: scanErr var or namedBlock expected after proc
    .    e 1: last token  scanPosition .
    .    e 2: pos 15 in line 2:  b $@proc  $$q
$/tstCompSynProc9/ */
    call tstComp1 '@ tstCompSynProc9 +', 'a', ' b $@proc  $$q'

/*
$=/tstCompSynProcA/
    ### start tst tstCompSynProcA #####################################
    compile @, 2 lines: $@proc p1
    *** err: scanErr proc statement expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $@proc p1
$/tstCompSynProcA/ */
    call tstComp1 '@ tstCompSynProcA +', '$@proc p1', '  $$q'

/*
$=/tstCompSynCallB/
    ### start tst tstCompSynCallB #####################################
    compile @, 1 lines: $@% ¢roc p1$!
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@% ¢roc p1$!
    .    e 2: pos 1 in line 1: $@% ¢roc p1$!
$/tstCompSynCallB/ */

    call tstComp1 '@ tstCompSynCallB +', '$@% ¢roc p1$!'

/*
$=/tstCompSynCallC/
    ### start tst tstCompSynCallC #####################################
    compile @, 1 lines: $@%¢call roc p1 !
    *** err: scanErr ending $! expected after ¢
    .    e 1: last token  scanPosition .
    .    e 2: pos 18 in line 1: $@%¢call roc p1 !
$/tstCompSynCallC/ */
    call tstComp1 '@ tstCompSynCallC +', '$@%¢call roc p1 !'

/*
$=/tstCompSynCallD/
    ### start tst tstCompSynCallD #####################################
    compile @, 2 lines: $@^¢call( $** roc
    *** err: scanErr ending $! expected after ¢
    .    e 1: last token  scanPosition .
    .    e 2: pos 18 in line 1: $@^¢call( $** roc
$/tstCompSynCallD/ */
    call tstComp1 '@ tstCompSynCallD +',
        ,'$@^¢call( $** roc' , ' $*( p1 $*) )'
    return
endProcedure tstCompSynRun

tstCompObj: procedure expose m.
    call tstReset t
    call classIni
    cl = classNew('n? tstCompCla u v, f FEINS v, f FZWEI v')
    do rx=1 to 10
        o = oNew(cl)
        m.tstComp.rx = o
        m.o = 'o'rx
        if rx // 2 = 0 then do
            m.o.fEins = 'o'rx'.1'
            m.o.fZwei = 'o'rx'.fZwei'rx
            end
        else do
            m.o.fEins = 'o'rx'.fEins'
            m.o.fZwei = 'o'rx'.2'
            end
        call mAdd 'T.TRANS', m.tstComp.rx '<o'rx'>'
        end

/*
$=/tstCompObjRef/
    ### start tst tstCompObjRef #######################################
    compile @, 13 lines: o1=m.tstComp.1
    run without input
    out .$"string" o1
    string
    out . o1
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .{ o2 }
    tstR: @<o2> isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    out .¢ o3 $!
    tstR: @<o3> isA :tstCompCla = o3
    tstR:  .FEINS = o3.fEins
    tstR:  .FZWEI = o3.2
    out .¢ o4 $!
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    out ./-/ o5 $/-/
    tstR: @<o5> isA :tstCompCla = o5
    tstR:  .FEINS = o5.fEins
    tstR:  .FZWEI = o5.2
$/tstCompObjRef/ */
m.ttt=1
    call tstComp1 '@ tstCompObjRef' ,
        , 'o1=m.tstComp.1',
        , 'o2 = m.tstComp.2' ,
        , '$$ out $".$""string""" o1 $$.$"string"',
        , '$$ out . o1 $$. o1',
        , '$$ out .{ o2 } $$.¢ o2 $!',
        , '$$ out .¢ o3 $"$!" $$.¢ ', ' m.tstComp.3 ', ' $!',
        , '$$ out .¢ o4 $"$!" $$.¢ ', ' m.tstComp.4 ', ' $!',
        , '$$ out ./-/ o5 $"$/-/" $$./-/  m.tstComp.5 ', ' $/-/'

/*
$=/tstCompObjRefPri/
    ### start tst tstCompObjRefPri ####################################
    compile @, 9 lines: $$ out .$"$.{o1}" $$.¢ m.tstComp.1 $!
    run without input
    out .$.{o1}
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .$.-{o2}
    <o2>
    out .$.={o3}
    . m.tstComp.3 .
    out .$.@{out o4}
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    tstWriteO kindOf ORun oRun end   >>>
    out .$.@¢$$abc $$efg$!
    tstWriteO kindOf ORun oRun begin <<<
    abc
    efg
    tstWriteO kindOf ORun oRun end   >>>
    out .$.@¢o5$!
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o5> isA :tstCompCla = o5
    tstR:  .FEINS = o5.fEins
    tstR:  .FZWEI = o5.2
    abc
    tstWriteO kindOf ORun oRun end   >>>
$/tstCompObjRefPri/ */
    call tstComp1 '@ tstCompObjRefPri' ,
        , '$$ out .$"$.{o1}" $$.¢ m.tstComp.1 $!',
        , '$$ out .$"$.-{o2}" $$.-¢ m.tstComp.2 $!',
        , '$$ out .$"$.={o3}" $$.=¢ m.tstComp.3 $!',
        , '$$ out .$"$.@{out o4}" $$.@@¢ call outO m.tstComp.4 $!',
    , '$$ out .$"$.@¢$$abc $$efg$!" $$.$.@@¢ $$abc ', ' ', ' $$efg $!',
        , '$$ out .$"$.@¢o5$!" $$.$.@@¢ $$.m.tstComp.5', '$$abc $!'

/*
$=/tstCompObjRefFile/
    ### start tst tstCompObjRefFile ###################################
    compile @, 7 lines: $$ out .$".<.¢o1!" $$.<.¢ m.tstComp.1 $!
    run without input
    out ..<.¢o1!
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .<$.-{o2}
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o2> isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$.<{o3}
    tstWriteO kindOf JRW jWriteNow begin <<<
    . m.tstComp.3 .
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$.<@{out o4}
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$<@¢$$abc $$efg$!
    tstWriteO kindOf JRW jWriteNow begin <<<
    abc
    efg
    tstWriteO kindOf JRW jWriteNow end   >>>
$/tstCompObjRefFile/ */

    call tstComp1 '@ tstCompObjRefFile' ,
        , '$$ out .$".<.¢o1!" $$.<.¢ m.tstComp.1 $!',
        , '$$ out .$"<$.-{o2}" $$<.¢ m.tstComp.2 $!',
        , '$$ out .$"$.<{o3}" $$<=¢ m.tstComp.3 $!',
        , '$$ out .$"$.<@{out o4}" $$<@¢ call outO m.tstComp.4 $!',
    , '$$ out .$"$<@¢$$abc $$efg$!" $$<@¢ $$abc ', ' ', ' $$efg $!'

/*
$=/tstCompObjFor/
    ### start tst tstCompObjFor #######################################
    compile @, 2 lines: $@do rx=1 to 3 $$. m.tstComp.rx
    run without input
    FEINS=o1.fEins FZWEI=o1.2
    FEINS=o2.1 FZWEI=o2.fZwei2
    FEINS=o3.fEins FZWEI=o3.2
$/tstCompObjFor/
*/
    call tstComp1 '@ tstCompObjFor' ,
        , '$@do rx=1 to 3 $$. m.tstComp.rx' ,
        , '$| $@forWith witx $$ FEINS=$FEINS FZWEI=$FZWEI'

/*
$=/tstCompObjRun/
    ### start tst tstCompObjRun #######################################
    compile @, 4 lines: $$ out .$"$@¢o1!" $$@¢ $$. m.tstComp.1 $!
    run without input
    out .$@¢o1!
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstWriteO kindOf ORun oRun end   >>>
    out .$<@¢$$abc $$efg$!
    tstWriteO kindOf JRW jWriteNow begin <<<
    abc
    efg
    tstWriteO kindOf JRW jWriteNow end   >>>
$/tstCompObjRun/ */
    call tstComp1 '@ tstCompObjRun' ,
        , '$$ out .$"$@¢o1!" $$@¢ $$. m.tstComp.1 $!',
    , '$$ out .$"$<@¢$$abc $$efg$!" $$<@¢ $$abc ', ' ', ' $$efg $!'

    m.t.trans.0 = 0
/*
$=/tstCompObj/
    ### start tst tstCompObj ##########################################
    compile @, 6 lines: o1=m.tstComp.1
    run without input
    out . o1
    tstR: @tstWriteoV1 isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .{ o2 }
    tstR: @tstWriteoV2 isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    out .¢ o1, o2!
    tstR: @tstWriteoV1 isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstR: @tstWriteoV2 isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
$/tstCompObj/ */
    call tstComp1 '@ tstCompObj' ,
        , 'o1=m.tstComp.1',
        , 'o2 = m.tstComp.2' ,
        , '$$ out . o1 $$. o1',
        , '$$ out .{ o2 } $$.¢ o2 $!',
    , '$$ out .¢ o1, o2!$; $@<.¢  m.tstComp.1  ', '  m.tstComp.2  $!'
    return
    m.t.trans.0 = 0
endProcedure tstCompObj

tstCompORun: procedure expose  m.
/*
$=/tstCompORun/
    ### start tst tstCompORun #########################################
    compile @, 6 lines: $@oRun
    run without input
    oRun arg=1, v2=, v3=, v4=
    oRun arg=1, v2=, v3=, v4=
    oRun arg=2, v2=-{1 arg only} oder?, v3=, v4=
    oRun arg=2, v2=!.{1 obj only} oder?, v3=, v4=
    oRun arg=3, v2={2 args}, v3=und zwei?, v4=
    oRun arg=4, v2={3 args}, v3=zwei, v4=und drei?
$/tstCompORun/ */
    call compIni
    call vPutO 'oRun', oRunner('parse arg , v2, v3, v4;',
        'call tstOut t, "oRun arg="arg()", v2="v2", v3="v3", v4="v4' )
    call tstComp1 '@ tstCompORun',
        , '$@oRun', '$@%¢oRun$!' ,
        , '    $@%¢oRun $"-{1 arg only}" oder?$!' ,
        , '    $@%-¢oRun $.".{1 obj only}" ''oder?''$! $=v2=zwei' ,
        , '    $@%-¢oRun $"{2 args}", "und" $v2"?"$!' ,
        , '    $@%-¢oRun $"{3 args}", $v2, "und drei?"$!'
    return
endProcedure tstCompORun

tstCompDataIO: procedure expose m.
/*
$=/tstCompDataHereData/
    ### start tst tstCompDataHereData #################################
    compile =, 13 lines:  herdata $@#/stop/    .
    run without input
    . herdata .
    heredata 1 $x
    heredata 2 $y
    nach heredata
    . herdata ¢ .
    heredata 1 xValue
    heredata 2 yValueY
    nach heredata ¢
    . herdata { .
    HEREDATA 1 xValue
    heredata 2 yValueY
    nach heredata {
$/tstCompDataHereData/ */
    call tstComp1 '= tstCompDataHereData',
        , ' herdata $@#/stop/    ',
        , 'heredata 1 $x',
        , 'heredata 2 $y',
        , '$/stop/ $$ nach heredata',
        , ' herdata ¢ $@=/stop/    ',
        , '$=x=xValue$=y=yValueY',
        , 'heredata 1 $x',
        , 'heredata 2 $y',
        , '$/stop/ $$ nach heredata ¢',
        , ' herdata { $@/st/',
        , '; call out heredata 1 $x',
        , '$$heredata 2 $y',
        , '$/st/ $$ nach heredata {'
/*
$=/tstCompDataIO/
    ### start tst tstCompDataIO #######################################
    compile =, 5 lines:  input 1 $@.<-=¢$dsn $*+.
    run without input
    . input 1 .
    readInp line 1                       .
    readInp line 2                       .
    . nach dsn input und nochmals mit & .
    readInp line 1                       .
    readInp line 2                       .
    . und schluiss..
$/tstCompDataIO/ */
    dsn = word(tstPdsMbr(tstFilename('lib37', 'r'), 'readInp'), 1)
    dsnFB = strip(dsn tstFB('::F37', 0))
    b = jBuf(tstFB('readInp line 1', 37),
            ,tstFB('readInp line 2', 37))
    extFd = tstFB('&dsn('dsn') dd(xyz)', 0)
    extFd = tstFB(dsn 'dd(xyz) ::f', 0)
    if extFd = '' then
        extFd = dsn
    call jCat file(dsnFB), b
    call vPut 'dsn', dsn
    say 'dsn' dsn 'extFD' extFD'?'
    call tstComp1 '= tstCompDataIO',
        , ' input 1 $@.<-=¢$dsn $*+',
        , tstFB('::f', 0) '$!',
        , ' nach dsn input und nochmals mit & ' ,
        , '         $@.<"'extFD'"',
        , ' und schluiss.'
    return
endProcedure tstCompDataIO

tstObjVF: procedure expose m.
parse arg v, f
    obj  = oNew(classNew('n? TstClassVF u v, f FLD1 v'))
    m.obj = if(f=='','val='v, v)
    m.obj.fld1 = if(f=='','FLD1='v, f)
    return obj
endProcedure tstObjVF

tstCompFile: procedure expose m.
/*
$=/tstCompFileBloSrc/
    $=vv=value-of-vv
    ###file from empty # block
    $@<#¢
        $!
    ###file from 1 line # block
    $@<#¢
    the only $ix+1/0 line $vv
    $!
    ###file from 2 line # block
    $@<#¢
        first line /0 $*+ no comment
        second and last line $$ $wie
    $!
    ===file from empty = block
    $@<=¢     $*+ comment
        $!
    ===file from 1 line = block
    $@<=¢ the only line $!
    ===file from 2 line = block
    $@<=¢ first line$** comment
        second and last line  $!
    ---file from empty - block
    $@<-/s/
        $/s/
    ---file from 1 line - block
    $@<-/s/ the only "line" (1*1) $/s/
    ---file from 2 line = block
    $@<-// first "line" (1+0)
        second   and   "last  line" (1+1)  $//
    ...file from empty . block
    $@<.¢
        $!
    ...file from 1 line . block
    $@<.¢ tstObjVF('v-Eins', '1-Eins') $!
    ...file from 2 line . block
    $@<.¢ tstObjVF('v-Elf', '1-Elf')
        tstObjVF('zwoelf')  $!
    ...file from 3 line . block
    $@<.¢ tstObjVF('einUndDreissig')
            s2o('zweiUndDreissig' o2String($vv))
            tstObjVF('dreiUndDreissig')  $!
    @@@file from empty @ block
    $@<@¢
        $!
    $=noOutput=before
    @@@file from nooutput @ block
    $@<@¢ nop
        $=noOutput = run in block $!
    @@@nach noOutput=$noOutput
    @@@file from 1 line @ block
    $@<@¢ $$. tstObjVF('w-Eins', 'w1-Eins') $!
    @@@file from 2 line @ block
    $@<@¢ $$.tstObjVF('w-Elf', 'w1-Elf')
        y='zwoelf' $$-y  $!
    @@@file from 3 line @ block
    $@<@¢ $$.tstObjVF('w einUndDreissig')    $$ +
    zweiUndDreissig $$ 33 $vv$!
    {{{ empty ¢ block
    $@<¢     $!
    {{{ empty ¢ block with comment
    $@<¢    $*+ abc
          $!
    {{{ one line ¢ block
    $@<¢ the only $"¢...$!" line $*+.
        $vv $!
    {{{ one line -¢ block
    $@<-¢ the only $"-¢...$!"  "line" $vv  $!
    {{{ empty #¢ block
    $@<#¢
$!
    {{{ one line #¢ block
    $@<#¢ the only $"-¢...$!"  "line" $vv $¢vv${x}$!
$!
$/tstCompFileBloSrc/ */
/*
$=/tstCompFileBlo/
    ### start tst tstCompFileBlo ######################################
    compile =, 72 lines: $=vv=value-of-vv
    run without input
    ###file from empty # block
    ###file from 1 line # block
    the only $ix+1/0 line $vv
    ###file from 2 line # block
    first line /0 $*+ no comment
    second and last line $$ $wie
    ===file from empty = block
    ===file from 1 line = block
    . the only line .
    ===file from 2 line = block
    . first line
    second and last line  .
    ---file from empty - block
    ---file from 1 line - block
    THE ONLY line 1
    ---file from 2 line = block
    FIRST line 1
    SECOND AND last  line 2
    ...file from empty . block
    ...file from 1 line . block
    tstR: @tstWriteoV1 isA :TstClassVF = v-Eins
    tstR:  .FLD1 = 1-Eins
    ...file from 2 line . block
    tstR: @tstWriteoV2 isA :TstClassVF = v-Elf
    tstR:  .FLD1 = 1-Elf
    tstR: @tstWriteoV3 isA :TstClassVF = val=zwoelf
    tstR:  .FLD1 = FLD1=zwoelf
    ...file from 3 line . block
    tstR: @tstWriteoV4 isA :TstClassVF = val=einUndDreissig
    tstR:  .FLD1 = FLD1=einUndDreissig
    zweiUndDreissig value-of-vv
    tstR: @tstWriteoV5 isA :TstClassVF = val=dreiUndDreissig
    tstR:  .FLD1 = FLD1=dreiUndDreissig
    @@@file from empty @ block
    @@@file from nooutput @ block
    @@@nach noOutput=run in block
    @@@file from 1 line @ block
    tstR: @tstWriteoV6 isA :TstClassVF = w-Eins
    tstR:  .FLD1 = w1-Eins
    @@@file from 2 line @ block
    tstR: @tstWriteoV7 isA :TstClassVF = w-Elf
    tstR:  .FLD1 = w1-Elf
    zwoelf
    @@@file from 3 line @ block
    tstR: @tstWriteoV8 isA :TstClassVF = val=w einUndDreissig
    tstR:  .FLD1 = FLD1=w einUndDreissig
    zweiUndDreissig
    33 value-of-vv
    {{{ empty ¢ block
    {{{ empty ¢ block with comment
    {{{ one line ¢ block
    . the only ¢...$! line value-of-vv .
    {{{ one line -¢ block
    THE ONLY -¢...$! line value-of-vv
    {{{ empty #¢ block
    {{{ one line #¢ block
    . the only $"-¢...$!"  "line" $vv $¢vv${x}$!
$/tstCompFileBlo/ */
    call tstComp2 'tstCompFileBlo', '='
    m.t.trans.0 = 0

/*
$=/tstCompFileObjSrc/
    $=vv=value-vv-1
    $=fE=<¢ $!
    $=f2=.$.<.¢s2o("f2 line 1" o2String($vv))
         tstObjVF("f2 line2") $!
    ---empty file $"$@<$fE"
    $@fE
    ---file with 2 lines $"$@<$f2"
    $@.<.f2
    $=vv=value-vv-2
    ---file with 2 lines $"$@<$f2"
    $@.<.f2
    $= dsn =- word(tstPdsMbr(tstFilename('libvb', 'r'), 'fileObj'),1) +
                tstFB('::V', 0)
    $@¢
        fi=jOpen(file($dsn),'>')
        call jWrite fi, 'line one on' $"$dsn"
        call jWrite fi, 'line two on' $"$dsn"
        call jClose fi
    $!
    ---file on disk out
    $@.<.dsn
$/tstCompFileObjSrc/ */
/*
$=/tstCompFileObj/
    ### start tst tstCompFileObj ######################################
    compile =, 20 lines: $=vv=value-vv-1
    run without input
    ---empty file $@<$fE
    ---file with 2 lines $@<$f2
    f2 line 1 value-vv-1
    tstR: @tstWriteoV1 isA :TstClassVF = val=f2 line2
    tstR:  .FLD1 = FLD1=f2 line2
    ---file with 2 lines $@<$f2
    f2 line 1 value-vv-1
    tstR: @tstWriteoV1 isA :TstClassVF = val=f2 line2
    tstR:  .FLD1 = FLD1=f2 line2
    ---file on disk out
    line one on $dsn
    line two on $dsn
$/tstCompFileObj/ */
    call tstComp2 'tstCompFileObj', '='

    return
endProcedure tstCompFile

tstCompPipe: procedure expose m.
/*
$=/tstCompPipe1/
    ### start tst tstCompPipe1 ########################################
    compile @, 1 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    (1 eins zwei drei 1)
    #jIn 2# zehn elf zwoelf?
    (1 zehn elf zwoelf? 1)
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    (1 zwanzig 21 22 23 24 ... 29| 1)
    #jIn eof 4#
$/tstCompPipe1/ */
    call tstComp1 '@ tstCompPipe1 3',
        , ' call pipePreSuf "(1 ", " 1)"'
/*
$=/tstCompPipe2/
    ### start tst tstCompPipe2 ########################################
    compile @, 2 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    ¢2 (1 eins zwei drei 1) 2!
    ¢2 (1 zehn elf zwoelf? 1) 2!
    ¢2 (1 zwanzig 21 22 23 24 ... 29| 1) 2!
$/tstCompPipe2/ */
    call tstComp1 '@ tstCompPipe2 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| call pipePreSuf "¢2 ", " 2!"'

/*
$=/tstCompPipe3/
    ### start tst tstCompPipe3 ########################################
    compile @, 3 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <3 ¢2 (1 eins zwei drei 1) 2! 3>
    <3 ¢2 (1 zehn elf zwoelf? 1) 2! 3>
    <3 ¢2 (1 zwanzig 21 22 23 24 ... 29| 1) 2! 3>
$/tstCompPipe3/ */
    call tstComp1 '@ tstCompPipe3 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| call pipePreSuf "¢2 ", " 2!"',
        , ' $| call pipePreSuf "<3 ", " 3>"'

/*
$=/tstCompPipe4/
    ### start tst tstCompPipe4 ########################################
    compile @, 7 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <3 ¢222 ¢221 ¢21 ¢20 (1 eins zwei drei 1) 20! 21! 221! 222! 3>
    <3 ¢222 ¢221 ¢21 ¢20 (1 zehn elf zwoelf? 1) 20! 21! 221! 222! 3>
    <3 ¢222 ¢221 ¢21 ¢20 (1 zwanzig 21 22 23 24 ... 29| 1) 20! 21! 221!+
    . 222! 3>
$/tstCompPipe4/ */
    call tstComp1 '@ tstCompPipe4 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| $@¢    call pipePreSuf "¢20 ", " 20!"',
        ,        ' $| call pipePreSuf "¢21 ", " 21!"',
        ,        ' $| $@¢      call pipePreSuf "¢221 ", " 221!"',
        ,                 ' $| call pipePreSuf "¢222 ", " 222!"',
        ,     '$!     $! ',
        , ' $| call pipePreSuf "<3 ", " 3>"'
    return
endProcedure tstCompPipe


tstCompPip2: procedure expose m.
/*
$=/tstCompPip21/
    ### start tst tstCompPip21 ########################################
    compile @, 3 lines:  $<¢ zeile eins .
    run without input
    (1  zeile eins  1)
    (1    zeile zwei  1)
    run with 3 inputs
    (1  zeile eins  1)
    (1    zeile zwei  1)
$/tstCompPip21/ */
    call tstComp1 '@ tstCompPip21 3',
        , ' $<¢ zeile eins ' ,
        , '   zeile zwei $!' ,
        , ' call pipePreSuf "(1 ", " 1)"'
/*
$=/tstCompPip22/
    ### start tst tstCompPip22 ########################################
    compile @, 3 lines: if ${>i1} then $@¢
    run without input
    #jIn eof 1#
    nachher
    run with 3 inputs
    #jIn 1# eins zwei drei
    <zeile 1: eins zwei drei>
    <zwei>
    nachher
$/tstCompPip22/ */
    call tstComp1 '@ tstCompPip22 3',
        , 'if ${>i1} then $@¢'          ,
        , ' $$ zeile 1: $i1 $$ zwei $| call pipePreSuf "<",">" $!',
        , ' $$ nachher '
    return
endProcedure tstCompPip2

tstCompRedir: procedure expose m.
/*
$=/tstCompRedir/
    ### start tst tstCompRedir ########################################
    compile @, 6 lines:  $=eins=<@¢ $@for vv $$ <$vv> $! .
    run without input
    #jIn eof 1#
    output eins .
    output piped zwei .
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    output eins <eins zwei drei> <zehn elf zwoelf?> <zwanzig 21 22 23 2+
    4 ... 29|>
    output piped zwei ab<eins zwei drei>yz ab<zehn elf zwoelf?>yz ab<zw+
    anzig 21 22 23 24 ... 29|>yz
$/tstCompRedir/ */
    call pipeIni
    call vRemove 'eins'  /* alte Variable loswerden */
    dsn = word(tstPdsMbr(tstFilename('libvb', 'r'), 'redir1'), 1)
    call vPut 'dsn', dsn
    call tstComp1 '@ tstCompRedir 3' ,
        , ' $=eins=<@¢ $@for vv $$ <$vv> $! ',
        , ' $$ output eins $-=¢$eins$!$; ',
        , ' $@for ww $$b${ww}y ',
        , '     $>$-¢ $dsn $! 'tstFB('::v', 0),
        ,         '$| call pipePreSuf "a", "z" $<.$eins',
        , ' $; $$ output piped zwei $-=¢$@<dsn$! '
    return
endProcedure tstCompRedir

tstCompComp: procedure expose m.
/*
$=/tstCompCompShell/
    ### start tst tstCompCompShell ####################################
    compile @, 5 lines: $$compiling shell $; $= rrr =.$.^compile $<@#/+
    aaa/
    run without input
    compiling shell
    running einmal
    RUN 1 COMPILED einmal
    #jIn eof 1#
    running zweimal
    RUN 1 COMPILED zweimal
    #jIn eof 2#
    run with 3 inputs
    compiling shell
    running einmal
    RUN 1 COMPILED einmal
    #jIn 1# eins zwei drei
    compRun eins zwei dreieinmal
    #jIn 2# zehn elf zwoelf?
    compRun zehn elf zwoelf?einmal
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    compRun zwanzig 21 22 23 24 ... 29|einmal
    #jIn eof 4#
    running zweimal
    RUN 1 COMPILED zweimal
    #jIn eof 5#
$/tstCompCompShell/ */
    call tstComp1 '@ tstCompCompShell 3',
        ,  "$$compiling shell $; $= rrr =.$.^compile $<@#/aaa/",
        ,  "call out run 1*1*1 compiled $cc; $@for v $$ compRun $v$cc",
        ,  "$/aaa/ $;",
        ,  "$=cc=einmal $$ running $cc $@rrr",
        ,  "$=cc=zweimal $$ running $cc $@rrr"
/*
$=/tstCompCompData/
    ### start tst tstCompCompData #####################################
    compile @, 5 lines: $$compiling data $; $= rrr =. $.^¢compile =$!  +
    $<@#/aaa/
    run without input
    compiling data
    running einmal
    call out run 1*1*1 compiled einmal
    running zweimal
    call out run 1*1*1 compiled zweimal
    run with 3 inputs
    compiling data
    running einmal
    call out run 1*1*1 compiled einmal
    running zweimal
    call out run 1*1*1 compiled zweimal
$/tstCompCompData/ */
    call tstComp1 '@ tstCompCompData 3',
        ,  "$$compiling data $; $= rrr =. $.^¢compile =$!  $<@#/aaa/",
        ,  "call out run 1*1*1 compiled $cc",
        ,  "$/aaa/ $;",
        ,  "$=cc=einmal $$ running $cc $@rrr",
        ,  "$=cc=zweimal $$ running $cc $@rrr"
    return
endProcedure tstCompComp

tstCompDir: procedure expose m.
/*
$=/tstCompDirSrc/
  'in src v1='$v1
  $#@ call out 'src @ out v1='$v1
  $#. $*(komm$*) s2o('src . v1=')
       $v1
  $#-
    'src - v1='$v1
  $#=
    src = v1=$v1
$/tstCompDirSrc/

$=/tstCompDir/
    ### start tst tstCompDir ##########################################
    compile @call out 'before v1='$v1 $=v1=eins $#. s2o('. v1='$-v1) $#+
    @ call out '@ v1='$v1 $#= = v1=$v1 $#- '- v1='$v1 $#-, 8 lines: 'in+
    . src v1='$v1
    run without input
    before v1=v1Before
    .. v1=eins
    @ v1=eins
    = v1=eins .
    - v1=eins
    in src v1=eins
    src @ out v1=eins
    src . v1=
    eins
    src - v1=eins
    src = v1=eins
$/tstCompDir/ */
    call compIni
    call vPut 'v1', 'v1Before'
    call tstComp2 'tstCompDir', "@call out 'before v1='$v1 $=v1=eins" ,
        "$#. s2o('. v1='$-v1) $#@ call out '@ v1='$v1" ,
        "$#= = v1=$v1 $#- '- v1='$v1 $#-"
/*
$=/tstCompDirPiSrc/
  zeile 1 v1=$v1
  zweite Zeile vor $"$@$#-"
  $#@  $@proc pi2 $@-¢
  $'zeile drei nach $@$#- v1='v1
  vierte und letzte Zeile $!
$/tstCompDirPiSrc/ */
/*
$=/tstCompDirPi/
    ### start tst tstCompDirPi ########################################
    compile @call pipePreSuf '<','>' $=v1=eiPi $<.$pi2 $#=, 5 lines: ze+
    ile 1 v1=$v1
    run without input
    <zeile drei nach $@$#- v1=V1>
    <VIERTE UND LETZTE ZEILE>
    zeile 1 v1=eiPi
    zweite Zeile vor $@$#-
$/tstCompDirPi/ */
    call tstComp2 'tstCompDirPi',
            , "@call pipePreSuf '<','>' $=v1=eiPi $<.$pi2 $#="
    return
endProcedure tstCompDir

tstCompColon: procedure expose m.
/*
$=/tstCompColon1/
    ### start tst tstCompColon1 #######################################
    compile :, 12 lines: vA = valueVonA
    run without input
    vA = valueVonA
    vA=valueVonA vB=valueVonB vC=valueVonC
    vC=valueVonC vD=valueVonD vE=valueVonvE
    vF=6
$/tstCompColon1/ */
    call tstComp1 ': tstCompColon1',
        , 'vA = valueVonA' ,
        , ' $$ vA = $vA' ,
        , '        * kommentar ' ,
        , '=vB=- "valueVonB"' ,
        , '=/vC/valueVonC$/vC/' ,
        , ' $$ vA=$vA vB=$vB vC=$vC' ,
        , '=/vD/valueVonD',
        , '$/vD/ vE=valueVonvE' ,
        , '        * kommentar ' ,
        , ' $$ vC=$vC vD=$vD vE=$vE',
        , 'vF=- 2*3 $=vG=@@¢ $$ vF=$vF$!' ,
        , '@vG'

/*
$=/tstCompColon2/
    ### start tst tstCompColon2 #######################################
    compile :, 7 lines: ix=0
    run without input
    #jIn eof 1#
    proc p1 arg(2) total 0 im argumentchen
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <<for 1 -> eins zwei drei>>
    <<for 2 -> zehn elf zwoelf?>>
    <<for 3 -> zwanzig 21 22 23 24 ... 29|>>
    proc p1 arg(2) total 3 im argumentchen
$/tstCompColon2/

*/
    call tstComp1 ': tstCompColon2 3',
        , 'ix=0' ,
        , 'for v @:¢ix=- $ix+1' ,
        , ' $$ for $ix -> $v' ,
        , '! | @¢call pipePreSuf "<<",">>"',
        , '$! @%¢p1 total $ix im argumentchen$!',
        , 'proc @:/p1/$$- "proc p1 arg(2)" arg(2)' ,
        , '/p1/'
/*
$=/tstCompColon3/
    ### start tst tstCompColon3 #######################################
    compile :, 11 lines: tc3Eins=freeVar1
    run without input
    tc3Eins=freeVar1 o2&tc3Eins= o2&tc3Zwei=
    tc3Eins=freeVar1 o2&tc3Eins=with3Eins o2&tc3Zwei=with3Zwei
    tc3Eins=freeVar1 o2&tc3Eins=ass4Eins o2&tc3Zwei=with5Zwei
    o3&tc3Eins=ass4Eins o3&tc3Zwei=with5 o3 Zwei
    tc3Eins=freeVar1 o2&tc3Eins=ass4Eins o2&tc3Zwei=with5Zwei
$/tstCompColon3/
*/
    call classNew 'n? TstCompColon3 u f tc3Eins v, f tc3Zwei v'
    showO2 = 'tc3Eins=$tc3Eins' ,
            'o2&tc3Eins=${o2&tc3Eins} o2&tc3Zwei=${o2&tc3Zwei}'
    showO3 = 'o3&tc3Eins=${o3&tc3Eins} o3&tc3Zwei=${o3&tc3Zwei}'
    call tstComp1 ': tstCompColon3',
        , 'tc3Eins=freeVar1' ,
     , 'o2 =. oNew("TstCompColon3")' ,
        , '$$' showO2 ,
        , 'with $o2 $@:¢tc3Eins = with3Eins',
        ,     'tc3Zwei = with3Zwei',
        ,    '! $$' showO2 ,
        , '{o2&tc3Eins} = ass4Eins',
        , 'with $o2 $=tc3Zwei = with5Zwei',
        , '$$' showO2 ,
        , 'with o3 =. oCopy($o2) $=tc3Zwei = with5 o3 Zwei',
        , '$$' showO3 '$$' showO2
    return
endProcedure tstCompColon

tstCompTable: procedure expose m.
/*
$=/tstCompTable1/
    ### start tst tstCompTable1 #######################################
    compile :, 6 lines: table $*( sdf $*)   .
    run without input
    tstR: @tstWriteoV2 isA :<TstCT1Class>
    tstR:  .fEins = v1
    tstR:  .fZwei = valueZwei
    tstR:  .fDrei = undDrei
    zweite
    tstR: @tstWriteoV3 isA :<TstCT1Class>
    tstR:  .fEins = w1
    tstR:  .fZwei = wZwei
    tstR:  .fDrei = wwwDrei
$/tstCompTable1/

 */
    cl = classNew('n* CompTable u f fEins v, f fZwei v, f fDrei v')
    call tstComp1 ': tstCompTable1',
        , 'table $*( sdf $*)   ' ,
        , 'fEins   fZwei $*(....$*) fDrei  ' ,
        , '$@¢call mAdd t.trans, className("'cl'") "<TstCT1Class>"$!',
        , '    v1     valueZwei undDrei     ' ,
        , '$$ zweite',
        , ' w1 wZwei                    wwwDrei     '


/*
$=/tstCompWithNew/
    ### start tst tstCompWithNew ######################################
    compile :, 12 lines: withNew nn $*( sdf $*)  $@:¢  .
    run without input
    tstR: @tstWriteoV2 isA :<TstCT1Class>
    tstR:  .fEins = withNewValue fEins
    tstR:  .fZwei = withNewValue fZwei
    tstR:  .fDrei = withNewValuel drei
    tstR: @tstWriteoV3 isA :<TstCT1Class>
    tstR:  .fEins = withNewValue fEinsB
    tstR:  .fZwei = withNewValue fZweiB
    tstR:  .fDrei = withNewValue fDreiB
    tstR: @tstWriteoV4 isA :<TstCT1Class>
    tstR:  .fEins = withValue fEinsC
    tstR:  .fZwei = .
    tstR:  .fDrei = withValue fDreiC
$/tstCompWithNew/
*/
    call tstComp1 ': tstCompWithNew',
        , 'withNew nn $*( sdf $*)  $@:¢  ' ,
        , 'fEins = withNewValue fEins' ,
        , 'fZwei = withNewValue fZwei' ,
        , '$@¢call mAdd t.trans, className("'cl'") "<TstCT1Class>"',
        , '$@:¢   fDrei = withNewValuel drei $! $!! $$ $.nn' ,
        , 'withNew out $*( sdf $*)  $@:¢  ' ,
        , 'fEins = withNewValue fEinsB' ,
        , 'fZwei = withNewValue fZweiB',
        , 'fDrei = withNewValue fDreiB',
        , '! with out =. oNew('cl') $@:¢',
        , 'fEins = withValue fEinsC' ,
        , 'fDrei = withValue fDreiC $!'

    return
endProcedure tstCompTable

tstCompSql: procedure expose m.
/*
$=/tstCompSqlSrc/
$@=¢
   select strip(creator) cr, strip(name) tb,
            (row_number()over())*(row_number()over()) rr
       from sysibm.sysTables
       where creator='SYSIBM' and name like 'SYSTABL%'
       order by 2 fetch first 4 rows only
$!
$| call sqlSel
$| call fTabAuto
$/tstCompSqlSrc/
$=/tstCompSql/
    ### start tst tstCompSql ##########################################
    compile @, 9 lines: $@=¢
    run without input
    CR     TB                RR
    SYSIBM SYSTABLEPART       1
    SYSIBM SYSTABLEPART_HIST  4
    SYSIBM SYSTABLES          9
    SYSIBM SYSTABLESPACE     16
$/tstCompSql/
*/
    call sqlConnect
    call tstComp2 'tstCompSql', '@'

    call sqlDisConnect
    return
endProcedure tstCompFile
tstTut0: procedure expose m.
/*
$=/tstTut01Src/
$#=
$*+>.fSub()                               Kommentar
$*+>~tmp.jcl(t)                           Kommentar
$*+@=¢                                    Kommentar
$=subsys=DP4G
$=db=DA540769
$=ts=A977A
$*+@<~wk.jcl(jc)                          Kommentar
??* ?-¢sysvar(sysnode) date() time()?!ts=$ts 10*len=$-¢length($ts)*10$!
//P02     EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769C.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
  $@¢if right($ts, 2) == '7A' then $@=¢
    FULL YES
  $! else
    $$ $''    FULL NO
  $!
    SHRLEVEL CHANGE
$*+!                                      Kommentar
$#out original/src
$/tstTut01Src/
$=/tstTut01/
    ### start tst tstTut01 ############################################
    compile , 28 lines: $#=
    run without input
    ??* ?-¢sysvar(sysnode) date() time()?!ts=A977A 10*len=50
    //P02     EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A540769C.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977A*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    FULL YES
    SHRLEVEL CHANGE
$/tstTut01/
$=/tstTut02Src/
$#@
$**>.fSub()
$**@¢
$=subsys=DP4G
$=db=DA540769
$=jx=0
$@do tx = 976 to 977 $@=¢
    $=ts=A$tx
    $=jx=-$jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$**!
$#out original/src
$/tstTut02Src/
$=/tstTut02/
    ### start tst tstTut02 ############################################
    compile , 28 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut02/
$=/tstTut03Src/
$#@
$=subsys=DP4G
$@:¢table
      db         ts
      DGDB9998   A976
      DA540769   A977
$!
$** $| call fTabAuto
$**    $#end
$|
$=jx=0
$@forWith o $@=¢
    $=jx=-$jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$#out original/src
$/tstTut03Src/
$=/tstTut03/
    ### start tst tstTut03 ############################################
    compile , 31 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut03/
$=/tstTut04Src/
$#@
$=subsys=DP4G
$=db=DA540769
call sqlConnect $subsys
$@=¢  select dbName  db , tsName  ts
          from sysibm.sysTables
          where creator = 'SYSIBM' and name like 'SYSINDEXPAR%'
          order by name desc
$!
$| call sqlSel
$** $| call fTabAuto
$** $#end
$|
$=jx=0
$@forWith o $@=¢
    $=jx=-$jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$TS    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $DB.$TS*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
call sqlDisConnect
$#out original/src
$/tstTut04Src/
$=/tstTut04/
    ### start tst tstTut04 ############################################
    compile , 35 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CSYSHIST     EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DSNDB06 .SYSHIST *   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CSYSTSIPT    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DSNDB06 .SYSTSIPT*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut04/
$=/tstTut05Src/
$#:
subsys = DP4G
lst =<:¢withNew out :¢
    db = DGDB9998
    ts =<:¢table
             ts
             A976
             A977
    $!
    db = DA540769
    <|/ts/
             ts
             A976
             A975
    /ts/
!
$** $$. $lst
$** $@ct $@¢$=tool =. $.compile@ $<~.wk.rexx(wshtut06)$!
$** $@$tool
$@do sx=1 to ${lst.0} $@¢
    $=db = ${lst.$sx.db}
    $** $$. ${lst.$sx}
    $@do tx=1 to ${lst.$sx.ts.0} $@=¢
        $*+ $$. ${lst.$sx.ts.$tx}
        $=ts= ${lst.$sx.ts.$tx.ts}
        $@¢ say $-=¢subsys $subsys db $db ts $ts $! $!
        $@copy()
        $!
    $!
$@ct $@¢
cl = classNew('n? DbTsList u s' ,
     classNew('n? DbTs u f db v, f ts s' ,
     classNew('n? Ts u f ts v')))
$=lst=. oNew(cl)
$!
$@proc copy $@=¢
$@ct $=jx=0
$=jx=-$jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$#out original/src
$/tstTut05Src/
$=/tstTut05/
    ### start tst tstTut05 ############################################
    compile , 56 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407693 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407693.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407694 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA975    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407694.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A975*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut05/
   tstTut06   ==> platz für copy aus tut05
$=/tstTut07Src/
$**$>.fEdit()
call sqlConnect dp4g
$@:¢table
   ts
VTCASHFLOW
VTCASHFLOWAMOUNT
VTINSTRFLATDATA
$!
$| $@=¢
    select name, statstime, strip(dbName) || '.' || strip(tsName) dbts
       from sysibm.sysTables
       where creator = 'VDPS2' and name in
  $=co=(
  $@forWith t $@=¢
                                           $co '$ts'
      $=co=,
  $!
                                           )
$!
$| call sqlSel
$** $| call fTabAuto
$|
$=jx=0
$@forWith t $@=¢
$=jx=-$jx+1
//A540769$jx JOB (CP00,KE50),
//       'CATALOG',MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//STEP$jx EXEC PGM=DSNUTILB,TIME=1440,
//   PARM=(DP4G,'A540769$jx.RUNSTA'),
//   REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//UTPRINT  DD SYSOUT=*
//SYSOUT   DD SYSOUT=*
//RNPRIN01 DD SYSOUT=*
//SYSIN DD *
  LISTDEF LST#STA   INCLUDE TABLESPACE $DBTS
   OPTIONS EVENT (ITEMERROR, SKIP)

   RUNSTATS TABLESPACE LIST LST#STA
         SHRLEVEL CHANGE
           INDEX(ALL KEYCARD)
           REPORT YES UPDATE ALL
$!
call sqlDisconnect
$#out original/src
$/tstTut07Src/
$=/tstTut07/
$=/tstTut07/
    ### start tst tstTut07 ############################################
    compile , 47 lines: $**$>.fEdit()
    run without input
    //A5407691 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP1 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407691.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV27A1T.VDPS329
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
    //A5407692 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP2 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407692.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV28A1T.VDPS390
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
    //A5407693 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP3 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407693.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV21A1T.VDPS004
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
$/tstTut07/
$=/tstTut08Src/
$/tstTut08Src/
$=/tstTut08/
$/tstTut08/
*/
    call sqlIni
    call sqlDisconnect '*'
    call tstComp2 'tstTut01'
    call tstComp2 'tstTut02'
    call tstComp2 'tstTut03'
    if m.err.os == 'TSO' then do
        call tstComp2 'tstTut04'
        /* call tstComp2 'tstTut05' */
     /* call tstComp2 'tstTut07'  ???? anderes Beispiel ???? */
        end
    call tstTotal
    return
endProcedure tstTut0
/****** tstBase ********************************************************
     test the basic classes
***********************************************************************/
tstBase: procedure expose m.
    call tstTstSay
    call tstM
    call tstUtc2d
    call tstMap
    call tstMapVia
    call tstClass
    call tstClass2
    call tstClass3
    call scanIni
    call tstO
    call classIni
    call tstF
    call tstFtst
    call tstMCat
    call tstOEins
    call jIni
    call tstJSay
    call tstJ
    call tstJ2
    call tstJCatSql
    call catIni
    call tstCat
    call pipeIni
    CALL TstEnv
    CALL TstEnvCat
    call tstPipe
    call tstPipeS
    call tstEnvVars
    call tstvWith
    call tstTotal
    call tstPipeLazy
    call tstEnvClass
    call tstFile
    call tstFileList
    call tstFE
    call tstFTab
    call tstFmt
    call tstfUnits
    call tstCsv
    call tstTotal
    call tstSb
    call tstSb2
    call tstScan
    call ScanReadIni
    call tstScanRead
    call tstScanUtilInto
    call tstScanWin
    call tstScanSQL
    call tstTotal
    return
endProcedure tstBase

/*--- test the tst Module, check the output visually  ----------------*/
tstTstSay: procedure expose m.
    call tstIni
    oldErr = m.tst.err
    oldNames = m.tst.errNames
    say '+++ tstTstSay start with' oldErr 'totErrs and',
            m.tst.tests 'tests'
/*
$=/tstTstSayEins/
    ### start tst tstTstSayEins #######################################
    test eins einzige testZeile
$/tstTstSayEins/
$=/tstTstSayZwei/
    ### start tst tstTstSayZwei #######################################
    zwei 1. testZeile
    zwei 2. und letzte testZeile
$/tstTstSayZwei/
$=/tstTstSayDrei/
    ### start tst tstTstSayDrei #######################################
    drei 1. testZeile vor empty Zeile
    ..
    drei 3. testZeile vor 10 space
    .          .
    drei 5. testZeile ziemlich lang 66                                 +
    .                                77  5   10   15++++++++++++++++++++
    .+++++++++++++++++++++++++++++++++++++++++++.
$/tstTstSayDrei/
*/
    call tst x, 'tstTstSayEins'
    call tstOut x, "test eins einzige testZeile"
    call tstEnd x, 'err 0'


    call tst x, 'tstTstSayZwei'
    call tstOut x, "zwei 1. testZeile"
    call tstOut x, "zwei 2. und letzte testZeile"
    call tstEnd x, 'err 0'

    call tst x, 'tstTstSayZwei'
    call tstOut x, "zwei 1. testZeile "    /* ein space zuviel */
    call tstOut x, "zwei 2. und letzte testZeile"
    call tstOut x, "zwei 3. zuviel"
    call tstEnd x, 'err 3'

    call tst y, 'tstTstSayDrei'
    call tstOut y, 'drei 1. testZeile vor empty Zeile'
    call tstOut y, ''
    call tstOut y, 'drei 3. testZeile vor 10 space'
    call tstOut y, left('', 10)
    call tstOut y, 'drei 5. testZeile ziemlich lang',
                left(66, 66) left('77  5   10   15', 77, '+')
    call tstEnd y, 'err 0'
    if m.y.err <> 0 then
        call err '+++ tstTstSay errs' m.x.err 'expected' 0
    if m.tst.err <> oldErr + 3 then
        call err '+++ tstTstSay totErrs' m.tst.err 'expected' 3
    say '+++ tstTstSay end Ok with' m.tst.err 'totErrs and' ,
        m.tst.tests 'tests'
    m.tst.err = oldErr
    m.tst.errNames = oldNames
    return
endProcedure tstTstSay

tstMark: procedure expose m.
parse arg m, msg
    if symbol('m.m') == 'VAR' then
        m.m = msg';' m.m
    else
        m.m = msg 'new'
    return m
endProcedure tstMark
tstM: procedure expose m.
/*
$=/tstMa/
    ### start tst tstMa ###############################################
    mNew() 1=newM1 2=newM2
    mNew(tst...) 2=2 new 3=4; 3; 1 new 4=5 new
    iter 4; 3; 1 new
    iter 2 new
    iter 5 new
$/tstMa/
*/
    call tst t, 'tstMa'
    m1 = mNew()
    m2 = mNew()
    m.m1 = 'newM1'
    m.m2 = 'newM2'
    call tstOut t, 'mNew() 1='m.m1 '2='m.m2
    call mNewArea 'tst'm1
    t1 = tstMark(mNew('tst'm1), '1')
    t2 = tstMark(mNew('tst'm1), '2')
    call mFree tstMark(t1, '3')
    t3 = tstMark(mNew('tst'm1), '4')
    t4 = tstMark(mNew('tst'm1), '5')
    call tstOut t, 'mNew(tst...) 2='m.t2 '3='m.t3 '4='m.t4
    i = mIterBegin('tst'm1)
    do while assNN('i', mIter(i))
        call tstOut t, 'iter' m.i
        end
    call tstEnd t
/*
$=/tstM/
    ### start tst tstM ################################################
    symbol m.b LIT
    symbol m.a LIT
    mAdd a A.2
    mAdd a A.3
    m.a: 3: 1=eins 2=zwei 3=drei 4=M.A.4
    m.c: 5: 1=c vorAddSt a 2=eins 3=zwei 4=drei 5=c nacAddSt a 6=M.C.6
$/tstM/ */


    drop m.b m.a m.a.0 m.a.1 m.a.2
    call tst t, 'tstM'
    call tstOut t, 'symbol m.b' symbol('m.b')
    m.b = 1
    call tstOut t, 'symbol m.a' symbol('m.a')
    call tstOut t, 'mAdd a' mAdd(mCut(a, 0), 'eins', 'zwei')
    call tstOut t, 'mAdd a' mAdd(a, 'drei')
    call tstOut t, 'm.a:' m.a.0': 1='m.a.1 '2='m.a.2 '3='m.a.3 '4='m.a.4
    call mAdd mCut(c, 0), 'c vorAddSt a'
    call mAddSt c, a
    call mAdd c, 'c nacAddSt a'
    call tstOut t, 'm.c:' m.c.0': 1='m.c.1 '2='m.c.2 '3='m.c.3,
                    '4='m.c.4 '5='m.c.5 '6='m.c.6
    call tstEnd t
    return
endProcedure tstM

tstMCat: procedure expose m.
/*
$=/tstMCat/
    ### start tst tstMCat #############################################
    mCat(0, )                     =;
    mCat(0, 1)                    =;
    mCat(0, 112222%,%,%,11)       =;
    mCat(0, 231%,%c%,1%,2)        =;
    mCat(0, 33341%,%c2@%c%,1%,333)=;
    mCat(0, 45%,1%c2@%c3@%c%,%,4) =;
    mCat(1, )                     =eins;
    mCat(1, 1)                    =eins;
    mCat(1, 112222%,%,%,11)       =eins11;
    mCat(1, 231%,%c%,1%,2)        =1eins2;
    mCat(1, 33341%,%c2@%c%,1%,333)=1eins2eins333;
    mCat(1, 45%,1%c2@%c3@%c%,%,4) =1eins2eins3eins4;
    mCat(2, )                     =einszwei;
    mCat(2, 1)                    =eins1zwei;
    mCat(2, 112222%,%,%,11)       =eins112222zwei11;
    mCat(2, 231%,%c%,1%,2)        =1eins231zwei2;
    mCat(2, 33341%,%c2@%c%,1%,333)=1eins2eins33341zwei2zwei333;
    mCat(2, 45%,1%c2@%c3@%c%,%,4) =1eins2eins3eins451zwei2zwei3zwei4;
    mCat(3, )                     =einszweidrei;
    mCat(3, 1)                    =eins1zwei1drei;
    mCat(3, 112222%,%,%,11)       =eins112222zwei112222drei11;
    mCat(3, 231%,%c%,1%,2)        =1eins231zwei231drei2;
    mCat(3, 33341%,%c2@%c%,1%,333)=1eins2eins33341zwei2zwei33341drei2dr+
    ei333;
    mCat(3, 45%,1%c2@%c3@%c%,%,4) =1eins2eins3eins451zwei2zwei3zwei451d+
    rei2drei3drei4;
$/tstMCat/ */
    call mIni
    call tst t, "tstMCat"
     m.qq.1 = "eins"
     m.qq.2 = "zwei"
     m.qq.3 = "drei"
     do qx = 0 to 3
         m.qq.0 = qx
         call tstMCat1 qx
         call tstMCat1 qx, '1'
         call tstMCat1 qx, '112222%,%,%,11'
         call tstMCat1 qx, '231%,%c%,1%,2'
         call tstMCat1 qx, '33341%,%c2@%c%,1%,333'
         call tstMCat1 qx, '45%,1%c2@%c3@%c%,%,4'
         end
     call tstEnd t
     return
endProcedure tstMCat

tstMCat1: procedure expose m.
parse arg m.qq.0, fmt
    call out left("mCat("m.qq.0"," fmt")", 30)"="mCat(qq, fmt)";"
return
endProcedure tstMCat1

tstMap: procedure expose m.
/*
$=/tstMap/
    ### start tst tstMap ##############################################
    mapNew m keys m-keys 0
    map m zwei --> 2
    map m Zwei is not defined
    map stem m-keys 4
    map m eins --> 1
    map m zwei --> 2
    map m drei --> 3
    map m vier --> 4
    *** err: duplicate key eins in map m
    map m zwei is not defined
    q 2 zw dr
    map stem Q 2
    map Q zw --> 2Q
    map Q dr --> 3Q
    map stem m 3
    map m eins --> 1
    map m zwei --> 2PUT
    map m vier --> 4PUT
    *** err: duplicate key zwei in map m
    tstMapLong eins keys 3
    tstMapLong zweiMal keys 48
    tstMapLong dreiMal keys 93
    tstMapLong vier    keys 138
    tstMapLong <fuenf> keys 188
    tstMap clear keys 0
    inline1 3
    inline1 1 ==    inline1 eins==
    inline1 2 ====
    inline1 3 ==    inline1 drei==
    inline2 1 1 ==    inline2 eins==
    inline3 ?
$/tstMap/ */
/*
$=/tstMapInline1/
    inline1 eins

    inline1 drei
$/tstMapInline1/ */
/*
$=/tstMapInline2/
    inline2 eins
$/tstMapInline2/ */

    call tst t, 'tstMap'
    m = mapNew('K')
    ky = mapKeys(m)
    call mAdd t'.TRANS', m 'm', ky 'm-keys'
    call tstOut t, 'mapNew' m 'keys' ky m.ky.0
    call mapAdd m, 'eins', 1
    call mapAdd m, 'zwei', 2
    call mapAdd m, 'drei', 3
    call mapAdd m, 'vier', 4
    call tstMapShow m, 'zwei'
    call tstMapShow m, 'Zwei'
    call tstMapShowSt m, mapKeys(m)
    call mapAdd m, 'eins', 1
    call mapReset m, '='
    call tstMapShow m, 'zwei'
    call mapAdd m, 'eins', 1
    call mapAdd m, 'zwei', 2
    call mapPut m, 'zwei', 2Put
    call mapPut m, 'vier', 4Put
    call mapReset q, '='
    call mapAdd q, 'zw', 2q
    call mapAdd q, 'dr', 3q
    call tstOut t, 'q' m.q.0 m.q.1 m.q.2
    call tstMapShowSt q, mapKeys(q)
    call tstMapShowSt m, mapKeys(m)
    call mapAdd m, 'zwei', 2addDup
    call tstMapLong m, 'eins'      ,201, 2000, -2, 2
    call tstMapLong m, 'zweiMal'   ,201, 2000, -2, 2
    call tstMapLong m, 'dreiMal'   ,201, 2000,  2,-2
    call tstMapLong m, 'vier   '   ,2010, 201, -2, 2
    call tstMapLong m, '<fuenf>'   ,2010, 201,  2,-2
    call mapClear m
    keys = mapKeys(m)
    call tstOut t, 'tstMap clear keys' m.keys.0
    i = mapInline('tstMapInline1')
    call tstOut t, 'inline1' m.i.0
    do x=1 to m.i.0
        call tstOut t, 'inline1' x '=='m.i.x'=='
        end
    i = mapInline('tstMapInline2')
    call tstOut t, 'inline2' m.i.0 '1 =='m.i.1'=='
    call tstOut t, 'inline3' mapInline('tstMapInline3', 'r')'?'
    call tstEnd t
    return
endProcedure tstMap

tstMapLong: procedure expose m.
parse arg m, w, f1, t1, f2, t2
    if f1 < t1 then
        b1 = 201
    else
        b1 = -201
    if f2 < t2 then
        b2 = 1
    else
        b2 = -1
    lo = copies(w, 2100 % length(w))
    keys = mapKeys(m)
    keyCn = m.keys.0
    call tstOut t, 'tstMapLong' w 'keys' keyCn
    do x = f1 by b1 to t1
        do y = x+f2 by b2 to x+t2
            k = left(lo, y)
            if mapHasKey(m, k) then
                call err 'mapLong hasKey before' w y
            call mapAdd m, k, w y
            if \ mapHasKey(m, k) then
                call err 'mapLong \ hasKey after' w y
            if mapGet(m, k) \== w y then
                call err 'mapLong \ get <> ' w y
            keys = mapKeys(m)
            if keyCn + 1 \= m.keys.0 then
                call err 'mapLong keys .0 <> ' w y
            keyCn = m.keys.0
            if k \== m.keys.keyCn then
                call err 'mapLong keys . ' keyCn '<>' w y
            end
        end
    return
endProcedure tstMapLong

tstMapVia: procedure expose m.
/*
$=/tstMapVia/
    ### start tst tstMapVia ###########################################
    map M K --> A
    mapVia(m, K)      A
    *** err: missing m.A at 3 in mapVia(M, K|)
    mapVia(m, K|)     M.A
    mapVia(m, K|)     valAt m.a
    mapVia(m, K|)     valAt m.a
    *** err: missing m.A.aB at 5 in mapVia(M, K|aB)
    mapVia(m, K|aB)   M.A.aB
    mapVia(m, K|aB)   valAt m.A.aB
    *** err: missing m.valAt m.a at 4 in mapVia(M, K||)
    mapVia(m, K||)    M.valAt m.a
    mapVia(m, K||)    valAt m.valAt m.a
    mapVia(m, K||F)   valAt m.valAt m.a.F
$/tstMapVia/ */
    call tst t, 'tstMapVia'
    u = 'A.aB'
    v = 'valAt m.a'
    drop m.a m.u m.v m.v.f
    call mapReset m, 'K'
    call mapAdd m, k, a
    call tstMapShow m, k
    call tstOut t, 'mapVia(m, K)     ' mapVia(m, 'K')
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    m.a = v
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    call tstOut t, 'mapVia(m, K|aB)  ' mapVia(m, 'K|aB')
    m.u = 'valAt m.'u
    call tstOut t, 'mapVia(m, K|aB)  ' mapVia(m, 'K|aB')
    call tstOut t, 'mapVia(m, K||)   ' mapVia(m, 'K||')
    v = m.a
    m.v = 'valAt m.'v
    m.v.f = 'valAt m.'v'.F'
    call tstOut t, 'mapVia(m, K||)   ' mapVia(m, 'K||')
    call tstOut t, 'mapVia(m, K||F)  ' mapVia(m, 'K||F')
    call tstEnd t
    return
endProcedure tstMapVia

tstMapShow: procedure expose m.
parse arg a, key
    if mapHasKey(a, key) then
        call tstOut t, 'map' a key '-->' mapGet(a, key)
    else
        call tstOut t, 'map' a key 'is not defined'
    return
endProcedure tstMapShow

tstMapShowSt: procedure expose m.
parse arg a, st
    call tstOut t, 'map stem' st m.st.0
    do wx=1 to m.st.0
        call tstMapShow a, m.st.wx
        end
    return
endProcedure tstMapShow

tstClass2: procedure expose m.
/*
$=/tstClass2/
    ### start tst tstClass2 ###########################################
    @CLASS.10 :class = u
    . choice u union
    .  .NAME = class
    . stem 8
    .  .1 refTo @CLASS.4 :class = u
    .   choice u union
    .    .NAME = v
    .   stem 3
    .    .1 refTo @CLASS.1 :class = m
    .     choice m union
    .      .NAME = oCopy
    .      .MET = return oCopyV(m, t)
    .     stem 0
    .    .2 refTo @CLASS.2 :class = m
    .     choice m union
    .      .NAME = o2String
    .      .MET = return m.m
    .     stem 0
    .    .3 refTo @CLASS.3 :class = m
    .     choice m union
    .      .NAME = o2File
    .      .MET = return file(m.m)
    .     stem 0
    .  .2 refTo @CLASS.13 :class = c
    .   choice c union
    .    .NAME = u
    .   stem 1
    .    .1 refTo @CLASS.12 :class = u
    .     choice u union
    .      .NAME = .
    .     stem 1
    .      .1 refTo @CLASS.11 :class = f
    .       choice f union
    .        .NAME = NAME
    .       stem 1
    .        .1 refTo @CLASS.4 done :class @CLASS.4
    .  .3 refTo @CLASS.14 :class = c
    .   choice c union
    .    .NAME = f
    .   stem 1
    .    .1 refTo @CLASS.12 done :class @CLASS.12
    .  .4 refTo @CLASS.16 :class = c
    .   choice c union
    .    .NAME = s
    .   stem 1
    .    .1 refTo @CLASS.15 :class = u
    .     choice u union
    .      .NAME = .
    .     stem 0
    .  .5 refTo @CLASS.17 :class = c
    .   choice c union
    .    .NAME = c
    .   stem 1
    .    .1 refTo @CLASS.12 done :class @CLASS.12
    .  .6 refTo @CLASS.18 :class = c
    .   choice c union
    .    .NAME = r
    .   stem 1
    .    .1 refTo @CLASS.15 done :class @CLASS.15
    .  .7 refTo @CLASS.21 :class = c
    .   choice c union
    .    .NAME = m
    .   stem 1
    .    .1 refTo @CLASS.20 :class = u
    .     choice u union
    .      .NAME = .
    .     stem 2
    .      .1 refTo @CLASS.11 done :class @CLASS.11
    .      .2 refTo @CLASS.19 :class = f
    .       choice f union
    .        .NAME = MET
    .       stem 1
    .        .1 refTo @CLASS.4 done :class @CLASS.4
    .  .8 refTo @CLASS.23 :class = s
    .   choice s union
    .   stem 1
    .    .1 refTo @CLASS.22 :class = r
    .     choice r union
    .     stem 1
    .      .1 refTo @CLASS.10 done :class @CLASS.10
$/tstClass2/

$=/tstClass2oo/
    ### start tst tstClass2 ###########################################
    @CLASS.4 isA :class = u
    . choice u union
    .  .NAME = class
    .  stem 7
    .   .1 refTo @CLASS.1 :class = u
    .    choice u union
    .     .NAME = v
    .     stem 2
    .      .1 refTo @CLASS.20 :class = m
    .       choice m union
    .        .NAME = o2String
    .        .MET = return m.m
    .      .2 refTo @CLASS.108 :class = m
    .       choice m union
    .        .NAME = o2File
    .        .MET = return file(m.m)
    .   .2 refTo @CLASS.5 :class = c
    .    choice c union
    .     .NAME = u
    .     .CLASS refTo @CLASS.6 :class = u
    .      choice u union
    .       .NAME = .
    .       stem 2
    .        .1 refTo @CLASS.7 :class = f
    .         choice f union
    .          .NAME = NAME
    .          .CLASS refTo @CLASS.1 done :class @CLASS.1
    .        .2 refTo @CLASS.8 :class = s
    .         choice s .CLASS refTo @CLASS.9 :class = r
    .          choice r .CLASS refTo @CLASS.4 done :class @CLASS.4
    .   .3 refTo @CLASS.10 :class = c
    .    choice c union
    .     .NAME = f
    .     .CLASS refTo @CLASS.11 :class = u
    .      choice u union
    .       .NAME = .
    .       stem 2
    .        .1 refTo @CLASS.7 done :class @CLASS.7
    .        .2 refTo @CLASS.12 :class = f
    .         choice f union
    .          .NAME = CLASS
    .          .CLASS refTo @CLASS.9 done :class @CLASS.9
    .   .4 refTo @CLASS.13 :class = c
    .    choice c union
    .     .NAME = s
    .     .CLASS refTo @CLASS.12 done :class @CLASS.12
    .   .5 refTo @CLASS.14 :class = c
    .    choice c union
    .     .NAME = c
    .     .CLASS refTo @CLASS.11 done :class @CLASS.11
    .   .6 refTo @CLASS.15 :class = c
    .    choice c union
    .     .NAME = m
    .     .CLASS refTo @CLASS.16 :class = u
    .      choice u union
    .       .NAME = .
    .       stem 2
    .        .1 refTo @CLASS.7 done :class @CLASS.7
    .        .2 refTo @CLASS.17 :class = f
    .         choice f union
    .          .NAME = MET
    .          .CLASS refTo @CLASS.1 done :class @CLASS.1
    .   .7 refTo @CLASS.18 :class = c
    .    choice c union
    .     .NAME = r
    .     .CLASS refTo @CLASS.12 done :class @CLASS.12
$/tstClass2oo/ */

    call classIni
    call tst t, 'tstClass2'
    call classOut m.class_C, m.class_C
    call tstEnd t
    return
endProcedure tstClass2

tstClass3: procedure expose m.
/*
$=/tstClass3/
    ### start tst tstClass3 ###########################################
    met v#o2String return m.m
    met w#o2String return substr(m, 2)
    met w#o2String return substr(m, 2)
    *** err: no method nonono in class w
    met w#nonono 0
    met class#oCopy return classCopy('<class class>', m, t)
    t1 4 flds FV, FR
    clear q1 FV= FR= FW=! FO=
    orig R1 FV=valFV FR=refFR FW=!valFW FO=obj.FO
    copy <s1> FV=valFV FR=refFR FW=!valFW FO=obj.FO
    t2 2 flds , EINS.ZWEI
    clear q2 EINS.ZWEI= val=
    orig R2 EINS.ZWEI=valR2.eins.zwei val=valR2Self
    copy <s2> EINS.ZWEI=valR2.eins.zwei val=valR2Self
    t3 0 flds M.<class tst...Tf33>.FLDS.1, M.<class tst...Tf33>.FLDS.2
    clear q3 s1.0=0
    orig R3 s1.0=1 s1.1=M.R3.S1.1 s1.1.f1=M.R3.S1.1.F1 s1.1.s2.0=2 s1.1+
    ..s2.1.f2=M.R3.S1.1.S2.1.F2 s1.1.s2.2.f2=M.R3.S1.1.S2.2.F2
    copy <s3> s1.0=1 s1.1=M.R3.S1.1 s1.1.f1=M.R3.S1.1.F1 s1.1.s2.0=2 s1+
    ..1.s2.1.f2=M.R3.S1.1.S2.1.F2 s1.1.s2.2.f2=M.R3.S1.1.S2.2.F2
$/tstClass3/ */

    call classIni
    m.o_escW = '!'
    call tst t, 'tstClass3'
    call mAdd t.trans, m.class_C '<class class>'
    call tstOut t, 'met v#o2String' classMet(m.class_V, 'o2String')
    call tstOut t, 'met w#o2String' classMet(m.class_W, 'o2String')
    call tstOut t, 'met w#o2String' classMet(m.class_W, 'o2String')
    call tstOut t, 'met w#nonono' classMet(m.class_W, 'nonono')
    call tstOut t, 'met class#oCopy' classMet(m.class_C, 'oCopy')
    all =  classNew('n? tstClassTf31 u f FV v, f FR r, f FW w, f FO o'),
           classNew('n? tstClassTf32 u f EINS f ZWEI v, v') ,
           classNew('n? tstClassTf33 u f S1' classNew('s u v, f F1 v,',
                           'f S2 s f F2 v'))
    call mAdd t.trans, word(all, 3) '<class tst...Tf33>'
    m.r1.fv = 'valFV'
    m.r1.fr = 'refFR'
    m.r1.fw = '!valFW'
    m.r1.fo = 'obj.FO'
    m.r2    = 'valR2Self'
    m.r2.eins.zwei  = 'valR2.eins.zwei'
    m.r3.s1.0 = 1
    m.r3.s1.1.s2.0 = 2
    o.1 = "q 'FV='m.q.FV 'FR='m.q.fr 'FW='m.q.fw 'FO='m.q.fo"
    o.2 = "q 'EINS.ZWEI='m.q.EINS.zwei 'val='m.q"
    o.3 = "q 's1.0='m.q.s1.0"
    p.1 = o.1
    p.2 = o.2
    p.3 = "q 's1.0='m.q.s1.0 's1.1='m.q.s1.1 's1.1.f1='m.q.s1.1.f1" ,
            "'s1.1.s2.0='m.q.s1.1.s2.0 's1.1.s2.1.f2='m.q.s1.1.s2.1.f2",
                                      "'s1.1.s2.2.f2='m.q.s1.1.s2.2.f2"
    do tx=1 to words(all)
        t1 = word(all, tx)
        u1 = classFlds(t1)
        q = 'q'tx
        call tstOut t, 't'tx m.u1.0 'flds' m.u1.1',' m.u1.2
        call utInter("m='"q"';" classMet(t1, 'oClear'))
        interpret "call tstOut t, 'clear'" o.tx
        q = 'R'tx
        interpret "call tstOut t, 'orig'" p.tx
        q = utInter("m='"q"';t='';" classMet(t1, 'oCopy'))
        call mAdd t.trans, q '<s'tx'>'
        interpret "call tstOut t, 'copy'" p.tx
        end
    call tstEnd t
    return
endProcedure tstClass3

tstClass: procedure expose m.
/*
$=/tstClass/
    ### start tst tstClass ############################################
    Q u =className= tstClassTf12
    Q.eins.zwei v ==> M.Q.eins.zwei
    *** err: bad type v: classNew(v tstClassTf12)
    *** err: missing key in mapGet(CLASS_N2C, 0)
    R u =className= uststClassTf12
    R u =className= uststClassTf12in
    R u =className= tstClassTf12
    R.eins.zwei v ==> M.R.eins.zwei
    R s =stem.0= 2
    R.1 r ==> M.R.1 :CLASS.8
    R.1 u =className= tstClassTf12
    R.1.eins.zwei v ==> M.R.1.eins.zwei
    R.2 r ==> M.R.2 :CLASS.8
    R.2 u =className= tstClassTf12
    R.2.eins.zwei v ==> M.R.2.eins.zwei
    S u =className= TstClass7
    S s =stem.0= 2
    S.1 u =className= TstClass7s
    S.1.eins v ==> M.S.1.eins
    S.1 m =met-metA--> say "metA"
    S.1 m =met-metB--> say "metB"
    S.2 u =className= TstClass7s
    S.2.zwei v ==> M.S.2.zwei
    S.2 m =met-metA--> say "metA"
    S.2 m =met-metB--> say "metB"
    class of mutate qq tstClassTf12
$/tstClass/ */

    f = 'eins.zwei'
    e = 'eins'
    z = 'zwei'
    drop m.q.f m.r.f m.r.0 m.r.1 m.r.1.f m.r.2 m.r.2.f
    drop m.s.0 m.s.1 m.s.1.e m.s.2 m.s.2.z
    call classIni
    call tst t, 'tstClass'
    t1  =  classNew('n? tstClassTf12 u f eins f zwei v')
    call tstClassOut t, t1, q
    z = m.class.0
    if class4name('tstClassB', '') == '' then do
        t2 = classNew('n tstClassB u n tstClassC u tstClassTf12,',
            's u v tstClassTf12')
        end
    else do /*  the second time we would get a duplicate error */
        call tstOut t, '*** err: bad type v: classNew(v tstClassTf12)'
        call tstOut t, '*** err: missing key in mapGet(CLASS_N2C, 0)'
        end
    t2 = classNew('n? uststClassTf12 u' ,
           'n? uststClassTf12in u tstClassTf12',
        , classNew('s u r, tstClassTf12'))
    m.r.0 = 2
    call tstClassOut t, t2, r
    t3 = classNew('n? TstClass7 u s',
         classNew('n? TstClass7s u c 1 f eins v, c 2 f zwei v',
            ,'m', 'metA say "metA"', 'metB say "metB"'))
    m.s.0 = 2
    m.s.1 = 1
    m.s.2 = 2
    call tstClassOut t, t3, s
    call oMutatName qq, 'tstClassTf12'
    tt = objClass(qq)
    call tstOut t, 'class of mutate qq' className(tt)
    call tstEnd t
    return
endProcedure tstClass

tstClassOut: procedure expose m.
parse arg o, t, a
    if wordPos(t, m.class_V m.class_W m.class_O) > 0 then
        return tstOut(o, a m.t.name '==>' m.a)
    if m.t == 'r' then
        return tstOut(o, a m.t '==>' m.a ':'if(m.t.0==0,'',m.t.1))
    if m.t == 'u' & m.t.name \== '' then
        call tstOut o, a m.t '=className=' m.t.name
    if m.t == 'f' then
        return tstClassOut(o, m.t.1, a'.'m.t.name)
    if m.t = 'u' then do
        do ux=1 to m.t.0
            call tstClassOut o, m.t.ux, a
            end
        return 0
        end
    if m.t = 's' then do
        call tstOut o, a m.t '=stem.0=' m.a.0
        do ux=1 to m.a.0
            call tstClassOut o, m.t.1, a'.'ux
            end
        return 0
        end
    if m.t = 'c' then do
        if m.t.name = m.a then
            call tstClassOut o, m.t.1, a
         return 0
        end
    if m.t = 'm' then
        return tstOut(o, a m.t '=met-'m.t.name'-->' m.t.met)
    call err 'bad class type' m.t
endProcedure tstClassOut


tstO: procedure expose m.
/*
$=/tstO/
    ### start tst tstO ################################################
    o1.class ---
    o1.class <class T..1>
    o1#met1 metEins
    o1#met2 metZwei
    o1#new return classClear('<class T..1>', oMutate(mNew('<class T..1>+
    '), '<class T..1>'))
$/tstO/
*/
    call mIni
    call tst t, 'tstO'
    call classIni
    c1 = classNew('n? TstOCla1 u', 'm', 'met1 metEins', 'met2 metZwei')
    call mAdd t.trans, c1 '<class T..1>'
    o1 = 'tst_o1'
    call tstOut t, 'o1.class' objClass(o1, '---')
    o1 = oMutate('o1', c1)
    call tstOut t, 'o1.class' objClass(o1, '---')
    call tstOut t, 'o1#met1' objMet(o1, 'met1')
    call tstOut t, 'o1#met2' objMet(o1, 'met2')
    call tstOut t, 'o1#new' objMet(o1, 'new')
    call tstEnd t
    return
endProcedure tstO


tstOEins: procedure expose m.
/*
$=/tstOEins/
    ### start tst tstOEins ############################################
    class method calls of TstOEins
    .  met Eins.eins M
     flds of <obj e of TstOEins> FEINS, FZWEI
     methodcalls of object e of TstOEins
    .  met Eins.eins <obj e of TstOEins>
    .  met Eins.zwei <obj e2 of TstOEins>
    *** err: no method nein in class TstOEins
    *** err: no class found for object noObj
    class method calls of TstOEins
    .  met Elf.zwei M
    flds of <obj f of TstOElf> FEINS, FZWEI, FELF
    methodcalls of object f of TstOElf
    .  met Eins.eins <obj f of TstOElf>
    .  met Elf.zwei <obj f of TstOElf>
    .  met Elf.drei <obj f of TstOElf>
    oCopy c1 of class TstOEins, c2
    C1 u =className= TstOEins
    C1.FEINS v ==> M.C1.FEINS
    C1.FZWEI v ==> M.C1.FZWEI
    C1 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C1 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    C2 u =className= TstOEins
    C2.FEINS v ==> M.C1.FEINS
    C2.FZWEI v ==> M.C1.FZWEI
    C2 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C2 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    oCopy c3 of class TstOElf, c4
    C4 u =className= TstOElf
    C4 u =className= TstOEins
    C4.FEINS v ==> M.C3.FEINS
    C4.FZWEI v ==> M.C3.FZWEI
    C4 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C4 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    C4.FELF r ==> M.C3.FELF :<class O>
    C4 m =met-zwei--> call tstOut t, "  met Elf.zwei" m
    C4 m =met-drei--> call tstOut t, "  met Elf.drei" m
    tEinsDop <class TstOEins>
    oRun 7*3 21
    oRun 12*12 144
$/tstOEins/ */
    call scanIni
    call tst t, 'tstOEins'
    tEins = classNew('n? TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
    call mAdd t.trans, tEins '<class TstOEins>' ,
                   , m.class_o '<class O>'
    call tstOut t, 'class method calls of TstOEins'
    interpret classMet('TstOEins', 'eins')
    e = oNew('TstOEins')
    e2 = oNew('TstOEins')
    call mAdd t.trans, e '<obj e of TstOEins>'
    call mAdd t.trans, e2 '<obj e2 of TstOEins>'
    call tstOut t, 'flds of' e mCat(oFlds(e), ', ')
    call tstOut t, 'methodcalls of object e of TstOEins'
    call tstOmet e, 'eins'
    call tstOmet e2, 'zwei'
    call tstOmet e, 'nein'
    call tstOmet 'noObj', 'nein'
    tElf = classNew('n? TstOElf u TstOEins, f FELF r', 'm',
        , 'zwei call tstOut t, "  met Elf.zwei" m',
        , 'drei call tstOut t, "  met Elf.drei" m')
    call tstOut t, 'class method calls of TstOEins'
    interpret classMet('TstOElf', 'zwei')
    f = oNew('TstOElf')
    call mAdd t.trans, f '<obj f of TstOElf>'
    call tstOut t, 'flds of' f mCat(oFlds(f), ', ')
    call tstOut t, 'methodcalls of object f of TstOElf'
    call tstOmet f, 'eins'
    call tstOmet f, 'zwei'
    call tstOmet f, 'drei'
    call oMutatName c1, 'TstOEins'
    call tstOut t, 'oCopy c1 of class TstOEins, c2'
    call tstClassOut t, tEins, c1
    call oCopy c1, c2
    call tstClassOut t, tEins, c2
    call tstOut t, 'oCopy c3 of class TstOElf, c4'
    call oMutatName c3, 'TstOElf'
    call oCopy c3, c4
    call tstClassOut t, tElf, c4

/*    tEinsDop = classNew('n TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
*/ tEinsDop = tEins
    call tstOut t, 'tEinsDop' tEinsDop
    e3 = oNew('TstOEins')
    if e3 <<= e | e3 <<= e2 then
        call err 'doppelt reinitialised objects'
    rr = oRunner('return 7 * 3')
    call tstOut t, 'oRun 7*3' oRun(rr)
    r12 = oRunner('return 12 * 12')
    call tstOut t, 'oRun 12*12' oRun(r12)
    call tstEnd t
    return
endProcedure tstOEins

tstOmet: procedure expose m.
parse arg m, met
    interpret objMet(m, met)
    return
endProcedure tstOmet

tstJSay: procedure expose m.
/*
$=/tstJSay/
    ### start tst tstJSay #############################################
    *** err: bad option openArg in jOpen(<obj j of JRW>, openArg)
    *** err: jWrite(<obj j of JRW>, writeArg) but not opened w
    *** err: can only write JSay.jOpen(<obj s of JSay>, <)
    *** err: jWrite(<obj s of JSay>, write s vor open) but not opened+
    . w
    *** err: can only read JRWEof.jOpen(<obj e of JRWEof>, >)
    *** err: jRead(<obj e of JRWEof>, XX) but not opened r
    read e vor open 0 m.xx valueBefore
    read e nach open 0 m.xx M.XX
    out eins
    #jIn 1# tst in line 1 eins ,
    out zwei in 1 vv=readAdrVV
    #jIn 2# tst in line 2 zwei ;   .
    out drei in 1 vv=readAdrVV Schluss
$/tstJSay/ */

    call jIni
    call tst t, 'tstJSay'
    jrw = oNew('JRW')
    call mAdd t'.TRANS', jrw '<obj j of JRW>'
    call jOpen jrw, 'openArg'
    call jWrite jrw, 'writeArg'
    s = oNew('JSay')
    call mAdd t'.TRANS', s '<obj s of JSay>'
    call jOpen s, m.j.cRead
    s = oNew('JSay')
    call mAdd t'.TRANS', s '<obj s of JSay>'
    call jWrite s, 'write s vor open'
    call jOpen s, '>'
    call jWrite s, 'write s nach open'
    e = oNew('JRWEof')
    call mAdd t'.TRANS', e '<obj e of JRWEof>'
    call jOpen e, '>'
    e = oNew('JRWEof')
    call mAdd t'.TRANS', e '<obj e of JRWEof>'
    m.xx = 'valueBefore'
    call tstOut t, 'read e vor open' jRead(e, xx) 'm.xx' m.xx
    call jOpen e, m.j.cRead
    call tstOut t, 'read e nach open' jRead(e, xx) 'm.xx' m.xx
    call out 'out eins'
    vv = 'readAdrVV'
    m.vv = 'readAdrVVValueBefore'
    call out 'out zwei in' in(vv) 'vv='vv
    m.vv = 'readAdrVVValueBefore'
    call out 'out drei in' in(vv) 'vv='vv 'Schluss'
    call tstEnd t
    return
endProcedure tstJSay

tstJ: procedure expose m.
/*
$=/tstJ/
    ### start tst tstJ ################################################
    out eins
    #jIn 1# tst in line 1 eins ,
    1 in() tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    2 in() tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    3 in() tst in line 3 drei .schluss..
    #jIn eof 4#
    in() 3 reads vv VV
    line buf line one
    line buf line two
    line buf line three
    line buf line four
    *** err: jWrite(<buf b>, buf line five while reading) but not opene+
    d w
$/tstJ/ */

    call jIni
    call tst t, "tstJ"
    b = jOpen(jBuf(), '>')
    call mAdd t'.TRANS', b '<buf b>'
    call out 'out eins'
    do lx=1 by 1 while in(var)
        call out lx 'in()' m.var
        end
    call out 'in()' (lx-1) 'reads vv' vv
    call jOpen b, '>'
    call jWrite b, 'buf line one'
    call jClose b
    call mAdd m.b.stem, 'buf line two', 'buf line three'
    call jWrite b, 'buf line four'
    call jClose b
    call jOpen b, m.j.cRead
    do while (jRead(b, line))
        call out 'line' m.line
        end
    call jWrite b, 'buf line five while reading'
    call jClose b
    call tstEnd t
    return
endProcedure tstJ

tstJ2: procedure expose m.
/*
$=/tstJ2/
    ### start tst tstJ2 ###############################################
    class1 <Tst?1 class> <Tst?1 name>
    class2 <Tst?1 class> <Tst?1 name>
    class3 <Tst?1 class> <Tst?1 name>
    b read EINS feld eins, ZWEI feld zwei, DREI feld drei
    b read EINS feld eins, ZWEI feld zwei 2, DREI feld drei
    c read EINS feld eins, ZWEI feld zwei, DREI drei cat 1
    tstR: @tstWriteoV3 isA :<Tst?1 name>
    tstR:  .EINS = feld eins
    tstR:  .ZWEI = feld zwei
    tstR:  .DREI = drei cat 1
    c read EINS feld eins, ZWEI feld zwei 2, DREI drei cat 2
    tstR: @tstWriteoV4 isA :<Tst?1 name>
    tstR:  .EINS = feld eins
    tstR:  .ZWEI = feld zwei 2
    tstR:  .DREI = drei cat 2
$/tstJ2/ */

    call jIni
    call tst t, "tstJ2"
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call mAdd t'.TRANS', ty '<Tst?1 class>'
    call mAdd t'.TRANS', m.ty.name '<Tst?1 name>'
    call tstOut t, 'class1' ty m.ty.name
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call tstOut t, 'class2' ty m.ty.name
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call tstOut t, 'class3' ty m.ty.name
    call oMutate qq, ty
    m.qq.eins = 'feld eins'
    m.qq.zwei = 'feld zwei'
    m.qq.drei = 'feld drei'
    b = jBuf()
    call jOpen b, '>'
    call jWriteO b, oCopy(qq)
    m.qq.zwei = 'feld zwei 2'
    call jWriteO b, qq
    call jOpen jClose(b), m.j.cRead
    c = jOpen(jBuf(), '>')
    do xx=1 while assNN('res', jReadO(b))
        call out 'b read EINS' m.res.eins', ZWEI' m.res.zwei,
                                     || ', DREI' m.res.drei
        m.res.drei = 'drei cat' xx
        call jWriteO c, res
        end
    call jOpen jClose(c), m.j.cRead
    do while assNN('ccc', jReadO(c))
        call out 'c read EINS' m.ccc.eins', ZWEI' m.ccc.zwei,
                                     || ', DREI' m.ccc.drei
        call outO ccc
        end
    call tstEnd t
    return
endProcedure tstJ2

tstCat: procedure expose m.
/*
$=/tstCat/
    ### start tst tstCat ##############################################
    catRead 1 line 1
    catRead 2 line 2
    catRead 3 line 3
    appRead 1 line 1
    appRead 2 line 2
    appRead 3 line 3
    appRead 4 append 4
    appRead 5 append 5
$/tstCat/ */
    call catIni
    call tst t, "tstCat"
    i = cat(jBuf('line 1', 'line 2'), jBuf('line 3'))
    call jOpen i, m.j.cRead
    do lx=1 by 1 while jRead(i, v)
        call tstOut t, 'catRead' lx m.v
        end
    call jOpen jClose(i), m.j.cApp
    call jWrite i, 'append 4'
    call jWrite i, 'append 5'
    call jOpen jClose(i), m.j.cRead
    do lx=1 by 1 while jRead(i, v)
        call tstOut t, 'appRead' lx m.v
        end
    call tstEnd t
    return
endProcedure tstCat

tstEnv: procedure expose m.
    call pipeIni
/*
$=/tstEnv/
    ### start tst tstEnv ##############################################
    before pipeBeLa
    after pipeEnd
    *** err: jWrite(<jBuf c>, write nach pop) but not opened w
    #jIn 1# tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    before writeNow 2 c --> std
    before writeNow 1 b --> c
    b line eins
    b zwei |
    nach writeNow 1 b --> c
    add nach pop
    after push c only
    tst in line 1 eins ,
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    nach writeNow 2 c --> std
    *** err: jWrite(<jBuf c>, ) but not opened w
$/tstEnv/ */

    call tst t, "tstEnv"
    c = jBuf()
    call mAdd t'.TRANS', c '<jBuf c>'
    call out 'before pipeBeLa'
    b = jBuf("b line eins", "b zwei |")
    call pipe '+Ff', c, b
    call out 'before writeNow 1 b --> c'
    call pipeWriteNow
    call out 'nach writeNow 1 b --> c'
    call pipe '-'
    call out 'after pipeEnd'
    call jWrite c, 'write nach pop'
    call mAdd c'.BUF', 'add nach pop'
    call pipe '+A', c
    call out 'after push c only'
    call pipeWriteNow
    call pipe '-'
    call pipe '+f', , c
    call out 'before writeNow 2 c --> std'
    call pipeWriteNow
    call out 'nach writeNow 2 c --> std'
    call pipe '-'
    call jWrite c
    call tstEnd t
    return
endProcedure tstEnv

tstEnvCat: procedure expose m.
    call pipeIni
/*
$=/tstEnvCat/
    ### start tst tstEnvCat ###########################################
    c1 contents
    c1 line eins |
    before writeNow 1 b* --> c*
    b1 line eins|
    b2 line eins
    b2 zwei |
    c2 line eins |
    after writeNow 1 b* --> c*
    c2 contents
    c2 line eins |
$/tstEnvCat/ */

    call tst t, "tstEnvCat"

    b0= jBuf()
    b0= jBuf()
    b1= jBuf("b1 line eins|")
    b2 = jBuf("b2 line eins", "b2 zwei |")
    c1 = jBuf("c1 line eins |")
    c2 = jBuf("c2 line eins |")
    call pipe '+Af', c1, b0, b1, b2, c2
    call out 'before writeNow 1 b* --> c*'
    call pipeWriteNow
    call out 'after writeNow 1 b* --> c*'
    call pipe '-'
    call out 'c1 contents'
    call pipe '+f' , , c1
    call pipeWriteNow
    call pipe '-'
    call pipe '+f' , , c2
    call out 'c2 contents'
    call pipeWriteNow
    call pipe '-'
    call tstEnd t
    return
endProcedure tstEnvCat

tstPipe: procedure expose m.
    call pipeIni
/*
$=/tstPipe/
    ### start tst tstPipe #############################################
    .+0 vor pipeBegin
    #jIn 1# tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    .+7 nach pipeLast
    ¢7 +6 nach pipe 7!
    ¢7 +2 nach pipe 7!
    ¢7 +4 nach nested pipeLast 7!
    ¢7 (4 +3 nach nested pipeBegin 4) 7!
    ¢7 (4 (3 +1 nach pipeBegin 3) 4) 7!
    ¢7 (4 (3 tst in line 1 eins , 3) 4) 7!
    ¢7 (4 (3 tst in line 2 zwei ;    3) 4) 7!
    ¢7 (4 (3 tst in line 3 drei .schluss. 3) 4) 7!
    ¢7 (4 (3 +1 nach writeNow vor pipe 3) 4) 7!
    ¢7 (4 +3 nach preSuf vor nested pipeLast 4) 7!
    ¢7 +4 nach preSuf vor nested pipeEnd 7!
    ¢7 +5 nach nested pipeEnd vor pipe 7!
    ¢7 +6 nach writeNow vor pipeLast 7!
    .+7 nach writeNow vor pipeEnd
    .+8 nach pipeEnd
$/tstPipe/ */

    say 'x0' m.pipe.0
    call tst t, 'tstPipe'
    call out '+0 vor pipeBegin'
    say 'x1' m.pipe.0
    call pipe '+N'
    call out '+1 nach pipeBegin'
    call pipeWriteNow
    call out '+1 nach writeNow vor pipe'
    call pipe 'N|'
    call out '+2 nach pipe'
    call pipe '+N'
    call out '+3 nach nested pipeBegin'
    call pipePreSuf '(3 ', ' 3)'
    call out '+3 nach preSuf vor nested pipeLast'
    call pipe 'P|'
    call out '+4 nach nested pipeLast'
    call pipePreSuf '(4 ', ' 4)'
    call out '+4 nach preSuf vor nested pipeEnd'
    call pipe '-'
    call out '+5 nach nested pipeEnd vor pipe'
    call pipe 'N|'
    call out '+6 nach pipe'
    call pipeWriteNow
    say 'out +6 nach writeNow vor pipeLast'
    call out '+6 nach writeNow vor pipeLast'
    call pipe 'P|'
    call out '+7 nach pipeLast'
    call pipePreSuf '¢7 ', ' 7!'
    call out '+7 nach writeNow vor pipeEnd'
    call pipe '-'
    call out '+8 nach pipeEnd'
    say 'xx' m.pipe.0
    call tstEnd t
    return
endProcedure tstPipe

tstPipeS: procedure expose m.
/*
$=/tstPipeS/
    ### start tst tstPipeS ############################################
    eine einzige zeile
    nach all einzige Zeile
    select strip(creator) cr, strip(name) tb,
    (row_number()over())*(row_number()over()) rr
    from sysibm.sysTables
$/tstPipeS/
*/
    call tst t, "tstPipeS"
    call pipe '+s',, 'eine einzige zeile'
    call pipeWriteAll
    call out 'nach all einzige Zeile'
    call pipe 's',,
              , "select strip(creator) cr, strip(name) tb," ,
              ,      "(row_number()over())*(row_number()over()) rr" ,
              ,      "from sysibm.sysTables"
    call pipeWriteAll
    call pipe '-'
    call tstEnd t
    return
endProcedure tstPipeS

tstEnvVars: procedure expose m.
    call pipeIni
/*
$=/tstEnvVars/
    ### start tst tstEnvVars ##########################################
    put v1 value eins
    v1 hasKey 1 get value eins
    v2 hasKey 0
    one to theBur
    two to theBuf
    v1=value eins o=TST.ADR1
    v3=v3WieGehts? o=!v3WieGehts?
    v4=v4WieGehts? o=!v4WieGehts?
    o o0=<o0>
    *** err: no method o2String in class TstEnvVars0
    *** err: o2String did not return
    s o0=0
    o o0=<o0>
    *** err: no method o2String in class TstEnvVars0
    *** err: o2String did not return
    s o0=0
    o0&fSt0=rexx o0.fSt0 o=!rexx o0.fSt0
    o0&fRe0=rexx o0.fRe0 o=!rexx o0.fRe0
    o0&=rexx o0-value o=!rexx o0-value
        o o0=<o0>
    *** err: no method o2String in class TstEnvVars0
    *** err: o2String did not return
    s o0=0
    o0&fSt0=put o0.fSt0 o=!put o0.fSt0
    o0&fRe0=putO o0.fRe0 o=!putO o0.fRe0
    o0&=put o0-value o=!put o0-value
$/tstEnvVars/
$=/tstEnvVars1/
    ### start tst tstEnvVars1 #########################################
    m.o1=put-o1-value m.o1.fStr=put-o1.fStr m.o1.fRef=<o0>
    *** err: no method o2String in class TstEnvVars1
    *** err: o2String did not return
    o o1=<o1> s o1=0
    o1&fStr=put-o1.fStr o=!put-o1.fStr
    o1&=put-o1-value o=!put-o1-value
    *** err: no method o2String in class TstEnvVars0
    *** err: o2String did not return
    o1&fRef=0 o=<o0>
    o1&fRef&fSt0=put o0.fSt0 o=!put o0.fSt0
    o1&fRef&fRe0=putO o0.fRe0 o=!putO o0.fRe0
    m.o1&fNest.fSt0= put-o1.fNest.fSt0 m.o1&fNest.fRe0= !put-o1&fNest.f+
    Re0
    o1&fNest.fSt0=put-o1.fNest.fSt0 o=!put-o1.fNest.fSt0
    o1&fNest&fRe0=put-o1&fNest.fRe0 o=!put-o1&fNest.fRe0
$/tstEnvVars1/
$=/tstEnvVars2/
    ### start tst tstEnvVars2 #########################################
    o2=<o2> getO(o2)=<o2> getO(o2&fRef)=<o1>
    o2&fRef&fStr=put-o1.fStr o=!put-o1.fStr
    o2&fRef&=put-o1-value o=!put-o1-value
    *** err: no method o2String in class TstEnvVars0
    *** err: o2String did not return
    o2&fRef&fRef=0 o=<o0>
    o2&fRef&fRef&fSt0=put o0.fSt0 o=!put o0.fSt0
    o2&fRef&fRef&fRe0=putO o0.fRe0 o=!putO o0.fRe0
    o2&fRef&fNest.fSt0=put-o1.fNest.fSt0 o=!put-o1.fNest.fSt0
    o2&fRef&fNest&fRe0=put-o1&fNest.fRe0 o=!put-o1&fNest.fRe0
$/tstEnvVars2/
$=/tstEnvVarsS/
    ### start tst tstEnvVarsS #########################################
    oS=<oS> oS&fStS=<put oS.fStS>
    oS&fStV.0=1 oS&fStV.1=<put oS.fStV.1>
    m.oS.fStR.0=2 .2=!<put oS.fStR.2>
    oS&fStR.0=2 .1=<put oS.fStR.1> .2=<put oS.fStR.2>
    m.oS.0=9876 .1234=<put oS.1234>
    oS&0=9876 .12=M.<oS>.12 .1234=<put oS.1234>
$/tstEnvVarsS/
 */
    c0 = classNew('n? TstEnvVars0 u f fSt0 v, f = v, f fRe0 r')
    c1 = classNew('n? TstEnvVars1 u f fStr v,f fRef r' ,
              ', f fNest TstEnvVars0, f = v')
    o0 = oNew(c0)
    o1 = oNew(c1)
    o2 = oNew(c1)
    call tst t, "tstEnvVars"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'
    call vRemove 'v2'
    m.tst.adr1 = 'value eins'
    put1 = vPutO('v1', oMutate(tst'.'adr1, m.class_V))
    call tstOut t, 'put v1' m.put1
    call tstOut t, 'v1 hasKey' vHasKey('v1') 'get' vGet('v1')
    call tstOut t, 'v2 hasKey' vHasKey('v2')
    if 0 then
        call tstOut t, 'v2 get'    vGet('v2')
    call vPutO 'theBuf', jBuf()
    call pipe '+F' , vGetO('theBuf')
    call out 'one to theBur'
    call out 'two to theBuf'
    call pipe '-'
    call pipe '+f',, vGetO('theBuf')
    call pipeWriteNow
    call pipe '-'
    call tstOut t, 'v1='vGet('v1') 'o='vGetO('v1')
    call vPut 'v3', 'v3WieGehts?'
    call tstOut t, 'v3='vGet('v3') 'o='vGetO('v3')
    call vPutO 'v4', s2o('v4WieGehts?')
    call tstOut t, 'v4='vGet('v4') 'o='vGetO('v4')

    call vPutO 'o0', o0
    call tstOut t, 'o o0='vGetO('o0')
    call tstOut t, 's o0='vGet('o0')
    fSt0 = 'fSt0'
    fRe0 = 'fRe0'
    m.o0 = 'rexx o0-value'
    m.o0.fSt0 = 'rexx o0.fSt0'
    m.o0.fRe0 = s2o('rexx o0.fRe0')
    call tstOut t, 'o o0='vGetO('o0')
    call tstOut t, 's o0='vGet('o0')
    call tstOut t, 'o0&fSt0='vGet('o0&fSt0') 'o='vGetO('o0&fSt0')
    call tstOut t, 'o0&fRe0='vGet('o0&fRe0') 'o='vGetO('o0&fRe0')
    call tstOut t, 'o0&='vGet('o0&') 'o='vGetO('o0&')

    call vPut 'o0&', 'put o0-value'
    call vPut 'o0&fSt0', 'put o0.fSt0'
    call vPutO 'o0&fRe0', s2o('putO o0.fRe0')
    call tstOut t, 'o o0='vGetO('o0')
    call tstOut t, 's o0='vGet('o0')
    call tstOut t, 'o0&fSt0='vGet('o0&fSt0') 'o='vGetO('o0&fSt0')
    call tstOut t, 'o0&fRe0='vGet('o0&fRe0') 'o='vGetO('o0&fRe0')
    call tstOut t, 'o0&='vGet('o0&') 'o='vGetO('o0&')

    call tstEnd t
    call tst t, "tstEnvVars1"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'

    call vPutO 'o1', o1
    call vPut 'o1&', 'put-o1-value'
    call vPut 'o1&fStr', 'put-o1.fStr'
    call vPutO 'o1&fRef', vGetO('o0')
    call tstOut t, 'm.o1='m.o1 'm.o1.fStr='mGet(o1'.fStr'),
         'm.o1.fRef='mGet(o1'.fRef')
    call tstOut t, 'o o1='vGetO('o1') 's o1='vGet('o1')
    call tstOut t, 'o1&fStr='vGet('o1&fStr') 'o='vGetO('o1&fStr')
    call tstOut t, 'o1&='vGet('o1&') 'o='vGetO('o1&')

    call tstOut t, 'o1&fRef='vGet('o1&fRef') 'o='vGetO('o1&fRef')
    call tstOut t, 'o1&fRef&fSt0='vGet('o1&fRef&fSt0') ,
        'o='vGetO('o1&fRef&fSt0')
    call tstOut t, 'o1&fRef&fRe0='vGet('o1&fRef&fRe0'),
         'o='vGetO('o1&fRef&fRe0')

    call vPut 'o1&fNest.fSt0', 'put-o1.fNest.fSt0'
    call vPutO 'o1&fNest.fRe0', s2o('put-o1&fNest.fRe0')
    call tstOut t, 'm.o1&fNest.fSt0=' mGet(o1'.fNest.fSt0') ,
            'm.o1&fNest.fRe0=' mGet(o1'.fNest.fRe0')
    call tstOut t, 'o1&fNest.fSt0='vGet('o1&fNest.fSt0'),
         'o='vGetO('o1&fNest.fSt0')
    call tstOut t, 'o1&fNest&fRe0='vGet('o1&fNest.fRe0'),
         'o='vGetO('o1&fNest.fRe0')
    call tstEnd t

    call tst t, "tstEnvVars2"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'
    call vPutO 'o2', o2
    call vPutO 'o2&fRef', vGetO('o1')
    call tstOut t, 'o2='o2 'getO(o2)='vGetO('o2'),
        'getO(o2&fRef)='vGetO('o2&fRef')

    call tstOut t, 'o2&fRef&fStr='vGet('o2&fRef&fStr'),
         'o='vGetO('o2&fRef&fStr')
    call tstOut t, 'o2&fRef&='vGet('o2&fRef&'),
     'o='vGetO('o2&fRef&')

    call tstOut t, 'o2&fRef&fRef='vGet('o2&fRef&fRef') ,
        'o='vGetO('o2&fRef&fRef')
    call tstOut t, 'o2&fRef&fRef&fSt0='vGet('o2&fRef&fRef&fSt0') ,
        'o='vGetO('o2&fRef&fRef&fSt0')
    call tstOut t, 'o2&fRef&fRef&fRe0='vGet('o2&fRef&fRef&fRe0'),
         'o='vGetO('o2&fRef&fRef&fRe0')
    call tstOut t, 'o2&fRef&fNest.fSt0='vGet('o2&fRef&fNest.fSt0'),
         'o='vGetO('o2&fRef&fNest.fSt0')
    call tstOut t, 'o2&fRef&fNest&fRe0='vGet('o2&fRef&fNest.fRe0'),
         'o='vGetO('o1&fNest.fRe0')
    call tstEnd t

    cS = classNew('n? TstEnvVarsS u f fStS v,f fStV s v, f fStR s r',
        ', f fNeS s TstEnvVars0, f = s v')
    oS = oNew(cS)
    call vPutO 'oS', oS
    oT = oNew(cS)
    call tst t, "tstEnvVarsS"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>',
        , oS '<oS>', oT '<oT>'
    call mPut oS'.fStS', '<put oS.fStS>'
    call tstOut t, 'oS='vGetO('oS') 'oS&fStS='vGet('oS&fStS')
    call mPut oS'.fStV.1', '<put oS.fStV.1>'
    call mPut oS'.fStV.0', 1
    call tstOut t, 'oS&fStV.0='vGet('oS&fStV.0'),
         'oS&fStV.1='vGet('oS&fStV.1')
    call mPut oS'.fStR.1', s2o('<put oS.fStR.1>')
    call mPut oS'.fStR.2', s2o('<put oS.fStR.2>')
    call mPut oS'.fStR.0', 2
    call tstOut t, 'm.oS.fStR.0='mGet(oS'.fStR.0'),
     '.2='mGet(oS'.fStR.2')
    call tstOut t, 'oS&fStR.0='vGet('oS&fStR.0'),
         '.1='vGet('oS&fStR.1') '.2='vGet('oS&fStR.2')
    call mPut oS'.1234', '<put oS.1234>'
    call mPut oS'.0', 9876
    call mPut oS'.fStR.0', 2
    call tstOut t, 'm.oS.0='mGet(oS'.0'),
     '.1234='mGet(oS'.1234')
    call tstOut t, 'oS&0='vGet('oS&0'),
         '.12='vGet('oS&12') '.1234='vGet('oS&1234')
    call tstEnd t
    return
endProcedure tstEnvVars

tstvWith: procedure expose m.
/*
$=/tstEW2/
    ### start tst tstEW2 ##############################################
    tstK1             TSTEW1
    tstK1&            get1 w
    tstK1&f1          get1.f1 v
    tstK1&f2          get1.f2 w
    tstK1&F3          get1.f3 v
    ttstK1&F3.FEINS   get1.f3.fEins v
    tstK1&F3.FZWEI    get1.f3.fZwei w
    tstK1&F3.FDREI o  !get1.f3.fDrei w
    tstK1&F3.FDREI    get1.f3.fDrei w
    tstK1&F3.1        get1.f3.1 w
    tstK1&F3.2        TSTEW1
    tstK1&F3.2&F1     get1.f1 v
    tstK1&F3.2&F3.2&F2 get1.f2 w
    *** err: missing key in mapGet(V, F1)
    *** err: no class found for object 0
    *** err: o2String did not return
    F1          0
    F1          get1.f1 v
    f2          get1.f2 w
    F3          get1.f3 v
    F3.FEINS    get1.f3.fEins v
    F3.FZWEI    get1.f3.fZwei w
    F3.FDREI o  !get1.f3.fDrei w
    F3.1        get1.f3.1 w
    pu1 F1      get1.f1 v
    pu2 F1      get2.f1 v
    po-2 F1     get1.f1 v
    *** err: missing key in mapGet(V, F1)
    *** err: no class found for object 0
    *** err: o2String did not return
    po-1 F1     0
$/tstEW2/  */
    call pipeIni
    c0 = classNew('n? TstEW0 u f FEINS v,f FZWEI w, f FDREI r,v,s r')
    call classMet c0, 'oFlds' /* new would do it, but we donot use it */
    cl = classNew('n? TstEW u r TstEW0, f F1 v, f F2 r, f F3 TstEW0')
    call classMet cl, 'oFlds' /* new would do it, but we donot use it */
    call oMutate tstEW1, cl
    m.tstEW1    = s2o('get1 w')
    m.tstEW1.f1 = 'get1.f1 v'
    m.tstEW1.f2 = s2o('get1.f2 w')
    m.tstEW1.f3 = 'get1.f3 v'
    m.tstEW1.f3.fEins = 'get1.f3.fEins v'
    m.tstEW1.f3.fZwei = s2o('get1.f3.fZwei w')
    m.tstEW1.f3.fDrei = s2o('get1.f3.fDrei w')
    m.tstEW1.f3.0 = 3
    m.tstEW1.f3.1 = s2o('get1.f3.1 w')
    m.tstEW1.f3.2 = tstEW1
    m.tstEW1.f3.3 = s2o('get1.f3.3 w')
    call oMutate tstEW2, cl
    m.tstEW2    = s2o('get2 w')
    m.tstEW2.f1 = 'get2.f1 v'
    m.tstEW2.f2 = s2o('get2.f2 w')
    call vPutO 'tstK1', tstEW1

    call tst t, 'tstEW2'
    call tstOut t, 'tstK1            ' vGetO('tstK1')
    call tstOut t, 'tstK1&           ' vGet('tstK1&')
    call tstOut t, 'tstK1&f1         ' vGet('tstK1&F1')
    call tstOut t, 'tstK1&f2         ' vGet('tstK1&F2')
    call tstOut t, 'tstK1&F3         ' vGet('tstK1&F3')
    call tstOut t, 'ttstK1&F3.FEINS  ' vGet('tstK1&F3.FEINS')
    call tstOut t, 'tstK1&F3.FZWEI   ' vGet('tstK1&F3.FZWEI')
    call tstOut t, 'tstK1&F3.FDREI o ' vGetO('tstK1&F3.FDREI')
    call tstOut t, 'tstK1&F3.FDREI   ' vGet('tstK1&F3.FDREI')
    call tstOut t, 'tstK1&F3.1       ' vGet('tstK1&F3.1')
    call tstOut t, 'tstK1&F3.2       ' vGetO('tstK1&F3.2')
    call tstOut t, 'tstK1&F3.2&F1    ' vGet('tstK1&F3.2&F1')
    call tstOut t, 'tstK1&F3.2&F3.2&F2' ,
                                vGet('tstK1&F3.2&F3.2&F2')
    call tstOut t, 'F1         ' vGet('F1')
    call vWith '+', tstEW1
    call tstOut t, 'F1         ' vGet('F1')
    call tstOut t, 'f2         ' vGet('F2')
    call tstOut t, 'F3         ' vGet('F3')
    call tstOut t, 'F3.FEINS   ' vGet('F3.FEINS')
    call tstOut t, 'F3.FZWEI   ' vGet('F3.FZWEI')
    call tstOut t, 'F3.FDREI o ' vGetO('F3.FDREI')
    call tstOut t, 'F3.1       ' vGet('F3.1')
    call tstOut t, 'pu1 F1     ' vGet('F1')
    call vWith '+', tstEW2
    call tstOut t, 'pu2 F1     ' vGet('F1')
    call vWith '-'
    call tstOut t, 'po-2 F1    ' vGet('F1')

    call vWith '-'
    call tstOut t, 'po-1 F1    ' vGet('F1')
    call tstEnd t
/*
$=/tstEW3/
    ### start tst tstEW3 ##############################################
    .          s c3&F1          = v(c3&f1)
    *** err: vAdrObj undef v(c3&f1) in name .F1&FEINS
    *** err: vGet flag 0
    .          s c3&F1&FEINS    = 0
    *** err: vAdrObj null at .F3& in name .F3&FEINS
    *** err: vGet flag 0
    .          s c3&F3&FEINS    = 0
    .          s c3&F3.FEINS    = val(c3&F3.FEINS)
    *** err: vAdrStem no stem cl TstEW, o <c3>, f .FEINS
    *** err: vAdrStem not a stem cl=TstEW st= o=<c3>
    *** err: vGet flag 0
    .          s c3&FEINS       = 0
    getO c3&
    aft Put   s c3&&FEINS      = v&&fEins
    Push c3   s F3.FEINS       = val(c3&F3.FEINS)
    aftPut=   s F3.FEINS       = pushPut(F3.FEINS)
    push c4   s F1             = v(c4&f1)
    put f2    s F2             = put(f2)
    put ..    s F3.FEINS       = put(f3.fEins)
    popW c4   s F1             = v(c3&f1)
    *** err: missing key in mapGet(V, F1)
    *** err: no class found for object 0
    *** err: o2String did not return
    popW c3   s F1             = 0
    .          s F222           = f222 pop stop
$/tstEW3/
*/
    call tst t, 'tstEW3'
    c3 = oNew('TstEW')
    call mAdd t.trans, c3 '<c3>'
    m.c3.f1 = 'v(c3&f1)'
    call vPutO 'c3', c3
    call tstEnvSG , 'c3&F1'
    call tstEnvSG , 'c3&F1&FEINS'
    call tstEnvSG , 'c3&F3&FEINS'
    call vPut 'c3&F3.FEINS', 'val(c3&F3.FEINS)'
    call tstEnvSG , 'c3&F3.FEINS'
    call tstEnvSG , 'c3&FEINS'
    call tstOut t,  'getO c3&', vGetO('c3&')
    call vPutO 'c3&', oNew('TstEW0')
    call vPut 'c3&&FEINS', 'v&&fEins'
    call tstEnvSG 'aft Put', 'c3&&FEINS'
    call vWith '+', c3
    call tstEnvSG 'Push c3', 'F3.FEINS'
    call vPut 'F3.FEINS', 'pushPut(F3.FEINS)'
    call tstEnvSG 'aftPut=', 'F3.FEINS'

    c4 = oNew('TstEW')
    call mAdd t.trans, c4 '<c4>'
    m.c4.f1 = 'v(c4&f1)'
    call vPut f222, 'f222 no stop'
    call vWith '+',  c4
    call tstEnvSG 'push c4', f1
    call vPut f2, 'put(f2)'
    call tstEnvSG 'put f2', f2
    call vPut f222, 'f222 stopped', 1
    call vPut 'F3.FEINS', 'put(f3.fEins)'
    call tstEnvSG 'put .. ', 'F3.FEINS'
    call vWith '-'
    call tstEnvSG 'popW c4', f1
    call vWith '-'
    call vPut f222, 'f222 pop stop'
    call tstEnvSG 'popW c3', f1
    call tstEnvSG          , f222
    call tstEnd t
    return
endProcedure tstvWith

tstEnvSG: procedure expose m. t
parse arg txt, nm
    call tstOut t, left(txt,10)'s' left(nm, 15)'=' vGet(nm)
    return

tstPipeLazy: procedure expose m.
    call pipeIni
/*
$=/tstPipeLazy/
    ### start tst tstPipeLazy #########################################
    a1 vor pipeBegin loop lazy 0 writeNow *** <class TstPipeLazyBuf>
    bufOpen <
    bufClose
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    a5 vor 2 writeNow in inIx 4
    a2 vor writeNow jBuf
    jBuf line 1
    jBuf line 2
    a3 vor writeNow in inIx 1
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    a4 vor barLast inIx 4
    a6 vor barEnd inIx 4
    a7 nach barEnd lazy 0 writeNow ***
    b1 vor barBegin lazy 0 writeNow *** <class TstPipeLazyRdr>
    RdrOpen <
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    rdrClose
    b4 vor writeNow
    b2 vor writeNow rdr inIx 2
    jRead lazyRdr
    tst in line 3 drei .schluss..
    jRead lazyRdr
    b3 vor barLast inIx 4
    b5 vor barEnd inIx 4
    b6 nach barEnd lazy 0 writeNow ***
    a1 vor pipeBegin loop lazy 1 writeAll *** +
        .<class TstPipeLazyBuf>
    a5 vor 2 writeAll in inIx 0
    a2 vor writeAll jBuf
    bufOpen <
    jBuf line 1
    jBuf line 2
    bufClose
    a3 vor writeAll in inIx 0
    #jIn 1# tst in line 1 eins ,
    tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    #jIn eof 4#
    a4 vor barLast inIx 0
    a6 vor barEnd inIx 4
    a7 nach barEnd lazy 1 writeAll ***
    b1 vor barBegin lazy 1 writeAll *** <class TstPipeLazyRdr>
    b4 vor writeAll
    b2 vor writeAll rdr inIx 1
    RdrOpen <
    jRead lazyRdr
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    jRead lazyRdr
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    jRead lazyRdr
    #jIn eof 4#
    rdrClose
    b3 vor barLast inIx 1
    b5 vor barEnd inIx 4
    b6 nach barEnd lazy 1 writeAll ***
$/tstPipeLazy/ */
    call tst t, "tstPipeLazy"
    do lz=0 to 1
        if lz then
            w = 'writeAll'
        else
            w = 'writeNow'
        m.t.inIx = 1-lz
        ty = class4Name('TstPipeLazyBuf', '')
        if ty == '' then do
            ty = classNew('n TstPipeLazyBuf u JRWDeleg', 'm',
            , 'jOpen call tstOut "T", "bufOpen" opt;',
              'call jOpen m.m.deleg, opt',
            , 'jClose call tstOut "T", "bufClose";',
              'call jClose m.m.deleg')
            end
        if \ lz then
             call mAdd t'.TRANS', ty '<class TstPipeLazyBuf>'
        call out 'a1 vor pipeBegin loop lazy' lz w '***' ty
        call pipe '+N'
        call out 'a2 vor' w 'jBuf'
        b = oNew('TstPipeLazyBuf', jBuf('jBuf line 1','jBuf line 2'))
        interpret 'call pipe'w 'b'
        call out 'a3 vor' w 'in inIx' m.t.inIx
        interpret 'call pipe'w
        call out 'a4 vor barLast inIx' m.t.inIx
        call pipe 'P|'
        call out 'a5 vor 2' w 'in inIx' m.t.inIx
        interpret 'call pipe'w
        call out 'a6 vor barEnd inIx' m.t.inIx
        call pipe '-'
        call out 'a7 nach barEnd lazy' lz w '***'

        ty = class4Name('TstPipeLazyRdr', '')
        if ty == '' then
            ty = classNew('n TstPipeLazyRdr u JRW', 'm',
            , 'jOpen call tstOut "T", "RdrOpen" opt',
            , 'jRead call out "jRead lazyRdr";' ,
                  'return jRead(m.m.rdr, var);',
            , 'jClose call tstOut "T",  "rdrClose";')
        if \ lz then
             call mAdd t'.TRANS', ty '<class TstPipeLazyRdr>'

        r = oNew('TstPipeLazyRdr')
            m.r.rdr = m.j.in
        if lz then
            call mAdd t'.TRANS', r '<lazyRdr>'
     m.t.inIx = 2-lz
     call out 'b1 vor barBegin lazy' lz w '***' ty
     call pipe '+N'
     call out 'b2 vor' w 'rdr inIx' m.t.inIx
     interpret 'call pipe'w 'r'
     call out 'b3 vor barLast inIx' m.t.inIx
     call pipe 'P|'
        call out 'b4 vor' w
        interpret 'call pipe'w
        call out 'b5 vor barEnd inIx' m.t.inIx
        call pipe '-'
     call out 'b6 nach barEnd lazy' lz w '***'
        end
    call tstEnd t
    return
endProcedure tstPipeLazy

tstEnvClass: procedure expose m.
    call pipeIni
/*
$=/tstEnvClass/
    ### start tst tstEnvClass #########################################
    a0 vor pipeBegin loop lazy 0 writeNow *** TY
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    a5 vor writeNow
    a1 vor jBuf()
    a2 vor writeNow b
    tstR: @<o20 of TstEnvClass10> isA :TstEnvClass10
    tstR:  .f11 = .
    tstR:  .F12 = value F12 of o1 <o20 of TstEnvClass10>
    tstR:  .f13 = .
    WriteO o2
    tstR: @<oc0 of TstEnvClass20> isA :TstEnvClass20 = valueO2Lazy0
    tstR:  .f24 = .
    tstR:  .F25 = value F25 of o20 <o20 of TstEnvClass20>
    a3 vor writeNow
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    a4 vor barLast inIx 4
    a6 vor barEnd
    a7 nach barEnd lazy 0 writeNow ***
    a0 vor pipeBegin loop lazy 1 writeAll *** TY
    a5 vor writeAll
    a1 vor jBuf()
    a2 vor writeAll b
    tstR: @<o21 of TstEnvClass10> isA :TstEnvClass10
    tstR:  .f11 = .
    tstR:  .F12 = value F12 of o1 <o21 of TstEnvClass10>
    tstR:  .f13 = .
    WriteO o2
    tstR: @<oc1 of TstEnvClass20> isA :TstEnvClass20 = valueO2Lazy1
    tstR:  .f24 = .
    tstR:  .F25 = value F25 of o21 <o21 of TstEnvClass20>
    a3 vor writeAll
    #jIn 1# tst in line 1 eins ,
    tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    #jIn eof 4#
    a4 vor barLast inIx 0
    a6 vor barEnd
    a7 nach barEnd lazy 1 writeAll ***
$/tstEnvClass/ */

    call tst t, "tstEnvClass"
    t10 = classNew('n? TstEnvClass10 u f f11 v, f F12 v, f f13 v')
    t20 = classNew('n? TstEnvClass20 u v, f f24 v, f F25 v')
    do lz=0 to 1
        if lz then
            w = 'writeAll'
        else
            w = 'writeNow'
        m.t.inIx = 1-lz
        call out 'a0 vor pipeBegin loop lazy' lz w '***' ty
        call pipe '+N'
        call out 'a1 vor jBuf()'
        b = jOpen(jBuf(), m.j.cWri)
        o1 = oNew('TstEnvClass10')
        m.o1.F12 = 'value F12 of o1' o1
        call mAdd t'.TRANS', o1 '<o2'lz 'of TstEnvClass10>'
        call jWriteO b, o1
        call jWrite b, 'WriteO o2'
        o2 = oNew('TstEnvClass20')
        m.o2 = 'valueO2Lazy'lz
        m.o2.F25 = 'value F25 of o2'lz o2
        oc = oCopy(oCopy(o2))
        call mAdd t'.TRANS', o2 '<o2'lz 'of TstEnvClass20>'
        call mAdd t'.TRANS', oc '<oc'lz 'of TstEnvClass20>'
        call jWriteO b, oc
        call out 'a2 vor' w 'b'
        interpret 'call pipe'w jClose(b)
        call out 'a3 vor' w
        interpret 'call pipe'w
        call out 'a4 vor barLast inIx' m.t.inIx
        call pipe 'P|'
        call out 'a5 vor' w
        interpret 'call pipe'w
        call out 'a6 vor barEnd'
        call pipe '-'
        call out 'a7 nach barEnd lazy' lz w '***'
        end
    call tstEnd t
    m.t.trans.0 = 0
    return
endProcedure tstEnvClass

tstFile: procedure expose m.
    call catIni
/*
$=/tstFile/
    ### start tst tstFile #############################################
    write read 0 last 10 vor anfang
    write read 1 last 80  links1 1   und rechts |  ..
    write read 2 last 80 liinks2 2   und rechts |  ..
    write read 5 last 80 links5 5 rechts5
    write read 99 last 80 links99 99 rechts
    write read 100 last 80 links100 100 rechts
    write read 101 last 80 links101 101 rechts
    write read 999 last 80 links999 999 rechts
    write read 1000 last 80 links1000 1000 rechts
    write read 1001 last 80 links1001 1001 rechts
    write read 2109 last 80 links2109 2109 rechts
    out > eins 1                                                       +
    .             .
    out > eins 2 schluss.                                              +
    .             .
    buf eins
    buf zwei
    buf drei
    out > zwei mit einer einzigen Zeile                                +
    .             .
    . links1 1   und rechts |  .                                       +
    .              .
$/tstFile/ */
    call tst t, "tstFile"
    pds = tstFilename('lib', 'r')
    call tstFileWr pds, 0, ' links0', '  und rechts |  .  '
    call tstFileWr pds, 1, ' links1', '  und rechts |  .  '
    call tstFileWr pds, 2, 'liinks2', '  und rechts |  .  '
    call tstFileWr pds, 5, 'links5', 'rechts5'
    call tstFileWr pds, 99, 'links99', 'rechts'
    call tstFileWr pds, 100, 'links100', 'rechts'
    call tstFileWr pds, 101, 'links101', 'rechts'
    call tstFileWr pds, 999, 'links999', 'rechts'
    call tstFileWr pds, 1000, 'links1000', 'rechts'
    call tstFileWr pds, 1001, 'links1001', 'rechts'
    call tstFileWr pds, 2109, 'links2109', 'rechts'
    pd2 = tstFilename('li2', 'r')
    call pipeIni
    call pipe '+F', s2o(tstPdsMbr(pd2, 'eins'))
    call out tstFB('out > eins 1') /* simulate fixBlock on linux */
    call out tstFB('out > eins 2 schluss.')
    call pipe '-'
    call pipe '+F', s2o(tstPdsMbr(pd2, 'zwei'))
    call out tstFB('out > zwei mit einer einzigen Zeile')
    call pipe '-'
    b = jBuf("buf eins", "buf zwei", "buf drei")
    call pipe '+f', , s2o(tstPdsMbr(pd2, 'eins')), b,
                    ,jBuf(),
                    ,s2o(tstPdsMbr(pd2, 'zwei')),
                    ,s2o(tstPdsMbr(pds, 'wr0')),
                    ,s2o(tstPdsMbr(pds, 'wr1'))
    call pipeWriteNow
    call pipe '-'
    call tstEnd t
    return
endProcedure tstFile

/*--- simulate fixBlock 80 on linux ---------------------------------*/
tstFB: procedure expose m.
parse arg line, recL
    if m.err.os \== 'LINUX' then
        return line
    else if recL == '' then
        return left(line, 80)
    else
        return left(line, recL)
endProcedure tstFB

tstPdsMbr: procedure expose m.
parse arg pds, mbr
    if m.err.os = 'TSO' then
        return pds'('mbr') ::F'
    if m.err.os = 'LINUX' then
        return pds'.'mbr
    call err 'tstPdsMbr implement os' m.err.os
endProcedure tstPdsMbr

tstFileWR: procedure expose m.
parse arg dsn, num, le, ri
    io = file(tstPdsMbr(dsn, 'wr'num))
    call jOpen io, m.j.cWri
    do x = 1 to num /* simulate fixBlock 80 on LINUX*/
        call jWrite io, tstFB(le x ri)
        end
    call jClose io
    if num > 100 then
        call jReset io, tstPdsMbr(dsn, 'wr'num)

    call jOpen io, m.j.cRead
    m.vv = 'vor anfang'
    do x = 1 to num
        if \ jRead(io, vv) then
            call err x 'not jRead'
        else if m.vv <> le x ri then
            call err x 'read mismatch' m.vv
        end
    if jRead(io, vv) then
        call err x 'jRead but should be eof 1'
    if jRead(io, vv) then
        call err x'+1 jjRead but should be eof 2'
    call jClose io
    call tstOut t, 'write read' num 'last' length(m.vv) strip(m.vv,'t')
    return
endProcedure tstFileRW

tstFileList: procedure expose m.
    call catIni
/*
$=/tstFileList/
    ### start tst tstFileList #########################################
    empty dir
    filled dir
    <<pref 2 List>>eins
    <<pref 2 List>>zwei
    <<pref 2 List>>drei
    <<pref 2 List>>vier
    filled dir recursive
    <<pref 2 List>>eins
    <<pref 2 List>>zwei
    <<pref 2 List>>drei
    <<pref 2 List>>vier
    <<pref 1 vier>>eins
    <<pref 1 vier>>zwei
    <<pref 1 vier>>drei
$/tstFileList/ */
/*
$=/tstFileListTSO/
    ### start tst tstFileListTSO ######################################
    empty dir
    filled dir
    <<pref 2 LIST>>DREI
    <<pref 2 LIST>>EINS
    <<pref 2 LIST>>ZWEI
    filled dir recursive
    <<pref 2 LIST>>DREI
    <<pref 2 LIST>>EINS
    <<pref 1 VIER>>DREI
    <<pref 1 VIER>>EINS
    <<pref 1 VIER>>ZWEI
    <<pref 2 LIST>>ZWEI
$/tstFileListTSO/ */
    if m.err.os = 'TSO' then
        call tst t, "tstFileListTSO"
    else
        call tst t, "tstFileList"
    fi = file(tstFileName('FileList', 'r'))
    call fileMkDir fi
    fl = fileList(fi)
    call tstOut t, 'empty dir'
    call jWriteNow t, fl
    call tstFileListMake t, fi, 2
    call tstOut t, 'filled dir'
    call jWriteNow t, fl
    call tstOut t, 'filled dir recursive'
    call jWriteNow t, fileList(fi, 'r')
    call tstEnd t
    return
endProcedure tstFileList

tstFileListMake: procedure expose m.
parse arg t, fi, lev
    if \ fileIsDir(fi) then
        call fileMkDir fi
    call mAdd t'.TRANS', filePath(fi) || m.file.sep ,
                '<<pref' lev right(filePath(fi),4)'>>'
    call jCat fileChild(fi, 'eins','::F'),jBuf('zeile eins','eins' lev)
    call jCat fileChild(fi, 'zwei','::F'),jBuf('zeile zwei','zwei' lev)
    call jCat fileChild(fi, 'drei','::F'),jBuf('zeile drei','drei' lev)
    if lev > 1 then
        call tstFileListMake t, fileChild(fi, 'vier'), lev-1
    return
endProcedure tstFileListMake

tstMail: procedure expose m.
do i=1 to 3
    call mailHead xy, 'mail from walter''s rexx' time() i, A540769
    call mAdd xy, 'und hier kommt der text' ,
                , 'und zeile zwei timestamp' i':' date('s') time() ,
                , left('und eine lange Zeile 159', 156, '+')159 ,
                , left('und eine lange Zeile 160', 157, '+')160 ,
                , left('und eine lange Zeile 161', 158, '+')161 ,
                , left('und eine lange Zeile ', 200, '+')203 'y ',
                , 'und kurz' ,
                , 'und Schluss'
    call mailSend xy
    call sleep 60
    end
    return
endprocedure tstMail

tstF: procedure expose m.
/*
$=/tstF/
    ### start tst tstF ################################################
    f(1 23%c345%c67%%8, eins,  zwei ) =1 23eins345 zwei 67%8;
    f(1\S23%c345%S67%%8, eins,  zwei ) =1\S23eins345zwei67%8;
    f(1 23%C345%C67%%8, eins,  zwei ) =1 23eins345 zwei 67%8;
    f(1 23%c345%S67%%8, eins,  zwei ) =1 23eins345zwei67%8;
    f(1%S2%c3@2%S4@%c5, eins,  zwei ) =1eins2 zwei 3zwei4 zwei 5;
    f(1%-2C2%3C3@2%3.2C4, eins,  zwei ) =1ei2ei 3zwe4;
    f(1@F1%c2@f2%c3@F3%c4, eins,  zwei ) =1fEins2fZwei3fDrei4;
    tstF2 _ %-9C @%5I @%8I @%+8I @%-8I -----
    _ 0             0        0       +0 0       .
    _ -1.2         -1       -1       -1 -1      .
    _ 2.34          2        2       +2 2       .
    _ -34.8765    -35      -35      -35 -35     .
    _ 567.91234   568      568     +568 568     .
    _ -8901     -8901    -8901    -8901 -8901   .
    _ 23456     23456    23456   +23456 23456   .
    _ -789012   *****  -789012  -789012 -789012 .
    _ 34e6      ***** 34000000 ******** 34000000
    _ -56e7     ***** ******** ******** ********
    _ 89e8      ***** ******** ******** ********
    _ txtli     txtli    txtli    txtli txtli   .
    _ undEinLan undEi undEinLa undEinLa undEinLa
    tstF2 _ %-9C @%5.2I @%12.2I @%+12.2I @%-12.2I -----
    _ 0          0.00         0.00        +0.00 0.00        .
    _ -1.2      -1.20        -1.20        -1.20 -1.20       .
    _ 2.34       2.34         2.34        +2.34 2.34        .
    _ -34.8765  *****       -34.88       -34.88 -34.88      .
    _ 567.91234 *****       567.91      +567.91 567.91      .
    _ -8901     *****     -8901.00     -8901.00 -8901.00    .
    _ 23456     *****     23456.00    +23456.00 23456.00    .
    _ -789012   *****   -789012.00   -789012.00 -789012.00  .
    _ 34e6      *****  34000000.00 +34000000.00 34000000.00 .
    _ -56e7     ***** ************ ************ ************
    _ 89e8      ***** ************ ************ ************
    _ txtli     txtli        txtli        txtli txtli       .
    _ undEinLan undEi undEinLanger undEinLanger undEinLanger
    tstF2 _ %-9C @%7e @% 8E @% 9.3e @% 11.4E -----
    _ 0         0.00e00  0.00E00  0.000e00  0.0000E000
    _ -1.2      -1.2e00 -1.20E00 -1.200e00 -1.2000E000
    _ 2.34      2.34e00  2.34E00  2.340e00  2.3400E000
    _ -34.8765  -3.5e01 -3.49E01 -3.488e01 -3.4877E001
    _ 567.91234 5.68e02  5.68E02  5.679e02  5.6791E002
    _ -8901     -8.9e03 -8.90E03 -8.901e03 -8.9010E003
    _ 23456     2.35e04  2.35E04  2.346e04  2.3456E004
    _ -789012   -7.9e05 -7.89E05 -7.890e05 -7.8901E005
    _ 34e6      3.40e07  3.40E07  3.400e07  3.4000E007
    _ -56e7     -5.6e08 -5.60E08 -5.600e08 -5.6000E008
    _ 89e8      8.90e09  8.90E09  8.900e09  8.9000E009
    _ txtli       txtli    txtli     txtli       txtli.
    _ undEinLan undEinL undEinLa undEinLan undEinLange
    _ 8.76e-07  8.76e-7  8.76E-7  8.760e-7  8.7600E-07
    _ 5.43e-11  5.4e-11  5.4E-11  5.43e-11  5.4300E-11
    _ -8.76e-07 -8.8e-7 -8.76E-7 -8.760e-7 -8.7600E-07
    _ -5.43e-11 -5e-011 -5.4E-11 -5.43e-11 -5.4300E-11
$/tstF/ */
    call tst t, 'tstF'
    call tstF1 '1 23%c345%c67%%8'
    call tstF1 '1\S23%c345%S67%%8'
    call tstF1 '1 23%C345%C67%%8'
    call tstF1 '1 23%c345%S67%%8'
    call tstF1 '1%S2%c3@2%S4@%c5'
    call tstF1 '1%-2C2%3C3@2%3.2C4'
    call tstF1 '1@F1%c2@f2%c3@F3%c4'
    nums = '0 -1.2 2.34 -34.8765 567.91234 -8901 23456' ,
                '-789012 34e6 -56e7 89e8 txtli undEinLangerText?'
    call tstF2 '_ %-9C @%5I @%8I @%+8I @%-8I', nums
    call tstF2 '_ %-9C @%5.2I @%12.2I @%+12.2I @%-12.2I', nums
    num2 = ' 8.76e-07  5.43e-11 -8.76e-07  -5.43e-11'
    call tstF2 '_ %-9C @%7e @% 8E @% 9.3e @% 11.4E', nums num2
    call tstEnd t
    return
endProcedure tstF

tstF1: procedure expose m.
parse arg fmt
    e='eins'
    z=' zwei '
    f2 = 'f2'
    m.e.f1 = 'fEins'
    m.e.f2 = 'fZwei'
    m.e.f3 = 'fDrei'
    call out "f("fmt"," e"," z") ="f(fmt, e, z)";"
    return
endProcedure tstF1

tstF2: procedure expose m.
parse arg fmt, vals
    call out 'tstF2' fmt '-----'
    do vx=1 to words(vals)
        call out f(fmt, word(vals, vx))
        end
    return
endProcedure tstF2

tstFe: procedure expose m.
/*
$=/tstFe/
  ### start tst tstFe ###############################################
  .                   1 < 1.00e00> <1.00e00>
  .                   0 < 0.00e00> <0.00e00>
  .                -2.1 <-2.10e00> <-2.1e00>
  .                  .3 < 3.00e-1> <3.00e-1>
  .             -.45678 <-4.57e-1> <-4.6e-1>
  .                 901 < 9.01e02> <9.01e02>
  .               -2345 <-2.35e03> <-2.3e03>
  .              678e90 < 6.78e92> <6.78e92>
  .              123e-4 < 1.23e-2> <1.23e-2>
  .             567e-89 < 5.7e-87> <5.7e-87>
  .              12e456 < 1.2e457> <1.2e457>
  .             78e-901 < 8e-0900> <8e-0900>
  .           2345e5789 < 2e05792> <2e05792>
  .           123e-4567 < 1e-4565> <1e-4565>
  .          8901e23456 < 9e23459> <9e23459>
  .          -123e-4567 <-1e-4565> <-0e-999>
  .          567e890123 <********> <*******>
  .       45678e-901234 < 0e-9999> <0e-9999>
  .                kurz <    kurz> <kurz   >
  .       undLangerText <undLange> <undLang>
$/tstFe/
*/
    call tst t, 'tstFe'
    vAll = '1 0 -2.1 .3 -.45678 901 -2345 678e90 123e-4' ,
            '567e-89 12e456 78e-901 2345e5789  123e-4567 8901e23456' ,
            '-123e-4567 567e890123 45678e-901234' ,
            'kurz undLangerText'
    do vx=1 to words(vAll)
        v = word(vAll, vx)
        call tstOut t, right(v, 20)  '<'fe(v, 8, 2, 'e', ' ')'>' ,
                                     '<'fe(v, 7, 1, 'e', '-')'>'
        end
    call tstEnd t
    return
endProcedure

tstFTst: procedure expose m.
/*
$=/tstFTstS/
    ### start tst tstFTstS ############################################
    1956-01-29-23.34.56.987654     SS => 1956-01-29-23.34.56.987654|
    1956-01-29-23.34.56.987654     Ss => 1956-01-29-23.34.56|
    1956-01-29-23.34.56.987654     S  => 1956-01-29-23.34.56|
    1956-01-29-23.34.56.987654     SD => 19560129|
    1956-01-29-23.34.56.987654     Sd => 560129|
    1956-01-29-23.34.56.987654     SE => 29.01.1956|
    1956-01-29-23.34.56.987654     Se => 29.01.56|
    1956-01-29-23.34.56.987654     St => 23.34.56|
    1956-01-29-23.34.56.987654     ST => 23:34:56.987654|
    1956-01-29-23.34.56.987654     SY => GB29|
    1956-01-29-23.34.56.987654     SM => B2923345|
    1956-01-29-23.34.56.987654     SH => C33456|
$/tstFTstS/
$=/tstFTsts/
    ### start tst tstFTsts ############################################
    2014-12-23-16.57.38            sS => 2014-12-23-16.57.38.000000|
    2014-12-23-16.57.38            ss => 2014-12-23-16.57.38|
    2014-12-23-16.57.38            s  => 2014-12-23-16.57.38|
    2014-12-23-16.57.38            sD => 20141223|
    2014-12-23-16.57.38            sd => 141223|
    2014-12-23-16.57.38            sE => 23.12.2014|
    2014-12-23-16.57.38            se => 23.12.14|
    2014-12-23-16.57.38            st => 16.57.38|
    2014-12-23-16.57.38            sT => 16:57:38.000000|
    2014-12-23-16.57.38            sY => EM23|
    2014-12-23-16.57.38            sM => M2316573|
    2014-12-23-16.57.38            sH => B65738|
$/tstFTsts/
$=/tstFTstD/
    ### start tst tstFTstD ############################################
    23450618                       DS => 2345-06-18-00.00.00.000000|
    23450618                       Ds => 2345-06-18-00.00.00|
    23450618                       D  => 2345-06-18-00.00.00|
    23450618                       DD => 23450618|
    23450618                       Dd => 450618|
    23450618                       DE => 18.06.2345|
    23450618                       De => 18.06.45|
    23450618                       Dt => 00.00.00|
    23450618                       DT => 00:00:00.000000|
    23450618                       DY => PG18|
    23450618                       DM => G1800000|
    23450618                       DH => A00000|
$/tstFTstD/
0----+----1----+----2----+----3
abcdefghijklmnopqrstuvwxyz
$=/tstFTstd/
    ### start tst tstFTstd ############################################
    120724                         dS => 2012-07-24-00.00.00.000000|
    120724                         ds => 2012-07-24-00.00.00|
    120724                         d  => 2012-07-24-00.00.00|
    120724                         dD => 20120724|
    120724                         dd => 120724|
    120724                         dE => 24.07.2012|
    120724                         de => 24.07.12|
    120724                         dt => 00.00.00|
    120724                         dT => 00:00:00.000000|
    120724                         dY => CH24|
    120724                         dM => H2400000|
    120724                         dH => A00000|
$/tstFTstd/
$=/tstFTstE/
    ### start tst tstFTstE ############################################
    09.12.1345                     ES => 1345-12-09-00.00.00.000000|
    09.12.1345                     Es => 1345-12-09-00.00.00|
    09.12.1345                     E  => 1345-12-09-00.00.00|
    09.12.1345                     ED => 13451209|
    09.12.1345                     Ed => 451209|
    09.12.1345                     EE => 09.12.1345|
    09.12.1345                     Ee => 09.12.45|
    09.12.1345                     Et => 00.00.00|
    09.12.1345                     ET => 00:00:00.000000|
    09.12.1345                     EY => PM09|
    09.12.1345                     EM => M0900000|
    09.12.1345                     EH => A00000|
$/tstFTstE/
$=/tstFTste/
    ### start tst tstFTste ############################################
    31.05.2467                     eS => 2024-05-31-00.00.00.000000|
    31.05.2467                     es => 2024-05-31-00.00.00|
    31.05.2467                     e  => 2024-05-31-00.00.00|
    31.05.2467                     eD => 20240531|
    31.05.2467                     ed => 240531|
    31.05.2467                     eE => 31.05.2024|
    31.05.2467                     ee => 31.05.2467|
    31.05.2467                     et => 00.00.00|
    31.05.2467                     eT => 00:00:00.000000|
    31.05.2467                     eY => OF31|
    31.05.2467                     eM => F3100000|
    31.05.2467                     eH => A00000|
$/tstFTste/
$=/tstFTstt/
### start tst tstFTstt ############################################
    12.34.56                       tS => 0000-01-01-12.34.56.000000|
    12.34.56                       ts => 0000-01-01-12.34.56|
    12.34.56                       t  => 0000-01-01-12.34.56|
    12.34.56                       tD => 00000101|
    12.34.56                       td => 000101|
    12.34.56                       tE => 01.01.0000|
    12.34.56                       te => 01.01.00|
    12.34.56                       tt => 12.34.56|
    12.34.56                       tT => 12:34:56.000000|
    12.34.56                       tY => ??01|
    12.34.56                       tM => ?0112345|
    12.34.56                       tH => B23456|
$/tstFTstt/
$=/tstFTstT/
    ### start tst tstFTstT ############################################
    23.45.06.784019                TS => 0000-01-01-23.45.06.784019|
    23.45.06.784019                Ts => 0000-01-01-23.45.06|
    23.45.06.784019                T  => 0000-01-01-23.45.06|
    23.45.06.784019                TD => 00000101|
    23.45.06.784019                Td => 000101|
    23.45.06.784019                TE => 01.01.0000|
    23.45.06.784019                Te => 01.01.00|
    23.45.06.784019                Tt => 23.45.06|
    23.45.06.784019                TT => 23.45.06.784019|
    23.45.06.784019                TY => ??01|
    23.45.06.784019                TM => ?0123450|
    23.45.06.784019                TH => C34506|
$/tstFTstT/
$=/tstFTstY/
    ### start tst tstFTstY ############################################
    FE25                           YS => 2015-04-25-00.00.00.000000|
    FE25                           Ys => 2015-04-25-00.00.00|
    FE25                           Y  => 2015-04-25-00.00.00|
    FE25                           YD => 20150425|
    FE25                           Yd => 150425|
    FE25                           YE => 25.04.2015|
    FE25                           Ye => 25.04.15|
    FE25                           Yt => 00.00.00|
    FE25                           YT => 00:00:00.000000|
    FE25                           YY => FE25|
    FE25                           YM => E2500000|
    FE25                           YH => A00000|
$/tstFTstY/
$=/tstFTstM/
    ### start tst tstFTstM ############################################
    I2317495                       MS => 0000-08-23-17.49.50.000000|
    I2317495                       Ms => 0000-08-23-17.49.50|
    I2317495                       M  => 0000-08-23-17.49.50|
    I2317495                       MD => 00000823|
    I2317495                       Md => 000823|
    I2317495                       ME => 23.08.0000|
    I2317495                       Me => 23.08.00|
    I2317495                       Mt => 17.49.50|
    I2317495                       MT => 17:49:50.000000|
    I2317495                       MY => ?I23|
    I2317495                       MM => I2317495|
    I2317495                       MH => B74950|
$/tstFTstM/
$=/tstFTstH/
    ### start tst tstFTstH ############################################
    B23456                         HS => 0000-01-01-12.34.56.000000|
    B23456                         Hs => 0000-01-01-12.34.56|
    B23456                         H  => 0000-01-01-12.34.56|
    B23456                         HD => 00000101|
    B23456                         Hd => 000101|
    B23456                         HE => 01.01.0000|
    B23456                         He => 01.01.00|
    B23456                         Ht => 12.34.56|
    B23456                         HT => 12:34:56.000000|
    B23456                         HY => ??01|
    B23456                         HM => ?0112345|
    B23456                         HH => B23456|
$/tstFTstH/
$=/tstFTstn/
    ### start tst tstFTstn ############################################
    19560423 17:58:29              nS => 1956-04-23-17.58.29.000000|
    19560423 17:58:29              ns => 1956-04-23-17.58.29|
    19560423 17:58:29              n  => 1956-04-23-17.58.29|
    19560423 17:58:29              nD => 19560423|
    19560423 17:58:29              nd => 560423|
    19560423 17:58:29              nE => 23.04.1956|
    19560423 17:58:29              ne => 23.04.56|
    19560423 17:58:29              nt => 17.58.29|
    19560423 17:58:29              nT => 17:58:29.000000|
    19560423 17:58:29              nY => GE23|
    19560423 17:58:29              nM => E2317582|
    19560423 17:58:29              nH => B75829|
$/tstFTstn/
$=/tstFTstN/
    ### start tst tstFTstN ############################################
    32102130 10:21:32.456789       NS => 3210-21-30-10.21.32.456789|
    32102130 10:21:32.456789       Ns => 3210-21-30-10.21.32|
    32102130 10:21:32.456789       N  => 3210-21-30-10.21.32|
    32102130 10:21:32.456789       ND => 32102130|
    32102130 10:21:32.456789       Nd => 102130|
    32102130 10:21:32.456789       NE => 30.21.3210|
    32102130 10:21:32.456789       Ne => 30.21.10|
    32102130 10:21:32.456789       Nt => 10.21.32|
    32102130 10:21:32.456789       NT => 10:21:32.456789|
    32102130 10:21:32.456789       NY => AV30|
    32102130 10:21:32.456789       NM => V3010213|
    32102130 10:21:32.456789       NH => B02132|
$/tstFTstN/
*/
    call scanIni
    say "f('%t  ')" f('%t  ')
    allOut = 'Ss DdEetTYMH'
    allIn  = 'S1956-01-29-23.34.56.987654' ,
             's2014-12-23-16.57.38' ,
             'D23450618' ,
             'd120724'   ,
             'E09.12.1345' ,
             'e31.05.2467' ,
             't12.34.56'  ,
             'T23.45.06.784019' ,
             'YFE25' ,
             'MI2317495' ,
             'HB23456' ,
             'n19560423*17:58:29' ,
             'N32102130*10:21:32.456789'
    do ix=1 to words(allIn)
        parse value word(allIn, ix) with iF 2 iV
        iv = translate(iv, ' ', '*')
        call tst t, "tstFTst"iF
        do ox=1 to length(allOut)
            ft = iF || substr(allOut, ox, 1)
            call tstOut t, left(iV, 30) ft  '=>' f('%t'ft, iV)'|'
            if 0 & iF = 'Y' then
                say '???' ft '>>>' mGet('F_GEN.%t'ft)
            end
        call tstEnd t
        end
    return
endProcedure tstFTst

tstFmt: procedure expose m.
    call pipeIni
/*
$=/tstFmt/
    ### start tst tstFmt ##############################################
    =   a2i b3b   d4                       fl5          ex6
    -11 -11 b3    d4-11+d4++++    -111.1000000 -1.11000e-12
    -1  -10 b     d4-10+d4+++            null1        null3
    -    -9 b3b-9 d4-9+d4+++       -11.0000000 -1.11900e-10
    -8+  -8 b3b-  d4-8+d4++        -18.0000000 -1.18000e010
    -7   -7 b3b   d4-7+d4+          -7.0000000 -1.70000e-07
    -    -6 b3    d4-6+d4           -0.1111160 -6.00000e006
    -5+  -5 b     d4-5+d                 null2        null2
    -4   -4 b3b-4 d4-4+         -11114.0000000 -1.11140e008
    -    -3 b3b-  d4-3              -0.1130000 -1.13000e-04
    -2+  -2 b3b   d4-               -0.1200000 -1.20000e001
    -1   -1 b3    d4                -0.1000000 -1.00000e-02
    0     0 b     d                      null1        null1
    1+    1 b3    d4                 0.1000000  1.00000e-02
    2++   2 b3b   d42                0.1200000  1.20000e001
    3     3 b3b3  d43+               0.1130000  1.13000e-04
    4+    4 b3b4+ d44+d          11114.0000000  1.11140e008
    5++   5 b     d45+d4                 null2        null2
    6     6 b3    d46+d4+            0.1111160  1.11116e005
    7+    7 b3b   d47+d4++           0.1111117  7.00000e-08
    8++   8 b3b8  d48+d4+++          8.0000000  1.80000e009
    9     9 b3b9+ d49+d4++++         0.9000000  1.19000e-08
    10   10 b     d410+d4++++            null1        null3
    11+  11 b3    d411+d4+++++       0.1110000  1.00000e-12
    1    12 b3b   d412+d4++++++  11112.0000000  2.00000e012
    13   13 b3b1  d               1111.3000000  1.13000e-12
    14+  14 b3b14 d4            111111.0000000  1.40000e013
    1    15 b     d41                    null2        null1
    16   16 b3    d416               6.0000000  1.16000e003
    17+  17 b3b   d417+              0.7000000  1.11170e-03
    1    18 b3b1  d418+d            11.0000000  1.11800e003
    19   19 b3b19 d419+d4            0.1190000  9.00000e-05
    20+  20 b     d420+d4+               null1        null2
    2    21 b3    d421+d4++         11.1210000  1.11000e-05
    22   22 b3b   d422+d4+++     11111.2000000  2.00000e007
    23+  23 b3b2  d423+d4++++        0.1111123  1.11230e-09
    c3L      a2i drei  d4                 fl5          ex6
    -11 -1.10E01 b3    d4-11+d   -111.1000000 -1.11000e-12
    -1  -1.00E01 b     d4-10+d          null1        null3
    -   -9.00E00 b3b-9 d4-9+d4    -11.0000000 -1.11900e-10
    -8+ -8.00E00 b3b-  d4-8+d4    -18.0000000 -1.18000e010
    -7  -7.00E00 b3b   d4-7+d4     -7.0000000 -1.70000e-07
    -   -6.00E00 b3    d4-6+d4     -0.1111160 -6.00000e006
    -5+ -5.00E00 b     d4-5+d           null2        null2
    -4  -4.00E00 b3b-4 d4-4+   -11114.0000000 -1.11140e008
    -   -3.00E00 b3b-  d4-3        -0.1130000 -1.13000e-04
    -2+ -2.00E00 b3b   d4-         -0.1200000 -1.20000e001
    -1  -1.00E00 b3    d4          -0.1000000 -1.00000e-02
    0    0.00E00 b     d                null1        null1
    1+   1.00E00 b3    d4           0.1000000  1.00000e-02
    2++  2.00E00 b3b   d42          0.1200000  1.20000e001
    3    3.00E00 b3b3  d43+         0.1130000  1.13000e-04
    4+   4.00E00 b3b4+ d44+d    11114.0000000  1.11140e008
    5++  5.00E00 b     d45+d4           null2        null2
    6    6.00E00 b3    d46+d4+      0.1111160  1.11116e005
    7+   7.00E00 b3b   d47+d4+      0.1111117  7.00000e-08
    8++  8.00E00 b3b8  d48+d4+      8.0000000  1.80000e009
    9    9.00E00 b3b9+ d49+d4+      0.9000000  1.19000e-08
    10   1.00E01 b     d410+d4          null1        null3
    11+  1.10E01 b3    d411+d4      0.1110000  1.00000e-12
    1    1.20E01 b3b   d412+d4  11112.0000000  2.00000e012
    13   1.30E01 b3b1  d         1111.3000000  1.13000e-12
    14+  1.40E01 b3b14 d4      111111.0000000  1.40000e013
    1    1.50E01 b     d41              null2        null1
    16   1.60E01 b3    d416         6.0000000  1.16000e003
    17+  1.70E01 b3b   d417+        0.7000000  1.11170e-03
    1    1.80E01 b3b1  d418+d      11.0000000  1.11800e003
    19   1.90E01 b3b19 d419+d4      0.1190000  9.00000e-05
    20+  2.00E01 b     d420+d4          null1        null2
    2    2.10E01 b3    d421+d4     11.1210000  1.11000e-05
    22   2.20E01 b3b   d422+d4  11111.2000000  2.00000e007
    23+  2.30E01 b3b2  d423+d4      0.1111123  1.11230e-09
$/tstFmt/ */

    call tst t, "tstFmt"
    b = jBuf()
    st = b'.BUF'
    call pipe '+F', b
    call tstDataClassOut '. c3 a2i i b3b c5 d4 c13 fl5 f8n2 ex6 e9n3',
        , -11, + 23
    call pipe '-'
    call fTabAuto fTabReset(abc, 1), b
    call fTabReset abc, 1
    call fTabAddDetect abc,      , st   , 'c3L'
    call fTabAdd       abc, 'a2i', ' % 8E'
    call fTabAddDetect abc, 'b3b', st   , 'drei'
    call fTabAdd       abc, 'd4', ' %-7C'
    call fTabAddDetect abc, 'fl5', st
    call fTabAddDetect abc, 'ex6', st
    call fTab abc, b
    call tstEnd t
    return
endProcedure tstFmt

tstFTab: procedure expose m.
    call pipeIni
    call scanIni
/*
$=/tstFTab/
    ### start tst tstFTab #############################################
    testData begin
    ..---------a2i-b3b------------------d4------fl5-----ex6---
    -11       -11 b3           -11+d4++++ -111.100 -1e-012
    -1        -10 b            4-10+d4+++    null1   null3
    -          -9 b3b-9        d4-9+d4+++  -11.000 -1e-010
    -8+        -8 b3b-          d4-8+d4++  -18.000 -1.2e10
    -7         -7 b3b            d4-7+d4+   -7.000 -1.7e-7
    -          -6 b3              d4-6+d4   -0.111 -6.0e06
    -5+        -5 b                d4-5+d    null2   null2
    -4         -4 b3b-4             d4-4+ ******** -1.1e08
    -          -3 b3b-               d4-3   -0.113 -1.1e-4
    -2+        -2 b3b                 d4-   -0.120 -1.2e01
    -1         -1 b3                   d4   -0.100 -1.0e-2
    0           0 b                     d    null1   null1
    1+          1 b3                   d4    0.100 1.00e-2
    2++         2 b3b                 d42    0.120 1.20e01
    3           3 b3b3               d43+    0.113 1.13e-4
    4+          4 b3b4+             d44+d ******** 1.11e08
    5++         5 b                d45+d4    null2   null2
    6           6 b3              d46+d4+    0.111 1.11e05
    7+          7 b3b            d47+d4++    0.111 7.00e-8
    8++         8 b3b8          d48+d4+++    8.000 1.80e09
    9           9 b3b9+        d49+d4++++    0.900 1.19e-8
    10         10 b            410+d4++++    null1   null3
    11+        11 b3           11+d4+++++    0.111 1.0e-12
    1          12 b3b          2+d4++++++ ******** 2.00e12
    13         13 b3b1                  d 1111.300 1.1e-12
    14+        14 b3b14                d4 ******** 1.40e13
    1          15 b                   d41    null2   null1
    16         16 b3                 d416    6.000 1.16e03
    17+        17 b3b               d417+    0.700 1.11e-3
    1          18 b3b1             d418+d   11.000 1.12e03
    19         19 b3b19           d419+d4    0.119 9.00e-5
    20+        20 b              d420+d4+    null1   null2
    2          21 b3            d421+d4++   11.121 1.11e-5
    22         22 b3b          d422+d4+++ ******** 2.00e07
    23+        23 b3b2         423+d4++++    0.111 1.11e-9
    ..---------a2i-b3b------------------d4------fl5-----ex6---
    testData end
$/tstFTab/ */

    call tst t, "tstFTab"
    b = jBuf()
    st = b'.BUF'
    call pipe '+F', b
    call tstDataClassOut '. c3 a2i i b3b c5 d4 c13 fl5 f8n2 ex6 e9n3',
        , -11, + 23
    call pipe 'P|'
    call fTabReset ft, 2 1, 1 3, '-'
    call fTabAdd   ft, '='   , '%-6C' , '.', 'testData begin',
                                            , 'testData end'
    call fTabAdd   ft, 'a2i' , ' %6i'
    call fTabAdd   ft, 'b3b' , ' %-12C'
    call fTabAdd   ft, 'd4'  , ' %10C'
    call fTabAdd   ft, 'fl5' , ' %8.3I'
    call fTabAdd   ft, 'ex6' , ' %7e'
    call fTab ft
    call pipe '-'
    call tstEnd t
    return
endProcedure tstFTab

tstCSV: procedure
/*
$=/tstCSV/
    ### start tst tstCSV ##############################################
    value,value eins,value zwei
    value,"value, , eins",value zwei
    value,"","value ""zwei"" oder?"
    value,,"value ""zwei"" oder?"
$/tstCSV/ */
    m.tstCsv.c.1 = ''
    m.tstCsv.c.2 = eins
    m.tstCsv.c.3 = zwei
    m.tstCsv.c.0 = 3
    call tst t, "tstCSV"
    m.tstCsv.o      = 'value'
    m.tstCsv.o.eins = 'value eins'
    m.tstCsv.o.zwei = 'value zwei'
    call tstOut t, csv4Obj(tstCsv'.'o, tstCsv'.'c, 0)
    m.tstCsv.o.eins = 'value, , eins'
    call tstOut t, csv4Obj(tstCsv'.'o, tstCsv'.'c, 0)
    m.tstCsv.o.eins = ''
    m.tstCsv.o.zwei = 'value "zwei" oder?'
    call tstOut t, csv4Obj(tstCsv'.'o, tstCsv'.'c, 0)
    m.tstCsv.o.eins = '---'
    call tstOut t, csv4Obj(tstCsv'.'o, tstCsv'.'c, 1, '---')
    call tstEnd t
    return
endProcedure tstCSV

tstfUnits: procedure
/*
$=/tstfUnits/
    ### start tst tstfUnits ###########################################
    .            .3 ==>  0s30 ++>   0s30 -+> -0s30 -->  -0s30
    .            .8 ==>  0s80 ++>   0s80 -+> -0s80 -->  -0s80
    .             1 ==>  1s00 ++>   1s00 -+> -1s00 -->  -1s00
    .           1.2 ==>  1s20 ++>   1s20 -+> -1s20 -->  -1s20
    .            59 ==> 59s00 ++>  59s00 -+> -59s0 --> -59s00
    .         59.07 ==> 59s07 ++>  59s07 -+> -59s0 --> -59s07
    .        59.997 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .            60 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .          60.1 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .           611 ==> 10m11 ++>  10m11 -+> -10m1 --> -10m11
    .        3599.4 ==> 59m59 ++>  59m59 -+> -59m5 --> -59m59
    .        3599.5 ==>  1h00 ++>   1h00 -+> -1h00 -->  -1h00
    .          3661 ==>  1h01 ++>   1h01 -+> -1h01 -->  -1h01
    .         83400 ==> 23h10 ++>  23h10 -+> -23h1 --> -23h10
    .         84700 ==> 23h32 ++>  23h32 -+> -23h3 --> -23h32
    .         86400 ==>  1d00 ++>   1d00 -+> -1d00 -->  -1d00
    .         89900 ==>  1d01 ++>   1d01 -+> -1d01 -->  -1d01
    .       8467200 ==> 98d00 ++>  98d00 -+> -98d0 --> -98d00
    .    8595936.00 ==> 99d12 ++>  99d12 -+> -99d1 --> -99d12
    .    8638704.00 ==>  100d ++>   100d -+> -100d -->  -100d
    .       8640000 ==>  100d ++>   100d -+> -100d -->  -100d
    .     863913600 ==> 9999d ++>  9999d -+> ----d --> -9999d
    .     863965440 ==> ++++d ++> 10000d -+> ----d --> -----d
    .     8.6400E+9 ==> ++++d ++> +++++d -+> ----d --> -----d
    .            .3 ==>   0.300 ++>    0.300 -+>  -0.300 -->   -0.300
    .            .8 ==>   0.800 ++>    0.800 -+>  -0.800 -->   -0.800
    .             1 ==>   1.000 ++>    1.000 -+>  -1.000 -->   -1.000
    .           1.2 ==>   1.200 ++>    1.200 -+>  -1.200 -->   -1.200
    .            59 ==>  59.000 ++>   59.000 -+> -59.000 -->  -59.000
    .         59.07 ==>  59.070 ++>   59.070 -+> -59.070 -->  -59.070
    .        59.997 ==>  59.997 ++>   59.997 -+> -59.997 -->  -59.997
    .            60 ==>  60.000 ++>   60.000 -+> -60.000 -->  -60.000
    .          60.1 ==>  60.100 ++>   60.100 -+> -60.100 -->  -60.100
    .           611 ==> 611.000 ++>  611.000 -+> -611.00 --> -611.000
    .        3599.4 ==>   3k599 ++>    3k599 -+>  -3k599 -->   -3k599
    .        3599.5 ==>   3k600 ++>    3k600 -+>  -3k600 -->   -3k600
    .          3661 ==>   3k661 ++>    3k661 -+>  -3k661 -->   -3k661
    .         83400 ==>  83k400 ++>   83k400 -+> -83k400 -->  -83k400
    .     999999.44 ==> 999k999 ++>  999k999 -+> -999k99 --> -999k999
    .      999999.5 ==>   1M000 ++>    1M000 -+>  -1M000 -->   -1M000
    .    567.6543E6 ==> 567M654 ++>  567M654 -+> -567M65 --> -567M654
    .    .9999991E9 ==> 999M999 ++>  999M999 -+> -999M99 --> -999M999
    .    .9999996E9 ==>   1G000 ++>    1G000 -+>  -1G000 -->   -1G000
    .   .9999991E12 ==> 999G999 ++>  999G999 -+> -999G99 --> -999G999
    .   .9999996E12 ==>   1T000 ++>    1T000 -+>  -1T000 -->   -1T000
    .   567.6543E12 ==> 567T654 ++>  567T654 -+> -567T65 --> -567T654
    .   .9999991E15 ==> 999T999 ++>  999T999 -+> -999T99 --> -999T999
    .   .9999996E15 ==>   1P000 ++>    1P000 -+>  -1P000 -->   -1P000
    .   .9999991E18 ==> 999P999 ++>  999P999 -+> -999P99 --> -999P999
    .   .9999996E18 ==>   1E000 ++>    1E000 -+>  -1E000 -->   -1E000
    .   567.6543E18 ==> 567E654 ++>  567E654 -+> -567E65 --> -567E654
    .   .9999991E21 ==> 999E999 ++>  999E999 -+> -999E99 --> -999E999
    .   .9999996E21 ==>   1000E ++>    1000E -+>  -1000E -->   -1000E
    .   .9999992E24 ==> 999999E ++>  999999E -+> ------E --> -999999E
    .   .9999995E24 ==> ++++++E ++> 1000000E -+> ------E --> -------E
    .    10.6543E24 ==> ++++++E ++> +++++++E -+> ------E --> -------E
$/tstfUnits/ */
    call jIni
    call tst t, "tstfUnits"
    d = 86400
    lst = .3 .8 1 1.2 59 59.07 59.997 60 60.1 611 ,
          3599.4 3599.5 3661 d-3000 d-1700 d d+3500 ,
          d * 98 d * 99.49 d * 99.985 d*100 d * 9999 d * 9999.6 ,
          d * 1e5
    do wx=1 to words(lst)
        call tstOut t, right(word(lst, wx), 14) ,
                 '==>' fTime(     word(lst, wx)   ) ,
                 '++>' fTime(     word(lst, wx), 1),
                 '-+>' fTime('-'word(lst, wx),    ),
                 '-->' fTime('-'word(lst, wx), 1)
        end
    lst = subword(lst, 1, 14) 999999.44 999999.5,
        567.6543e6 .9999991e9 .9999996e9 .9999991e12 .9999996e12 ,
        567.6543e12 .9999991e15 .9999996e15 .9999991e18 .9999996e18 ,
        567.6543e18 .9999991e21 .9999996e21 .9999992e24 .9999995e24 ,
         10.6543e24
    do wx=1 to words(lst)
        call tstOut t, right(word(lst, wx), 14) ,
                 '==>' fDec(      word(lst, wx)   ) ,
                 '++>' fDec(      word(lst, wx), 1),
                 '-+>' fDec('-'word(lst, wx),     ),
                 '-->' fDec('-'word(lst, wx), 1)
        end
    call tstEnd t
    return
endProcedure tstfUnits

tstSb: procedure expose m.
/*
$=/tstSb/
    ### start tst tstSb ###############################################
    end        : 0
    char  3    : 1 abc
    lit   d?   : 0 .
    lit   de   : 1 de
    lit   de ? fg fgh: 1 fg
    while HIJ  : 0 .
    end        : 0
    while Jih  : 1 hi
    while ? klj: 1 jklkl ?
    end        : 1
    while ? klj: 0 .
    char  3    : 0 .
    lit        : 0 .
    until cba  : 0 .
    until ?qd  : 1 abc
    until ?qr  : 1 defdef .
    until ?qr  : 0 .
    strEnd ?   : 1 ?
    strEnd ?   : 0 .
    strEnd ?   : 1 ab??cd????gh?
    strEnd ") ": 1 ab) .
    strEnd ") ": 1 cd) ) gh) .
$/tstSb/ */
    call tst t, 'tstSb'
    call scanSBSrc s, 'abcdefghijklkl ?'
    call out 'end        :' scanSBEnd(s)
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit   d?   :' scanLit(s, 'd?') m.s.tok
    call out 'lit   de   :' scanLit(s, 'de') m.s.tok
    call out 'lit   de ? fg fgh:',
            scanLit(s, 'de', '?', 'fg', 'fgh') m.s.tok
    call out 'while HIJ  :' scanWhile(s, 'HIJ') m.s.tok
    call out 'end        :' scanSBEnd(s)
    call out 'while Jih  :' scanWhile(s, 'Jih') m.s.tok
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'end        :' scanSBEnd(s)
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit        :' scanLit(s) m.s.tok
    call scanSBSrc s, 'abcdefdef ?'
    call out 'until cba  :' scanUntil(s, 'cba') m.s.tok
    call out 'until ?qd  :' scanUntil(s, '?qd') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSBSrc s, 'ab??cd????gh?ijk'
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSBSrc s, 'ab) cd) ) gh) jk) )'
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call tstEnd t
    return
endProcedure tstSb

tstSb2: procedure expose m.
/*
$=/tstSb2/
    ### start tst tstSb2 ##############################################
    end        : 0
    char  3    : 1 abc
    lit   d?   : 0 .
    lit   de   : 1 de
    lit   de ? fg fgh: 1 fg
    while HIJ  : 0 .
    end        : 0
    while Jih  : 1 hi
    while ? klj: 1 jklkl ?
    end        : 1
    while ? klj: 0 .
    char  3    : 0 .
    lit        : 0 .
    until cba  : 0 .
    until ?qd  : 1 abc
    until ?qr  : 1 defdef .
    until ?qr  : 0 .
    strEnd ?   : 1 ?
    strEnd ?   : 0 .
    strEnd ?   : 1 ab??cd????gh?
    strEnd ") ": 1 ab) .
    strEnd ") ": 1 cd) ) gh) .
$/tstSb2/ */
    call tst t, 'tstSb2'
    call scanIni
    call scanSrc s, 'abcdefghijklkl ?'
    call out 'end        :' scanEnd(s)
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit   d?   :' scanLit(s, 'd?') m.s.tok
    call out 'lit   de   :' scanLit(s, 'de') m.s.tok
    call out 'lit   de ? fg fgh:',
            scanLit(s, 'de', '?', 'fg', 'fgh') m.s.tok
    call out 'while HIJ  :' scanWhile(s, 'HIJ') m.s.tok
    call out 'end        :' scanEnd(s)
    call out 'while Jih  :' scanWhile(s, 'Jih') m.s.tok
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'end        :' scanEnd(s)
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit        :' scanLit(s) m.s.tok
    call scanSrc s, 'abcdefdef ?'
    call out 'until cba  :' scanUntil(s, 'cba') m.s.tok
    call out 'until ?qd  :' scanUntil(s, '?qd') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab??cd????gh?ijk'
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab) cd) ) gh) jk) )'
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call tstEnd t
    return
endProcedure tstSb2

tstScan: procedure expose m.
/*
$=/tstScan.1/
    ### start tst tstScan.1 ###########################################
    scan src a034,Und hr123sdfER"st1" 'str2''mit''apo''s' .
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    scan n tok 3: Und key  val .
    scan v tok 1:   key  val .
    scan n tok 10: hr123sdfER key  val .
    scan q tok 5: "st1" key  val st1
    scan v tok 1:   key  val st1
    scan a tok 19: 'str2''mit''apo''s' key  val str2'mit'apo's
    scan v tok 1:   key  val str2'mit'apo's
$/tstScan.1/ */
    call scanIni
    call tst t, 'tstScan.1'
    call tstScan1 ,'l"litEins"l"litZwei"ndv" "aq1' ,
        ,"a034,Und hr123sdfER""st1"" 'str2''mit''apo''s' "
    call tstEnd t

/*
$=/tstScan.2/
    ### start tst tstScan.2 ###########################################
    scan src a034,Und hr123sdfER"st1" 'str2''mit''apo''s' .
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    scan n tok 3: Und key  val .
    scan b tok 1:   key  val .
    scan n tok 10: hr123sdfER key  val .
    scan " tok 5: "st1" key  val st1
    scan b tok 1:   key  val st1
    scan ' tok 19: 'str2''mit''apo''s' key  val str2'mit'apo's
    scan b tok 1:   key  val str2'mit'apo's
$/tstScan.2/ */
    call tst t, 'tstScan.2'
    call tstScan1 , 'ndsb1' ,
        ,"a034,Und hr123sdfER""st1"" 'str2''mit''apo''s' "
    call tstEnd t

/*
$=/tstScan.3/
    ### start tst tstScan.3 ###########################################
    scan src a034,'wie 789abc
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    *** err: scanErr ending Apostroph(') missing
    .    e 1: last token  scanPosition 'wie 789abc
    .    e 2: pos 6 in string a034,'wie 789abc
    scan ' tok 1: ' key  val .
    scan n tok 3: wie key  val .
    scan s tok 1:   key  val .
    *** err: scanErr illegal number end after 789
    .    e 1: last token 789 scanPosition abc
    .    e 2: pos 14 in string a034,'wie 789abc
    scan d tok 3: 789 key  val .
    scan n tok 3: abc key  val .
$/tstScan.3/ */
    call tst t, 'tstScan.3'
    call tstScan1 , 'nds1' ,
        ,"a034,'wie 789abc"
    call tstEnd t

/*
$=/tstScan.4/
    ### start tst tstScan.4 ###########################################
    scan src litEinsefr 23 sdfER'str1'litZwei "str2""mit quo"s .
    scan l tok 7: litEins key  val .
    scan n tok 3: efr key  val .
    scan b tok 1:   key  val .
    scan d tok 2: 23 key  val .
    scan b tok 1:   key  val .
    scan n tok 5: sdfER key  val .
    scan a tok 6: 'str1' key  val str1
    scan l tok 7: litZwei key  val str1
    scan b tok 1:   key  val str1
    scan q tok 15: "str2""mit quo" key  val str2"mit quo
    scan n tok 1: s key  val str2"mit quo
    scan b tok 1:   key  val str2"mit quo
$/tstScan.4/ */
    call tst t, 'tstScan.4'
    call tstScan1 , 'l"litEins"l"litZwei"ndbaq1' ,
           ,"litEinsefr 23 sdfER'str1'litZwei ""str2""""mit quo""s "
    call tstEnd t

/*
$=/tstScan.5/
    ### start tst tstScan.5 ###########################################
    scan src  aha;+-=f ab=cdEf eF='strIng' .
    scan b tok 1:   key  val .
    scan k tok 4:  no= key aha val def
    scan ; tok 1: ; key aha val def
    scan + tok 1: + key aha val def
    scan - tok 1: - key aha val def
    scan = tok 1: = key aha val def
    scan k tok 4:  no= key f val def
    scan k tok 4: cdEf key ab val cdEf
    scan b tok 1:   key ab val cdEf
    scan k tok 8: 'strIng' key eF val strIng
    scan b tok 1:   key eF val strIng
$/tstScan.5/ */
    call tst t, 'tstScan.5'
    call tstScan1 , 'bk1'," aha;+-=f ab=cdEf eF='strIng' "
    call tstEnd t
    return
endProcedure tstScan

tstScanRead: procedure expose m.
/*
$=/tstScanRead/
    ### start tst tstScanRead #########################################
    name erste
    space
    name Zeile
    space
    nextLine
    nextLine
    space
    name dritte
    space
    name Zeile
    space
    name schluss
    space
$/tstScanRead/ */
    call scanReadIni
    call tst t, 'tstScanRead'
    b = jBuf('erste Zeile  ',,'  dritte Zeile  schluss  ')
    s = jOpen(scanRead(b), m.j.cRead)
    do while \scanEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanVerify(s, ' ') then call tstOut t, 'space'
        else if scanReadNL(s) then      call tstOut t, 'nextLine'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        end
    call jClose s
    call tstEnd t

/*
$=/tstScanReadMitSpaceLn/
    ### start tst tstScanReadMitSpaceLn ###############################
    name erste
    spaceLn
    name Zeile
    spaceLn
    name dritte
    spaceLn
    name Zeile
    spaceLn
    name schluss
    spaceLn
$/tstScanReadMitSpaceLn/ */
    call tst t, 'tstScanReadMitSpaceLn'
    s = scanOpen(scanRead(b))
    do forever
        if scanName(s) then         call out 'name' m.s.tok
        else if scanSpace(s) then call out 'spaceLn'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        else                        leave
        end
    call scanClose s
    call tstEnd t

/*
$=/tstScanJRead/
    ### start tst tstScanJRead ########################################
    1 jRead n tok erste val .
    2 jRead s tok   val .
    3 jRead n tok Zeile val .
    4 jRead s tok   val .
    5 jRead n tok dritte val .
    6 jRead s tok   val .
    7 jRead n tok Zeile val .
    8 jRead s tok   val .
    9 jRead n tok schluss val .
    10 jRead s tok   val .
    11 jRead 0 tok 1 val 1
    12 jRead s tok   val 1
    13 jRead + tok + val 1
    14 jRead s tok   val 1
    15 jRead 0 tok 2. val 2..
    16 jRead s tok   val 2..
    17 jRead + tok + val 2..
    18 jRead . tok . val 2..
    19 jRead s tok   val 2..
    20 jRead 0 tok +.3 val +.3
    21 jRead 0 tok -45e-3 val -45E-3
    22 jRead s tok   val -45E-3
    23 jRead " tok "a""b" val a"b
    24 jRead s tok   val a"b
    25 jRead ' tok 'c''d' val c'd
    className 1: ScanRes 18: ScanRes
$/tstScanJRead/ */

    call tst t, 'tstScanJRead'
    call jWrite jOpen(b,'>>'), '1  + 2. +. +.3-45e-3 "a""b"' "'c''d'"
    s = jOpen(scanRead(jClose(b)), '<')
    do x=1 while ass('v', jReadO(s))  \== ''
        call out x 'jRead' m.v.type 'tok' m.v.tok 'val' m.v.val
        v.x = v
        end
    call jClose s
    call out 'className 1:' className(objClass(v.1)),
                    '18:' className(objClass(v.18))
    call tstEnd t
    return
endProcedure tstScanRead

tstScanUtilInto: procedure expose m.
/*
$=/tstScanUtilIntoL/
TEMPLATE P3
     DSN('DBAF.DA540769.A802A.P00003.BV5I3NRN.REC')
     DISP(OLD,KEEP,KEEP)
TEMPLATE P4
     DSN('DBAF.DA540769.A802A.P00004.BV5I3NTK.REC')
     DISP(OLD,KEEP,KEEP)
LOAD DATA        LOG NO RESUME NO REPLACE COPYDDN(TCOPYD)
           STATISTICS INDEX(ALL) REPORT NO UPDATE ALL
 EBCDIC  CCSID(00500,00000,00000)
 SORTKEYS
  -- ENFORCE NO
  SORTDEVT DISK
  SORTNUM 160
  WORKDDN(TSYUTD,TSOUTD)
  INTO TABLE OA1P.TWB981 PART 1 INDDN TREC134
 WORKDDN(TSYUTS,TSOUTS)
 INTO TABLE "A540769"
   ."TWK802A1"
 PART 00001 INDDN P0
 WHEN(00001:00002) = X'0041'
 ( "DE1"
  POSITION(  00003:00010) DECIMAL
 , "CH2"
  POSITION(  00011:00015) CHAR(00005)
 , "TS3"
  POSITION(  00016:00041) TIMESTAMP EXTERNAL
 , "TI4"
  POSITION(  00042:00049) TIME EXTERNAL
 , "DA5"
  POSITION(  00050:00059) DATE EXTERNAL
 , "IN6"
  POSITION(  00060:00063) INTEGER
 , "RE7"
  POSITION(  00064:00067) FLOAT(21)
 )
 INTO TABLE "A540769"."TWK802A1"
 PART 00002 INDDN P0
 WHEN(00001:00002) = X'0041'
 ( "DE1"
  POSITION(  00003:00010) DECIMAL
 , "CH2"
  POSITION(  00011:00015) CHAR(00005)
 )
 dobido
$/tstScanUtilIntoL/
$=/tstScanUtilInto/
  ### start tst tstScanUtilInto #####################################
  -- 1 scanUtilInto
  . ( "DE1"
  .  POSITION(  00003:00010) DECIMAL
  . , "CH2"
  .  POSITION(  00011:00015) CHAR(00005)
  . , "TS3"
  .  POSITION(  00016:00041) TIMESTAMP EXTERNAL
  . , "TI4"
  .  POSITION(  00042:00049) TIME EXTERNAL
  . , "DA5"
  .  POSITION(  00050:00059) DATE EXTERNAL
  . , "IN6"
  .  POSITION(  00060:00063) INTEGER
  . , "RE7"
  .  POSITION(  00064:00067) FLOAT(21)
  . ) .
  .  -- table OA1P.TWB981 part 00001
  -- 2 scanUtilInto
  . ( "DE1"
  .  POSITION(  00003:00010) DECIMAL
  . , "CH2"
  .  POSITION(  00011:00015) CHAR(00005)
  . ) .
  .  -- table A540769.TWK802A1 part 00002
  -- 3 scanUtilInto
$/tstScanUtilInto/ */

    call scanReadIni
    b = jBuf()
    call mAddst b'.BUF', mapInline('tstScanUtilIntoL')
    call tst t, 'tstScanUtilInto'
    s = jOpen(scanUtilReset(ScanRead(b)), '<')
    do ix=1
        call out '--' ix 'scanUtilInto'
        if \ scanUtilInto(s) then
            leave
        call out '  -- table' m.s.tb 'part' m.s.part
        end
    call tstEnd t
    return
endProcedure tstSCanUtilInto

tstScanWin: procedure expose m.
/*
$=/tstScanWin/
    ### start tst tstScanWin ##########################################
    info 0: last token  scanPosition erste     Zeile                 dr+
    itteZe\npos 1 in line 1: erste     Zeile
    name erste
    spaceNL
    name Zeile
    spaceNL
    name dritteZeeeile
    info 5: last token dritteZeeeile scanPosition    zeile4            +
    .    fuenfueberSechs\npos 1 in line 4:    zeile4
    spaceNL
    name zeile4
    spaceNL
    name fuenfueberSechsUnddSiebenUNDundUndUAcht
    spaceNL
    info 10: last token   scanPosition undZehnueberElfundNochWeiterZwoe+
    lfundim1\npos 9 in line 10:         undZehn
    name undZehnueberElfundNochWeiterZwoelfundim13
    spaceNL
    name Punkt
    infoE 14: last token Punkt scanPosition \natEnd after line 13: im13+
    .      Punkt
$/tstScanWin/ */
    call scanWinIni
    call tst t, 'tstScanWin'
    b = jBuf('?erste     Zeile?',,'?  dritteZeeeile?', '?   zeile4 ',
       ,'?          fuenf?', '?ueberSechsUnddS?', '?iebenUNDundUndU?',
       ,'?Acht           ?', '?               ?', '?        undZehn?',
       ,'?ueberElfundNoch?', '?WeiterZwoelfund?', '?im13      Punkt?')
    s = jOpen(scanWin(b, , , 2, 15), m.j.cRead)
    call tstOut t, 'info 0:' scanInfo(s)
    do sx=1 while \scanEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        if sx // 5 = 0 then
            call tstOut t, 'info' sx':' scanInfo(s)
        end
    call tstOut t, 'infoE' sx':' scanInfo(s)
    call tstEnd t

/*
$=/tstScanWinRead/
    ### start tst tstScanWinRead ######################################
    info 0: last token  scanPosition erste     Zeile                z3 +
    com Ze\npos 1 in line 1: erste     Zeile
    name erste
    spaceNL
    name Zeile
    spaceNL
    name z3
    info 5: last token z3 scanPosition  com Zeeeile z4 come4          f+
    uenf\npos 4 in line 3:  z3 com Zeeeile
    spaceNL
    name z4
    spaceNL
    name fuenf
    spaceNL
    info 10: last token   scanPosition com    Sechs  com  sieben   comA+
    cht  com\npos 15 in line 5:     fuenf     c
    name com
    spaceNL
$/tstScanWinRead/ */
    call tst t, 'tstScanWinRead'
    call mAdd t.cmp,
       ,  "name Sechs",
       ,  "spaceNL",
       ,  "name com",
       ,  "info 15: last token com scanPosition   sieben   comAcht  c",
       || "om com    com\npos 2 in line 7: m  sieben   com" ,
       ,  "spaceNL",
       ,  "name sieben",
       ,  "spaceNL",
       ,  "name Acht",
       ,  "spaceNL",
       ,  "info 20: last token   scanPosition ueberElfundNochWeit com ",
       || "elfundim13\npos 1 in line 11: ueberElfundNoch",
       ,  "name ueberElfundNochWeit",
       ,  "spaceNL",
       ,  "name im13",
       ,  "spaceNL",
       ,  "name Punkt",
       ,  "info 25: last token Punkt scanPosition \natEnd after line ",
       || "13: im13      Punkt",
       ,  "infoE 26: last token Punkt scanPosition \natEnd after line",
       || " 13: im13      Punkt"
    b = jBuf('?erste     Zeile?',,'? z3 com Zeeeile?', '? z4 come4 ',
       ,'?    fuenf     c?', '?om    Sechs  co?', '?m  sieben   com?',
       ,'?Acht  com com  ?', '?  com          ?', '?  com   undZehn?',
       ,'?ueberElfundNoch?', '?Weit com elfund?', '?im13      Punkt?')
    s = scanWin(b, , , 2, 15)
    call scanOpts s, , , 'com'
    call tstOut t, 'info 0:' scanInfo(jOpen(s, m.j.cRead))
    do sx=1 while \scanEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        if sx // 5 = 0 then
            call tstOut t, 'info' sx':' scanInfo(s)
        end
    call tstOut t, 'infoE' sx':' scanInfo(s)
    call tstEnd t
    return
endProcedure tstScanWin

tstjCatSql: procedure expose m.
/*
$=/tstJCatSql/
    ### start tst tstJCatSql ##########################################
    cmd1 select     current time                 stamp from s.1
    cmd2 update ";--""'/*"
    cmd3 delete '*/''"' / 3 - 1
    cmd4 .
$/tstJCatSql/ */
    call tst t, 'tstJCatSql'
    b = jBuf('select -- /* c1', ' /* c1 */ current/* c2 " '' ',
       ,'c3"', '  c4   */time', 'stamp-- c5', 'from s.1; /* c6 */ ;' ,
       ,';update ";--""''/*";;       del123',
       , 'ete ''*/''''"'' / 3 - 1  -- c7', '/*c8 */   ')
    call jCatSqlReset tstJCat, , jOpen(b, '<'), 30
    do sx=1 until nx = ''
        nx = jCatSqlNext(tstJCat, ';')
        call tstOut t, 'cmd'sx nx
        end
    call jClose b
    call tstEnd t
    return
endProcedure tstJCatSql

tstScanSql: procedure expose m.
    call scanWinIni
/*
$=/tstScanSqlId/
    ### start tst tstScanSqlId ########################################
    sqlId ABC
    spaceNL
    sqlId AB__345EF
    spaceNL
$/tstScanSqlId/ */
    call tst t, 'tstScanSqlId'
    b = jBuf('abc  --  kommentar', right('ab_', 72), '_345ef-- kom')
    s = jOpen(scanSql(b), m.j.cRead)
    do sx=1 while \scanEnd(s)
        if scanSqlID(s) then       call tstOut t, 'sqlId' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlDelimited/
    ### start tst tstScanSqlDelimited #################################
    sqlDeId ABC
    spaceNL
    sqlDeId AB_3F
    spaceNL
    sqlDeId abc
    spaceNL
    sqlDeId ab_Ef
    spaceNL
$/tstScanSqlDelimited/ */
    call tst t, 'tstScanSqlDelimited'
    b = jBuf('abc  --  kommentar',,'  -- ',,right('ab_', 72),'3F-- kom',
           , '"abc"  --  ko', ' -- ',,right('"ab_', 72),'Ef"-- kom')
    s = jOpen(scanSql(b), m.j.cRead)
    do sx=1 while \scanEnd(s)
        if scanSqlDeID(s) then       call tstOut t, 'sqlDeId' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlQualified/
    ### start tst tstScanSqlQualified #################################
    sqlQuId ABC 1 ABC
    sqlQuId AB_3F 1 AB_3F
    sqlQuId abc 1 abc
    sqlQuId ab_Ef 1 ab_Ef
    sqlQuId EINS.Zwei.DREI 3 EINS
    sqlQuId vi er.fu  enf 2 vi er
$/tstScanSqlQualified/ */
    call tst t, 'tstScanSqlQualified'
    b = jBuf('abc  --  kommentar',,'  -- ',,right('ab_', 72),'3F-- kom',
           , '"abc"  --  ko', ' -- ',right('"ab_', 72),'Ef"-- kom',
           , 'eins."Zwei', '" -- com', ' . -- com', '  -- com',
           , 'drei -- ko', '"vi er"."fu  enf   " -- co')
    s = jOpen(scanSql(b), m.j.cRead)
    do sx=1 while \scanEnd(s)
        if scanSqlQuID(s) then
            call tstOut t, 'sqlQuId' m.s.val m.s.val.0 m.s.val.1
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlNum/
    ### start tst tstScanSqlNum #######################################
    sqlNum 1
    spaceNL
    sqlNum 2..
    spaceNL
    sqlNum .3
    spaceNL
    sqlNum 4.5
    spaceNL
    sqlNum +6
    spaceNL
    sqlNum +7.03
    spaceNL
    sqlNum -8
    spaceNL
    sqlNum -.9
    spaceNL
    sqlNum 1E2
    spaceNL
    sqlNum -2.E-2
    spaceNL
    sqlNum +.3E+3
    spaceNL
$/tstScanSqlNum/ */
    call tst t, 'tstScanSqlNum'
    b = jBuf('1 2. .3 4.5 +6 + --kom', , ' -- com ', , ' 7.03 -8 -  .9',
             '1e2 - 2.e-2 + .3e+3')
    s = jOpen(scanSql(b), m.j.cRead)
    do sx=1 while \scanEnd(s)
        if scanSqlNum(s) then
            call tstOut t, 'sqlNum' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlNumUnit/
    ### start tst tstScanSqlNumUnit ###################################
    sqlNumUnit 1 KB
    spaceNL
    sqlNumUnit .3 MB
    sqlNumUnit .5
    sqlNumUnit +6.E-5 B
    spaceNL
    sqlNumUnit -7
    char *
    spaceNL
    sqlNumUnit -.8
    char T
    char B
    spaceNL
    *** err: scanErr scanSqlNumUnit after +9. bad unit TB
    .    e 1: last token Tb scanPosition .
    .    e 2: pos 41 in line 1: 1 kb .3mB.5 +   6.e-5B -7* -.8 TB + 9.T+
    b
    sqlNumUnit +9..
    spaceNL
$/tstScanSqlNumUnit/ */
    call tst t, 'tstScanSqlNumUnit'
    b = jBuf('1 kb .3mB.5 +   6.e-5B -7* -.8 TB + 9.Tb')
    s = jOpen(scanSql(b), m.j.cRead)
    do sx=1 while \scanEnd(s)
        if scanSqlNumUnit(s, 0, 'B KB MB') then
            call tstOut t, 'sqlNumUnit' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else if scanChar(s ,1) then call tstOut t, 'char' m.s.tok
        else call scanErr s, 'cannot scan'
        end
    call tstEnd t
    return
endProcedure tstScanSql

/*--- one single test scan with lines to scan in stem ln -------------*/
tstScan1:
parse arg sc, classs, ln
    if sc == '' then do
        call tstOut t, 'scan src' ln
        call scanSrc scanOpts(s), ln
        end
    else do
        call tstOut t, 'scan scanner' sc
        s = sc
        end
    m.s.key = ''
    m.s.val = ''
    do forever
        x = tstScanType(s, classs)
        if x == '' then
           leave
        call tstOut t, 'scan' x 'tok' length(m.s.tok)':' m.s.tok ,
                  'key' m.s.key 'val' m.s.val
        end
    return
endProcedure tstScan1

tstScanType: procedure expose m.
parse arg s, opt
    cx = 1
    a2 = ''
    res = 0
    do while cx <= length(opt)
        f = substr(opt, cx, 1)
        cx = cx + 1
        if pos(substr(opt, cx, 1), "'""") > 0 then do
            m.tstScanType.src = opt
            m.tstScanType.pos = cx
            call scanString tstScanType
            a2 = m.tstScanType.val
            cx = m.tstScanType.pos
            end
        if      f == 'a' then
            res = scanString(s, "'")
        else if f == 'b' then
            res = scanSpace(s)
        else if f == 'c' then
            res = scanChar(s, a2)
        else if f == 'd' then
            res = scanNat(s, a2)
        else if f == 'k' then
            res = scanKeyValue(s, 'def')
        else if f == 'l' then
            res = scanLit(s, a2)
        else if f == 'q' then
            res = scanString(s, '"')
        else if f == 'v' then
            res = scanVerify(s, a2)
        else if f == 'w' then
            res = scanWord(s)
        else if f == 'y' then
            res = scanVerify(s, a2, 'm')
        if res then
            return f
        end
    return scanType(s)
endProcedure tstScanType


/****** tstData begin *************************************************/
tstData: procedure expose m.
parse arg ty 2 le 'n' nu, l, r, num
    call scanIni
    abs = abs(num)
    if nu \== '' then do
        if abs // 5 = 0 then
            return 'null' || (abs % 5 // nu + 1)
        end
    if ty = 'c' then do
        if le = '' then
            le = 8
        le = abs // le + 1
        if r = '' then
            r = '+'
        return left(l || num || r, le, right(r, 1))
        end
    if pos(ty, 'ief') < 1 then
        call err 'bad type' ty
    nn = abs
    if abbrev(num, '-') | abbrev(num, '+') then
        parse var num si 2 nn
    else
        si = ''
    if ty == 'e' then
        ex = 'e' || left('-', abs // 2) || (abs // 15)
    else
        ex = ''
    if le \== '' then
        nn = right(nn, abs // max(1, le - length(si||ex)) + 1, 1)
    if ty \== 'i' & (abs // 4) \= 0 & length(nn) > 1 then
            nn = overlay('.', nn, length(nn) - abs // length(nn))
    return si || nn || ex
endProcedure tstData

tstDataClassFo: procedure expose m.
parse arg flds
    ty = ''
    do fx=1 by 2 to words(flds)
        if word(flds, fx) = '.' then
            ty = ty', v'
        else
            ty = ty', f' word(flds, fx) 'v'
        end
    t = classNew('n* tstData u' substr(ty, 2))
    fo = oNew(m.t.name)
    ff = oFlds(fo)
    do fx=1 to m.ff.0
        f = fo || left('.', m.ff.fx \== '') || m.ff.fx
        m.f = word(flds, 2*fx)
        end
    return fo
endProcedure tstDataClassFo


tstDataClassOut: procedure expose m.
parse arg flds, f, t
    fo = tstDataClassFo(flds)
    ff = oFlds(fo)
    do x=f to t
        o = oCopy(fo)
        do fx=1 to m.ff.0
            f = o || left('.', m.ff.fx \== '') || m.ff.fx
            m.f = tstData(m.f, m.ff.fx, '+'m.ff.fx'+', x)
            end
        call outO o
        end
    return
endProcedure tstDataClassOut
/****** tst **********************************************************
      test infrastructure
***********************************************************************/
/*--- migrate all compares to new ones:
        tstCI input compare
        tstCO ouput migrated compares
        tstCIO input and output -------------------------------------*/
tstUtTime: procedure expose m.
    say 'begin' utTime()  sysvar('sysnode')
    do 3000000
       end
    call sleep 1
    say 'end  ' utTime()
return

tstUtc2d: procedure expose m.
/*
$=/tstUtc2d/
    ### start tst tstUtc2d ############################################
    .             ff            255
    .           ffff          65535
    .          10000          65536          65536 = 1 * 16 ** 4
    .          10001          65537
    .         ffffff       16777215
    .        1000000       16777216       16777216 = 1 * 16 ** 6
    .        1000001       16777217
    .        20000FF       33554687
    .      100000000     4294967296     4294967296 = 1 * 16 ** 8
    .      300000000    12884901888    12884901888 = 3 * 16 ** 8
    .      3020000EF    12918456559
$/tstUtc2d/
*/
    numeric digits 33
    call tst t, 'tstUtc2d'
    all = 'ff ffff 10000 10001 ffffff 1000000 1000001 20000FF' ,
           '100000000 300000000 3020000EF'
    do ax = 1 to words(all)
        a = word(all, ax)
        if substr(a, 2) = 0 then
            b = right(left(a, 1) * 16 ** (length(a)-1), 15) ,
              '=' left(a, 1) '* 16 **' (length(a)-1)
        else
            b = ''
        call tstout t, right(a, 15)right(utc2d(x2c(a)), 15)b
        end
    call tstEnd t
    return
endProcedure tstUtc2d

tstCI: procedure expose m.
parse arg m, nm
    m.m.CIO = 0
    signal tstCIwork
tstCIO: procedure expose m.
parse arg m, nm
    m.m.CIO = 1
tstCIwork:
    m.m.name = nm
    m.m.cmp.1 = left('### start tst' nm '', 67, '#')

    do ix=2 to arg()-1
        m.m.cmp.ix = arg(ix+1)
        end
    m.m.cmp.0 = ix-1
    if m.m.CIO then
        call tstCO m
    return

tstCO: procedure expose m.
parse arg m
    call tst2dpSay m.m.name, m'.CMP', 68
    return
/*--- initialise m as tester with name nm
        use inline input nm as compare lines -----------------------*/
tstReset: procedure expose m.
parse arg m, nm
    call tstIni
    m.m.name = nm
    m.m.inIx  = 0
    m.m.out.0 = 0
    m.m.err   = 0
    m.err.count = 0
    m.tst.act = m
    if \ datatype(m.m.trans.0, 'n') then
        m.m.trans.0 = 0
    m.m.trans.old = m.m.trans.0
    return
endProcedure tstReset

tst: procedure expose m.
parse arg m, nm, cmpSt
    call tstReset m, nm
    m.tst.tests = m.tst.tests+1
    if cmpSt == '' then do
        cmpSt = mCut(t'.CMP', 0)
        call tst4dp cmpSt, mapInline(nm)
        end
    m.m.cmp = cmpSt
    m.m.moreOutOk = 0
    call mAdd mCut(m'.IN', 0), 'tst in line 1 eins ,'    ,
                     , 'tst in line 2 zwei ;   ' ,
                     , 'tst in line 3 drei .schluss.'
    call tstOut m, left('### start tst' nm '', 67, '#')
    call errReset 'h', 'return tstErrHandler(ggTxt)'
    m.m.errCleanup = m.err.cleanup
    if m.tst.ini.j \== 1 then do
        m.tst_m = m
/*      call err implement outDest 'i', 'call tstOut' quote(m)', msg'
*/      end
    else do
        drop m.tst_m
        call oMutatName m, 'Tst'
        call oMutatName m'.IN', 'Tst'
        m.m.jReading = 1
        m.m.jWriting = 1
        m.m.jUsers = 0
        m.m.in.jReading = 1
        m.m.in.jWriting = 1
        m.m.in.jUsers = 0
        if m.tst.ini.e \== 1 then do
            m.m.oldJin = m.j.in
            m.m.oldOut = m.j.out
            m.j.in = m'.IN'
            m.j.out = m
            end
        else do
            if m.pipe.0 <> 2 then
                call tstErr m, 'm.pipe.0' m.pipe.0 '<> 2'
            call pipe '+Ff', m , m'.IN'
            end
        end
    return m
endProcedure tst

tstEnd: procedure expose m.
parse arg m, opt opt2
    cmp = m.m.cmp
    m.m.jReading = 0
    m.m.jWriting = 0
    drop m.tst_m
    if m.tst.ini.j == 1 then do
        m.m.jReading = 0
        m.m.jWriting = 0
           if m.tst.ini.e \== 1 then do
            m.j.in = m.m.oldJin
            m.j.out = m.m.oldOut
            end
        else do
            if m.j.in \== m'.IN' | m.j.out \== m then
                call tstErr m, m.j.in '\==' m '|' m.j.out '\==' m
            call pipe '-'
            if m.pipe.0 <> 2 then
                call tstErr m, 'm.pipe.0' m.pipe.0 '<> 2'
            end
        end
    if m.m.err = 0 then
        if m.m.errCleanup \= m.err.cleanup then
            call tstErr m, 'err.Cleanup' m.err.cleanup '<> old',
                        m.m.errCleanup
    if m.m.out.0 \= m.cmp.0 & \ (m.m.moreOutOk,
            &  m.m.out.0 > m.cmp.0) then do
        call tstErr m, 'old' m.cmp.0 'lines \= new' m.m.out.0
        do nx = m.m.out.0 + 1 to min(m.m.out.0+10, m.cmp.0)
            say 'old -  ' m.cmp.nx
            end
        end
    call errReset 'h'
    m.tst.act = ''
    soll = 0
    if opt = 'err' then do
        soll = opt2
        if m.m.err \= soll then
            call err soll 'errors expected, but got' m.m.err
        end
    if m.m.err \= soll then do
        say 'new lines:' (m.m.out.0 - 1)
           call tst2dpSay m.m.name, m'.OUT', 68
        end
    say left('###' m.m.name 'end with' m.m.err 'errors ', 67, '#')

    if 1 & m.m.err \= soll then
        call err 'dying because of' m.m.err 'errors'
    m.m.trans.0 = m.m.trans.old
    return
endProcedure tstEnd

tst2dp: procedure expose m.
parse arg st, dp, ml
    dx = m.dp.0
    do sx=1 to m.st.0
        li = m.st.sx
        cx = 1
        do until cx > length(li)
            c = substr(li, cx, 1)
            ou = left('.', strip(c) == '' | pos(c, '.+') > 0)
            ex = min(length(li),cx+ml-length(ou)-2)
            ou = ou || substr(li, cx, ex+1-cx)
            dx = dx + 1
            c = right(ou, 1)
            if ex < length(li) then
                m.dp.dx = ou || '+'
            else if strip(c) == '' | pos(c, '.+') > 0 then
                m.dp.dx = ou || '.'
            else
                m.dp.dx = ou
            cx = ex +1
            end
        end
    m.dp.0 = dx
    return
endProcedure tst2dp

tst2dpSay: procedure expose m.
parse arg name, st, ml
    say '$=/'name'/'
    call tst2dp st, mCut('TST.TMP', 0), 68
    do nx=1 to m.tst.tmp.0
           say '   ' m.tst.tmp.nx
           end
     say '$/'name'/'
return tst2dpSay

tst4dp: procedure expose m.
parse arg st, dp
    sx = m.st.0
    inData = 0
    data = ''
    do dx=1 to m.dp.0
        li = strip(m.dp.dx)
        if pos(left(li, 1), '.+') > 0 then
         li = substr(li, 2)
        if right(li, 1) == '+' then do
            inData = 1
            data = data || left(li, length(li)-1)
            iterate
            end
        if right(li, 1) == '.' then
            li = left(li, length(li)-1)
        sx = sx + 1
        m.st.sx = repAll(data || li, '$ä', '/*', '$ö', '*/')
        inData = 0
        data = ''
        end
    m.st.0 = sx
    if inData then
        call err 'end inData'
    return
endProcedure tst4dp

/*--- write to test: say lines and compare them ----------------------*/
tstWrite: procedure expose m.
parse arg m, arg
    call tstOut m, 'out:' arg
    return
endProcedure tstWrite

tstOut: procedure expose m.
parse arg m, arg
    do tx=m.m.trans.0 by -1 to 1
        arg = repAll(arg, word(m.m.trans.tx, 1),
            , subword(m.m.trans.tx, 2))
        end
    call mAdd m'.OUT', arg
    nx = m.m.out.0
    cmp = m.m.cmp
    c = m.cmp.nx
    if nx > m.cmp.0 then do
        if nx = m.cmp.0+1 & \ m.m.moreOutOK then
            call tstErr m, 'more new Lines' nx
        end
    else if c \== arg then do
        do cx=1 to min(length(c), length(arg)) ,
             while substr(c, cx, 1) == substr(arg, cx, 1)
             end
         msg = 'old line' nx '<> new overnext, firstDiff' cx',',
                 'len old' length(c)', new' length(arg)

        if cx > 10 then
            msg = overlay('|', msg, cx-10)
        call tstErr m, msg
        say c
        end
    say arg
    return 0
endProcedure tstOut

tstWriteO: procedure expose m.
parse arg m, var
    cl = objClass(var, '')
    if cl == '' then do
        if var == '' then
            call tstOut t, 'tstR: @ obj null'
        else
            call tstOut t, 'no class for' var 'in tstWriteO|'
        end
    else if abbrev(var, m.o_escW) then do
        call tstOut t, o2String(var)
        end
    else if cl == m.class_V then do
        call tstOut t, m.var
        end
    else if oKindOf(var, 'JRW') then do
        call tstOut t, 'tstWriteO kindOf JRW jWriteNow begin <<<'
        call jWriteNow m, var
        call tstOut t, 'tstWriteO kindOf JRW jWriteNow end   >>>'
        end
    else if oKindOf(var, 'ORun') then do
        call tstOut t, 'tstWriteO kindOf ORun oRun begin <<<'
        call oRun var
        call tstOut t, 'tstWriteO kindOf ORun oRun end   >>>'
        end
    else do
        do tx=m.m.trans.0 by -1 to 1 ,
                while word(m.m.trans.tx, 1) \== var
            end
        if tx < 1 then
            call mAdd M'.TRANS', var 'tstWriteoV' || (m.m.trans.0+1)
        call classOut , var, 'tstR: '
        end
    return
endProcedure tstWriteO

tstReadO: procedure expose m.
parse arg m, arg
    if right(m, 3) == '.IN' then
       m = left(m, length(m)-3)
    else
        call err 'tstReadO bad m' m
    ix = m.m.inIx + 1
    m.m.inIx = ix
    if ix <= m.m.in.0 then do
        call tstOut m, '#jIn' ix'#' m.m.in.ix
        return s2o(m.m.in.ix)
        end
    call tstOut m, '#jIn eof' ix'#'
    return ''
endProcedure tstReadO

tstFilename: procedure expose m.
parse arg suf, opt
    if m.err.os == 'TSO' then do
        dsn = dsn2jcl('~tmp.tst.'suf)
        if opt = 'r' then do
            if sysDsn("'"dsn"'") \== 'DATASET NOT FOUND' then
                call adrTso "delete '"dsn"'"
            call csiOpen 'TST.CSI', dsn'.**'
            do while csiNext('TST.CSI', 'TST.FINA')
                say 'deleting csiNext' m.tst.fina
                call adrTso "delete '"m.tst.fina"'"
                end
            end
        return dsn
        end
    else if m.err.os == 'LINUX' then do
        if abbrev(suf, '/') then
            fn = suf
        else
            fn = .Stream%%new('~/tmp/tst/'suf)%%qualify /* full path */
        cx = lastPos('/', fn)
        if cx > 0 then do
            dir = left(fn, cx-1)
            if \sysIsFileDirectory(dir) then
                call adrSh "mkdir -p" dir
            if \sysIsFileDirectory(dir) then
                call err 'tstFileName could not create dir' dir
            end
        if opt \= 'r' then
            nop
        else if sysIsFile(fn) then
            call sysFileDelete fn
        else if sysIsFileDirectory(fn) then
            call adrSh 'rm -r' fn
        return fn
        end
    else
        call err 'tstFilename does not implement os' m.err.os
endProcedure tstFilename

/*--- say total errors and fail if not zero --------------------------*/
tstTotal: procedure expose m.
    say '######'
    say '######'
    say '######' m.tst.tests 'tests with' ,
                 m.tst.err 'errors in' m.tst.errNames
    say '######'
    say '######'
    if m.tst.err \== 0 then
        call err m.tst.err 'errors total'
    return
endProcedure tstTotal

/*--- test err: message, count it and continue -----------------------*/
tstErr: procedure expose m.
parse arg m, msg
    say '### error' msg
    m.m.err = m.m.err + 1
    m.tst.err = m.tst.err + 1
    nm = m.m.name
    if wordPos(nm, m.tst.errNames) < 1 then
        m.tst.errNames = m.tst.errNames nm
    return 0
endProcedure tstErr

/*--- tstErrHandler: intercept errors --------------------------------*/
tstErrHandler: procedure expose m.
parse arg ggTxt
    m = m.tst.act
    if m == '' then
        call err ggTxt
    m.err.count = m.err.count + 1
    call errMsg ' }'ggTxt
    call tstOut m.tst.act, '*** err:' m.err.1
    do x=2 to m.err.0
        call tstOut m, '    e' (x-1)':' m.err.x
        end
    return 0
endSubroutine tstErrHandler

tstTrc: procedure expose m.
parse arg msg
    m.tst.trc = m.tst.trc + 1
    say 'tstTrc' m.tst.trc msg
    return m.tst.trc
endProcedure tstTrc

/*--- tstIni: global initialization ----------------------------------*/
tstIni: procedure expose m.
    if m.tst.ini \== 1 then do
        m.tst.ini = 1
        call mapIni
        m.tst.err = 0
        m.tst.trc = 0
        m.tst.errNames = ''
        m.tst.tests = 0
        m.tst.act = ''
        end
    if m.tst.ini.j \== 1 & m.j.ini == 1 then do
        m.tst.ini.j = 1
          call classNew 'n Tst u JRWO', 'm',
             , "jReadO return tstReadO(m)",
             , "jWrite call tstOut m, line",
             , "jWriteO call tstWriteO m, var"
        end
    if m.tst.ini.e \== 1 & m.pipe.ini == 1 then do
        m.tst.ini.e = 1
        end
    return
endProcedure tstIni
/* copy tstAll end   **************************************************/
}¢--- A540769.WK.REXX(WSH30RZ2) cre=2015-03-09 mod=2015-03-09-12.52.42 A540769 ---
/* rexx ****************************************************************
  wsh: walter's rexx shell                                   version 2.2
  interfaces:                                                    9. 2.14
      edit macro: for adhoc evaluation or programming
              either block selection: q or qq and b or a
              oder mit Directives ($#...) im Text
      wsh i:  tso interpreter
      batch:  input in dd wsh
      docu:   http://chw20025641/host/db2wiki/pmwiki.php?n=Main.Wsh
      syntax: http://chw20025641/host/db2wiki/pmwiki.php?n=Main.WshSyn
--- history ------------------------------------------------------------
19.03.14 ii = installation Info
*********/ /*** end of help ********************************************
 9.01.14 walter: redesign formatting (fmt eliminiert), csm.div.p0.exec
 3.12.13 walter: db2 interface radikal geputzt
 3.10.13 walter: uCount fuer TSO <-> unitCount fuer Csm
23. 9.13 walter: ws2 syntax
 6. 2.13 w.keller csmCopy aendert mgmtClas für neue lokale Files
23. 1.13 w.keller sqlErrHandler sowie sqlRx und sql
11. 6.12 w.keller sqlUpdComLoop
23. 5.12 w.keller fix sqlStmt: drop accepts -204
31. 3.12 w.keller sql Query interface incl. sql über CSM
10. 2.12 w.keller div catTb* und eLong
 2. 6.11 w.keller sql error with current location and dsnTiar
 2. 5.11 w.keller sqlStmt etc..
16. 3.11 w.keller basic new r '' ==> r m.class.classO
28. 2.11 w.keller compFile by exprBlock, fix $@do forever, |
 7. 2.11 w.keller cleanup block / with sqlPush....
 2. 2.11 w.keller simplified syntax, ast for blocks, ? for help
19. 6.10 w.keller open/close stacked mit jUsers, framed eliminiert
19. 5.10 w.keller class wird direkt erzeugt ohne temporary
18. 4.10 w.keller scanUtilInto ==> testCase noch einbauen
17. 4.10 w.keller oMutate in fileTsoList eingebaut
14. 1.10 w.keller batch interface no longer dies on openIfNotYet
                  CSM.RZ1.P0.EXEC korrigiert
***********************************************************************/

/*--- main code wsh --------------------------------------------------*/
    call errReset 'hI'
    call pipeIni  /* without tstClass2 gives different result */
    m.mylib = DSN.DB2.EXEC
    call wshLog
    parse arg spec
    isEdit = 0
    if spec = '' & m.err.ispf then do /* z/OS edit macro */
        isEdit = adrEdit('macro (spec) NOPROCESS', '*') == 0
        if isEdit then do
            call adrEdit '(d) = dataset'
            call adrEdit '(m) = member'
            m.editDsn = dsnSetMbr(d, m)
            if spec = '' & m.editDsn = 'A540769.WK.REXX(WS3)' then
                spec = 't'
            end
        end
    if spec = '?' then
        return help()
    call utIni
    f1 = spec
    rest = ''
    call wshIni
    inp = ''
    out = ''
    if m.err.os == 'TSO' then do
        if isEdit then do
            parse value wshEditBegin(spec) with inp out
            end
        else if sysvar('sysEnv') = 'FORE' then do
            end
        else do
            inp = file('dd(wsh)')
            useOut = listDsi('OUT FILE')
            if \ (useOut = 16 & sysReason = 2) then
                out = file('dd(out)')
            end
        end
    else if m.err.os == 'LINUX' then do
        inp = file('&in')
        out = file('&out')
        end
    else
        call err 'implement wsh for os' m.err.os
    m.wshInfo = 'compile'
    call compRun spec, inp, out, wshInfo
    if isEdit then
        call wshEditEnd
exit 0

wshIni: procedure expose m.
    call compIni
    call sqlIni
    call scanWinIni
    return
endProcedure wshIni

tstRts: procedure expose m.
    call wshIni
    call sqlConnect dbaf
    call sqlQuery 3, "select * from sysibm.sysTableSpaceSTats" ,
                    "where dbName = 'MF01A1A' and name = 'A150A'",
                    "order by partition  asc"
    do while sqlFetch(3, rr)
        say f('@.DBNAME%-8C.@NAME%-8C @PARTITION %4C' ,rr)
        end
    call sqlDisconnect
endProcedure tstRts

wshLog: procedure expose m.
parse arg msg, st
    lNm = 'dsn.wsh.log'
    lN2 = lNm'.R' || ( random() // 19)
    f1 = dsnAlloc('dd(log) mod' lNm '::f', , , '*')
    if datatype(f1, 'n') then do
        lNm = lNm'.R' || ( random() // 19)
        f1 = dsnAlloc('dd(log) old' lNm '::f', , , '*')
        if datatype(f1, 'n') then do
            say 'could not allocate log' lNm lN2
            return
            end
        end
    parse source . . s3 .
    o.1 = 'wshLog' m.myLib'('s3')' sysvar(sysnode) ,
            'job='mvsvar('symdef', 'jobname') ,
             'user='userid() date('o') time()
    if msg <> '' then
        o.2 = left(msg, 80)
    ox = 1 + (msg <> '')
    if st <> '' then do sx=1 to m.st.0
        ox = ox+1
        o.ox = left(m.st.sx, 80)
        end
    call writedd log, o., ox
    call tsoClose log
    call tsoFree log
    return
endProcedure wshLog

/*--- test hook ----------------------------------------------------*/
wshHook_T: procedure expose m.
parse arg cmp
    rest = strip(scanLook(m.cmp.scan))
    call jClose m.cmp.scan
    if rest = '' then do /* default */
        call tstfTst
        return 0
        call sqlConnect DBAF
        call catColCom 'cmnBatch', 'DSN_PGROUP_TABLE',
                     , 'cmnBatch', 'DSN_PGROUP_TABLE_new'
        call sqlDisConnect DBAF
        return 0
        end
    c = ''
    do wx=1 to words(rest)
        c = c 'call tst'word(rest, wx)';'
        end
    if wx > 2 then
        c = c 'call tstTotal;'
    say 'wsh interpreting' c
    interpret c
    exit 0
endProcedure wshTst

/*--- i hook: interpret user input: rexx, expr, data or shell -------*/
wshHook_I: procedure expose m.
parse arg cmp
    inp = strip(scanLook(m.cmp.scan))
    call jClose m.cmp.scan
    mode = '*'
    do forever
        if pos(left(inp, 1), '/;:*@.-=') > 0 then
            parse var inp mode 2 inp
        if mode == '/' then
            exit 0
        mode = translate(mode, ';', ':')
        if inp <> '' then do
            say 'as' mode 'interpreting' inp
            if mode = ';' then
                interpret inp
            else if mode = '*' then
                interpret 'say' inp
            else do
                call errReset 'h', 'say "******* intercepting error";',
                    'call errSay ggTxt; return "";'
                call oRun compile(comp(jBuf(inp)), mode)
                call errReset 'h'
                end
            end
        say 'enter' mode 'expression, / for end, ; or * for Rexx' ,
                                                 '@ . - =  for wsh'
        parse pull inp
        end
endProcedure wshInter

/*--- sql hook -----------------------------------------------------*/
wshHook_S: procedure expose m.
parse arg cmp
    s = m.cmp.scan
    ki = '='
    call scanVerify s, m.m.chSpa
    if scanVerify(s, m.comp.chKind) then
        ki = left(m.s.tok, 1)
    call scanChar s
    rest = m.s.tok
    call scanNl s
    dbSy = word(rest, 1)
    if abbrev(dbSy, '-') | \ (length(dbSy) = 4 ,
                   | (length(dbsy) = 8 & substr(dbSy,4,1) == '/')) then
        dbSy = ''
    else
        rest = subWord(rest, 2)
    call sqlConnect dbSy
    r = oRunner(compAst2Rx(cmp, ';', compUnit(cmp, ki, '$#')))
    call sqlStmtsOpt r, rest
    call sqlDisConnect
    return ''
endProcedure wshHook_s


wshEditBegin: procedure expose m.
parse arg spec
    dst = ''
    li = ''
    m.wsh.editHdr = 0
    pc = adrEdit("process dest range Q", 0 4 8 12 16)
    if pc = 16 then
        call err 'bad range must be q'
    if pc = 0 | pc = 8 then do
        call adrEdit "(rFi) = lineNum .zFrange"
        call adrEdit "(rLa) = lineNum .zLrange"
    /*  say 'range' rFi '-' rLa */
        end
    else do
        rFi = ''
    /*  say 'no range' */
        end
    if pc = 0 | pc = 4 then do
        call adrEdit "(dst) = lineNum .zDest"
    /*  say 'dest' dst */
        dst = dst + 1
        end
    else do
    /*  say 'no dest' */
        if adrEdit("find first '$#out' 1", 4) = 0 then do
            call adrEdit "(dst) = cursor"
    /*      say '$#out' dst   */
            call adrEdit "(li) = line" dst
            m.wsh.editHdr = 1
            end
        end
    m.wsh.editDst = dst
    m.wsh.editOut = ''
    if dst \== '' then do
        m.wsh.editOut = jOpen(jBufTxt(), '>')
        if m.wsh.editHdr then
            call jWrite m.wsh.editOut, left(li, 50) date('s') time()
        end
    if rFi == '' then do
        call adrEdit "(zLa) = lineNum .zl"
        if adrEdit("find first '$#' 1", 4) = 0 then do
            call adrEdit "(rFi) = cursor"
            call adrEdit "(li) = line" rFi
            if abbrev(li, '$#out') | abbrev(li, '$#end') then
                rFi = 1
            if rFi < dst & dst \== '' then
                rLa = dst-1
            else
                rLa = zLa
            end
        else do
            rFi = 1
            rLa = zLa
            end
        end
    /*  say 'range' c1 'rc' pc':' rFi '-' rLa   'after' dst */

    m.wsh.editIn = jOpen(jBuf(), m.j.cWri)
    do lx=rFi to rLa
        call adrEdit "(li) = line" lx
        call jWrite m.wsh.editIn, li
        end
    call errReset 'h',
             , 'return wshEditErrH(ggTxt, ' rFi',' rLa')'
    return jClose(m.wsh.editIn) m.wsh.editOut
endProcedure wshEditBegin

wshEditEnd: procedure expose m.
    call errReset 'h'
    if m.wsh.editOut == '' then
        return 0
    call jClose(m.wsh.editOut)
    lab = wshEditInsLinSt(m.wsh.editDst, 0, , m.wsh.editOut'.BUF')
    call wshEditLocate max(1, m.wsh.editDst-7)
    return 1
endProcedure wshEditEnd

wshEditLocate: procedure
parse arg ln
    call adrEdit '(la) = linenum .zl'
    call adrEdit 'locate '  max(1, min(ln, la - 37))
    return
endProcedure wshEditLocate

wshEditErrH: procedure expose m.
parse arg ggTxt, rFi, rLa
    call errCleanup
    call errReset 'h'
    call errMsg ' }'ggTxt
    call mMove err, 1, 2
    isScan = 0
    if wordPos("pos", m.err.4) > 0 ,
        & pos(" in line ", m.err.4) > 0 then do
        parse var m.err.4 "pos " pos .     " in line " lin":"
        if pos = '' then do
            parse var m.err.4 " line " lin":"
            pos = 0
            end
        isScan = lin \== ''
        end
    m.err.1 = '***' m.wshInfo 'error ***'
    if m.wshInfo=='compile' & isScan then do
        do sx=1 to m.err.0
            call out m.err.sx
            end
        lab = rFi + lin
        if pos \= '' then
            lab = wshEditInsLin(lab, 'msgline', right('*',pos))
        lab = wshEditInsLinSt((rFi+lin),0, 'msgline', err)
        call wshEditLocate rFi+lin-25
        end
    else do
        if m.wsh.editOut \== '' then do
            do sx=1 to m.err.0
                call jWrite m.wsh.editOut, m.err.sx
                end
            lab = wshEditInsLinSt(m.wsh.editDst, 0, ,
                , m.wsh.editOut'.BUF')
            call wshEditInsLinSt m.wsh.editDst, m.wsh.editHdr,
                , msgline, err
            call wshEditLocate max(1, m.wsh.editDst-7)
            end
        else do
            do sx=1 to m.err.0
                say m.err.sx
                end
            end
        end
    call errCleanup
    exit
endSubroutine wshEditErrH

wshEditInsLinCmd: procedure
parse arg wh
    if dataType(wh, 'n')  then do
        if adrEdit("label" wh "= .a", 0 8 12) \= 12 then
            return 'line_before .a ='
        else
            return 'line_after .zl ='
        end
    else if left(wh, 1) == '.' then
        return 'line_before' wh '='
    else
        return wh
endProcedure wshEditInsLinCmd

wshEditInsLin: procedure
parse arg wh, type
    cmd = wshEditInsLinCmd(wh)
    do ax=3 to arg()
        li = strip(arg(ax), 't')
        if li == '' then
            iterate
        if translate(type) = 'MSGLINE' then do while length(li) > 72
            sx = lastPos(' ', li, 72)
            if sx < 10 then
                sx = 72
            one = left(li, sx)
            li = '    'strip(substr(li, sx))
            call adrEdit cmd type "(one)"
            end
        call adrEdit cmd type "(li)", 0 4
        end
    return cmd
endProcedure wshEditInsLin

wshEditInsLinSt: procedure expose m.
parse arg wh, pl, type, st
    if wh == '' then do
        do ox=1 to m.st.0
            say m.st.ox
            end
        return ''
        end
    wh = wh + pl
    cmd = wshEditInsLinCmd(wh)
    do ax=1 to m.st.0
        call wshEditInsLin cmd, type, m.st.ax
        end
    return cmd
endProcedure wshEditInsLinSt


/*** end wsh, begin all copies ****************************************/
/* copy tstAll begin  *************************************************/
/*----------- neu, noch versorgen |||||-------------------------------*/
tstWiki:
    call mapReset docs, 'k'
    call addFiles docs, 'n', '/media/wkData/literature/notes'
    call addFiles docs, 'd', '/media/wkData/literature/docs'

    in = jOpen(file('wiki.old'), '<')
    out = jOpen(file('wiki.new'), '>')
    abc = '(:abc: %l%'
    do cx=1 to length(m.ut_alfLC)
        c1 = substr(m.ut_alfLC, cx, 1)
        abc = abc '¢¢#'c1 '|' c1'!!'
        end
    call jWrite out, abc ':)'
    inTxt = 0
    li = m.i
    do lx=1 while jRead(in, i)
        if 0 then
            say length(m.i) m.i
        if m.i = '' then
            iterate
        li = m.i
        do forever
            bx = pos('¢=', li)
            if bx < 1 then
                leave
            ex = pos('=!', li)
            if ex <= bx then
                call err '=! before ¢= in' lx li
            li = left(li, bx-1)substr(li,bx+2, ex-bx-2)substr(li,ex+2)
            end
        li = strip(li)
        if abbrev(li, '|') then do
            w = word(substr(li, 2), 1)
            call jWrite out, '¢¢#'w'!! {$:abc}'
            call jWrite out, '|||' substr(li, 2)
            inTxt=1
            iterate
            end
        if \ inTxt then do
            call jWrite out, li
            iterate
            end
        if \ (abbrev(li, '->') | abbrev(li, '#') ,
                | abbrev(li, '¢')) then do
            call jWrite out, '-<' li
            iterate
            end
        cx = 1
        if substr(li, cx, 2) == '->' then
            cx = verify(li, ' ', 'n', cx+2)
        hasCross = substr(li, cx, 1) == '#'
        if hasCross then
            cx = verify(li, ' ', 'n', cx+1)
        ex = verify(li, '!:\, ', 'm', cx)
        ex = ex - (substr(li, ex, 1) \== '!')
        hasBr = substr(li, cx, 1) == '¢'
        if \ hasBr then
            w = substr(li, cx, ex+1-cx)
        else if substr(li, ex, 1) == '!' then
            w = substr(li, cx+1, ex-1-cx)
        else
            call err 'br not closed' substr(w, cx+1, ex-1-cx) 'in' lx li
        hasPdf = right(w, 4) == '.pdf'
        if hasPdf then
            w = left(w, length(w)-4)
        if verify(w, '#?', 'm') > 0 then do
            w = translate(w, '__', '#?')
            say '*** changing to' w 'in' lx li
            end
        o = '-< {def+'w'}'
        o = '-< ¢¢'w'!!'
        k = translate(w)
        if k.k == 1 then
            say '*** doppelter key' k 'in:' lx left(li,80)
        k.k = 1
        dT = ''
        if mapHasKey(docs, k) then do
            parse value mapGet(docs, k) with dT dC dN
            call mapPut docs, k, dT (dC+1) dN
            do tx=1 to length(dT)
                t1 = substr(dT, tx, 1)
                o = o '¢¢Lit'translate(t1)':'word(dN, tx) '|' t1 '!!'
                end
            end
        qSeq = 'nd'
        qq = left(qSeq, 1)
        qx = 0
        do forever
            qx = pos('@'qq, li, qx+1)
            if qx < 1 then do
                qq = substr(qSeq, 1 + pos(qq, qSeq), 1)
                qx=0
                if qq = '' then
                    leave
                else
                    iterate
                end
            if pos(qq, dT) < 1 then do
                say '*** @'qq 'document not found:' lx li
                iterate
                end
            do qb = qx-1 by -1 while substr(li, qb, 1) == ' '
                end
            do qe = qx+2 by 1 while substr(li, qe, 1) == ' '
                end
            if substr(li, qb, 1) == '.' & substr(li, qe, 1) == '.' then
                li = left(li, qb)substr(li, qe+1)
            else
                li = left(li, qb) substr(li, qe)
            end
        o = o':' strip(substr(li, ex+1+(substr(li,ex+1,1)==':')))
        if 0 then say left(li, 30) '==>' left(o, 30)
        call jWrite out, o
        end
    dk = mapKeys(docs)
    do dx=1 to m.dk.0
        parse value mapGet(docs, m.dk.dx) with dT dC dN
        if dC < 1 then
            say '*** document not used:' dT dC dn
        end
    call jClose in
    call jClose out
    return
endProcedure tstWiki

addFiles: procedure expose m.
parse arg m, ty, file
    fl = jOpen(fileList(file(file)), '<')
    do while jRead(fl, fi1)
        nm = substr(m.fi1, lastPos('/', m.fi1)+1)
        k = translate(left(nm, pos('.', nm)-1))
        if \ mapHasKey(m, k) then do
            call mapAdd m, k, ty 0 nm
            end
        else do
            parse value mapGet(m, k) with dT dC dN
            call mapPut m, k, dT || ty 0 dN nm
            end
        end
    call jClose fl
    return
endProcedure addFiles

tstAll: procedure expose m.
    say 'tstAll ws2 25.2.13...............'
    call tstBase
    call tstComp
    call tstDiv
    if m.err.os = 'TSO' then do
        call tstZos
        call tstTut0
        end
    return 0
endProcedure tstAll

/* copx tstZos begin **************************************************/
tstZOs:
    call tstTime
    call tstII
    call sqlIni
    call tstSqlRx
    call tstSql
    call tstSqlCsm
    call tstSqlC
    call tstSqlCsv
    call tstSqlRxUpd
    call tstSqlUpd
    call tstSqlUpdPre
    call tstSqlE
    call tstSqlB
    call tstSqlO1
    call tstSqlO2
    call tstSqlStmt
    call tstSqlStmts
    call tstSqlUpdComLoop
    call tstSqls1
    call tstSqlO
    call tstSqlFTab
    call tstTotal
    return
endProcedure tstZOs

tstWshBatch:
    call adrTso 'alloc dd(WSH) shr dsn(WK.TEXT(WSHBATCH))'
    call wshBatch
    return adrTso('free dd(WSH)')

tstLmdTiming:
parse arg lev
    say timing() lev
    call lmdBegin abc, lev
    c = 0
    do while lmdNext(abc, st.)
        c = c + st.0
        end
    call lmdEnd   abc
    say timing() lev 'with group - without reading' c
    call adrIsp 'lmdinit listid(lmdId) level('lev')'
    do c=0 while adrIsp('lmdlist listid(&lmdId) dataset(abc)', 8) = 0
        end
    call adrIsp 'lmdfree listid(&lmdId)'
    say timing() lev 'with list' c
    return
endProcedure lmdTiming

tstCsi: procedure expose m.
    if 0 then do
        call lmd 'A540769.*K'
        call tstCsiCla 'A540769.WK.REXX'
        call tstCsiCla 'A540769.AAA.DATASETS'
        call tstCsiCla 'A540769.RRR.DATASETS'
        end
    if 0 then do
        call tstCsiOpNx 'A540769.WK.*E*'
        call tstCsiOpNx 'A540769.AAA.DATASETS'
        call tstCsiOpNx 'A540769.RRR.DATASETS'
        end
    if 1 then do
        call tstCsiNxCl 'A540769.WK.**'
        call tstCsiNxCl 'DBTF.M*.**'
        call tstCsiNxCl 'DBTF.BE*.**'
        end
    return
tstCsi: procedure expose m.
 /* call lmd 'A540769.*K' */
    call tstCsi1   'A540769.WK.REXX'
    call tstCsi1   'A540769.AAA.DATASETS'
    call tstCsi1   'A540769.RRR.DATASETS'
    return

tstCsiCla:
parse arg ds
    say ds '-->' csiCla(ds)
    return

tstCsiOpNx: procedure expose m.
parse arg ds
    m = 'NUE123'
    s = 'res89'
    flds = 'devtyp volser mgmtclas comudsiz NOBYTTRK UDATASIZ HARBA'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    do while csiNext(m, s)
        say m.s     'dev' c2x(m.s.devTyp) ,
                    'vol' m.s.volSer 'cla' m.s.mgmtclas,
                    'comuDsiz' m.s.comuDsiz 'noBytTrk' m.s.noBytTrk,
                    'udatasiz' c2x(m.s.udatasiz) ,
                    'harba' c2x(m.s.harba)
        end
    return

tstCsiNxCl: procedure expose m.
parse arg ds
    m = 'ABC123'
    s = 'efg89'
    flds = 'devtyp volser mgmtclas'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    say timing() 'begin'
    do i=1 while csiNext(m, s)
        nn = csiArcTape(m.s.volser, m.s.mgmtClas, m.s.devtyp, m.s)
/*      oo = csiCla(strip(m.s))
        if oo <> nn then
            say nn '<>' oo m.s
 */     if i // 1000 = 0 then
            say timing() i nn m.s
        end
    say timing() (i-1) nn m.s
    return
endProcedure tstCsiNxCl

listCatClass: procedure expose m.
parse upper arg dsn
    rt = adrTso("listcat volume entry('"dsn"')", 4)
    /* say 'listct rc =' rt 'lines' m.tso_trap.0 */
    cl = ''
    vo = ''
    if word(m.tso_trap.1, 3) \== dsn then
        call out 'err ??? for dsn' dsn 'bad first line' m.tso_trap.1
    else if pos('NOT FOUND', m.tso_trap.1) > 0 then
        return 'notFound'
    else if word(m.tso_trap.1, 1)\== 'NONVSAM' then
        call out 'err ??? for dsn' dsn 'bad first line' m.tso_trap.1
    do tx=2 to m.tso_trap.0 while vo = '' ,
              & left(m.tso_trap.tx, 1) = ' '
     /* say m.tso_trap.tx */
        p = pos('MANAGEMENTCLASS-', m.tso_trap.tx)
        if p > 0 then
            vo = strip(word(substr(m.tso_trap.tx, p+16), 1), 'l', '-')
        p = pos('VOLSER--', m.tso_trap.tx)
        if p > 0 then
            vo = strip(word(substr(m.tso_trap.tx, p+6), 1), 'l', '-')
        p = pos('DEVCLASS--', m.tso_trap.tx)
            dt = strip(word(substr(m.tso_trap.tx, p+8), 1), 'l', '-')
        end
   /*  say 'lc' cl 'vo' vo 'dt' dt 'dsn' dsn */
    if vo = '' then
        call out '??? err no volume for dsn' dsn
    else if vo = 'ARCIVE' then
        res =  'arcive'
    else if cl = '' then
        res = 'tape'
    else
        res = cl
    if   abbrev(res, 'ar') \= abbrev(dt, "X'0") ,
       | abbrev(res, 'ta') \= abbrev(dt, "X'7") ,
       | (left(res, 1) >= 'A') \= abbrev(dt, "X'3") then
       call out 'err ??? mismatch cl' cl 'vo' vo 'dt' dt 'dsn' dsn
    return res
endProcedure listCatClass

/* copx tstZos end   **************************************************/
/* copx tstDiv begin **************************************************/
tstDiv:
    call tstSort
    call tstMatch
    call tstTotal
    return
endProcedure tstDiv


tstSort: procedure expose m.
    call tstSortComp
    call tstSortComp '<<='
    call tstSortComp 'm.aLe <<= m.aRi'
    call tstSortComp 'if 1 then cmp = m.aLe <<= m.aRi; else call err sd'
    return
endProcedure tstSort

tstSortComp: procedure expose m.
parse arg cmp
/*
$=/tstSort/
    ### start tst tstSort #############################################
    sort 29  c ACHT DREI DREIZEHN EINS ELF FUENF M.I.25 M.I.26 M.I.27 M+
    ..I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI ZWOELF 0 1 1 1 2 2+
    . 3 3 4 4
    sort 22  c ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27 M.I.29 NEUN VIERZ+
    EHN ZEHN ZWOELF 0 1 1 1 2 2 3 3 4 4
    sort 15  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1 1 2 2 3 3 4 4
    sort 8  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1
    sort 1  M.I.29
$/tstSort/ */
/*
$=/tstSortAscii/
    ### start tst tstSortAscii ########################################
    sort 29  0 1 1 1 2 2 3 3 4 4 ACHT DREI DREIZEHN EINS ELF FUENF M.I.+
    25 M.I.26 M.I.27 M.I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI Z+
    WOELF c
    sort 22  0 1 1 1 2 2 3 3 4 4 ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27+
    . M.I.29 NEUN VIERZEHN ZEHN ZWOELF c
    sort 15  0 1 1 1 2 2 3 3 4 4 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 8  0 1 1 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 1  M.I.29
$/tstSortAscii/ */
    say '### start with comparator' cmp '###'
    if m.err.os == 'LINUX' then
        call tst t, "tstSortAscii"
    else
        call tst t, "tstSort"
    call mAdd mCut(i, 0), eins, zwei, drei, vier, fuenf, sechs,
        ,sieben, acht, neun, zehn, elf, zwoelf, dreizehn, vierzehn
    call mAdd i, 1, 2, 3, 4, 4, 3, 2, 1, 0, 1,
        , 'M.I.25', 'M.I.26', 'M.I.27', 'c', 'M.I.29'

    do yy = m.i.0 by -1 to 1
        do x = 0 to yy
            m.i.0 = x
            call sort i, o, cmp
            m = ''
            la = ''
            if x <> m.o.0 then
                call err 'size mismatch' x '<>' m.o.0
            do y=1 to m.o.0
                m = m m.o.y
                if \ (la << m.o.y) then
                    call err 'sort mismatch' yy x y '\' la '<<' m.o.y
                end
            end
        if yy // 7 = 1 then
           call tstOut t, 'sort' yy m
        do x = 2 to yy
            x1 = x-1
            m.i.x1 = m.i.x
            end
        end
    call tstEnd t
    return
endProcedure tstSort
tstMatch: procedure expose m.
/*
$=/tstMatch/
    ### start tst tstMatch ############################################
    match(eins, e?n*) 1 1 2,i,s trans(E?N*) EiNs
    match(eins, eins) 1 1 0 trans(EINS) EINS
    match(e1nss, e?n*) 1 1 2,1,ss trans(?*) 1ss
    match(eiinss, e?n*) 0 0 -9
    match(einss, e?n *) 0 0 -9
    match(ein s, e?n *) 1 1 2,i,s trans(E?N *) EiN s
    match(ein abss  , ?i*b*) 1 1 3,e,n a,ss   trans(?I*B*) eIn aBss  .
    match(ein abss wie gehtsssxdirx und auch , ) 0 0 -9
    match(ies000, *000) 1 1 1,ies trans(*000) ies000
    match(xx0x0000, *000) 1 1 1,xx0x0 trans(*000) xx0x0000
    match(000x00000xx, 000*) 1 1 1,x00000xx trans(000*) 000x00000xx
    match(000xx, *0*) 1 1 2,00,xx trans(ab*cd*ef) ab00cdxxef
$/tstMatch/ */
    call tst t, "tstMatch"
    call tstOut t, matchTest1('eins', 'e?n*'                        )
    call tstOut t, matchTest1('eins', 'eins'                        )
    call tstOut t, matchTest1('e1nss', 'e?n*', '?*'                 )
    call tstOut t, matchTest1('eiinss', 'e?n*'                      )
    call tstOut t, matchTest1('einss', 'e?n *'                      )
    call tstOut t, matchTest1('ein s', 'e?n *'                      )
    call tstOut t, matchTest1('ein abss  ', '?i*b*'                 )
    call tstOut t, matchTest1('ein abss wie gehtsssxdirx und auch ' )
    call tstOut t, matchTest1('ies000', '*000'                      )
    call tstOut t, matchTest1('xx0x0000', '*000'                    )
    call tstOut t, matchTest1('000x00000xx', '000*'                 )
    call tstOut t, matchTest1('000xx', '*0*', 'ab*cd*ef'            )
    call tstEnd t
return

matchTest1:
parse arg w, m, m2
    r = 'match('w',' m')' match(w, m) match(w, m, vv) m.vv.0
    do x=1 to m.vv.0
        r = r','m.vv.x
        end
    if m2 = '' then
        m2 = translate(m)
    if m.vv.0 >= 0 then
        r = r 'trans('m2')' matchTrans(m2, vv)
    return r
endProcedure matchTest1

tstIntRdr: procedure expose m.
    i.1 = "//A540769J JOB (CP00,KE50),'DB2 REO',"                       00010000
    i.2 = "//         MSGCLASS=T,TIME=1440,"                            00020000
    i.3 = "//         NOTIFY=&SYSUID,REGION=0M,SCHENV=DB2"              00030003
    i.4 = "//*MAIN CLASS=LOG"                                           00040003
    i.5 = "//S1       EXEC PGM=IEFBR14"                                 00040003
    call writeDsn 'RR2/intRdr', i., 5, 1
    return
endProcedure tstIntRdr

tstII: procedure expose m.
/*
$=/tstII/
    ### start tst tstII ###############################################
    iiRz2C(RZ2)       2
    *** err: no rz=R?Y in ii II_RZ2C
    iiRz2C(R?Y)       0
    iiRz2C(RZY)       Y
    iiDbSys2C(de0G)   E
    *** err: no dbSys=D??? in ii II_DB2C
    iiDbSys2C(d???)   0
    iiDbSys2C(DBOF)   F
    iiSys2RZ(S27)     RZ2
    iiMbr2DbSys(DBP5) DVBP
    ii_rz             RZ1 RZ2 RR2 RZ4 RZX RZY RZZ
    ii_dbSys.rzx      DE0G DEVG DPXG DX0G
$/tstII/
*/
    call tst t, 'tstII'
    call tstOut t, 'iiRz2C(RZ2)      '  iiRz2C(RZ2)
    call tstOut t, 'iiRz2C(R?Y)      '  iiRz2C(R?Y)
    call tstOut t, 'iiRz2C(RZY)      '  iiRz2C(RZY)
    call tstOut t, 'iiDbSys2C(de0G)  '  iiDbSys2C('de0G')
    call tstOut t, 'iiDbSys2C(d???)  '  iiDbSys2C('d???')
    call tstOut t, 'iiDbSys2C(DBOF)  '  iiDbSys2C('DBOF')
    call tstOut t, 'iiSys2RZ(S27)    '  iiSys2RZ(S27)
    call tstOut t, 'iiMbr2DbSys(DBP5)'  iiMbr2DbSys(DBP5)
    call tstOut t, 'ii_rz            '  m.ii_rz
    call tstOut t, 'ii_dbSys.rzx     '  m.ii_dbSys.rzx
    call tstEnd t
    return
endProcedure tstII

tstTime: procedure
/*         Winterzeit dez 2011
$=/tstTime/
    ### start tst tstTime #############################################
    Lrsn2Lzt(C5E963363741) 2010-05-01-11.34.55.789008
    Achtung: output ist abhaengig von Winter/Sommerzeit und LeapSecs
    timeZone 3600.00000 leapSecs 25.0000000
    2jul(2011-03-31-14.35.01.234567)  11090
    Lrsn2Gmt(C5E963363741) 2010-05-01-10.35.20.789008
    Lrsn2Lzt(C5E963363741) 2010-05-01-11.34.55.789008
    gmt2Lrsn(2011-03-31-14.35.01.234567) C78D87B86E38
    lzt2Lrsn(2011-03-31-14.35.01.234567) C78D7A670B7C
    Lrsn2Gmt(gmt2Lrsn(2011-03-31-14.35.01.234567) 2011-03-31-14.35.01.2+
    34560
    gmt2Lrsn(Lrsn2Gmt(C5E963363741) C5E963363741
    Lrsn2LZt(LZt2Lrsn(2011-03-31-14.35.01.234567) 2011-03-31-14.35.01.2+
    34560
    LZt2Stc(Lrsn2LZt(C5E963363741)( C5E963363741
$/tstTime/
*/
    call jIni
    call tst t, 'tstTime'
    t1 = '2011-03-31-14.35.01.234567'
    s1 = 'C5E963363741'
    call out 'Lrsn2Lzt('s1')' timeLrsn2Lzt(s1)
    call out ,
     'Achtung: output ist abhaengig von Winter/Sommerzeit und LeapSecs'
    call out 'timeZone' m.timeZone * m.timeStckUnit ,
             'leapSecs' m.timeLeap * m.timeStckUnit
    call timeReadCvt 1
    call out '2jul('t1') ' time2jul(t1)
    call out 'Lrsn2Gmt('s1')' timeLrsn2Gmt(s1)
    call out 'Lrsn2Lzt('s1')' timeLrsn2Lzt(s1)
    call out 'gmt2Lrsn('t1')' timeGmt2Lrsn(t1)
    call out 'lzt2Lrsn('t1')' timeLzt2Lrsn(t1)
    call out 'Lrsn2Gmt(gmt2Lrsn('t1')' timeLrsn2Gmt(timeGmt2Lrsn(t1))
    call out 'gmt2Lrsn(Lrsn2Gmt('s1')' timeGmt2Lrsn(timeLrsn2Gmt(s1))
    call out 'Lrsn2LZt(LZt2Lrsn('t1')' timeLrsn2LZt(timeLZt2Lrsn(t1))
    call out 'LZt2Stc(Lrsn2LZt('s1')(' timeLZt2Lrsn(timeLrsn2LZt(s1))
    call tstEnd t
    return
endProcedure tstTime
/* copx tstDiv end   **************************************************/

/* copx tstSql begin **************************************************/
tstSqlUtils: procedure expose m.
    call sqlConnect 'DBAF'
    id = 'A540769.dsnUtils'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "TEMPLATE TCOPYD",
            "DSN('&SSID..&JO..&US..P&PART(2)..D&DATE(3)..T&TIME.')",
            "DATACLAS(NULL12) MGMTCLAS(COM#A011) STORCLAS(FAR$N)",
            "SPACE (150,3750) TRK UNCNT 59;",
         "listdef abc include tablespace DA540769.A002* partlevel;",
         "listdef mf  include tablespace MF01A1A.A110A partlevel;",
         "copy list abc copyddn(tcopyd) shrlevel change;"
    st = translate(st)
    call sqlExec "call SYSPROC.DSNUTILS ( :id, :rst,",
                   ":st,:retcode, :ANY" copies(',:e,:e,:z',12) ")"
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.DSNUTILS'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    exit
endProcedure tstSqlUtils

tstSqlStored: procedure expose m.
    call sqlConnect 'DBAF'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "abc"
    call sqlExec "call SYSPROC.COMMAND ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.COMMAND'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStored

tstSqlStoredWK: procedure expose m.
    call sqlConnect 'DBAF'
 /* st = "direct wkUtiSub"
    rst = 'NO'
    say 'before call st='st 'rst='rst
    call sqlExec "call A540769.WKUTILSUB ( :st, :rst)"
    say 'after call st='st 'rst='rst
 */ rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    prc = 'DB2UTIL.DB2UTIL'
    st = "DA540769.A2*" /*  DA540769.A1*" */
    say  "call" prc "("st", ...)"
    call sqlExec "call" prc "(:st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE' prc
 /* say 'results' results  */
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say ''
    say '***** utility output'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say '***** end output'
    call sqlDisconnect
    return 0
endProcedure tstSqlStoredWK

tstSqlStoredSQL1: procedure expose m.
    call sqlConnect 'DBIA'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "DA540769.A2* DA540769.A1*"
    call sqlExec "call A540769.WKSQL1 ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE A540769.WKUTIL'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStoredSQL1

tstSqlTriggerTiming:
    parse upper arg tb ni
    m.noInsert = ni == 0
    cnt = 200000
    if tb = '' then
        TB = 'GDB9998.TWK511TRI'
    call sqlConnect dbaf
    say timing() 'noInsert' m.noInsert 'tb' tb
    call sql2St 'select max(pri) MX from' tb, cc
    if m.cc.1.mx == m.sqlNull then
        m.cc.1.mx = 0
    von = m.cc.1.mx + 1
    bis = m.cc.1.mx + cnt
    say m.cc.0 'max' m.cc.1.mx 'von' von 'bis' bis 'count' cnt tb
    if right(tb, 2) = 'A1' then do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long, par)' ,
                   'values (?, ?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    else do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long)' ,
                   'values (?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    /*  call sqlLn 5,,, 'select * from' tb 'where pri >=' von */
    call sqlCommit
    say timing()
    call sqlDisconnect
    return
endProcedure tstSqlTriggerTiming

tstSqlRx: procedure expose m.
/*
$=/tstSqlRx/
    ### start tst tstSqlRx ############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s7 into :M.SQL.7.D from :src
    .    e 3: with into :M.SQL.7.D = M.SQL.7.D
    fetchA 1 ab=m.abcdef.123.AB=abc ef=efg
    fetchA 0 ab=m.abcdef.123.AB=abc ef=efg
    fetchB 1 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchB 0 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
    fetchBT 1 SYSTABLES
    fetchBT 0 SYSTABLES
    fetchBi 1 SYSINDEXES
    fetchBi 0 SYSINDEXES
$/tstSqlRx/ */
    call tst t, "tstSqlRx"
    call sqlRxConnect
    cx = 7
    call sqlRxQuery cx, 'select * from sysdummy'
    call sqlRxQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlRxFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    call sqlRxClose cx
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlRxQuery cx, sql, '?ab cd ?ef ?gh'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlRxFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    call sqlRxClose cx
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlRxQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlRxFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    call sqlRxClose cx
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call sqlQueryPrep cx, "select name" ,
                        "from sysibm.sysTables" ,
                        "where creator = 'SYSIBM' and name = ?",':m.nm'
    call sqlQueryArgs cx, 'SYSTABLES'
    call out 'fetchBT' sqlRxFetch(cx) m.nm
    call out 'fetchBT' sqlRxFetch(cx) m.nm
    call sqlRxClose cx
    call sqlQueryArgs cx, 'SYSINDEXES'
    call out 'fetchBi' sqlRxFetch(cx) m.nm
    call out 'fetchBi' sqlRxFetch(cx) m.nm
    call tstEnd t
    call sqlRxDisconnect
    return
endProcedure tstSqlRx

tstSql: procedure expose m.
/*
$=/tstSql/
    ### start tst tstSql ##############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s7 into :M.SQL.7.D from :src
    .    e 3: with into :M.SQL.7.D = M.SQL.7.D
    fetchA 1 ab=m.abcdef.123.AB=abc ef=efg
    fetchA 0 ab=m.abcdef.123.AB=abc ef=efg
    fetchB 1 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchB 0 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
    sql2St 1 st.0=1
    sql2St:1 a=a b=2 c=--- d=d
    sql2One a
    sql2One a=a b=2 c=--- d=d
    fetchBT 1 SYSTABLES
    fetchBT 0 SYSTABLES
    fetchBi 1 SYSINDEXES
    fetchBi 0 SYSINDEXES
$/tstSql/ */
    call tst t, "tstSql"
    call sqlConnect
    cx = 7
    call sqlQuery cx, 'select * from sysdummy'
    call sqlQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    call sqlClose cx
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlQuery cx, sql, '?ab cd ?ef ?gh'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    call sqlClose cx
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    call sqlClose cx
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call out 'sql2St' sql2St(sql, st) 'st.0='m.st.0
    do i=1 to m.st.0
        call out 'sql2St:'i ,
            'a='m.st.i.a 'b='m.st.i.b 'c='m.st.i.c 'd='m.st.i.d
        end
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    drop m.st.a m.st.b m.st.c m.st.d m.st.0
    call out 'sql2One' sql2One(sql, st)
    call out 'sql2One' ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
    drop m.st.a m.st.b m.st.c m.st.d m.st.0
    call sqlQueryPrep cx, "select name" ,
                        "from sysibm.sysTables" ,
                        "where creator = 'SYSIBM' and name = ?",':m.nm'
    call sqlQueryArgs cx, 'SYSTABLES'
    call out 'fetchBT' sqlFetch(cx) m.nm
    call out 'fetchBT' sqlFetch(cx) m.nm
    call sqlClose cx
    call sqlQueryArgs cx, 'SYSINDEXES'
    call out 'fetchBi' sqlFetch(cx) m.nm
    call out 'fetchBi' sqlFetch(cx) m.nm
    call tstEnd t
    call sqlDisconnect
    return
endProcedure tstSql

tstSqlCsm: procedure expose m.
/*
$=/tstSqlCsm/
    ### start tst tstSqlCsm ###########################################
    *** err: SQLCODE = -204: S100447.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: subsys = DE0G, host = RZZ
    *** err: fetchVars with : for csm: :m.dst.ab, :m.dst.ef
    fetchA 0 ab=m.abcdef.123.AB=M.abcdef.123.AB ef=M.abcdef.123.EF
    fetchA 0 ab=m.abcdef.123.AB=M.abcdef.123.AB ef=M.abcdef.123.EF
    fetchB 1 ab=a cd=2 ef=--- ind=M.abc.Def.123.EF.SQLIND gh=d ind=M.ab+
    c.Def.123.GH.SQLIND
    fetchB 0 ab=a cd=2 ef=--- ind=M.abc.Def.123.EF.SQLIND gh=d ind=M.ab+
    c.Def.123.GH.SQLIND
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
$/tstSqlCsm/ */
    call tst t, "tstSqlCsm"
    call sqlConnect 'RZZ/DE0G'
    cx = 7
    call sqlCsmQuery cx, 'select * from sysdummy'
    call sqlCsmQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlCsmFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlCsmQuery cx, sql, '?ab cd ?ef ?gh'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlCsmFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlCsmQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlCsmFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call tstEnd t
    call sqlDisconnect
    return
endProcedure tstsqlCsm

tstSqlCSV: procedure expose m.
/*
$=/tstSqlCSV/
    ### start tst tstSqlCSV ###########################################
    NAME,CREATOR,MITCOM,MITQUO,MITNU,COL6
    SYSTABLES,SYSIBM  ,"a,b","a""b",1,8
    SYSTABLESPACE,SYSIBM  ,"a,b","a""b",---,8
    SYSTABLESPACESTATS,SYSIBM,"a,b","a""b",---,6
$/tstSqlCSV/ */
    call csvIni
    call sqlConnect
    call tst t, "tstSqlCSV"
    r = csvWrt(sqlRdr("select name, creator, 'a,b' mitCom",
         ", 'a""b' mitQuo" ,
         ", case when name='SYSTABLES' then 1 else null end mitNu" ,
         ",length(creator)" ,
              "from sysibm.sysTables" ,
              "where creator = 'SYSIBM' and name like 'SYSTABLES%'",
              "fetch first 3 rows only"))
    call pipeWriteAll r
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlCsv

tstSqlB: procedure expose m.
/*
$=/tstSqlB/
    ### start tst tstSqlB #############################################
    #jIn 1# select strip(name) "tb", strip(creator) cr
    #jIn 2# , case when name = 'SYSTABLES' then 1 else null end
    #jIn 3# from sysibm.sysTables
    #jIn 4# where creator = 'SYSIBM' and name like 'SYSTABLES%'
    #jIn 5# .
    #jIn 6# order by name
    #jIn 7# fetch first 3 rows only
    #jIn eof 8#
    dest1.fet: SYSTABLES SYSIBM 1
    dest2.fet: SYSTABLESPACE SYSIBM ---
    dest3.fet: SYSTABLESPACESTATS SYSIBM ---
$/tstSqlB/ */
    call tst t, "tstSqlB"
    cx = 9
    call sqlConnect
    call jIni
    call mAdd mCut(t'.IN', 0),
      , 'select strip(name) "tb", strip(creator) cr' ,
      ,     ", case when name = 'SYSTABLES' then 1 else null end" ,
      ,   "from sysibm.sysTables" ,
      ,   "where creator = 'SYSIBM' and name like 'SYSTABLES%'", ,
      ,   "order by name",
      ,   "fetch first 3 rows only"
     call sqlQuery cx
     do qx=1 while sqlFetch(cx, 'dest'qx'.fet')
         dst = 'dest'qx'.fet'
         call out dst':' m.dst.tb m.dst.cr m.dst.col3
         drop m.dst.tb m.dst.cr m.dst.col3
         end
     call sqlClose cx
     call sqlDisconnect
     call tstEnd t
     return
endProcedure tstSqlB

tstSqlO: procedure expose m.
/*
$=/tstSqlO/
    ### start tst tstSqlO #############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s10 into :M.SQL.10.D from :src
    .    e 3: with into :M.SQL.10.D = M.SQL.10.D
    REQD=Y col=123 case=--- .sqlInd:-1 col5=anonym geburri=1956-04-01+
    -06.00.00.000000
$/tstSqlO/
*/
    call sqlConnect
    call tst t, "tstSqlO"
    call sqlStmt 'set current schema = A540769';
    call sqlStmt 'select * from sysdummy';
    r = sqlRdr( ,
          "select d.*, 123, timestamp('01.04.1956','06:00:00')" ,
                             '"geburri walter",',
                    'case when 1=0 then 1 else null end caseNull,',
                    "'anonym'" ,
               'from sysibm.sysdummy1 d')
    call jOpen r, '<'
    do while assNN('o', jReadO(r))

        call out 'REQD='m.o.IBMREQD 'col='m.o.col2,
                  'case='m.o.CASENULL '.sqlInd:'m.o.caseNull.sqlInd,
                  'col5='m.o.col5,
                  'geburri='m.o.GEBURRI
        end
    call jClose r
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlO

tstSqlFTab: procedure expose m.
/*
$=/tstSqlFTab/
    ### start tst tstSqlFTab ##########################################
    UPDATESTATSTIME----------------NACTIVE------NPAGES-EXTENT-LOADRLAST+
    TIME--------------REORGLASTTIME--------------REORGINSERT-REORGDELET+
    E-REORGUPDATE-REORGUNCLUS-REORGDISORG-REORGMASSDE-REORGNEARIN-REORG+
    FARIND-STATSLASTTIME--------------STATSINSERT-STATSDELETE-STATSUPDA+
    TE-STATSMASSDE-COPYLASTTIME---------------COPYUPDATED-COPYCHANGES-C+
    OPYUP-COPYUPDATETIME-------------I---DBID---PSID-PARTIT-INSTAN---SP+
    ACE-TOTALRO-DATASIZ-UNCOMPR-DBNAME-------NAME---------REORGCL-REORG+
    SC-REORGHA-HASHLASTUS-DRI-L-STATS01----
    --- modified
    allg vorher                     others vorher
    db-------ts---------part---inst-UPDATESTATSTIME----------------NACT+
    IVE------NPAGES-EXTENT-LOADRLASTTIME--------------REORGLASTTIME----+
    ----------REORGINSERT-REORGDELETE-REORGUPDATE-REORGUNCLUS-REORGDISO+
    RG-REORGMASSDE-REORGNEARIN-REORGFARIND-STATSLASTTIME--------------S+
    TATSINSERT-STATSDELETE-STATSUPDATE-STATSMASSDE-COPYLASTTIME--------+
    -------COPYUPDATED-COPYCHANGES-COPYUP-COPYUPDATETIME-------------I-+
    --DBID---PSID---SPACE-TOTALRO-DATASIZ-UNCOMPR-REORGCL-REORGSC-REORG+
    HA-HASHLASTUS-DRI-L-STATS01----
    db-------ts---------part---inst-UPDATESTATSTIME----------------NACT+
    IVE------NPAGES-EXTENT-LOADRLASTTIME--------------REORGLASTTIME----+
    ----------REORGINSERT-REORGDELETE-REORGUPDATE-REORGUNCLUS-REORGDISO+
    RG-REORGMASSDE-REORGNEARIN-REORGFARIND-STATSLASTTIME--------------S+
    TATSINSERT-STATSDELETE-STATSUPDATE-STATSMASSDE-COPYLASTTIME--------+
    -------COPYUPDATED-COPYCHANGES-COPYUP-COPYUPDATETIME-------------I-+
    --DBID---PSID---SPACE-TOTALRO-DATASIZ-UNCOMPR-REORGCL-REORGSC-REORG+
    HA-HASHLASTUS-DRI-L-STATS01----
    allg nachher                    others nachher
    DBNAME                   INSTANCE                                  +
    .    NPAGES                                        REORGLASTTIME   +
    .                                   REORGUPDATES                   +
    .     REORGMASSDELETE                     STATSLASTTIME            +
    .                          STATSUPDATES                            +
    .           COPYUPDATEDPAGES               COPYUPDATETIME          +
    .            PSID                   DATASIZE                REORGSC+
    ANACCESS            DRIVETYPE
    .         NAME                   UPDATESTATSTIME                   +
    .                 EXTENTS                                          +
    .            REORGINSERTS                        REORGUNCLUSTINS   +
    .                  REORGNEARINDREF                                 +
    .   STATSINSERTS                        STATSMASSDELETE            +
    .                        COPYCHANGES                               +
    .    IBMREQD         SPACE                   UNCOMPRESSEDDATASIZE  +
    .  REORGHASHACCESS        LPFACILITY
    .                  PARTITION                                NACTIVE+
    .                        LOADRLASTTIME                             +
    .                        REORGDELETES                        REORGD+
    ISORGLOB                      REORGFARINDREF                       +
    .              STATSDELETES                        COPYLASTTIME    +
    .                                   COPYUPDATELRSN                 +
    .     DBID                  TOTALROWS               REORGCLUSTERSEN+
    S        HASHLASTUSED     STATS01
$/tstSqlFTab/
*/
    call tst t, 'tstSqlFTab'
    call sqlConnect
    call sqlQuery 17, 'select * from sysibm.sysTablespaceStats' ,
                "where dbName = 'xxxDB06' and name = 'SYSTSTAB'"
    call sqlFTabReset abc, 17, 1,     ,  12
    call sqlFTabDef      abc, 492, '%7e'
    call sqlFTabOthers abc
    call sqlfTab abc
    call sqlClose 17
    call out '--- modified'
    call sqlQuery 17, 'select * from sysibm.sysTablespaceStats' ,
                "where dbName = 'xxxDB06' and name = 'SYSTSTAB'"
    call sqlFTabReset abc, 17, 2 1, 1 3 'c', 12
    call sqlFTabDef      abc, 492, '%7e'
    call sqlFTabAdd      abc, DBNAME, '%-8C', 'db', 'allg vorher'  ,
                                                  , 'allg nachher'
    call sqlFTabAdd      abc, NAME  , '%-8C', 'ts'
    call sqlFTabAdd      abc, PARTITION , , 'part'
    call sqlFTabAdd      abc, INSTANCE  , , 'inst'
    call fTabAddTit      abc, 2,                    'others vorher'
    call fTabAddTit      abc, 3,                    'others nachher'
    call sqlFTabOthers abc
    call sqlFTab abc
    call sqlClose 17
    call tstEnd t
    return
endProcedure tstSqlFTab

tstSqlC: procedure expose m.
/*
$=/tstSqlCRx/
    ### start tst tstSqlCRx ###########################################
    *** err: SQLCODE = -104: ILLEGAL SYMBOL "?". SOME SYMBOLS THAT MIGH+
    T
    .    e 1:     BE LEGAL ARE: . <IDENTIFIER> JOIN INNER LEFT RIGHT FU+
    LL CROSS ,
    .    e 2:     HAVING GROUP
    .    e 3: src select * from sysibm?sysDummy1
    .    e 4:   >    >>>pos 21 of 30>>>
    .    e 5: sql = select * from sysibm?sysDummy1
    .    e 6: stmt = prepare s9 into :M.SQL.9.D from :src
    .    e 7: with into :M.SQL.9.D = M.SQL.9.D
    *** err: SQLCODE = -204: NONONO.SYSDUMMY1 IS AN UNDEFINED NAME
    .    e 1: sql = select * from nonono.sysDummy1
    .    e 2: stmt = prepare s9 into :M.SQL.9.D from :src
    .    e 3: with into :M.SQL.9.D = M.SQL.9.D
    sys  ==> server CHSKA000DP4G    .
    fetched a1=abc, i2=12, c3=---
    .  I1 C2  .
    .   1 eins
    2222 zwei
$/tstSqlCRx/
$=/tstSqlCCsm/
    ### start tst tstSqlCCsm ##########################################
    *** err: SQLCODE = -104: ILLEGAL SYMBOL "?". SOME SYMBOLS THAT MIGH+
    T
    .    e 1:     BE LEGAL ARE: . <IDENTIFIER> JOIN INNER LEFT RIGHT FU+
    LL CROSS ,
    .    e 2:     HAVING GROUP
    .    e 3: src select * from sysibm?sysDummy1
    .    e 4:   >    >>>pos 21 of 30>>>
    .    e 5: sql = select * from sysibm?sysDummy1
    .    e 6: subsys = DE0G, host = RZZ
    *** err: SQLCODE = -204: NONONO.SYSDUMMY1 IS AN UNDEFINED NAME
    .    e 1: sql = select * from nonono.sysDummy1
    .    e 2: subsys = DE0G, host = RZZ
    sys RZZ/DE0G ==> server CHROI00ZDE0G    .
    fetched a1=abc, i2=12, c3=---
    .  I1 C2  .
    .   1 eins
    2222 zwei
$/tstSqlCCsm/ */
    sqlBuf = jBuf("select 1 i1, 'eins' c2 from sysibm.sysDummy1",
      , "union all select 2222 i1, 'zwei' c2 from sysibm.sysDummy1")
    do tx=1 to 2
        if tx = 1 then do
            call tst t, "tstSqlCRx"
            sys = ''
            end
        else do
            call tst t, "tstSqlCCsm"
            sys =  'RZZ/DE0G'
            end
        call sqlConnect sys
        cx = 9
        call sqlQuery cx, 'select * from sysibm?sysDummy1'
        call sqlQuery cx, 'select * from nonono.sysDummy1'
        call sqlQuery cx, "select 'abc' a1, 12 i2, current server srv",
                     ", case when 1=0 then 1 else null end c3",
                 "from sysibm.sysDummy1"
        do while sqlFetch(cx, dst)
            call out 'sys' sys '==> server' m.dst.srv
            call out 'fetched a1='m.dst.a1', i2='m.dst.i2', c3='m.dst.c3
            end
        call fTabAuto , sqlRdr(sqlBuf)
        call sqlDisconnect
        call tstEnd t
        end
    return
endProcedure tstSqlC

tstSqlUpd: procedure expose m.
/*
$=/tstSqlUpd/
    ### start tst tstSqlUpd ###########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table (update session.dgtt  set c2 = 'u' +
    || c2)
    stmt = prepare s9 into :M.SQL.9.D from :src
    with into :M.SQL.9.D = M.SQL.9.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstSqlUpd/ */
    call tst t, "tstSqlUpd"
    cx = 9
    qx = 3
    call sqlConnect
    call sqlUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call sqlUpdate,"insert into session.dgtt" ,
                    "values(1, 'eins', '2012-04-01 06.07.08')"
    call sqlUpdate,"insert into session.dgtt" ,
                    "values(2, 'zwei', '2012-02-29 15:44:33.22')"
    call out 'insert updC' m.sql..updateCount
    call sqlUpdate,"insert into session.dgtt" ,
                      "select i1+10, 'zehn+'||strip(c2), t3+10 days",
                           "from session.dgtt"
    call out 'insert select updC' m.sql..updateCount
    call sqlQuery cx, 'select d.*' ,
               ', case when mod(i1,2) = 1 then 1 else null end grad' ,
               'from session.dgtt d'
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlClose cx
    call sqlQuery cx, "select * from final table (update session.dgtt",
                   " set c2 = 'u' || c2)"
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSqlUpd
tstSqlUpdPre: procedure expose m.
/*
$=/tstSqlUpdPre/
    ### start tst tstSqlUpdPre ########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table ( update session.dgtt set c2 = ? ||+
    . c2)
    stmt = prepare s5 into :M.SQL.5.D from :src
    with into :M.SQL.5.D = M.SQL.5.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstSqlUpdPre/ */
    call tst t, "tstSqlUpdPre"
    cx = 5
    qx = 3
    call sqlConnect
    call sqlUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call sqlUpdPrep 5, "insert into session.dgtt values (?, ?, ?)"
    call sqlUpdArgs 5, 1, 'eins', '2012-04-01 06.07.08'
    call sqlUpdArgs 5, 2, 'zwei', '2012-02-29 15:44:33.22'
    call out 'insert updC' m.sql.5.updateCount
    call sqlUpdPrep 5,"insert into session.dgtt" ,
                      "select i1+?, 'zehn+'||strip(c2), t3+? days",
                           "from session.dgtt"
    call sqlUpdArgs 5, 10, 10
    call out 'insert select updC' m.sql.5.updateCount
    call sqlQueryPrep cx, 'select d.*' ,
               ', case when mod(i1,2) = ? then 0+? else null end grad' ,
               'from session.dgtt d'
    call sqlQueryArgs cx, 1, 1
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlClose cx
    call sqlQueryPrep cx, "select * from final table (" ,
              "update session.dgtt set c2 = ? || c2)"
    call sqlQueryArgs cx, "u"
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSqlUpdPre
tstsqlRxUpd: procedure expose m.
/*
$=/tstsqlRxUpd/
    ### start tst tstsqlRxUpd #########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table (update session.dgtt set c2 = 'u' +
    || c2)
    stmt = prepare s9 into :M.SQL.9.D from :src
    with into :M.SQL.9.D = M.SQL.9.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstsqlRxUpd/ */
    call tst t, "tstsqlRxUpd"
    cx = 9
    qx = 3
    call sqlRxConnect
    call sqlRxUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call sqlRxUpdate,"insert into session.dgtt" ,
                    "values(1, 'eins', '2012-04-01 06.07.08')"
    call sqlRxUpdate,"insert into session.dgtt" ,
                    "values(2, 'zwei', '2012-02-29 15:44:33.22')"
    call out 'insert updC' m.sql..updateCount
    call sqlRxUpdate,"insert into session.dgtt" ,
                      "select i1+10, 'zehn+'||strip(c2), t3+10 days",
                           "from session.dgtt"
    call out 'insert select updC' m.sql..updateCount
    call sqlRxQuery cx, 'select d.*' ,
               ', case when mod(i1,2) = 1 then 1 else null end grad' ,
               'from session.dgtt d'
    do qx=qx+1 while sqlRxFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlRxClose cx
    call sqlRxQuery cx, "select * from final table",
                 "(update session.dgtt set c2 = 'u' || c2)"

    do qx=qx+1 while sqlRxFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlRxClose cx
    call sqlRxDisconnect
    call tstEnd t
    return
endProcedure tstsqlRxUpd

tstSqlE: procedure expose m.
/*
$=/tstSqlE/
    ### start tst tstSqlE #############################################
    *** err: SQLCODE = -713: THE REPLACEMENT VALUE FOR CURRENT SCHEMA I+
    S
    .    e 1:     INVALID
    .    e 2: sql = set current schema = 'sysibm'
    .    e 3: stmt = execute immediate :src
    -713 set schema ''
    0 set schema
    0 select
    fetch=1 SYSIBM
$/tstSqlE/
*/
    call sqlConnect
    call tst t, "tstSqlE"
    call tstOut t, sqlExecute(3, "set current schema = 'sysibm'") ,
                                 "set schema ''"
    call tstOut t, sqlExecute(3, " set current schema = sysibm ") ,
                                 "set schema"
    call tstOut t, sqlExecute(3, " select current schema c"      ,
                                      "from sysibm.sysDummy1") 'select'
    call tstOut t, 'fetch='sqlFetch(3, aa) m.aa.c
    call sqlClose 3
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlE
tstSqlO: procedure expose m.
/*
$=/tstSqlO/
    ### start tst tstSqlO #############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: stmt = prepare s7 from :src
    .    e 2: with from :src = select * from sysdummy
    REQD=Y col=123 case=--- .sqlInd:-1 col5=anonym geburri=1956-04-01+
    -06.00.00.000000
$/tstSqlO/
*/
    call sqlConnect
    call sqlStmt 'set current schema = A540769';
    call tst t, "tstSqlO"
    src = 'select * from sysdummy'
    call sqlExec 'prepare s7 from :src'
    r = sqlRdr( ,
          "select d.*, 123, timestamp('01.04.1956','06:00:00')" ,
                             '"geburri walter",',
                    'case when 1=0 then 1 else null end caseNull,',
                    "'anonym'" ,
               'from sysibm.sysdummy1 d')
    call jOpen r, '<'
    do while assNN('o', jReadO(r))

        call out 'REQD='m.o.IBMREQD 'col='m.o.col2,
                  'case='m.o.CASENULL '.sqlInd:'m.o.caseNull.sqlInd,
                  'col5='m.o.col5,
                  'geburri='m.o.GEBURRI
        end
    call jClose r
    call tstEnd t
    return
endProcedure tstSqlO

tstSqlUpdComLoop: procedure expose m.
/*
$=/tstSqlUpdComLoop/
    ### start tst tstSqlUpdComLoop ####################################
    sqlCode 0: declare global temporary table session.dgtt (i1 int) on +
    commit ....
    sqlCode 0, 123 rows inserted: insert into session.dgtt select row_n+
    umber()....
    CNT
    123
    1 rows fetched: select count(*) cnt from session.dgtt
    123 rows deleted, 10 commits: delete from session.dgtt d where i1 i+
    n (sele....
    C
    0
    1 rows fetched: select count(*) cnt from session.dgtt
$/tstSqlUpdComLoop/ */
    call tst t, "tstSqlUpdComLoop"
    call sqlConnect
    call out sqlStmt("declare global temporary table session.dgtt",
                           "(i1 int) on commit preserve rows")
    call out sqlStmt("insert into session.dgtt",
       "select row_number() over() from sysibm.sysTables",
           "fetch first 123 rows only")
    call out sqlStmt("select count(*) cnt from session.dgtt")
    call out sqlUpdComLoop("delete from session.dgtt d where i1 in",
       "(select i1 from session.dgtt fetch first 13 rows only)")
    call out sqlStmt("select count(*) cnt from session.dgtt")
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlUpdComLoop

tstSqlO1: procedure expose m.
/*
$=/tstSqlO1/
    ### start tst tstSqlO1 ############################################
    tstR: @tstWriteoV2 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART
    tstR: @tstWriteoV3 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART_HIST
    tstR: @tstWriteoV4 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLES
    tstR: @tstWriteoV5 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLESPACE
    --- writeAll
    tstR: @tstWriteoV6 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART
    tstR: @tstWriteoV7 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART_HIST
    tstR: @tstWriteoV8 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLES
    tstR: @tstWriteoV9 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLESPACE
$/tstSqlO1/
*/
    call sqlConnect
    call tst t, "tstSqlO1"
    qr = sqlRdr("select strip(creator) cr, strip(name) tb",
                     "from sysibm.sysTables",
                     "where creator='SYSIBM' and name like 'SYSTABL%'",
                     "order by 2 fetch first 4 rows only")
    call jOpen qr, m.j.cRead
    call mAdd t.trans, className(m.qr.type) '<tstSqlO1Type>'
    do while assNN('ABC', jReadO(qr))
        if m.qr.rowCount = 1 then do
            cx = m.qr.cursor
            end
        call outO abc
        end
    call jClose qr
    call out '--- writeAll'
    call pipeWriteAll qr
    call tstEnd t
    return 0
endProcedure tstSqlO1

tstSqlO2: procedure expose m.
/*
$=/tstSqlO2/
    ### start tst tstSqlO2 ############################################
    CR     TB                RR
    SYSIBM SYSTABLEPART       1
    SYSIBM SYSTABLEPART_HIST  4
    SYSIBM SYSTABLES          9
    SYSIBM SYSTABLESPACE     16
$/tstSqlO2/
*/
    call sqlConnect
    call tst t, "tstSqlO2"
    call pipe '+N'
    call out    "select strip(creator) cr, strip(name) tb,"
    call out         "(row_number()over())*(row_number()over()) rr"
    call out         "from sysibm.sysTables"
    call out         "where creator='SYSIBM' and name like 'SYSTABL%'"
    call out         "order by 2 fetch first 4 rows only"
    call pipe 'N|'
    call sqlSel
    call pipe 'P|'
    call fTabAuto fTabReset(abc, 1)
    call pipe '-'
    call tstEnd t
    return 0
endProcedure tstSqlO2
tstSqlS1: procedure expose m.
/*
$=/tstSqlS1/
    ### start tst tstSqlS1 ############################################
    select c, a from sysibm.sysDummy1
    tstR: @tstWriteoV2 isA :<cla sql c a>
    tstR:  .C = 1
    tstR:  .A = a
    select ... where 1=0
    tstR: @ obj null
$/tstSqlS1/
*/
    call sqlIni
    call tst t, "tstSqlS1"
    call sqlConnect
    s1 = jSingle( ,
        sqlRdr("select count(*) c, 'a' a from sysibm.sysdummy1"))
    call mAdd t.trans, className(objClass(s1)) '<cla sql c a>'
    call out 'select c, a from sysibm.sysDummy1'
    call tstWriteO t, s1
    call out 'select ... where 1=0'
    call tstWriteO t, jSingle( ,
        sqlRdr("select*from sysibm.sysdummy1 where 1=0"), '')
    call tstEnd t
    return
endProcedure tstSqlS1
tstSqlStmt: procedure expose m.
/*
$=/tstSqlStmt/
    ### start tst tstSqlStmt ##########################################
    *** err: SQLCODE = -713: THE REPLACEMENT VALUE FOR CURRENT SCHEMA I+
    S
    .    e 1:     INVALID
    .    e 2: sql = set current schema = 'sysibm'
    .    e 3: stmt = execute immediate :src
    sqlCode -713: set current schema = 'sysibm'
    sqlCode 0: set current schema =  sysibm
    tstR: @tstWriteoV2 isA :<sql?sc>
    tstR:  .C = SYSIBM
    1 rows fetched: select current schema c  from sysDummy1
    tstR: @tstWriteoV3 isA :<sql?sc>
    tstR:  .C = SYSIBM
    1 rows fetched: (select current schema c from sysDummy1)
$/tstSqlStmt/
*/
    call sqlConnect
    call tst t, "tstSqlStmt"
    cn = className(classNew('n* SQL u f C v'))
    call mAdd t.trans, cn '<sql?sc>'
    call tstOut t, sqlStmt("set current schema = 'sysibm'")
    call tstOut t, sqlStmt("  set current schema =  sysibm ")
    call tstOut t, sqlStmt("   select current schema c  from sysDummy1",
                           , ,'o')
    call tstOut t, sqlStmt("  (select current schema c from sysDummy1)",
                           , ,'o')
    call tstEnd t
    return
endProcedure tstSqlStmt
tstSqlStmts: procedure expose m.
/*
$=/tstSqlStmts/
   ### start tst tstSqlStmts #########################################
   *** err: SQLCODE = -104: ILLEGAL SYMBOL "BLABLA". SOME SYMBOLS THAT
   .    e 1:     MIGHT BE LEGAL ARE: <ERR_STMT> <WNG_STMT> GET SQL SAV+
   EPOINT HOLD
   .    e 2:     FREE ASSOCIATE
   .    e 3: src blabla
   .    e 4:   > <<<pos 1 of 6<<<
   .    e 5: sql = blabla
   sqlCode -104: blabla
   sqlCode 0: set current schema=  sysIbm
   C
   1
   1 rows fetched: select count(*) "c" from sysDummy1  with    ur
   C
   1
   1 rows fetched: select count(*) "c" from sysDummy1  with    ur
   #jIn 1# set current -- sdf
   #jIn 2# schema = s100447;
   sqlCode 0: set current  schema = s100447
   #jIn eof 3#
$/tstSqlStmts/ */
    call sqlConnect
    call scanReadIni
    call scanWinIni
    call tst t, "tstSqlStmts"
    call sqlStmts "blabla ;;set current schema=  sysIbm "
    b = jBuf('select count(*) "c" from sysDummy1 --com' ,
             ,'with  /* comm */ ur;')
    call sqlStmts b
    call sqlStmts b, , '-sql72'
    call mAdd mCut(t'.IN', 0), 'set current -- sdf', 'schema = s100447;'
    call sqlStmts
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlStmts

tstCatTb:
/*
$=/tstCatTb/
    ### start tst tstCatTb ############################################
    ..
    select * from sysibm.SYSDUMMY1  .
    IBMREQD
    I .
    Y .
    I .
    IBMREQD
$/tstCatTb/
*/
    call sqlConnect
    call tst t, 'tstCatTb'
    call sqlCatTb 'sysDummy1'
    call sqlCatTb 'SYSTableSpaceStats',
             , "name = 'A403A1' and dbName = 'DA540769'"
    call tstEnd t
    return
endProcedure tstCatTb

tstSqlDisDb: procedure expose m.
    call sqlDsn di, 'DP4G', '-dis db(*) sp(*)' ,
                    'restrict advisory limit(*)', 12
    m.oo.0 = 0
    call sqlDisDb oo, di
    say 'di.0' m.di.0 '==> oo.0' m.oo.0
    trace ?r
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE)
    say 'DB2PDB6.RR2HHAGE  ==>' ix m.oo.ix.sta
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE, 3)
    say 'DB2PDB6.RR2HHAGE.3  ==>' ix m.oo.ix.sta
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE,22)
    say 'DB2PDB6.RR2HHAGE.22 ==>' ix m.oo.ix.sta
    return
endProcedure tstSqlDisDb

/* copx tstSql end  ***************************************************/
/* copx tstComp begin **************************************************
    test the wsh compiler
***********************************************************************/
tstComp: procedure expose m.
    call compIni
    call tstCompDataConst
    call tstCompDataVars
    call tstCompShell
    call tstCompPrimary
    call tstCompExpr
    call tstCompFile
    call tstCompStmt
    call tstCompDir
    call tstCompObj
    call tstCompORun
    call tstCompDataIO
    call tstCompPipe
    call tstCompPip2
    call tstCompRedir
    call tstCompComp
    call tstCompColon
    call tstCompTable
    call tstCompSyntax
    if m.err.os == 'TSO' then
        call tstCompSql
    call tstTotal
    return
endProcedure tstComp

tstComp1: procedure expose m.
parse arg ty nm cnt
    c1 = 0
    if cnt = 0 | cnt = '+' then do
        c1 = cnt
        cnt = ''
        end
    call jIni
    src = jBuf()
    call jOpen src, m.j.cWri
    do sx=2 to arg()
        call jWrite src, arg(sx)
        end
    call tstComp2 nm, ty, jClose(src), , c1, cnt
    return
endProcedure tstComp1

tstComp2: procedure expose m.
parse arg nm, spec, src, compSt
    call compIni
    call tst t, nm, compSt
    if src == '' then do
        src = jBuf()
        call tst4dp src'.BUF', mapInline(nm'Src')
        end
    m.t.moreOutOk = abbrev(strip(arg(5)), '+')
    oldErr = m.err.count
    cmp = comp(src)
    call out 'compile' spec',' (m.src.buf.0) 'lines:' m.src.buf.1
    r = compile(cmp, spec)
    noSyn = m.err.count = oldErr
    coErr = m.t.err
    say if( noSyn, "compiled", "*** syntaxed") r ":" objMet(r, 'oRun')

    cnt = 0
    do ax = 5 to max(arg(), 5) while m.t.err = coErr & noSyn
        a1 = strip(arg(ax))
        if a1 == '' & arg() >= 5 then
            iterate
        if abbrev(a1, '+') then do
            m.t.moreOutOk = 1
            a1 = strip(substr(a1, 2))
            end
        if datatype(a1, 'n') then
            cnt = a1
        else if a1 \== '' then
            call err 'tstComp2 bad arg('ax')' arg(ax)
        if cnt = 0 then do
            call mCut 'T.IN', 0
            call out "run without input"
            end
        else  do
            call mAdd mCut('T.IN', 0),
                ,"eins zwei drei", "zehn elf zwoelf?",
                , "zwanzig 21 22 23 24 ... 29|"
            do lx=4 to cnt
                call mAdd 'T.IN', left(lx "line" lx, lx+9, '-')';'
                end
            call out "run with" cnt "inputs"
            end
        m.t.inIx = 0
        call oRun r
        end
    call tstEnd t
    return
endProcedure tstComp2

tstCompDataConst: procedure expose m.
/*
$=/tstCompDataConst/
    ### start tst tstCompDataConst ####################################
    compile =, 8 lines:       Lline one, $** asdf
    run without input
    .      Lline one, .
    line two..
    line threecontinued on 4
    line five  fortsetzung
    line six   fortsetzung
$/tstCompDataConst/ */
    call tstComp1 '= tstCompDataConst',
        , '      Lline one, $** asdf',
        , 'line two.',
        , 'line three$*+ bla bla' ,
        , 'continued on 4',
        , 'line five $*( und so',
        , 'weiter $abc $? $''$*)'' $"$*)" und weiter $*) fortsetzung',
        , 'line six  $*( und $*( $** $*( so',
        , 'weiter $abc $? $*)'' $"$*)" und weiter $*) fortsetzung'

/*
$=/tstCompDataConstBefAftComm1/
    ### start tst tstCompDataConstBefAftComm1 #########################
    compile =, 3 lines:     $*(anfangs com.$*)       $*(plus$*) $** x
    run without input
    the only line;
$/tstCompDataConstBefAftComm1/ */
    call tstComp1 '= tstCompDataConstBefAftComm1',
        , '    $*(anfangs com.$*)       $*(plus$*) $** x',
        , 'the only line;',
        , '      $*(end kommentar$*)              '

/*
$=/tstCompDataConstBefAftComm2/
    ### start tst tstCompDataConstBefAftComm2 #########################
    compile =, 11 lines:     $*(anfangs com.$*)       $*(plus$*) $*+ x
    run without input
    the first non empty line;
    .      .
    befor an empty line with comments;
$/tstCompDataConstBefAftComm2/ */

    call tstComp1 '= tstCompDataConstBefAftComm2',
        , '    $*(anfangs com.$*)       $*(plus$*) $*+ x',
        , '    $*(forts Zeile com.$*)       $*(plus$*) $** x',
        , ' $*(comment 2. Zeile$*) $*(plus$*) $*+ x',
        , ' $*(forts Zeile com.$*) $*(plus$*) $** x',
        , 'the first non empty line;',
        , '      ',
        , 'befor an empty line with comments;',
        , ' $*(comment 4. Zeile$*) $*(plus$*) $*+ x',
        , ' $*(forts 4.Zeile com.$*) $*(plus$*) $** x',
        , '      $*(end kommentar$*)              $*+',
        , ' $*(forts end com.$*) $*(plus$*) $** x'
     return
endProcedure tstCompDataComm

tstCompDataVars: procedure expose m.
/*
$=/tstCompDataVars/
    ### start tst tstCompDataVars #####################################
    compile =, 5 lines:       Lline one, $** asdf
    run without input
    .      Lline one, .
    lline zwei output
    lline 3 .
    variable v1 = valueV1 ${v1}= valueV1; .
    .      $-.{""$v1} = valueV1; .
$/tstCompDataVars/ */
    call tstComp1 '= tstCompDataVars',
        , '      Lline one, $** asdf',
        , '   $$ lline zwei output',
        , 'lline 3 $=v1= valueV1 ' ,
        , 'variable v1 = $v1 $"${v1}=" ${  v1  }; ',
        , '      $"$-.{""""$v1} =" $-.{$""$"v1"}; '
    return
endProcedure tstCompDataVars

tstCompShell: procedure expose m.
/*
$=/tstCompShell3/
    ### start tst tstCompShell3 #######################################
    compile @, 8 lines: call tstOut "T",  "abc" $-¢2*3$! "efg"$-¢2*3$!"+
    hij"
    run without input
    abc 6 efg6hij
    insert into A540769x.tqt002 with n(n) as ( select 4 from sysibm.s
    insert into A540769x.tqt002 with n(n) as ( select 4 from sysibm.s  +
    .   union all .
    abc 6 efg6hij
$/tstCompShell3/ */
    call tstComp1 '@ tstCompShell3',
        , 'call tstOut "T",  "abc" $-¢2*3$! "efg"$-¢2*3$!"hij"',
        , 'ix=3' ,
        , 'call tstOut "T","insert into A540769x.tqt002" ,',
        ,     '"with n(n) as ( select" $-¢ix+1$! "from sysibm.s"',
        , 'call tstOut "T","insert into A540769x.tqt002"  ,  ',
        ,    '"with n(n) as ( select" $-¢ix+1$! "from sysibm.s" , ' ,
        ,    '"    union all "' ,
        , '$$ abc $-¢2*3$! efg$-¢2*3$!hij',
/*
$=/tstCompShell/
    ### start tst tstCompShell ########################################
    compile @, 12 lines:   $$  Lline one, $** asdf
    run without input
    Lline one,
    lline zwei output
    v1 = valueV1 ${v1}= valueV1|
    REXX OUT L5 CONTINUED L6 CONTINUED L7
    L8 ONE
    L9 TWO
    valueV1
    valueV1 valueV2
    out  valueV1 valueV2
    SCHLUSS
$/tstCompShell/ */
    call tstComp1 '@ tstCompShell',
        , '  $$  Lline one, $** asdf',
        , '   $$ lline zwei output',
        , '        $=v1= valueV1 ' ,
        , '$$       v1 = $v1 $"${v1}=" ${  v1  }| ' ,
        , 'call out rexx out l5, ' ,
        , '     continued l6 , ' ,
        , '     continued l7   ' ,
        , 'call out l8 one    ' ,
        , 'call out l9 two$=v2=valueV2  ',
        , '$$- $v1  $$- $v1 $v2   ',
        , 'call out   "out "     $v1 $v2   ',
        , '$$-   schluss    '
/*
$=/tstCompShell2/
    ### start tst tstCompShell2 #######################################
    compile @, 13 lines: $@do j=0 to 1 $@¢ $$ do j=$j
    run without input
    do j=0
    after if 0 $@¢ $!
    after if 0 $=@¢ $!
    do j=1
    if 1 then $@¢ a
    a2
    if 1 then $@=¢ b
    b2
    after if 1 $@¢ $!
    after if 1 $=@¢ $!
    end
$/tstCompShell2/ */
    call tstComp1 '@ tstCompShell2',
        , '$@do j=0 to 1 $@¢ $$ do j=$j' ,
        ,     'if $j then $@¢ ',
        ,          '$$ if $j then $"$@¢" a $$a2' ,
        ,          '$!',
        ,     'if $j then $@=¢ ',
        ,          '$$ if $j then $"$@=¢" b $$b2' ,
        ,          '$!',
        ,     'if $j then $@¢ $!' ,
        ,     '$$ after if $j $"$@¢ $!"' ,
        ,     'if $j then $@=¢ $!' ,
        ,     '$$ after if $j $"$=@¢ $!"' ,
        ,     '$!',
        , '$$ end'
    return
endProcedure tstCompShell

tstCompPrimary: procedure expose m.
    call compIni
/*
$=/tstCompPrimary/
    ### start tst tstCompPrimary ######################################
    compile =, 16 lines: Strings $"$""$""""$""" $'$''$''''$'''
    run without input
    Strings $"$""$" $'$''$'
    rexx $-¢ 3 * 5 $! = 15
    rexx $-¢ 5 * 7 $! = 35
    rexx $-// 7 * 11 $// = 77
    rexx $-/abcEf/ 11 * 13 $/abcEf/ = 143
    data  line three line four  bis hier
    shell line five line six bis hier
    var get   v1 value Eins, v1 value Eins .
    var isDef v1 1, v2 0 .
    #jIn eof 1#
    var read  >1 0 rr undefined
    #jIn eof 2#
    var read  >2 0 rr undefined
    no call abc$-¢4*5$! $-¢efg$-¢6*7$! abc20 EFG42
    brackets $-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$! 1000
    run with 3 inputs
    Strings $"$""$" $'$''$'
    rexx $-¢ 3 * 5 $! = 15
    rexx $-¢ 5 * 7 $! = 35
    rexx $-// 7 * 11 $// = 77
    rexx $-/abcEf/ 11 * 13 $/abcEf/ = 143
    data  line three line four  bis hier
    shell line five line six bis hier
    var get   v1 value Eins, v1 value Eins .
    var isDef v1 1, v2 0 .
    #jIn 1# eins zwei drei
    var read  >1 1 rr eins zwei drei
    #jIn 2# zehn elf zwoelf?
    var read  >2 1 rr zehn elf zwoelf?
    no call abc$-¢4*5$! $-¢efg$-¢6*7$! abc20 EFG42
    brackets $-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$! 1000
$/tstCompPrimary/ */
    call vRemove 'v2'
    call tstComp1 '= tstCompPrimary 3',
        , 'Strings $"$""$""""$"""' "$'$''$''''$'''",
        , 'rexx $"$-¢ 3 * 5 $! =" $-¢ 3 * 5 $!' ,
        , 'rexx $"$-¢ 5 * 7 $! =" $-¢ 5 * 7 $!' ,
        , 'rexx $"$-// 7 * 11 $// =" $-// 7 * 11 $//' ,
        , 'rexx $"$-/abcEf/ 11 * 13 $/abcEf/ ="',
            '$-/abcEf/ 11 * 13 $/abcEf/' ,
        , 'data $-=¢ line three',
        , 'line four $! bis hier'  ,
        , 'shell $-@¢ $$ line five',
        , '$$ line six $! bis hier' ,
        , '$= v1  =   value Eins  $=rr=undefined $= eins = 1 ',
        , 'var get   v1 $v1, v1 ${  v1  } ',
        , 'var isDef v1 ${? v${  eins  }  }, v2 ${?v2 } ',
        , 'var read  >1 ${> rr} rr $rr' ,
        , 'var read  >2 ${> rr} rr $rr',
        , 'no call $"abc$-¢4*5$! $-¢efg$-¢6*7$!"',
            'abc$-¢4*5$! $-¢efg$-¢6*7$!$!',
        , 'brackets $"$-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$!"',
            '$-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$!'
    return
endProcedure tstCompPrimary

tstCompExpr: procedure expose m.
    call compIni
/*
$=/tstCompExprStr/
    ### start tst tstCompExprStr ######################################
    compile -, 3 lines: $=vv=vvStr
    run without input
    vv=vvStr
    o2String($.vv)=vvStr
$/tstCompExprStr/ */
    call tstComp1 '- tstCompExprStr',
        , '$=vv=vvStr' ,
        , '"vv="$vv' ,
        , '$"o2String($.vv)="o2String($.vv)'
/*
$=/tstCompExprObj/
    ### start tst tstCompExprObj ######################################
    compile ., 5 lines: $=vv=vvStr
    run without input
    vv=
    vvStr
    s2o($.vv)=
    vvStr
$/tstCompExprObj/ */
    call tstComp1 '. tstCompExprObj',
        , '$=vv=vvStr' ,
        , '"!vv="', '$vv',
        , '$"s2o($.vv)="', 's2o($-vv)'
/*
$=/tstCompExprDat/
    ### start tst tstCompExprDat ######################################
    compile =, 4 lines: $=vv=vvDat
    run without input
    vv=vvDat
    $.vv= !vvDat
    $.-¢"abc"$!=!abc
$/tstCompExprDat/ */
    call tstComp1 '= tstCompExprDat',
        , '$=vv=vvDat' ,
        , 'vv=$vv',
        , '$"$.vv=" $.vv',
        , '$"$.-¢""abc""$!="$.-¢"abc"$!'

/*
$=/tstCompExprRun/
    ### start tst tstCompExprRun ######################################
    compile @, 3 lines: $=vv=vvRun
    run without input
    vv=vvRun
    o2string($.vv)=vvRun
$/tstCompExprRun/ */
    call tstComp1 '@ tstCompExprRun',
        , '$=vv=vvRun' ,
        , 'call out "vv="$vv',
        , 'call out $"o2string($.vv)="o2string($.vv)'
/*
$=/tstCompExprCon/
    ### start tst tstCompExprCon ######################################
    compile #, 2 lines: $$ in # drinnen
    run without input
    $$ in # drinnen
    call out "vv="$vv
$/tstCompExprCon/
$=/tstCompExprCo2/
    ### start tst tstCompExprCo2 ######################################
    compile #, 3 lines: $$ in # drinnen
    run without input
    $$ in # drinnen
    call out "vv="$vv
    nacgh $#@
$/tstCompExprCo2/
*/
    call tstComp1 '# tstCompExprCon',
        , '$$ in # drinnen' ,
        , 'call out "vv="$vv'

    call tstComp1 '# tstCompExprCo2',
        , '$$ in # drinnen' ,
        , 'call out "vv="$vv',
        , '$#@ $$ nacgh $"$#@"'

    return
endProcedure tstCompExpr

tstCompStmt: procedure expose m.
/*
$=/tstCompStmt1/
    ### start tst tstCompStmt1 ########################################
    compile @, 8 lines: $= v1 = value eins  $= v2  =- 3*5*7 .
    run without input
    data v1 value eins v2 105
    eins
    zwei
    drei
    vier
    fuenf
    elf
    . zwoelf  dreiZ  .
    . vierZ .
    fuenfZ
    lang v1 value eins v2 945
    oRun ouput 1
$/tstCompStmt1/ */
    call pipeIni
    call vPutO 'oRun', oRunner('call out "oRun ouput" (1*1)')
    call vRemove 'v2'
    call tstComp1 '@ tstCompStmt1',
        , '$= v1 = value eins  $= v2  =- 3*5*7 ',
        , '$$ data v1 $v1 v2 ${   v2  }',
        , '$$eins $@¢$$ zwei $$ drei  ',
        , '   $@¢   $!  $@//   $// $@/q r s /   $/q r s /',
             '       $@/eins/ $@¢ $$vier $! $/eins/ $! $$fuenf',
        , '$$elf $@=¢$@=¢ zwoelf  dreiZ  $!  ',
        , '   $@=¢   $! $@=¢ $@=¢ vierZ $! $! $! $$fuenfZ',
        , '$$- "lang v1" $v1 "v2" ${v2}*9',
        , '$@oRun'
/*
$=/tstCompStmt2/
    ### start tst tstCompStmt2 ########################################
    compile @, 1 lines: $@for qq $$ loop qq $qq
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    loop qq eins zwei drei
    #jIn 2# zehn elf zwoelf?
    loop qq zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    loop qq zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
$/tstCompStmt2/ */
    call tstComp1 '@ tstCompStmt2 3',
        , '$@for qq $$ loop qq $qq'

/*
$=/tstCompStmt3/
    ### start tst tstCompStmt3 ########################################
    compile @, 9 lines: $$ 1 begin run 1
    2 ct zwei
    ct 4 mit assign .
    run without input
    1 begin run 1
    3 run 3 ctV = ct 4 assign ctV|
    run 5 procCall $@prCa
    out in proc at 8
    run 6 vor call $@prCa
    out in proc at 8
    9 run end
    run with 3 inputs
    1 begin run 1
    3 run 3 ctV = ct 4 assign ctV|
    run 5 procCall $@prCa
    out in proc at 8
    run 6 vor call $@prCa
    out in proc at 8
    9 run end
$/tstCompStmt3/ */
    call tstComp1 '@ tstCompStmt3 3',
        , '$$ 1 begin run 1',
        , '$@ct $$ 2 ct zwei',
        , '$$ 3 run 3 ctV = $ctV|',
        , '$@ct $@=¢ct 4 mit assign $=ctV = ct 4 assign ctV $!',
        , '$$ run 5 procCall $"$@prCa" $@prCa',
        , '$$ run 6 vor call $"$@prCa"',
        , '$@prCa',
        , '$@proc prCa $$out in proc at 8',
        , '$$ 9 run end'

/*
$=/tstCompStmt4/
    ### start tst tstCompStmt4 ########################################
    compile @, 4 lines: $=eins=vorher
    run without input
    eins vorher
    eins aus named block eins .
$/tstCompStmt4/ */
    call tstComp1 '@ tstCompStmt4 0',
        , '$=eins=vorher' ,
        , '$$ eins $eins' ,
        , '$=/eins/aus named block eins $/eins/' ,
        , '$$ eins $eins'
/*
$=/tstCompStmtDo/
    ### start tst tstCompStmtDo #######################################
    compile @, 2 lines: ti=0$@do y=3 to 4 $@do 2 $@¢
    run without input
    y=3 ti1 z=7
    y=3 ti1 z=8
    y=3 ti2 z=7
    y=3 ti2 z=8
    y=4 ti3 z=7
    y=4 ti3 z=8
    y=4 ti4 z=7
    y=4 ti4 z=8
$/tstCompStmtDo/ */
    call tstComp1 '@ tstCompStmtDo', 'ti=0$@do y=3 to 4 $@do 2 $@¢',
     ,    'ti = ti + 1',
        '$@do $*(sdf$*) z $*(sdf$*) =7 to 8 $$ y=$y ti$-¢ti$! z=$z $!'

/*
$=/tstCompStmtDo2/
    ### start tst tstCompStmtDo2 ######################################
    compile @, 7 lines: $$ $-=/sqlSel/
    run without input
    select 1 abc select 2 abc after table .
$/tstCompStmtDo2/ */
    call tstComp1 '@ tstCompStmtDo2',
         , '$$ $-=/sqlSel/',
         ,     '$=ty = abc ',
         ,     '$@do tx=1 to 2 $@=/table/',
         ,          'select $tx $ty',
         , '$/table/',
         ,     '$=ty = abc',
         ,     'after table',
         '$/sqlSel/'
/*
$=/tstCompStmtWith/
    ### start tst tstCompStmtWith #####################################
    compile @, 3 lines: $@with $.vA $$ fEins=$FEINS fZwei=$FZWEI va&fEi+
    ns=${vA&FEINS}
    run without input
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
    fEins=2Eins fZwei=2Zwei va&fEins=1Eins
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
$/tstCompStmtWith/
*/
    cl = classNew('n? tstStmtWith u f FEINS v, f FZWEI v')
    v1 = onew(cl)
    m.v1.feins = '1Eins'
    m.v1.fzwei = '1Zwei'
    v2 = oNew(cl)
    m.v2.feins ='2Eins'
    m.v2.fzwei ='2Zwei'
    call vPutO 'vA', v1
    call vPutO 'vB', v2
    stmt = '$$ fEins=$FEINS fZwei=$FZWEI va&fEins=${vA&FEINS}'
    call tstComp1 '@ tstCompStmtWith',
         , '$@with $.vA' stmt ,
         , '$@with $vA $@¢' stmt ,
         , '$@with $vB ' stmt stmt '$!'
     return
endProcedure tstCompStmt

tstCompSyntax: procedure expose m.
    call tstCompSynPrimary
    call tstCompSynAss
    call tstCompSynRun
    return
endProcedure tstCompSyntax

tstCompSynPrimary: procedure expose m.

/*
$=/tstCompSynPri1/
    ### start tst tstCompSynPri1 ######################################
    compile @, 1 lines: a $ =
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $ =
    .    e 2: pos 3 in line 1: a $ =
    *** err: no class found for object 0
$/tstCompSynPri1/ */
    call tstComp1 '@ tstCompSynPri1 +', 'a $ ='

/*
$=/tstCompSynPri2/
    ### start tst tstCompSynPri2 ######################################
    compile @, 1 lines: a $. {
    *** err: scanErr primary not finished
    .    e 1: last token  scanPosition  {
    .    e 2: pos 5 in line 1: a $. {
$/tstCompSynPri2/ */
    call tstComp1 '@ tstCompSynPri2 +', 'a $. {'

/*
$=/tstCompSynPri3/
    ### start tst tstCompSynPri3 ######################################
    compile @, 1 lines: b $-  ¢  .
    *** err: scanErr primary not finished
    .    e 1: last token  scanPosition   ¢
    .    e 2: pos 5 in line 1: b $-  ¢
$/tstCompSynPri3/ */
    call tstComp1 '@ tstCompSynPri3 +', 'b $-  ¢  '

/*
$=/tstCompSynPri4/
    ### start tst tstCompSynPri4 ######################################
    compile @, 1 lines: a ${ $*( sdf$*) } =
    *** err: scanErr var name expected
    .    e 1: last token  scanPosition } =
    .    e 2: pos 17 in line 1: a ${ $*( sdf$*) } =
$/tstCompSynPri4/ */
    call tstComp1 '@ tstCompSynPri4 +', 'a ${ $*( sdf$*) } ='

/*
$=/tstCompSynFile/
    ### start tst tstCompSynFile ######################################
    compile @, 1 lines: $@.<$*( co1 $*) $$abc
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@.<$*( co1 $*) $$abc
    .    e 2: pos 1 in line 1: $@.<$*( co1 $*) $$abc
$/tstCompSynFile/ */
    call tstComp1 '@ tstCompSynFile +', '$@.<$*( co1 $*) $$abc'

    return
endProcedure tstCompSynPrimary

tstCompSynAss: procedure expose m.

/*
$=/tstCompSynAss1/
    ### start tst tstCompSynAss1 ######################################
    compile @, 1 lines: $=
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 3 in line 1: $=
$/tstCompSynAss1/ */
    call tstComp1 '@ tstCompSynAss1 +', '$='

/*
$=/tstCompSynAss2/
    ### start tst tstCompSynAss2 ######################################
    compile @, 2 lines: $=   .
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $=
$/tstCompSynAss2/ */
    call tstComp1 '@ tstCompSynAss2 +', '$=   ', 'eins'

/*
$=/tstCompSynAss3/
    ### start tst tstCompSynAss3 ######################################
    compile @, 2 lines: $=   $$
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition $$
    .    e 2: pos 6 in line 1: $=   $$
$/tstCompSynAss3/ */
    call tstComp1 '@ tstCompSynAss3 +', '$=   $$', 'eins'

/*
$=/tstCompSynAss4/
    ### start tst tstCompSynAss4 ######################################
    compile @, 1 lines: $=   eins
    *** err: scanErr = expected in assignment after $= var
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $=   eins
$/tstCompSynAss4/ */
    call tstComp1 '@ tstCompSynAss4 +', '$=   eins'

/*
$=/tstCompSynAss5/
    ### start tst tstCompSynAss5 ######################################
    compile @, 1 lines: $=  abc eins $$ = x
    *** err: scanErr = expected in assignment after $= var
    .    e 1: last token  scanPosition eins $$ = x
    .    e 2: pos 9 in line 1: $=  abc eins $$ = x
$/tstCompSynAss5/ */
    call tstComp1 '@ tstCompSynAss5 +', '$=  abc eins $$ = x'

/*
$=/tstCompSynAss6/
    ### start tst tstCompSynAss6 ######################################
    compile @, 1 lines: $=  abc =
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $=  abc =
$/tstCompSynAss6/ */
    call tstComp1 '@ tstCompSynAss6 +', '$=  abc ='

/*
$=/tstCompSynAss7/
    ### start tst tstCompSynAss7 ######################################
    compile @, 1 lines: $=  abc =..
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition ..
    .    e 2: pos 10 in line 1: $=  abc =..
$/tstCompSynAss7/ */
    call tstComp1 '@ tstCompSynAss7 +', '$=  abc =.'
    return
endProcedure tstCompSynAss

tstCompSynRun: procedure expose m.

/*
$=/tstCompSynRun1/
    ### start tst tstCompSynRun1 ######################################
    compile @, 1 lines: $@
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@
    .    e 2: pos 1 in line 1: $@
$/tstCompSynRun1/ */
    call tstComp1 '@ tstCompSynRun1 +', '$@'

/*
$=/tstCompSynRun2/
    ### start tst tstCompSynRun2 ######################################
    compile @, 1 lines: $@=
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@=
    .    e 2: pos 1 in line 1: $@=
$/tstCompSynRun2/ */
    call tstComp1 '@ tstCompSynRun2 +', '$@='

/*
$=/tstCompSynRun3/
    ### start tst tstCompSynRun3 ######################################
    compile @, 1 lines: $@: und
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@: und
    .    e 2: pos 1 in line 1: $@: und
    *** err: no class found for object 0
$/tstCompSynRun3/ */
    call tstComp1 '@ tstCompSynRun3 +', '$@: und'

/*
$=/tstCompSynFor4/
    ### start tst tstCompSynFor4 ######################################
    compile @, 1 lines: $@for
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $@for
$/tstCompSynFor4/ */
    call tstComp1 '@ tstCompSynFor4 +', '$@for'

/*
$=/tstCompSynFor5/
    ### start tst tstCompSynFor5 ######################################
    compile @, 2 lines: $@for
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $@for
$/tstCompSynFor5/ */
    call tstComp1 '@ tstCompSynFor5 +', '$@for', a

/*
$=/tstCompSynFor6/
    ### start tst tstCompSynFor6 ######################################
    compile @, 2 lines: a
    *** err: scanErr variable or named block after for
    .    e 1: last token  scanPosition .
    .    e 2: pos 15 in line 2:  b $@for   $$q
$/tstCompSynFor6/ */
    call tstComp1 '@ tstCompSynFor6 +', 'a', ' b $@for   $$q'

/*
$=/tstCompSynFor7/
    ### start tst tstCompSynFor7 ######################################
    compile @, 3 lines: a
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 11 in line 2:  b $@for a
$/tstCompSynFor7/ */
    call tstComp1 '@ tstCompSynFor7 +', 'a', ' b $@for a', '  $$q'

/*
$=/tstCompSynCt8/
    ### start tst tstCompSynCt8 #######################################
    compile @, 3 lines: a
    *** err: scanErr ct statement expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 8 in line 2:  b $@ct
$/tstCompSynCt8/ */
    call tstComp1 '@ tstCompSynCt8 +', 'a', ' b $@ct', '  $$q'

/*
$=/tstCompSynProc9/
    ### start tst tstCompSynProc9 #####################################
    compile @, 2 lines: a
    *** err: scanErr var or namedBlock expected after proc
    .    e 1: last token  scanPosition .
    .    e 2: pos 15 in line 2:  b $@proc  $$q
$/tstCompSynProc9/ */
    call tstComp1 '@ tstCompSynProc9 +', 'a', ' b $@proc  $$q'

/*
$=/tstCompSynProcA/
    ### start tst tstCompSynProcA #####################################
    compile @, 2 lines: $@proc p1
    *** err: scanErr proc statement expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $@proc p1
$/tstCompSynProcA/ */
    call tstComp1 '@ tstCompSynProcA +', '$@proc p1', '  $$q'

/*
$=/tstCompSynCallB/
    ### start tst tstCompSynCallB #####################################
    compile @, 1 lines: $@% ¢roc p1$!
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@% ¢roc p1$!
    .    e 2: pos 1 in line 1: $@% ¢roc p1$!
$/tstCompSynCallB/ */

    call tstComp1 '@ tstCompSynCallB +', '$@% ¢roc p1$!'

/*
$=/tstCompSynCallC/
    ### start tst tstCompSynCallC #####################################
    compile @, 1 lines: $@%¢call roc p1 !
    *** err: scanErr ending $! expected after ¢
    .    e 1: last token  scanPosition .
    .    e 2: pos 18 in line 1: $@%¢call roc p1 !
$/tstCompSynCallC/ */
    call tstComp1 '@ tstCompSynCallC +', '$@%¢call roc p1 !'

/*
$=/tstCompSynCallD/
    ### start tst tstCompSynCallD #####################################
    compile @, 2 lines: $@^¢call( $** roc
    *** err: scanErr ending $! expected after ¢
    .    e 1: last token  scanPosition .
    .    e 2: pos 18 in line 1: $@^¢call( $** roc
$/tstCompSynCallD/ */
    call tstComp1 '@ tstCompSynCallD +',
        ,'$@^¢call( $** roc' , ' $*( p1 $*) )'
    return
endProcedure tstCompSynRun

tstCompObj: procedure expose m.
    call tstReset t
    call oIni
    cl = classNew('n? tstCompCla u v, f FEINS v, f FZWEI v')
    do rx=1 to 10
        o = oNew(cl)
        m.tstComp.rx = o
        m.o = 'o'rx
        if rx // 2 = 0 then do
            m.o.fEins = 'o'rx'.1'
            m.o.fZwei = 'o'rx'.fZwei'rx
            end
        else do
            m.o.fEins = 'o'rx'.fEins'
            m.o.fZwei = 'o'rx'.2'
            end
        call mAdd 'T.TRANS', m.tstComp.rx '<o'rx'>'
        end

/*
$=/tstCompObjRef/
    ### start tst tstCompObjRef #######################################
    compile @, 13 lines: o1=m.tstComp.1
    run without input
    out .$"string" o1
    string
    out . o1
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .{ o2 }
    tstR: @<o2> isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    out .¢ o3 $!
    tstR: @<o3> isA :tstCompCla = o3
    tstR:  .FEINS = o3.fEins
    tstR:  .FZWEI = o3.2
    out .¢ o4 $!
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    out ./-/ o5 $/-/
    tstR: @<o5> isA :tstCompCla = o5
    tstR:  .FEINS = o5.fEins
    tstR:  .FZWEI = o5.2
$/tstCompObjRef/ */
m.ttt=1
    call tstComp1 '@ tstCompObjRef' ,
        , 'o1=m.tstComp.1',
        , 'o2 = m.tstComp.2' ,
        , '$$ out $".$""string""" o1 $$.$"string"',
        , '$$ out . o1 $$. o1',
        , '$$ out .{ o2 } $$.¢ o2 $!',
        , '$$ out .¢ o3 $"$!" $$.¢ ', ' m.tstComp.3 ', ' $!',
        , '$$ out .¢ o4 $"$!" $$.¢ ', ' m.tstComp.4 ', ' $!',
        , '$$ out ./-/ o5 $"$/-/" $$./-/  m.tstComp.5 ', ' $/-/'

/*
$=/tstCompObjRefPri/
    ### start tst tstCompObjRefPri ####################################
    compile @, 9 lines: $$ out .$"$.{o1}" $$.¢ m.tstComp.1 $!
    run without input
    out .$.{o1}
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .$.-{o2}
    <o2>
    out .$.={o3}
    . m.tstComp.3 .
    out .$.@{out o4}
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    tstWriteO kindOf ORun oRun end   >>>
    out .$.@¢$$abc $$efg$!
    tstWriteO kindOf ORun oRun begin <<<
    abc
    efg
    tstWriteO kindOf ORun oRun end   >>>
    out .$.@¢o5$!
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o5> isA :tstCompCla = o5
    tstR:  .FEINS = o5.fEins
    tstR:  .FZWEI = o5.2
    abc
    tstWriteO kindOf ORun oRun end   >>>
$/tstCompObjRefPri/ */
    call tstComp1 '@ tstCompObjRefPri' ,
        , '$$ out .$"$.{o1}" $$.¢ m.tstComp.1 $!',
        , '$$ out .$"$.-{o2}" $$.-¢ m.tstComp.2 $!',
        , '$$ out .$"$.={o3}" $$.=¢ m.tstComp.3 $!',
        , '$$ out .$"$.@{out o4}" $$.@@¢ call outO m.tstComp.4 $!',
    , '$$ out .$"$.@¢$$abc $$efg$!" $$.$.@@¢ $$abc ', ' ', ' $$efg $!',
        , '$$ out .$"$.@¢o5$!" $$.$.@@¢ $$.m.tstComp.5', '$$abc $!'

/*
$=/tstCompObjRefFile/
    ### start tst tstCompObjRefFile ###################################
    compile @, 7 lines: $$ out .$".<.¢o1!" $$.<.¢ m.tstComp.1 $!
    run without input
    out ..<.¢o1!
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .<$.-{o2}
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o2> isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$.<{o3}
    tstWriteO kindOf JRW jWriteNow begin <<<
    . m.tstComp.3 .
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$.<@{out o4}
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$<@¢$$abc $$efg$!
    tstWriteO kindOf JRW jWriteNow begin <<<
    abc
    efg
    tstWriteO kindOf JRW jWriteNow end   >>>
$/tstCompObjRefFile/ */

    call tstComp1 '@ tstCompObjRefFile' ,
        , '$$ out .$".<.¢o1!" $$.<.¢ m.tstComp.1 $!',
        , '$$ out .$"<$.-{o2}" $$<.¢ m.tstComp.2 $!',
        , '$$ out .$"$.<{o3}" $$<=¢ m.tstComp.3 $!',
        , '$$ out .$"$.<@{out o4}" $$<@¢ call outO m.tstComp.4 $!',
    , '$$ out .$"$<@¢$$abc $$efg$!" $$<@¢ $$abc ', ' ', ' $$efg $!'

/*
$=/tstCompObjFor/
    ### start tst tstCompObjFor #######################################
    compile @, 2 lines: $@do rx=1 to 3 $$. m.tstComp.rx
    run without input
    FEINS=o1.fEins FZWEI=o1.2
    FEINS=o2.1 FZWEI=o2.fZwei2
    FEINS=o3.fEins FZWEI=o3.2
$/tstCompObjFor/
*/
    call tstComp1 '@ tstCompObjFor' ,
        , '$@do rx=1 to 3 $$. m.tstComp.rx' ,
        , '$| $@forWith witx $$ FEINS=$FEINS FZWEI=$FZWEI'

/*
$=/tstCompObjRun/
    ### start tst tstCompObjRun #######################################
    compile @, 4 lines: $$ out .$"$@¢o1!" $$@¢ $$. m.tstComp.1 $!
    run without input
    out .$@¢o1!
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstWriteO kindOf ORun oRun end   >>>
    out .$<@¢$$abc $$efg$!
    tstWriteO kindOf JRW jWriteNow begin <<<
    abc
    efg
    tstWriteO kindOf JRW jWriteNow end   >>>
$/tstCompObjRun/ */
    call tstComp1 '@ tstCompObjRun' ,
        , '$$ out .$"$@¢o1!" $$@¢ $$. m.tstComp.1 $!',
    , '$$ out .$"$<@¢$$abc $$efg$!" $$<@¢ $$abc ', ' ', ' $$efg $!'

    m.t.trans.0 = 0
/*
$=/tstCompObj/
    ### start tst tstCompObj ##########################################
    compile @, 6 lines: o1=m.tstComp.1
    run without input
    out . o1
    tstR: @tstWriteoV1 isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .{ o2 }
    tstR: @tstWriteoV2 isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    out .¢ o1, o2!
    tstR: @tstWriteoV1 isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstR: @tstWriteoV2 isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
$/tstCompObj/ */
    call tstComp1 '@ tstCompObj' ,
        , 'o1=m.tstComp.1',
        , 'o2 = m.tstComp.2' ,
        , '$$ out . o1 $$. o1',
        , '$$ out .{ o2 } $$.¢ o2 $!',
    , '$$ out .¢ o1, o2!$; $@<.¢  m.tstComp.1  ', '  m.tstComp.2  $!'
    return
    m.t.trans.0 = 0
endProcedure tstCompObj

tstCompORun: procedure expose  m.
/*
$=/tstCompORun/
    ### start tst tstCompORun #########################################
    compile @, 6 lines: $@oRun
    run without input
    oRun arg=1, v2=, v3=, v4=
    oRun arg=1, v2=, v3=, v4=
    oRun arg=2, v2=-{1 arg only} oder?, v3=, v4=
    oRun arg=2, v2=!.{1 obj only} oder?, v3=, v4=
    oRun arg=3, v2={2 args}, v3=und zwei?, v4=
    oRun arg=4, v2={3 args}, v3=zwei, v4=und drei?
$/tstCompORun/ */
    call compIni
    call vPutO 'oRun', oRunner('parse arg , v2, v3, v4;',
        'call tstOut t, "oRun arg="arg()", v2="v2", v3="v3", v4="v4' )
    call tstComp1 '@ tstCompORun',
        , '$@oRun', '$@%¢oRun$!' ,
        , '    $@%¢oRun $"-{1 arg only}" oder?$!' ,
        , '    $@%-¢oRun $.".{1 obj only}" ''oder?''$! $=v2=zwei' ,
        , '    $@%-¢oRun $"{2 args}", "und" $v2"?"$!' ,
        , '    $@%-¢oRun $"{3 args}", $v2, "und drei?"$!'
    return
endProcedure tstCompORun

tstCompDataIO: procedure expose m.
/*
$=/tstCompDataHereData/
    ### start tst tstCompDataHereData #################################
    compile =, 13 lines:  herdata $@#/stop/    .
    run without input
    . herdata .
    heredata 1 $x
    heredata 2 $y
    nach heredata
    . herdata ¢ .
    heredata 1 xValue
    heredata 2 yValueY
    nach heredata ¢
    . herdata { .
    HEREDATA 1 xValue
    heredata 2 yValueY
    nach heredata {
$/tstCompDataHereData/ */
    call tstComp1 '= tstCompDataHereData',
        , ' herdata $@#/stop/    ',
        , 'heredata 1 $x',
        , 'heredata 2 $y',
        , '$/stop/ $$ nach heredata',
        , ' herdata ¢ $@=/stop/    ',
        , '$=x=xValue$=y=yValueY',
        , 'heredata 1 $x',
        , 'heredata 2 $y',
        , '$/stop/ $$ nach heredata ¢',
        , ' herdata { $@/st/',
        , '; call out heredata 1 $x',
        , '$$heredata 2 $y',
        , '$/st/ $$ nach heredata {'
/*
$=/tstCompDataIO/
    ### start tst tstCompDataIO #######################################
    compile =, 5 lines:  input 1 $@.<-=¢$dsn $*+.
    run without input
    . input 1 .
    readInp line 1                       .
    readInp line 2                       .
    . nach dsn input und nochmals mit & .
    readInp line 1                       .
    readInp line 2                       .
    . und schluiss..
$/tstCompDataIO/ */
    dsn = word(tstPdsMbr(tstFilename('lib37', 'r'), 'readInp'), 1)
    dsnFB = strip(dsn tstFB('::F37', 0))
    b = jBuf(tstFB('readInp line 1', 37),
            ,tstFB('readInp line 2', 37))
    extFd = tstFB('&dsn('dsn') dd(xyz)', 0)
    extFd = tstFB(dsn 'dd(xyz) ::f', 0)
    if extFd = '' then
        extFd = dsn
    call jCat file(dsnFB), b
    call vPut 'dsn', dsn
    say 'dsn' dsn 'extFD' extFD'?'
    call tstComp1 '= tstCompDataIO',
        , ' input 1 $@.<-=¢$dsn $*+',
        , tstFB('::f', 0) '$!',
        , ' nach dsn input und nochmals mit & ' ,
        , '         $@.<"'extFD'"',
        , ' und schluiss.'
    return
endProcedure tstCompDataIO

tstObjVF: procedure expose m.
parse arg v, f
    obj  = oNew(classNew('n? TstClassVF u v, f FLD1 v'))
    m.obj = if(f=='','val='v, v)
    m.obj.fld1 = if(f=='','FLD1='v, f)
    return obj
endProcedure tstObjVF

tstCompFile: procedure expose m.
/*
$=/tstCompFileBloSrc/
    $=vv=value-of-vv
    ###file from empty # block
    $@<#¢
        $!
    ###file from 1 line # block
    $@<#¢
    the only $ix+1/0 line $vv
    $!
    ###file from 2 line # block
    $@<#¢
        first line /0 $*+ no comment
        second and last line $$ $wie
    $!
    ===file from empty = block
    $@<=¢     $*+ comment
        $!
    ===file from 1 line = block
    $@<=¢ the only line $!
    ===file from 2 line = block
    $@<=¢ first line$** comment
        second and last line  $!
    ---file from empty - block
    $@<-/s/
        $/s/
    ---file from 1 line - block
    $@<-/s/ the only "line" (1*1) $/s/
    ---file from 2 line = block
    $@<-// first "line" (1+0)
        second   and   "last  line" (1+1)  $//
    ...file from empty . block
    $@<.¢
        $!
    ...file from 1 line . block
    $@<.¢ tstObjVF('v-Eins', '1-Eins') $!
    ...file from 2 line . block
    $@<.¢ tstObjVF('v-Elf', '1-Elf')
        tstObjVF('zwoelf')  $!
    ...file from 3 line . block
    $@<.¢ tstObjVF('einUndDreissig')
            s2o('zweiUndDreissig' o2String($vv))
            tstObjVF('dreiUndDreissig')  $!
    @@@file from empty @ block
    $@<@¢
        $!
    $=noOutput=before
    @@@file from nooutput @ block
    $@<@¢ nop
        $=noOutput = run in block $!
    @@@nach noOutput=$noOutput
    @@@file from 1 line @ block
    $@<@¢ $$. tstObjVF('w-Eins', 'w1-Eins') $!
    @@@file from 2 line @ block
    $@<@¢ $$.tstObjVF('w-Elf', 'w1-Elf')
        y='zwoelf' $$-y  $!
    @@@file from 3 line @ block
    $@<@¢ $$.tstObjVF('w einUndDreissig')    $$ +
    zweiUndDreissig $$ 33 $vv$!
    {{{ empty ¢ block
    $@<¢     $!
    {{{ empty ¢ block with comment
    $@<¢    $*+ abc
          $!
    {{{ one line ¢ block
    $@<¢ the only $"¢...$!" line $*+.
        $vv $!
    {{{ one line -¢ block
    $@<-¢ the only $"-¢...$!"  "line" $vv  $!
    {{{ empty #¢ block
    $@<#¢
$!
    {{{ one line #¢ block
    $@<#¢ the only $"-¢...$!"  "line" $vv $¢vv${x}$!
$!
$/tstCompFileBloSrc/ */
/*
$=/tstCompFileBlo/
    ### start tst tstCompFileBlo ######################################
    compile =, 72 lines: $=vv=value-of-vv
    run without input
    ###file from empty # block
    ###file from 1 line # block
    the only $ix+1/0 line $vv
    ###file from 2 line # block
    first line /0 $*+ no comment
    second and last line $$ $wie
    ===file from empty = block
    ===file from 1 line = block
    . the only line .
    ===file from 2 line = block
    . first line
    second and last line  .
    ---file from empty - block
    ---file from 1 line - block
    THE ONLY line 1
    ---file from 2 line = block
    FIRST line 1
    SECOND AND last  line 2
    ...file from empty . block
    ...file from 1 line . block
    tstR: @tstWriteoV1 isA :TstClassVF = v-Eins
    tstR:  .FLD1 = 1-Eins
    ...file from 2 line . block
    tstR: @tstWriteoV2 isA :TstClassVF = v-Elf
    tstR:  .FLD1 = 1-Elf
    tstR: @tstWriteoV3 isA :TstClassVF = val=zwoelf
    tstR:  .FLD1 = FLD1=zwoelf
    ...file from 3 line . block
    tstR: @tstWriteoV4 isA :TstClassVF = val=einUndDreissig
    tstR:  .FLD1 = FLD1=einUndDreissig
    zweiUndDreissig value-of-vv
    tstR: @tstWriteoV5 isA :TstClassVF = val=dreiUndDreissig
    tstR:  .FLD1 = FLD1=dreiUndDreissig
    @@@file from empty @ block
    @@@file from nooutput @ block
    @@@nach noOutput=run in block
    @@@file from 1 line @ block
    tstR: @tstWriteoV6 isA :TstClassVF = w-Eins
    tstR:  .FLD1 = w1-Eins
    @@@file from 2 line @ block
    tstR: @tstWriteoV7 isA :TstClassVF = w-Elf
    tstR:  .FLD1 = w1-Elf
    zwoelf
    @@@file from 3 line @ block
    tstR: @tstWriteoV8 isA :TstClassVF = val=w einUndDreissig
    tstR:  .FLD1 = FLD1=w einUndDreissig
    zweiUndDreissig
    33 value-of-vv
    {{{ empty ¢ block
    {{{ empty ¢ block with comment
    {{{ one line ¢ block
    . the only ¢...$! line value-of-vv .
    {{{ one line -¢ block
    THE ONLY -¢...$! line value-of-vv
    {{{ empty #¢ block
    {{{ one line #¢ block
    . the only $"-¢...$!"  "line" $vv $¢vv${x}$!
$/tstCompFileBlo/ */
    call tstComp2 'tstCompFileBlo', '='
    m.t.trans.0 = 0

/*
$=/tstCompFileObjSrc/
    $=vv=value-vv-1
    $=fE=<¢ $!
    $=f2=.$.<.¢s2o("f2 line 1" o2String($vv))
         tstObjVF("f2 line2") $!
    ---empty file $"$@<$fE"
    $@fE
    ---file with 2 lines $"$@<$f2"
    $@.<.f2
    $=vv=value-vv-2
    ---file with 2 lines $"$@<$f2"
    $@.<.f2
    $= dsn =- word(tstPdsMbr(tstFilename('libvb', 'r'), 'fileObj'),1) +
                tstFB('::V', 0)
    $@¢
        fi=jOpen(file($dsn),'>')
        call jWrite fi, 'line one on' $"$dsn"
        call jWrite fi, 'line two on' $"$dsn"
        call jClose fi
    $!
    ---file on disk out
    $@.<.dsn
$/tstCompFileObjSrc/ */
/*
$=/tstCompFileObj/
    ### start tst tstCompFileObj ######################################
    compile =, 20 lines: $=vv=value-vv-1
    run without input
    ---empty file $@<$fE
    ---file with 2 lines $@<$f2
    f2 line 1 value-vv-1
    tstR: @tstWriteoV1 isA :TstClassVF = val=f2 line2
    tstR:  .FLD1 = FLD1=f2 line2
    ---file with 2 lines $@<$f2
    f2 line 1 value-vv-1
    tstR: @tstWriteoV1 isA :TstClassVF = val=f2 line2
    tstR:  .FLD1 = FLD1=f2 line2
    ---file on disk out
    line one on $dsn
    line two on $dsn
$/tstCompFileObj/ */
    call tstComp2 'tstCompFileObj', '='

    return
endProcedure tstCompFile

tstCompPipe: procedure expose m.
/*
$=/tstCompPipe1/
    ### start tst tstCompPipe1 ########################################
    compile @, 1 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    (1 eins zwei drei 1)
    #jIn 2# zehn elf zwoelf?
    (1 zehn elf zwoelf? 1)
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    (1 zwanzig 21 22 23 24 ... 29| 1)
    #jIn eof 4#
$/tstCompPipe1/ */
    call tstComp1 '@ tstCompPipe1 3',
        , ' call pipePreSuf "(1 ", " 1)"'
/*
$=/tstCompPipe2/
    ### start tst tstCompPipe2 ########################################
    compile @, 2 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    ¢2 (1 eins zwei drei 1) 2!
    ¢2 (1 zehn elf zwoelf? 1) 2!
    ¢2 (1 zwanzig 21 22 23 24 ... 29| 1) 2!
$/tstCompPipe2/ */
    call tstComp1 '@ tstCompPipe2 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| call pipePreSuf "¢2 ", " 2!"'

/*
$=/tstCompPipe3/
    ### start tst tstCompPipe3 ########################################
    compile @, 3 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <3 ¢2 (1 eins zwei drei 1) 2! 3>
    <3 ¢2 (1 zehn elf zwoelf? 1) 2! 3>
    <3 ¢2 (1 zwanzig 21 22 23 24 ... 29| 1) 2! 3>
$/tstCompPipe3/ */
    call tstComp1 '@ tstCompPipe3 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| call pipePreSuf "¢2 ", " 2!"',
        , ' $| call pipePreSuf "<3 ", " 3>"'

/*
$=/tstCompPipe4/
    ### start tst tstCompPipe4 ########################################
    compile @, 7 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <3 ¢222 ¢221 ¢21 ¢20 (1 eins zwei drei 1) 20! 21! 221! 222! 3>
    <3 ¢222 ¢221 ¢21 ¢20 (1 zehn elf zwoelf? 1) 20! 21! 221! 222! 3>
    <3 ¢222 ¢221 ¢21 ¢20 (1 zwanzig 21 22 23 24 ... 29| 1) 20! 21! 221!+
    . 222! 3>
$/tstCompPipe4/ */
    call tstComp1 '@ tstCompPipe4 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| $@¢    call pipePreSuf "¢20 ", " 20!"',
        ,        ' $| call pipePreSuf "¢21 ", " 21!"',
        ,        ' $| $@¢      call pipePreSuf "¢221 ", " 221!"',
        ,                 ' $| call pipePreSuf "¢222 ", " 222!"',
        ,     '$!     $! ',
        , ' $| call pipePreSuf "<3 ", " 3>"'
    return
endProcedure tstCompPipe


tstCompPip2: procedure expose m.
/*
$=/tstCompPip21/
    ### start tst tstCompPip21 ########################################
    compile @, 3 lines:  $<¢ zeile eins .
    run without input
    (1  zeile eins  1)
    (1    zeile zwei  1)
    run with 3 inputs
    (1  zeile eins  1)
    (1    zeile zwei  1)
$/tstCompPip21/ */
    call tstComp1 '@ tstCompPip21 3',
        , ' $<¢ zeile eins ' ,
        , '   zeile zwei $!' ,
        , ' call pipePreSuf "(1 ", " 1)"'
/*
$=/tstCompPip22/
    ### start tst tstCompPip22 ########################################
    compile @, 3 lines: if ${>i1} then $@¢
    run without input
    #jIn eof 1#
    nachher
    run with 3 inputs
    #jIn 1# eins zwei drei
    <zeile 1: eins zwei drei>
    <zwei>
    nachher
$/tstCompPip22/ */
    call tstComp1 '@ tstCompPip22 3',
        , 'if ${>i1} then $@¢'          ,
        , ' $$ zeile 1: $i1 $$ zwei $| call pipePreSuf "<",">" $!',
        , ' $$ nachher '
    return
endProcedure tstCompPip2

tstCompRedir: procedure expose m.
/*
$=/tstCompRedir/
    ### start tst tstCompRedir ########################################
    compile @, 6 lines:  $=eins=<@¢ $@for vv $$ <$vv> $! .
    run without input
    #jIn eof 1#
    output eins .
    output piped zwei .
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    output eins <eins zwei drei> <zehn elf zwoelf?> <zwanzig 21 22 23 2+
    4 ... 29|>
    output piped zwei ab<eins zwei drei>yz ab<zehn elf zwoelf?>yz ab<zw+
    anzig 21 22 23 24 ... 29|>yz
$/tstCompRedir/ */
    call pipeIni
    call vRemove 'eins'  /* alte Variable loswerden */
    dsn = word(tstPdsMbr(tstFilename('libvb', 'r'), 'redir1'), 1)
    call vPut 'dsn', dsn
    call tstComp1 '@ tstCompRedir 3' ,
        , ' $=eins=<@¢ $@for vv $$ <$vv> $! ',
        , ' $$ output eins $-=¢$eins$!$; ',
        , ' $@for ww $$b${ww}y ',
        , '     $>$-¢ $dsn $! 'tstFB('::v', 0),
        ,         '$| call pipePreSuf "a", "z" $<.$eins',
        , ' $; $$ output piped zwei $-=¢$@<dsn$! '
    return
endProcedure tstCompRedir

tstCompComp: procedure expose m.
/*
$=/tstCompCompShell/
    ### start tst tstCompCompShell ####################################
    compile @, 5 lines: $$compiling shell $; $= rrr =.$.^compile $<@#/+
    aaa/
    run without input
    compiling shell
    running einmal
    RUN 1 COMPILED einmal
    #jIn eof 1#
    running zweimal
    RUN 1 COMPILED zweimal
    #jIn eof 2#
    run with 3 inputs
    compiling shell
    running einmal
    RUN 1 COMPILED einmal
    #jIn 1# eins zwei drei
    compRun eins zwei dreieinmal
    #jIn 2# zehn elf zwoelf?
    compRun zehn elf zwoelf?einmal
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    compRun zwanzig 21 22 23 24 ... 29|einmal
    #jIn eof 4#
    running zweimal
    RUN 1 COMPILED zweimal
    #jIn eof 5#
$/tstCompCompShell/ */
    call tstComp1 '@ tstCompCompShell 3',
        ,  "$$compiling shell $; $= rrr =.$.^compile $<@#/aaa/",
        ,  "call out run 1*1*1 compiled $cc; $@for v $$ compRun $v$cc",
        ,  "$/aaa/ $;",
        ,  "$=cc=einmal $$ running $cc $@rrr",
        ,  "$=cc=zweimal $$ running $cc $@rrr"
/*
$=/tstCompCompData/
    ### start tst tstCompCompData #####################################
    compile @, 5 lines: $$compiling data $; $= rrr =. $.^¢compile =$!  +
    $<@#/aaa/
    run without input
    compiling data
    running einmal
    call out run 1*1*1 compiled einmal
    running zweimal
    call out run 1*1*1 compiled zweimal
    run with 3 inputs
    compiling data
    running einmal
    call out run 1*1*1 compiled einmal
    running zweimal
    call out run 1*1*1 compiled zweimal
$/tstCompCompData/ */
    call tstComp1 '@ tstCompCompData 3',
        ,  "$$compiling data $; $= rrr =. $.^¢compile =$!  $<@#/aaa/",
        ,  "call out run 1*1*1 compiled $cc",
        ,  "$/aaa/ $;",
        ,  "$=cc=einmal $$ running $cc $@rrr",
        ,  "$=cc=zweimal $$ running $cc $@rrr"
    return
endProcedure tstCompComp

tstCompDir: procedure expose m.
/*
$=/tstCompDirSrc/
  'in src v1='$v1
  $#@ call out 'src @ out v1='$v1
  $#. $*(komm$*) s2o('src . v1=')
       $v1
  $#-
    'src - v1='$v1
  $#=
    src = v1=$v1
$/tstCompDirSrc/

$=/tstCompDir/
    ### start tst tstCompDir ##########################################
    compile @call out 'before v1='$v1 $=v1=eins $#. s2o('. v1='$-v1) $#+
    @ call out '@ v1='$v1 $#= = v1=$v1 $#- '- v1='$v1 $#-, 8 lines: 'in+
    . src v1='$v1
    run without input
    before v1=v1Before
    .. v1=eins
    @ v1=eins
    = v1=eins .
    - v1=eins
    in src v1=eins
    src @ out v1=eins
    src . v1=
    eins
    src - v1=eins
    src = v1=eins
$/tstCompDir/ */
    call vPut 'v1', 'v1Before'
    call tstComp2 'tstCompDir', "@call out 'before v1='$v1 $=v1=eins" ,
        "$#. s2o('. v1='$-v1) $#@ call out '@ v1='$v1" ,
        "$#= = v1=$v1 $#- '- v1='$v1 $#-"
/*
$=/tstCompDirPiSrc/
  zeile 1 v1=$v1
  zweite Zeile vor $"$@$#-"
  $#@  $@proc pi2 $@-¢
  $'zeile drei nach $@$#- v1='v1
  vierte und letzte Zeile $!
$/tstCompDirPiSrc/ */
/*
$=/tstCompDirPi/
    ### start tst tstCompDirPi ########################################
    compile @call pipePreSuf '<','>' $=v1=eiPi $<.$pi2 $#=, 5 lines: ze+
    ile 1 v1=$v1
    run without input
    <zeile drei nach $@$#- v1=V1>
    <VIERTE UND LETZTE ZEILE>
    zeile 1 v1=eiPi
    zweite Zeile vor $@$#-
$/tstCompDirPi/ */
    call tstComp2 'tstCompDirPi',
            , "@call pipePreSuf '<','>' $=v1=eiPi $<.$pi2 $#="
    return
endProcedure tstCompDir

tstCompColon: procedure expose m.
/*
$=/tstCompColon1/
    ### start tst tstCompColon1 #######################################
    compile :, 12 lines: vA = valueVonA
    run without input
    vA = valueVonA
    vA=valueVonA vB=valueVonB vC=valueVonC
    vC=valueVonC vD=valueVonD vE=valueVonvE
    vF=6
$/tstCompColon1/ */
    call tstComp1 ': tstCompColon1',
        , 'vA = valueVonA' ,
        , ' $$ vA = $vA' ,
        , '        * kommentar ' ,
        , '=vB=- "valueVonB"' ,
        , '=/vC/valueVonC$/vC/' ,
        , ' $$ vA=$vA vB=$vB vC=$vC' ,
        , '=/vD/valueVonD',
        , '$/vD/ vE=valueVonvE' ,
        , '        * kommentar ' ,
        , ' $$ vC=$vC vD=$vD vE=$vE',
        , 'vF=- 2*3 $=vG=@@¢ $$ vF=$vF$!' ,
        , '@vG'

/*
$=/tstCompColon2/
    ### start tst tstCompColon2 #######################################
    compile :, 7 lines: ix=0
    run without input
    #jIn eof 1#
    proc p1 arg(2) total 0 im argumentchen
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <<for 1 -> eins zwei drei>>
    <<for 2 -> zehn elf zwoelf?>>
    <<for 3 -> zwanzig 21 22 23 24 ... 29|>>
    proc p1 arg(2) total 3 im argumentchen
$/tstCompColon2/

*/
    call tstComp1 ': tstCompColon2 3',
        , 'ix=0' ,
        , 'for v @:¢ix=- $ix+1' ,
        , ' $$ for $ix -> $v' ,
        , '! | @¢call pipePreSuf "<<",">>"',
        , '$! @%¢p1 total $ix im argumentchen$!',
        , 'proc @:/p1/$$- "proc p1 arg(2)" arg(2)' ,
        , '/p1/'
/*
$=/tstCompColon3/
    ### start tst tstCompColon3 #######################################
    compile :, 11 lines: tc3Eins=freeVar1
    run without input
    tc3Eins=freeVar1 o2&tc3Eins= o2&tc3Zwei=
    tc3Eins=freeVar1 o2&tc3Eins=with3Eins o2&tc3Zwei=with3Zwei
    tc3Eins=freeVar1 o2&tc3Eins=ass4Eins o2&tc3Zwei=with5Zwei
    o3&tc3Eins=ass4Eins o3&tc3Zwei=with5 o3 Zwei
    tc3Eins=freeVar1 o2&tc3Eins=ass4Eins o2&tc3Zwei=with5Zwei
$/tstCompColon3/
*/
    call classNew 'n? TstCompColon3 u f tc3Eins v, f tc3Zwei v'
    showO2 = 'tc3Eins=$tc3Eins' ,
            'o2&tc3Eins=${o2&tc3Eins} o2&tc3Zwei=${o2&tc3Zwei}'
    showO3 = 'o3&tc3Eins=${o3&tc3Eins} o3&tc3Zwei=${o3&tc3Zwei}'
    call tstComp1 ': tstCompColon3',
        , 'tc3Eins=freeVar1' ,
     , 'o2 =. oNew("TstCompColon3")' ,
        , '$$' showO2 ,
        , 'with $o2 $@:¢tc3Eins = with3Eins',
        ,     'tc3Zwei = with3Zwei',
        ,    '! $$' showO2 ,
        , '{o2&tc3Eins} = ass4Eins',
        , 'with $o2 $=tc3Zwei = with5Zwei',
        , '$$' showO2 ,
        , 'with o3 =. oCopy($o2) $=tc3Zwei = with5 o3 Zwei',
        , '$$' showO3 '$$' showO2
    return
endProcedure tstCompColon

tstCompTable: procedure expose m.
/*
$=/tstCompTable1/
    ### start tst tstCompTable1 #######################################
    compile :, 6 lines: table $*( sdf $*)   .
    run without input
    tstR: @tstWriteoV2 isA :<TstCT1Class>
    tstR:  .fEins = v1
    tstR:  .fZwei = valueZwei
    tstR:  .fDrei = undDrei
    zweite
    tstR: @tstWriteoV3 isA :<TstCT1Class>
    tstR:  .fEins = w1
    tstR:  .fZwei = wZwei
    tstR:  .fDrei = wwwDrei
$/tstCompTable1/

 */
    cl = classNew('n* CompTable u f fEins v, f fZwei v, f fDrei v')
    call tstComp1 ': tstCompTable1',
        , 'table $*( sdf $*)   ' ,
        , 'fEins   fZwei $*(....$*) fDrei  ' ,
        , '$@¢call mAdd t.trans, className("'cl'") "<TstCT1Class>"$!',
        , '    v1     valueZwei undDrei     ' ,
        , '$$ zweite',
        , ' w1 wZwei                    wwwDrei     '


/*
$=/tstCompWithNew/
    ### start tst tstCompWithNew ######################################
    compile :, 12 lines: withNew nn $*( sdf $*)  $@:¢  .
    run without input
    tstR: @tstWriteoV2 isA :<TstCT1Class>
    tstR:  .fEins = withNewValue fEins
    tstR:  .fZwei = withNewValue fZwei
    tstR:  .fDrei = withNewValuel drei
    tstR: @tstWriteoV3 isA :<TstCT1Class>
    tstR:  .fEins = withNewValue fEinsB
    tstR:  .fZwei = withNewValue fZweiB
    tstR:  .fDrei = withNewValue fDreiB
    tstR: @tstWriteoV4 isA :<TstCT1Class>
    tstR:  .fEins = withValue fEinsC
    tstR:  .fZwei = .
    tstR:  .fDrei = withValue fDreiC
$/tstCompWithNew/
*/
    call tstComp1 ': tstCompWithNew',
        , 'withNew nn $*( sdf $*)  $@:¢  ' ,
        , 'fEins = withNewValue fEins' ,
        , 'fZwei = withNewValue fZwei' ,
        , '$@¢call mAdd t.trans, className("'cl'") "<TstCT1Class>"',
        , '$@:¢   fDrei = withNewValuel drei $! $!! $$ $.nn' ,
        , 'withNew out $*( sdf $*)  $@:¢  ' ,
        , 'fEins = withNewValue fEinsB' ,
        , 'fZwei = withNewValue fZweiB',
        , 'fDrei = withNewValue fDreiB',
        , '! with out =. oNew('cl') $@:¢',
        , 'fEins = withValue fEinsC' ,
        , 'fDrei = withValue fDreiC $!'

    return
endProcedure tstCompTable

tstCompSql: procedure expose m.
/*
$=/tstCompSqlSrc/
$@=¢
   select strip(creator) cr, strip(name) tb,
            (row_number()over())*(row_number()over()) rr
       from sysibm.sysTables
       where creator='SYSIBM' and name like 'SYSTABL%'
       order by 2 fetch first 4 rows only
$!
$| call sqlSel
$| call fTabAuto
$/tstCompSqlSrc/
$=/tstCompSql/
    ### start tst tstCompSql ##########################################
    compile @, 9 lines: $@=¢
    run without input
    CR     TB                RR
    SYSIBM SYSTABLEPART       1
    SYSIBM SYSTABLEPART_HIST  4
    SYSIBM SYSTABLES          9
    SYSIBM SYSTABLESPACE     16
$/tstCompSql/
*/
    call sqlConnect
    call tstComp2 'tstCompSql', '@'

    call sqlDisConnect
    return
endProcedure tstCompFile
tstTut0: procedure expose m.
/*
$=/tstTut01Src/
$#=
$*+>.fSub()                               Kommentar
$*+>~tmp.jcl(t)                           Kommentar
$*+@=¢                                    Kommentar
$=subsys=DP4G
$=db=DA540769
$=ts=A977A
$*+@<~wk.jcl(jc)                          Kommentar
??* ?-¢sysvar(sysnode) date() time()?!ts=$ts 10*len=$-¢length($ts)*10$!
//P02     EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769C.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
  $@¢if right($ts, 2) == '7A' then $@=¢
    FULL YES
  $! else
    $$ $''    FULL NO
  $!
    SHRLEVEL CHANGE
$*+!                                      Kommentar
$#out                                              20140319 16:53:24
$/tstTut01Src/
$=/tstTut01/
    ### start tst tstTut01 ############################################
    compile , 28 lines: $#=
    run without input
    ??* ?-¢sysvar(sysnode) date() time()?!ts=A977A 10*len=50
    //P02     EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A540769C.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977A*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    FULL YES
    SHRLEVEL CHANGE
$/tstTut01/
$=/tstTut02Src/
$#@
$**>.fSub()
$**@¢
$=subsys=DP4G
$=db=DA540769
$=jx=0
$@do tx = 976 to 977 $@=¢
    $=ts=A$tx
    $=jx=-$jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$**!
$#out                                              20101229 13
$/tstTut02Src/
$=/tstTut02/
    ### start tst tstTut02 ############################################
    compile , 28 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut02/
$=/tstTut03Src/
$#@
$=subsys=DP4G
$@:¢table
      db         ts
      DGDB9998   A976
      DA540769   A977
$!
$** $| call fTabAuto
$**    $#end
$|
$=jx=0
$@forWith o $@=¢
    $=jx=-$jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$#out
$/tstTut03Src/
$=/tstTut03/
    ### start tst tstTut03 ############################################
    compile , 31 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut03/
$=/tstTut04Src/
$#@
$=subsys=DP4G
$=db=DA540769
call sqlConnect $subsys
$@=¢  select dbName  db , tsName  ts
          from sysibm.sysTables
          where creator = 'SYSIBM' and name like 'SYSINDEXPAR%'
          order by name desc
$!
$| call sqlSel
$** $| call fTabAuto
$** $#end
$|
$=jx=0
$@forWith o $@=¢
    $=jx=-$jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$TS    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $DB.$TS*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
call sqlDisConnect
$#out                                              20101229
$/tstTut04Src/
$=/tstTut04/
    ### start tst tstTut04 ############################################
    compile , 35 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CSYSHIST     EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DSNDB06 .SYSHIST *   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CSYSTSIPT    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DSNDB06 .SYSTSIPT*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut04/
$=/tstTut05Src/
$#:
subsys = DP4G
lst =<:¢withNew out :¢
    db = DGDB9998
    ts =<:¢table
             ts
             A976
             A977
    $!
    db = DA540769
    <|/ts/
             ts
             A976
             A975
    /ts/
!
$** $$. $lst
$** $@ct $@¢$=tool =. $.compile@ $<~.wk.rexx(wshtut06)$!
$** $@$tool
$@do sx=1 to ${lst.0} $@¢
    $=db = ${lst.$sx.db}
    $** $$. ${lst.$sx}
    $@do tx=1 to ${lst.$sx.ts.0} $@=¢
        $*+ $$. ${lst.$sx.ts.$tx}
        $=ts= ${lst.$sx.ts.$tx.ts}
        $@¢ say $-=¢subsys $subsys db $db ts $ts $! $!
        $@copy()
        $!
    $!
$@ct $@¢
cl = classNew('n? DbTsList u s' ,
     classNew('n? DbTs u f db v, f ts s' ,
     classNew('n? Ts u f ts v')))
$=lst=. oNew(cl)
$!
$@proc copy $@=¢
$@ct $=jx=0
$=jx=-$jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$#out                                              201012
$/tstTut05Src/
$=/tstTut05/
    ### start tst tstTut05 ############################################
    compile , 56 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407693 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407693.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407694 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA975    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407694.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A975*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut05/
   tstTut06   ==> platz für copy aus tut05
$=/tstTut07Src/
$**$>.fEdit()
call sqlConnect dp4g
$@:¢table
   ts
VTCASHFLOW
VTCASHFLOWAMOUNT
VTINSTRFLATDATA
$!
$| $@=¢
    select name, statstime, strip(dbName) || '.' || strip(tsName) dbts
       from sysibm.sysTables
       where creator = 'VDPS2' and name in
  $=co=(
  $@forWith t $@=¢
                                           $co '$ts'
      $=co=,
  $!
                                           )
$!
$| call sqlSel
$** $| call fTabAuto
$|
$=jx=0
$@forWith t $@=¢
$=jx=-$jx+1
//A540769$jx JOB (CP00,KE50),
//       'CATALOG',MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//STEP$jx EXEC PGM=DSNUTILB,TIME=1440,
//   PARM=(DP4G,'A540769$jx.RUNSTA'),
//   REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//UTPRINT  DD SYSOUT=*
//SYSOUT   DD SYSOUT=*
//RNPRIN01 DD SYSOUT=*
//SYSIN DD *
  LISTDEF LST#STA   INCLUDE TABLESPACE $DBTS
   OPTIONS EVENT (ITEMERROR, SKIP)

   RUNSTATS TABLESPACE LIST LST#STA
         SHRLEVEL CHANGE
           INDEX(ALL KEYCARD)
           REPORT YES UPDATE ALL
$!
call sqlDisconnect
$#out                                              20101231 11:56:23
$/tstTut07Src/
$=/tstTut07/
$=/tstTut07/
    ### start tst tstTut07 ############################################
    compile , 47 lines: $**$>.fEdit()
    run without input
    //A5407691 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP1 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407691.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV27A1T.VDPS329
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
    //A5407692 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP2 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407692.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV28A1T.VDPS390
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
    //A5407693 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP3 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407693.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV21A1T.VDPS004
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
$/tstTut07/
$=/tstTut08Src/
$/tstTut08Src/
$=/tstTut08/
$/tstTut08/
*/
    call sqlIni
    call sqlDisconnect '*'
    call tstComp2 'tstTut01'
    call tstComp2 'tstTut02'
    call tstComp2 'tstTut03'
    if m.err.os == 'TSO' then do
        call tstComp2 'tstTut04'
        /* call tstComp2 'tstTut05' */
     /* call tstComp2 'tstTut07'  ???? anderes Beispiel ???? */
        end
    call tstTotal
    return
endProcedure tstTut0
/* copx tstComp end   *************************************************/
/* copx tstBase begin **************************************************
     test the basic classes
***********************************************************************/
tstBase: procedure expose m.
    call tstTstSay
    call oIni
    call scanIni
    call tstO
    call tstM
    call tstUtc2d
    call classIni
    call tstF
    call tstFtst
    call tstMCat
    call tstMap
    call tstMapVia
    call tstClass
    call tstClass2
    call tstOEins
    call jIni
    call tstJSay
    call tstJ
    call tstJ2
    call tstJCatSql
    call catIni
    call tstCat
    call pipeIni
    CALL TstEnv
    CALL TstEnvCat
    call tstPipe
    call tstPipeS
    call tstEnvVars
    call tstvWith
    call tstTotal
    call tstPipeLazy
    call tstEnvClass
    call tstFile
    call tstFileList
    call tstFE
    call tstFTab
    call tstFmt
    call tstfUnits
    call tstTotal
    call tstSb
    call tstSb2
    call tstScan
    call ScanReadIni
    call tstScanRead
    call tstScanUtilInto
    call tstScanWin
    call tstScanSQL
    call tstTotal
    return
endProcedure tstBase

/*--- test the tst Module, check the output visually  ----------------*/
tstTstSay: procedure expose m.
    call tstIni
    oldErr = m.tst.err
    oldNames = m.tst.errNames
    say '+++ tstTstSay start with' oldErr 'totErrs and',
            m.tst.tests 'tests'
/*
$=/tstTstSayEins/
    ### start tst tstTstSayEins #######################################
    test eins einzige testZeile
$/tstTstSayEins/
$=/tstTstSayZwei/
    ### start tst tstTstSayZwei #######################################
    zwei 1. testZeile
    zwei 2. und letzte testZeile
$/tstTstSayZwei/
$=/tstTstSayDrei/
    ### start tst tstTstSayDrei #######################################
    drei 1. testZeile vor empty Zeile
    ..
    drei 3. testZeile vor 10 space
    .          .
    drei 5. testZeile ziemlich lang 66                                 +
    .                                77  5   10   15++++++++++++++++++++
    .+++++++++++++++++++++++++++++++++++++++++++.
$/tstTstSayDrei/
*/
    call tst x, 'tstTstSayEins'
    call tstOut x, "test eins einzige testZeile"
    call tstEnd x, 'err 0'


    call tst x, 'tstTstSayZwei'
    call tstOut x, "zwei 1. testZeile"
    call tstOut x, "zwei 2. und letzte testZeile"
    call tstEnd x, 'err 0'

    call tst x, 'tstTstSayZwei'
    call tstOut x, "zwei 1. testZeile "    /* ein space zuviel */
    call tstOut x, "zwei 2. und letzte testZeile"
    call tstOut x, "zwei 3. zuviel"
    call tstEnd x, 'err 3'

    call tst y, 'tstTstSayDrei'
    call tstOut y, 'drei 1. testZeile vor empty Zeile'
    call tstOut y, ''
    call tstOut y, 'drei 3. testZeile vor 10 space'
    call tstOut y, left('', 10)
    call tstOut y, 'drei 5. testZeile ziemlich lang',
                left(66, 66) left('77  5   10   15', 77, '+')
    call tstEnd y, 'err 0'
    if m.y.err <> 0 then
        call err '+++ tstTstSay errs' m.x.err 'expected' 0
    if m.tst.err <> oldErr + 3 then
        call err '+++ tstTstSay totErrs' m.tst.err 'expected' 3
    say '+++ tstTstSay end Ok with' m.tst.err 'totErrs and' ,
        m.tst.tests 'tests'
    m.tst.err = oldErr
    m.tst.errNames = oldNames
    return
endProcedure tstTstSay

tstMark: procedure expose m.
parse arg m, msg
    if symbol('m.m') == 'VAR' then
        m.m = msg';' m.m
    else
        m.m = msg 'new'
    return m
endProcedure tstMark
tstM: procedure expose m.
/*
$=/tstMa/
    ### start tst tstMa ###############################################
    mNew() 1=newM1 2=newM2
    mNew(tst...) 2=2 new 3=4; 3; 1 new 4=5 new
    iter 4; 3; 1 new
    iter 2 new
    iter 5 new
$/tstMa/
*/
    call tst t, 'tstMa'
    m1 = mNew()
    m2 = mNew()
    m.m1 = 'newM1'
    m.m2 = 'newM2'
    call tstOut t, 'mNew() 1='m.m1 '2='m.m2
    call mNewArea 'tst'm1
    t1 = tstMark(mNew('tst'm1), '1')
    t2 = tstMark(mNew('tst'm1), '2')
    call mFree tstMark(t1, '3')
    t3 = tstMark(mNew('tst'm1), '4')
    t4 = tstMark(mNew('tst'm1), '5')
    call tstOut t, 'mNew(tst...) 2='m.t2 '3='m.t3 '4='m.t4
    i = mIterBegin('tst'm1)
    do while assNN('i', mIter(i))
        call tstOut t, 'iter' m.i
        end
    call tstEnd t
/*
$=/tstM/
    ### start tst tstM ################################################
    symbol m.b LIT
    symbol m.a LIT
    mAdd a A.2
    mAdd a A.3
    m.a: 3: 1=eins 2=zwei 3=drei 4=M.A.4
    m.c: 5: 1=c vorAddSt a 2=eins 3=zwei 4=drei 5=c nacAddSt a 6=M.C.6
$/tstM/ */


    drop m.b m.a m.a.0 m.a.1 m.a.2
    call tst t, 'tstM'
    call tstOut t, 'symbol m.b' symbol('m.b')
    m.b = 1
    call tstOut t, 'symbol m.a' symbol('m.a')
    call tstOut t, 'mAdd a' mAdd(mCut(a, 0), 'eins', 'zwei')
    call tstOut t, 'mAdd a' mAdd(a, 'drei')
    call tstOut t, 'm.a:' m.a.0': 1='m.a.1 '2='m.a.2 '3='m.a.3 '4='m.a.4
    call mAdd mCut(c, 0), 'c vorAddSt a'
    call mAddSt c, a
    call mAdd c, 'c nacAddSt a'
    call tstOut t, 'm.c:' m.c.0': 1='m.c.1 '2='m.c.2 '3='m.c.3,
                    '4='m.c.4 '5='m.c.5 '6='m.c.6
    call tstEnd t
    return
endProcedure tstM

tstMCat: procedure expose m.
/*
$=/tstMCat/
    ### start tst tstMCat #############################################
    mCat(0, )                     =;
    mCat(0, 1)                    =;
    mCat(0, 112222%,%,%,11)       =;
    mCat(0, 231%,%c%,1%,2)        =;
    mCat(0, 33341%,%c2@%c%,1%,333)=;
    mCat(0, 45%,1%c2@%c3@%c%,%,4) =;
    mCat(1, )                     =eins;
    mCat(1, 1)                    =eins;
    mCat(1, 112222%,%,%,11)       =eins11;
    mCat(1, 231%,%c%,1%,2)        =1eins2;
    mCat(1, 33341%,%c2@%c%,1%,333)=1eins2eins333;
    mCat(1, 45%,1%c2@%c3@%c%,%,4) =1eins2eins3eins4;
    mCat(2, )                     =einszwei;
    mCat(2, 1)                    =eins1zwei;
    mCat(2, 112222%,%,%,11)       =eins112222zwei11;
    mCat(2, 231%,%c%,1%,2)        =1eins231zwei2;
    mCat(2, 33341%,%c2@%c%,1%,333)=1eins2eins33341zwei2zwei333;
    mCat(2, 45%,1%c2@%c3@%c%,%,4) =1eins2eins3eins451zwei2zwei3zwei4;
    mCat(3, )                     =einszweidrei;
    mCat(3, 1)                    =eins1zwei1drei;
    mCat(3, 112222%,%,%,11)       =eins112222zwei112222drei11;
    mCat(3, 231%,%c%,1%,2)        =1eins231zwei231drei2;
    mCat(3, 33341%,%c2@%c%,1%,333)=1eins2eins33341zwei2zwei33341drei2dr+
    ei333;
    mCat(3, 45%,1%c2@%c3@%c%,%,4) =1eins2eins3eins451zwei2zwei3zwei451d+
    rei2drei3drei4;
$/tstMCat/ */
    call mIni
    call tst t, "tstMCat"
     m.qq.1 = "eins"
     m.qq.2 = "zwei"
     m.qq.3 = "drei"
     do qx = 0 to 3
         m.qq.0 = qx
         call tstMCat1 qx
         call tstMCat1 qx, '1'
         call tstMCat1 qx, '112222%,%,%,11'
         call tstMCat1 qx, '231%,%c%,1%,2'
         call tstMCat1 qx, '33341%,%c2@%c%,1%,333'
         call tstMCat1 qx, '45%,1%c2@%c3@%c%,%,4'
         end
     call tstEnd t
     return
endProcedure tstMCat

tstMCat1: procedure expose m.
parse arg m.qq.0, fmt
    call out left("mCat("m.qq.0"," fmt")", 30)"="mCat(qq, fmt)";"
return
endProcedure tstMCat1

tstMap: procedure expose m.
/*
$=/tstMap/
    ### start tst tstMap ##############################################
    mapNew m keys m-keys 0
    map m zwei --> 2
    map m Zwei is not defined
    map stem m-keys 4
    map m eins --> 1
    map m zwei --> 2
    map m drei --> 3
    map m vier --> 4
    *** err: duplicate key eins in map m
    map m zwei is not defined
    q 2 zw dr
    map stem Q 2
    map Q zw --> 2Q
    map Q dr --> 3Q
    map stem m 3
    map m eins --> 1
    map m zwei --> 2PUT
    map m vier --> 4PUT
    *** err: duplicate key zwei in map m
    tstMapLong eins keys 3
    tstMapLong zweiMal keys 48
    tstMapLong dreiMal keys 93
    tstMapLong vier    keys 138
    tstMapLong <fuenf> keys 188
    tstMap clear keys 0
    inline1 3
    inline1 1 ==    inline1 eins==
    inline1 2 ====
    inline1 3 ==    inline1 drei==
    inline2 1 1 ==    inline2 eins==
    inline3 ?
$/tstMap/ */
/*
$=/tstMapInline1/
    inline1 eins

    inline1 drei
$/tstMapInline1/ */
/*
$=/tstMapInline2/
    inline2 eins
$/tstMapInline2/ */

    call tst t, 'tstMap'
    m = mapNew('K')
    ky = mapKeys(m)
    call mAdd t'.TRANS', m 'm', ky 'm-keys'
    call tstOut t, 'mapNew' m 'keys' ky m.ky.0
    call mapAdd m, 'eins', 1
    call mapAdd m, 'zwei', 2
    call mapAdd m, 'drei', 3
    call mapAdd m, 'vier', 4
    call tstMapShow m, 'zwei'
    call tstMapShow m, 'Zwei'
    call tstMapShowSt m, mapKeys(m)
    call mapAdd m, 'eins', 1
    call mapReset m, '='
    call tstMapShow m, 'zwei'
    call mapAdd m, 'eins', 1
    call mapAdd m, 'zwei', 2
    call mapPut m, 'zwei', 2Put
    call mapPut m, 'vier', 4Put
    call mapReset q, '='
    call mapAdd q, 'zw', 2q
    call mapAdd q, 'dr', 3q
    call tstOut t, 'q' m.q.0 m.q.1 m.q.2
    call tstMapShowSt q, mapKeys(q)
    call tstMapShowSt m, mapKeys(m)
    call mapAdd m, 'zwei', 2addDup
    call tstMapLong m, 'eins'      ,201, 2000, -2, 2
    call tstMapLong m, 'zweiMal'   ,201, 2000, -2, 2
    call tstMapLong m, 'dreiMal'   ,201, 2000,  2,-2
    call tstMapLong m, 'vier   '   ,2010, 201, -2, 2
    call tstMapLong m, '<fuenf>'   ,2010, 201,  2,-2
    call mapClear m
    keys = mapKeys(m)
    call tstOut t, 'tstMap clear keys' m.keys.0
    i = mapInline('tstMapInline1')
    call tstOut t, 'inline1' m.i.0
    do x=1 to m.i.0
        call tstOut t, 'inline1' x '=='m.i.x'=='
        end
    i = mapInline('tstMapInline2')
    call tstOut t, 'inline2' m.i.0 '1 =='m.i.1'=='
    call tstOut t, 'inline3' mapInline('tstMapInline3', 'r')'?'
    call tstEnd t
    return
endProcedure tstMap

tstMapLong: procedure expose m.
parse arg m, w, f1, t1, f2, t2
    if f1 < t1 then
        b1 = 201
    else
        b1 = -201
    if f2 < t2 then
        b2 = 1
    else
        b2 = -1
    lo = copies(w, 2100 % length(w))
    keys = mapKeys(m)
    keyCn = m.keys.0
    call tstOut t, 'tstMapLong' w 'keys' keyCn
    do x = f1 by b1 to t1
        do y = x+f2 by b2 to x+t2
            k = left(lo, y)
            if mapHasKey(m, k) then
                call err 'mapLong hasKey before' w y
            call mapAdd m, k, w y
            if \ mapHasKey(m, k) then
                call err 'mapLong \ hasKey after' w y
            if mapGet(m, k) \== w y then
                call err 'mapLong \ get <> ' w y
            keys = mapKeys(m)
            if keyCn + 1 \= m.keys.0 then
                call err 'mapLong keys .0 <> ' w y
            keyCn = m.keys.0
            if k \== m.keys.keyCn then
                call err 'mapLong keys . ' keyCn '<>' w y
            end
        end
    return
endProcedure tstMapLong

tstMapVia: procedure expose m.
/*
$=/tstMapVia/
    ### start tst tstMapVia ###########################################
    map M K --> A
    mapVia(m, K)      A
    *** err: missing m.A at 3 in mapVia(M, K|)
    mapVia(m, K|)     M.A
    mapVia(m, K|)     valAt m.a
    mapVia(m, K|)     valAt m.a
    *** err: missing m.A.aB at 5 in mapVia(M, K|aB)
    mapVia(m, K|aB)   M.A.aB
    mapVia(m, K|aB)   valAt m.A.aB
    *** err: missing m.valAt m.a at 4 in mapVia(M, K||)
    mapVia(m, K||)    M.valAt m.a
    mapVia(m, K||)    valAt m.valAt m.a
    mapVia(m, K||F)   valAt m.valAt m.a.F
$/tstMapVia/ */
    call tst t, 'tstMapVia'
    u = 'A.aB'
    v = 'valAt m.a'
    drop m.a m.u m.v m.v.f
    call mapReset m, 'K'
    call mapAdd m, k, a
    call tstMapShow m, k
    call tstOut t, 'mapVia(m, K)     ' mapVia(m, 'K')
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    m.a = v
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    call tstOut t, 'mapVia(m, K|aB)  ' mapVia(m, 'K|aB')
    m.u = 'valAt m.'u
    call tstOut t, 'mapVia(m, K|aB)  ' mapVia(m, 'K|aB')
    call tstOut t, 'mapVia(m, K||)   ' mapVia(m, 'K||')
    v = m.a
    m.v = 'valAt m.'v
    m.v.f = 'valAt m.'v'.F'
    call tstOut t, 'mapVia(m, K||)   ' mapVia(m, 'K||')
    call tstOut t, 'mapVia(m, K||F)  ' mapVia(m, 'K||F')
    call tstEnd t
    return
endProcedure tstMapVia

tstMapShow: procedure expose m.
parse arg a, key
    if mapHasKey(a, key) then
        call tstOut t, 'map' a key '-->' mapGet(a, key)
    else
        call tstOut t, 'map' a key 'is not defined'
    return
endProcedure tstMapShow

tstMapShowSt: procedure expose m.
parse arg a, st
    call tstOut t, 'map stem' st m.st.0
    do wx=1 to m.st.0
        call tstMapShow a, m.st.wx
        end
    return
endProcedure tstMapShow

tstClass2: procedure expose m.
/*
$=/tstClass2o2/
    ### start tst tstClass2 ###########################################
    @CLASS.5 isA :class = u
    . choice u union
    .  .NAME = class
    .  stem 7
    .   .1 refTo @CLASS.1 :class = u
    .    choice v union
    .    choice c union
    .     .NAME = u
    .     .CLASS refTo @CLASS.15 :class = s
    .      choice s .CLASS refTo @CLASS.12 done :class @CLASS.12
    .   .2 refTo @CLASS.6 :class = c
    .    choice c union
    .     .NAME = v
    .     .CLASS refTo @CLASS.7 :class = u
    .      choice u stem 0
    .   .3 refTo @CLASS.8 :class = c
    .    choice c union
    .     .NAME = w
    .     .CLASS refTo @CLASS.7 done :class @CLASS.7
    .   .4 refTo @CLASS.9 :class = c
    .    choice c union
    .     .NAME = o
    .     .CLASS refTo @CLASS.7 done :class @CLASS.7
    .   .5 refTo @CLASS.10 :class = c
    .    choice c union
    .     .NAME = s
    .     .CLASS refTo @CLASS.11 :class = f
    .      choice f union
    .       .NAME = CLASS
    .       .CLASS refTo @CLASS.12 :class = r
    .        choice r .CLASS refTo @CLASS.5 done :class @CLASS.5
    .   .6 refTo @CLASS.13 :class = c
    .    choice c union
    .     .NAME = r
    .     .CLASS refTo @CLASS.11 done :class @CLASS.11
    .   .7 refTo @CLASS.14 :class = c
    .    choice c union
    .     .NAME = u
    .     .CLASS refTo @CLASS.15 :class = s
    .      choice s .CLASS refTo @CLASS.12 done :class @CLASS.12
    .   .8 refTo @CLASS.16 :class = c
    .    choice c union
    .     .NAME = n
    .     .CLASS refTo @CLASS.17 :class = u
    .      choice u stem 2
    .       .1 refTo @CLASS.18 :class = f
    .        choice f union
    .         .NAME = NAME
    .         .CLASS refTo @CLASS.1 done :class @CLASS.1
    .       .2 refTo @CLASS.15 done :class @CLASS.15
    .   .9 refTo @CLASS.19 :class = c
    .    choice c union
    .     .NAME = f
    .     .CLASS refTo @CLASS.20 :class = u
    .      choice u stem 2
    .       .1 refTo @CLASS.18 done :class @CLASS.18
    .       .2 refTo @CLASS.11 done :class @CLASS.11
    .   .10 refTo @CLASS.21 :class = c
    .    choice c union
    .     .NAME = c
    .     .CLASS refTo @CLASS.20 done :class @CLASS.20
    .   .11 refTo @CLASS.22 :class = c
    .    choice c union
    .     .NAME = m
    .     .CLASS refTo @CLASS.23 :class = u
    .      choice u stem 2
    .       .1 refTo @CLASS.18 done :class @CLASS.18
    .       .2 refTo @CLASS.24 :class = f
    .        choice f union
    .         .NAME = MET
    .         .CLASS refTo @CLASS.1 done :class @CLASS.1
$/tstClass2o2/

$=/tstClass2/
    ### start tst tstClass2 ###########################################
    @CLASS.4 isA :class = u
    . choice u union
    .  .NAME = class
    .  stem 7
    .   .1 refTo @CLASS.1 :class = u
    .    choice u union
    .     .NAME = v
    .     stem 2
    .      .1 refTo @CLASS.20 :class = m
    .       choice m union
    .        .NAME = o2String
    .        .MET = return m.m
    .      .2 refTo @CLASS.108 :class = m
    .       choice m union
    .        .NAME = o2File
    .        .MET = return file(m.m)
    .   .2 refTo @CLASS.5 :class = c
    .    choice c union
    .     .NAME = u
    .     .CLASS refTo @CLASS.6 :class = u
    .      choice u union
    .       .NAME = .
    .       stem 2
    .        .1 refTo @CLASS.7 :class = f
    .         choice f union
    .          .NAME = NAME
    .          .CLASS refTo @CLASS.1 done :class @CLASS.1
    .        .2 refTo @CLASS.8 :class = s
    .         choice s .CLASS refTo @CLASS.9 :class = r
    .          choice r .CLASS refTo @CLASS.4 done :class @CLASS.4
    .   .3 refTo @CLASS.10 :class = c
    .    choice c union
    .     .NAME = f
    .     .CLASS refTo @CLASS.11 :class = u
    .      choice u union
    .       .NAME = .
    .       stem 2
    .        .1 refTo @CLASS.7 done :class @CLASS.7
    .        .2 refTo @CLASS.12 :class = f
    .         choice f union
    .          .NAME = CLASS
    .          .CLASS refTo @CLASS.9 done :class @CLASS.9
    .   .4 refTo @CLASS.13 :class = c
    .    choice c union
    .     .NAME = s
    .     .CLASS refTo @CLASS.12 done :class @CLASS.12
    .   .5 refTo @CLASS.14 :class = c
    .    choice c union
    .     .NAME = c
    .     .CLASS refTo @CLASS.11 done :class @CLASS.11
    .   .6 refTo @CLASS.15 :class = c
    .    choice c union
    .     .NAME = m
    .     .CLASS refTo @CLASS.16 :class = u
    .      choice u union
    .       .NAME = .
    .       stem 2
    .        .1 refTo @CLASS.7 done :class @CLASS.7
    .        .2 refTo @CLASS.17 :class = f
    .         choice f union
    .          .NAME = MET
    .          .CLASS refTo @CLASS.1 done :class @CLASS.1
    .   .7 refTo @CLASS.18 :class = c
    .    choice c union
    .     .NAME = r
    .     .CLASS refTo @CLASS.12 done :class @CLASS.12
$/tstClass2/ */

    call classIni
    call tst t, 'tstClass2'
    call classOut , m.class.class
    call tstEnd t
    return
endProcedure tstClass2

tstClass: procedure expose m.
/*
$=/tstClass/
    ### start tst tstClass ############################################
    Q u =className= tstClassTf12
    Q.eins.zwei v ==> M.Q.eins.zwei
    *** err: bad type v: classNew(v tstClassTf12)
    *** err: bad type v: classBasicNew(v, tstClassTf12, )
    R u =className= uststClassTf12
    R u =className= uststClassTf12in
    R u =className= tstClassTf12
    R.eins.zwei v ==> M.R.eins.zwei
    R s =stem.0= 2
    R.1 r ==> M.R.1 :CLASS.3
    R.1 u =className= tstClassTf12
    R.1.eins.zwei v ==> M.R.1.eins.zwei
    R.2 r ==> M.R.2 :CLASS.3
    R.2 u =className= tstClassTf12
    R.2.eins.zwei v ==> M.R.2.eins.zwei
    S u =className= TstClass7
    S s =stem.0= 2
    S.1 u =className= TstClass7s
    S.1.eins v ==> M.S.1.eins
    S.1 m =met-metA--> say "metA"
    S.1 m =met-metB--> say "metB"
    S.2 u =className= TstClass7s
    S.2.zwei v ==> M.S.2.zwei
    S.2 m =met-metA--> say "metA"
    S.2 m =met-metB--> say "metB"
    class of mutate qq tstClassTf12
$/tstClass/ */

    f = 'eins.zwei'
    e = 'eins'
    z = 'zwei'
    drop m.q.f m.r.f m.r.0 m.r.1 m.r.1.f m.r.2 m.r.2.f
    drop m.s.0 m.s.1 m.s.1.e m.s.2 m.s.2.z
    call classIni
    call tst t, 'tstClass'
    t1  =  classNew('n? tstClassTf12 u f eins f zwei v')
    call tstClassOut t, t1, q
    z = m.class.0
    if class4name('tstClassB', '') == '' then do
        t2 = classNew('n tstClassB u n tstClassC u tstClassTf12,',
            's u v tstClassTf12')
        end
    else do /*  the second time we would get a duplicate error */
        call tstOut t, '*** err: bad type v: classNew(v tstClassTf12)'
        call tstOut t, '*** err: bad type v:' ,
            'classBasicNew(v, tstClassTf12, )'
        end
    t2 = classNew('n? uststClassTf12 u' ,
           'n? uststClassTf12in u tstClassTf12',
        , classNew('s u r, tstClassTf12'))
    m.r.0 = 2
    call tstClassOut t, t2, r
    t3 = classNew('n? TstClass7 u s',
         classNew('n? TstClass7s u c 1 f eins v, c 2 f zwei v',
            ,'m', 'metA say "metA"', 'metB say "metB"'))
    m.s.0 = 2
    m.s.1 = 1
    m.s.2 = 2
    call tstClassOut t, t3, s
    call oMutatName qq, 'tstClassTf12'
    tt = objClass(qq)
    call tstOut t, 'class of mutate qq' className(tt)
    call tstEnd t
    return
endProcedure tstClass

tstClassOut: procedure expose m.
parse arg o, t, a
    if wordPos(t, m.class.classV m.class.classW m.class.classO) > 0 then
        return tstOut(o, a m.t.name '==>' m.a)
    if m.t == 'r' then
        return tstOut(o, a m.t '==>' m.a ':'m.t.class)
    if m.t == 'u' & m.t.name \== '' then
        call tstOut o, a m.t '=className=' m.t.name
    if m.t == 'f' then
        return tstClassOut(o, m.t.class, a'.'m.t.name)
    if m.t = 'u' then do
        do ux=1 to m.t.0
            call tstClassOut o, m.t.ux, a
            end
        return 0
        end
    if m.t = 's' then do
        call tstOut o, a m.t '=stem.0=' m.a.0
        do ux=1 to m.a.0
            call tstClassOut o, m.t.class, a'.'ux
            end
        return 0
        end
    if m.t = 'c' then do
        if m.t.name = m.a then
            call tstClassOut o, m.t.class, a
         return 0
        end
    if m.t = 'm' then
        return tstOut(o, a m.t '=met-'m.t.name'-->' m.t.met)
    call err 'bad class type' m.t
endProcedure tstClassOut


tstO: procedure expose m.
/*
$=/tstO/
    ### start tst tstO ################################################
    oIsCla(TstOCla1) 0
    TstOCla1 -
    oIsCla(TstOCla1) 1
    TstOCla1 -
    oIsCla(TstOCla1) 1
    TstOCla1 contents of met1
    TstOCla1.met2 -
    TstOCla2.met1 contents of met1
    TstOCla2.met2 contents of met2
    TstOCla1.TstOMet3 -
    TstOCla1.TstOMet3 generated met TstOCla1:TstOMet3 code...;
    TstOCla2.TstOMet3 generated met TstOCla2:TstOMet3 code...;
    tstOObj1.met1 -
    tstOObj1.met1 contents of met1
$/tstO/
*/
    call mIni
    call tst t, 'tstO'
    call oIni
    c1 = 'TstOCla1'
    c2 = 'TstOCla2'
    m1 = 'met1'
    m2 = 'met2'
    m3 = 'TstOMet3'
    lg = m.o.lazyGen
    call tstOut t, 'oIsCla('c1')' oIsCla(c1)
    call tstOut t, c1 oClaMet(c1, 'met1', '-')
    call oAddCla c1
    call tstOut t, 'oIsCla('c1')' oIsCla(c1)
    call tstOut t, c1 oClaMet(c1, 'met1', '-')
    call oAddMet c1, m1, 'contents of met1'
    call tstOut t, 'oIsCla('c1')' oIsCla(c1)
    call tstOut t, c1 oClaMet(c1, m1, '-')
    call oAddCla c2, c1
    call oAddMet c2, 'met2', 'contents of met2'
    call tstOut t, c1'.met2' oClaMet(c1, 'met2', '-')
    call tstOut t, c2'.'m1 oClaMet(c2, m1, '-')
    call tstOut t, c2'.met2' oClaMet(c2, 'met2', '-')
    call tstOut t, c1'.'m3 oClaMet(c1, m3, '-')
    call oAddMet lg, m3,
            , "return 'generated met' cl':'me 'code...;'"
    call tstOut t, c1'.'m3 oClaMet(c1, m3, '-')
    call tstOut t, c2'.'m3 oClaMet(c2, m3, '-')
    o1 = 'tstOObj1'
    o2 = 'tstOObj2'
    call tstOut t, o1'.met1' objMet(o1, 'met1', '-')
    call oMutate o1, c1
    call tstOut t, o1'.met1' objMet(o1, 'met1', '-')
    call tstEnd t
    drop m.o.cParent.c1 m.o.cMet.c1.m1 m.o.cMet.c1.m2 m.o.cMet.c1.m3
    drop m.o.cParent.c2 m.o.cMet.c2.m1 m.o.cMet.c2.m2 m.o.cMet.c2.m3
    drop m.o.o2c.o1                                   m.o.cMet.lg.m3
    return
endProcedure tstO


tstOEins: procedure expose m.
/*
$=/tstOEins/
    ### start tst tstOEins ############################################
    class method calls of TstOEins
    .  met Eins.eins M
     FLDS of <obj e of TstOEins> .FEINS, .FZWEI
     methodcalls of object e of TstOEins
    .  met Eins.eins <obj e of TstOEins>
    .  met Eins.zwei <obj e2 of TstOEins>
    *** err: no method nein in class TstOEins of object <obj e+
    . of TstOEins>
    *** err: no class found for object noObj
    class method calls of TstOEins
    .  met Elf.zwei M
    FLDS of <obj f of TstOElf> .FEINS, .FZWEI, .FELF
    methodcalls of object f of TstOElf
    .  met Eins.eins <obj f of TstOElf>
    .  met Elf.zwei <obj f of TstOElf>
    .  met Elf.drei <obj f of TstOElf>
    oCopy c1 of class TstOEins, c2
    C1 u =className= TstOEins
    C1.FEINS v ==> M.C1.FEINS
    C1.FZWEI v ==> M.C1.FZWEI
    C1 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C1 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    C2 u =className= TstOEins
    C2.FEINS v ==> M.C1.FEINS
    C2.FZWEI v ==> M.C1.FZWEI
    C2 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C2 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    oCopy c3 of class TstOElf, c4
    C4 u =className= TstOElf
    C4 u =className= TstOEins
    C4.FEINS v ==> M.C3.FEINS
    C4.FZWEI v ==> M.C3.FZWEI
    C4 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C4 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    C4.FELF r ==> M.C3.FELF :CLASS.3
    C4 m =met-zwei--> call tstOut t, "  met Elf.zwei" m
    C4 m =met-drei--> call tstOut t, "  met Elf.drei" m
    tEinsDop <class TstOEins>
    oRun 7*3 21
    oRun 12*12 144
$/tstOEins/ */
    call classIni
    call tst t, 'tstOEins'
    tEins = classNew('n? TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
    call mAdd t.trans, tEins '<class TstOEins>'
    call tstOut t, 'class method calls of TstOEins'
    interpret oClaMet('TstOEins', 'eins')
    e = oNew('TstOEins')
    e2 = oNew('TstOEins')
    call mAdd t.trans, e '<obj e of TstOEins>'
    call mAdd t.trans, e2 '<obj e2 of TstOEins>'
    call tstOut t, 'FLDS of' e mCat(oFlds(e), ', ')
    call tstOut t, 'methodcalls of object e of TstOEins'
    call tstOmet e, 'eins'
    call tstOmet e2, 'zwei'
    call tstOmet e, 'nein'
    call tstOmet 'noObj', 'nein'
    tElf = classNew('n? TstOElf u TstOEins, f FELF r', 'm',
        , 'zwei call tstOut t, "  met Elf.zwei" m',
        , 'drei call tstOut t, "  met Elf.drei" m')
    call tstOut t, 'class method calls of TstOEins'
    interpret oClaMet('TstOElf', 'zwei')
    f = oNew('TstOElf')
    call mAdd t.trans, f '<obj f of TstOElf>'
    call tstOut t, 'FLDS of' f mCat(oFlds(f), ', ')
    call tstOut t, 'methodcalls of object f of TstOElf'
    call tstOmet f, 'eins'
    call tstOmet f, 'zwei'
    call tstOmet f, 'drei'
/*  call tstOut t, 'methodcalls of object f cast To TstOEins'
    call tstOmet oCast(f, 'TstOEins'), 'eins'
    call tstOmet oCast(f, 'TstOEins'), 'zwei'
    call tstOut t, 'FLDS of <cast(f, TstOEins)>',
        mCat(oFlds(oCast(f, 'TstOEins')), ', ')
 */
    call oMutatName c1, 'TstOEins'
    call tstOut t, 'oCopy c1 of class TstOEins, c2'
    call tstClassOut t, tEins, c1
    call oCopy c1, c2
    call tstClassOut t, tEins, c2
    call tstOut t, 'oCopy c3 of class TstOElf, c4'
    call oMutatName c3, 'TstOElf'
    call oCopy c3, c4
    call tstClassOut t, tElf, c4

/*    tEinsDop = classNew('n TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
*/ tEinsDop = tEins
    call tstOut t, 'tEinsDop' tEinsDop
    e3 = oNew('TstOEins')
    if e3 <<= e | e3 <<= e2 then
        call err 'doppelt reinitialised objects'
    rr = oRunner('return 7 * 3')
    call tstOut t, 'oRun 7*3' oRun(rr)
    r12 = oRunner('return 12 * 12')
    call tstOut t, 'oRun 12*12' oRun(r12)
    call tstEnd t
    return
endProcedure tstOEins

tstOmet: procedure expose m.
parse arg m, met
    interpret objMet(m, met)
    return
endProcedure tstOmet

tstJSay: procedure expose m.
/*
$=/tstJSay/
    ### start tst tstJSay #############################################
    *** err: bad option openArg in jOpen(<obj j of JRW>, openArg)
    *** err: jWrite(<obj j of JRW>, writeArg) but not opened w
    *** err: can only write JSay.jOpen(<obj s of JSay>, <)
    *** err: jWrite(<obj s of JSay>, write s vor open) but not opened+
    . w
    *** err: can only read JRWEof.jOpen(<obj e of JRWEof>, >)
    *** err: jRead(<obj e of JRWEof>, XX) but not opened r
    read e vor open 0 m.xx valueBefore
    read e nach open 0 m.xx M.XX
    out eins
    #jIn 1# tst in line 1 eins ,
    out zwei in 1 vv=readAdrVV
    #jIn 2# tst in line 2 zwei ;   .
    out drei in 1 vv=readAdrVV Schluss
$/tstJSay/ */

    call jIni
    call tst t, 'tstJSay'
    jrw = oNew('JRW')
    call mAdd t'.TRANS', jrw '<obj j of JRW>'
    call jOpen jrw, 'openArg'
    call jWrite jrw, 'writeArg'
    s = oNew('JSay')
    call mAdd t'.TRANS', s '<obj s of JSay>'
    call jOpen s, m.j.cRead
    s = oNew('JSay')
    call mAdd t'.TRANS', s '<obj s of JSay>'
    call jWrite s, 'write s vor open'
    call jOpen s, '>'
    call jWrite s, 'write s nach open'
    e = oNew('JRWEof')
    call mAdd t'.TRANS', e '<obj e of JRWEof>'
    call jOpen e, '>'
    e = oNew('JRWEof')
    call mAdd t'.TRANS', e '<obj e of JRWEof>'
    m.xx = 'valueBefore'
    call tstOut t, 'read e vor open' jRead(e, xx) 'm.xx' m.xx
    call jOpen e, m.j.cRead
    call tstOut t, 'read e nach open' jRead(e, xx) 'm.xx' m.xx
    call out 'out eins'
    vv = 'readAdrVV'
    m.vv = 'readAdrVVValueBefore'
    call out 'out zwei in' in(vv) 'vv='vv
    m.vv = 'readAdrVVValueBefore'
    call out 'out drei in' in(vv) 'vv='vv 'Schluss'
    call tstEnd t
    return
endProcedure tstJSay

tstJ: procedure expose m.
/*
$=/tstJ/
    ### start tst tstJ ################################################
    out eins
    #jIn 1# tst in line 1 eins ,
    1 in() tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    2 in() tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    3 in() tst in line 3 drei .schluss..
    #jIn eof 4#
    in() 3 reads vv VV
    line buf line one
    line buf line two
    line buf line three
    line buf line four
    *** err: jWrite(<buf b>, buf line five while reading) but not opene+
    d w
$/tstJ/ */

    call jIni
    call tst t, "tstJ"
    b = jOpen(jBuf(), '>')
    call mAdd t'.TRANS', b '<buf b>'
    call out 'out eins'
    do lx=1 by 1 while in(var)
        call out lx 'in()' m.var
        end
    call out 'in()' (lx-1) 'reads vv' vv
    call jOpen b, '>'
    call jWrite b, 'buf line one'
    call jClose b
    call mAdd m.b.stem, 'buf line two', 'buf line three'
    call jWrite b, 'buf line four'
    call jClose b
    call jOpen b, m.j.cRead
    do while (jRead(b, line))
        call out 'line' m.line
        end
    call jWrite b, 'buf line five while reading'
    call jClose b
    call tstEnd t
    return
endProcedure tstJ

tstJ2: procedure expose m.
/*
$=/tstJ2/
    ### start tst tstJ2 ###############################################
    class1 <Tst?1 class> <Tst?1 name>
    class2 <Tst?1 class> <Tst?1 name>
    class3 <Tst?1 class> <Tst?1 name>
    b read EINS feld eins, ZWEI feld zwei, DREI feld drei
    b read EINS feld eins, ZWEI feld zwei 2, DREI feld drei
    c read EINS feld eins, ZWEI feld zwei, DREI drei cat 1
    tstR: @tstWriteoV3 isA :<Tst?1 name>
    tstR:  .EINS = feld eins
    tstR:  .ZWEI = feld zwei
    tstR:  .DREI = drei cat 1
    c read EINS feld eins, ZWEI feld zwei 2, DREI drei cat 2
    tstR: @tstWriteoV4 isA :<Tst?1 name>
    tstR:  .EINS = feld eins
    tstR:  .ZWEI = feld zwei 2
    tstR:  .DREI = drei cat 2
$/tstJ2/ */

    call tst t, "tstJ2"
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call mAdd t'.TRANS', ty '<Tst?1 class>'
    call mAdd t'.TRANS', m.ty.name '<Tst?1 name>'
    call tstOut t, 'class1' ty m.ty.name
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call tstOut t, 'class2' ty m.ty.name
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call tstOut t, 'class3' ty m.ty.name
    call oMutate qq, ty
    m.qq.eins = 'feld eins'
    m.qq.zwei = 'feld zwei'
    m.qq.drei = 'feld drei'
    b = jBuf()
    call jOpen b, '>'
    call jWriteO b, oCopy(qq)
    m.qq.zwei = 'feld zwei 2'
    call jWriteO b, qq
    call jOpen jClose(b), m.j.cRead
    c = jOpen(jBuf(), '>')
    do xx=1 while assNN('res', jReadO(b))
        call out 'b read EINS' m.res.eins', ZWEI' m.res.zwei,
                                     || ', DREI' m.res.drei
        m.res.drei = 'drei cat' xx
        call jWriteO c, res
        end
    call jOpen jClose(c), m.j.cRead
    do while assNN('ccc', jReadO(c))
        call out 'c read EINS' m.ccc.eins', ZWEI' m.ccc.zwei,
                                     || ', DREI' m.ccc.drei
        call outO ccc
        end
    call tstEnd t
    return
endProcedure tstJ2

tstCat: procedure expose m.
/*
$=/tstCat/
    ### start tst tstCat ##############################################
    catRead 1 line 1
    catRead 2 line 2
    catRead 3 line 3
    appRead 1 line 1
    appRead 2 line 2
    appRead 3 line 3
    appRead 4 append 4
    appRead 5 append 5
$/tstCat/ */
    call catIni
    call tst t, "tstCat"
    i = cat(jBuf('line 1', 'line 2'), jBuf('line 3'))
    call jOpen i, m.j.cRead
    do lx=1 by 1 while jRead(i, v)
        call tstOut t, 'catRead' lx m.v
        end
    call jOpen jClose(i), m.j.cApp
    call jWrite i, 'append 4'
    call jWrite i, 'append 5'
    call jOpen jClose(i), m.j.cRead
    do lx=1 by 1 while jRead(i, v)
        call tstOut t, 'appRead' lx m.v
        end
    call tstEnd t
    return
endProcedure tstCat

tstEnv: procedure expose m.
    call pipeIni
/*
$=/tstEnv/
    ### start tst tstEnv ##############################################
    before pipeBeLa
    after pipeEnd
    *** err: jWrite(<jBuf c>, write nach pop) but not opened w
    #jIn 1# tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    before writeNow 2 c --> std
    before writeNow 1 b --> c
    b line eins
    b zwei |
    nach writeNow 1 b --> c
    add nach pop
    after push c only
    tst in line 1 eins ,
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    nach writeNow 2 c --> std
    *** err: jWrite(<jBuf c>, ) but not opened w
$/tstEnv/ */

    call tst t, "tstEnv"
    c = jBuf()
    call mAdd t'.TRANS', c '<jBuf c>'
    call out 'before pipeBeLa'
    b = jBuf("b line eins", "b zwei |")
    call pipe '+Ff', c, b
    call out 'before writeNow 1 b --> c'
    call pipeWriteNow
    call out 'nach writeNow 1 b --> c'
    call pipe '-'
    call out 'after pipeEnd'
    call jWrite c, 'write nach pop'
    call mAdd c'.BUF', 'add nach pop'
    call pipe '+A', c
    call out 'after push c only'
    call pipeWriteNow
    call pipe '-'
    call pipe '+f', , c
    call out 'before writeNow 2 c --> std'
    call pipeWriteNow
    call out 'nach writeNow 2 c --> std'
    call pipe '-'
    call jWrite c
    call tstEnd t
    return
endProcedure tstEnv

tstEnvCat: procedure expose m.
    call pipeIni
/*
$=/tstEnvCat/
    ### start tst tstEnvCat ###########################################
    c1 contents
    c1 line eins |
    before writeNow 1 b* --> c*
    b1 line eins|
    b2 line eins
    b2 zwei |
    c2 line eins |
    after writeNow 1 b* --> c*
    c2 contents
    c2 line eins |
$/tstEnvCat/ */

    call tst t, "tstEnvCat"

    b0= jBuf()
    b0= jBuf()
    b1= jBuf("b1 line eins|")
    b2 = jBuf("b2 line eins", "b2 zwei |")
    c1 = jBuf("c1 line eins |")
    c2 = jBuf("c2 line eins |")
    call pipe '+Af', c1, b0, b1, b2, c2
    call out 'before writeNow 1 b* --> c*'
    call pipeWriteNow
    call out 'after writeNow 1 b* --> c*'
    call pipe '-'
    call out 'c1 contents'
    call pipe '+f' , , c1
    call pipeWriteNow
    call pipe '-'
    call pipe '+f' , , c2
    call out 'c2 contents'
    call pipeWriteNow
    call pipe '-'
    call tstEnd t
    return
endProcedure tstEnvCat

tstPipe: procedure expose m.
    call pipeIni
/*
$=/tstPipe/
    ### start tst tstPipe #############################################
    .+0 vor pipeBegin
    #jIn 1# tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    .+7 nach pipeLast
    ¢7 +6 nach pipe 7!
    ¢7 +2 nach pipe 7!
    ¢7 +4 nach nested pipeLast 7!
    ¢7 (4 +3 nach nested pipeBegin 4) 7!
    ¢7 (4 (3 +1 nach pipeBegin 3) 4) 7!
    ¢7 (4 (3 tst in line 1 eins , 3) 4) 7!
    ¢7 (4 (3 tst in line 2 zwei ;    3) 4) 7!
    ¢7 (4 (3 tst in line 3 drei .schluss. 3) 4) 7!
    ¢7 (4 (3 +1 nach writeNow vor pipe 3) 4) 7!
    ¢7 (4 +3 nach preSuf vor nested pipeLast 4) 7!
    ¢7 +4 nach preSuf vor nested pipeEnd 7!
    ¢7 +5 nach nested pipeEnd vor pipe 7!
    ¢7 +6 nach writeNow vor pipeLast 7!
    .+7 nach writeNow vor pipeEnd
    .+8 nach pipeEnd
$/tstPipe/ */

    say 'x0' m.pipe.0
    call tst t, 'tstPipe'
    call out '+0 vor pipeBegin'
    say 'x1' m.pipe.0
    call pipe '+N'
    call out '+1 nach pipeBegin'
    call pipeWriteNow
    call out '+1 nach writeNow vor pipe'
    call pipe 'N|'
    call out '+2 nach pipe'
    call pipe '+N'
    call out '+3 nach nested pipeBegin'
    call pipePreSuf '(3 ', ' 3)'
    call out '+3 nach preSuf vor nested pipeLast'
    call pipe 'P|'
    call out '+4 nach nested pipeLast'
    call pipePreSuf '(4 ', ' 4)'
    call out '+4 nach preSuf vor nested pipeEnd'
    call pipe '-'
    call out '+5 nach nested pipeEnd vor pipe'
    call pipe 'N|'
    call out '+6 nach pipe'
    call pipeWriteNow
    say 'out +6 nach writeNow vor pipeLast'
    call out '+6 nach writeNow vor pipeLast'
    call pipe 'P|'
    call out '+7 nach pipeLast'
    call pipePreSuf '¢7 ', ' 7!'
    call out '+7 nach writeNow vor pipeEnd'
    call pipe '-'
    call out '+8 nach pipeEnd'
    say 'xx' m.pipe.0
    call tstEnd t
    return
endProcedure tstPipe

tstPipeS: procedure expose m.
/*
$=/tstPipeS/
    ### start tst tstPipeS ############################################
    eine einzige zeile
    nach all einzige Zeile
    select strip(creator) cr, strip(name) tb,
    (row_number()over())*(row_number()over()) rr
    from sysibm.sysTables
$/tstPipeS/
*/
    call tst t, "tstPipeS"
    call pipe '+s',, 'eine einzige zeile'
    call pipeWriteAll
    call out 'nach all einzige Zeile'
    call pipe 's',,
              , "select strip(creator) cr, strip(name) tb," ,
              ,      "(row_number()over())*(row_number()over()) rr" ,
              ,      "from sysibm.sysTables"
    call pipeWriteAll
    call pipe '-'
    call tstEnd t
    return
endProcedure tstPipeS

tstEnvVars: procedure expose m.
    call pipeIni
/*
$=/tstEnvVars/
    ### start tst tstEnvVars ##########################################
    put v1 value eins
    v1 hasKey 1 get value eins
    v2 hasKey 0
    one to theBur
    two to theBuf
    v1=value eins o=TST.ADR1
    v3=v3WieGehts? o=!v3WieGehts?
    v4=v4WieGehts? o=!v4WieGehts?
    o o0=<o0>
    *** err: no method o2String in class TstEnvVars0 of object <o0>
    *** err: o2String did not return
    s o0=0
    o o0=<o0>
    *** err: no method o2String in class TstEnvVars0 of object <o0>
    *** err: o2String did not return
    s o0=0
    o0&fSt0=rexx o0.fSt0 o=!rexx o0.fSt0
    o0&fRe0=rexx o0.fRe0 o=!rexx o0.fRe0
    o0&=rexx o0-value o=!rexx o0-value
        o o0=<o0>
    *** err: no method o2String in class TstEnvVars0 of object <o0>
    *** err: o2String did not return
    s o0=0
    o0&fSt0=put o0.fSt0 o=!put o0.fSt0
    o0&fRe0=putO o0.fRe0 o=!putO o0.fRe0
    o0&=put o0-value o=!put o0-value
$/tstEnvVars/
$=/tstEnvVars1/
    ### start tst tstEnvVars1 #########################################
    m.o1=put-o1-value m.o1.fStr=put-o1.fStr m.o1.fRef=<o0>
    *** err: no method o2String in class TstEnvVars1 of object <o1>
    *** err: o2String did not return
    o o1=<o1> s o1=0
    o1&fStr=put-o1.fStr o=!put-o1.fStr
    o1&=put-o1-value o=!put-o1-value
    *** err: no method o2String in class TstEnvVars0 of object <o0>
    *** err: o2String did not return
    o1&fRef=0 o=<o0>
    o1&fRef&fSt0=put o0.fSt0 o=!put o0.fSt0
    o1&fRef&fRe0=putO o0.fRe0 o=!putO o0.fRe0
    m.o1&fNest.fSt0= put-o1.fNest.fSt0 m.o1&fNest.fRe0= !put-o1&fNest.f+
    Re0
    o1&fNest.fSt0=put-o1.fNest.fSt0 o=!put-o1.fNest.fSt0
    o1&fNest&fRe0=put-o1&fNest.fRe0 o=!put-o1&fNest.fRe0
$/tstEnvVars1/
$=/tstEnvVars2/
    ### start tst tstEnvVars2 #########################################
    o2=<o2> getO(o2)=<o2> getO(o2&fRef)=<o1>
    o2&fRef&fStr=put-o1.fStr o=!put-o1.fStr
    o2&fRef&=put-o1-value o=!put-o1-value
    *** err: no method o2String in class TstEnvVars0 of object <o0>
    *** err: o2String did not return
    o2&fRef&fRef=0 o=<o0>
    o2&fRef&fRef&fSt0=put o0.fSt0 o=!put o0.fSt0
    o2&fRef&fRef&fRe0=putO o0.fRe0 o=!putO o0.fRe0
    o2&fRef&fNest.fSt0=put-o1.fNest.fSt0 o=!put-o1.fNest.fSt0
    o2&fRef&fNest&fRe0=put-o1&fNest.fRe0 o=!put-o1&fNest.fRe0
$/tstEnvVars2/
$=/tstEnvVarsS/
    ### start tst tstEnvVarsS #########################################
    oS=<oS> oS&fStS=<put oS.fStS>
    oS&fStV.0=1 oS&fStV.1=<put oS.fStV.1>
    m.oS.fStR.0=2 .2=!<put oS.fStR.2>
    oS&fStR.0=2 .1=<put oS.fStR.1> .2=<put oS.fStR.2>
    m.oS.0=9876 .1234=<put oS.1234>
    oS&0=9876 .12=M.<oS>.12 .1234=<put oS.1234>
$/tstEnvVarsS/
 */
    c0 = classNew('n? TstEnvVars0 u f fSt0 v, f = v, f fRe0 r')
    c1 = classNew('n? TstEnvVars1 u f fStr v,f fRef r' ,
              ', f fNest TstEnvVars0, f = v')
    o0 = oNew(c0)
    o1 = oNew(c1)
    o2 = oNew(c1)
    call tst t, "tstEnvVars"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'
    call vRemove 'v2'
    m.tst.adr1 = 'value eins'
    put1 = vPutO('v1', oMutate(tst'.'adr1, m.class.classV))
    call tstOut t, 'put v1' m.put1
    call tstOut t, 'v1 hasKey' vHasKey('v1') 'get' vGet('v1')
    call tstOut t, 'v2 hasKey' vHasKey('v2')
    if 0 then
        call tstOut t, 'v2 get'    vGet('v2')
    call vPutO 'theBuf', jBuf()
    call pipe '+F' , vGetO('theBuf')
    call out 'one to theBur'
    call out 'two to theBuf'
    call pipe '-'
    call pipe '+f',, vGetO('theBuf')
    call pipeWriteNow
    call pipe '-'
    call tstOut t, 'v1='vGet('v1') 'o='vGetO('v1')
    call vPut 'v3', 'v3WieGehts?'
    call tstOut t, 'v3='vGet('v3') 'o='vGetO('v3')
    call vPutO 'v4', s2o('v4WieGehts?')
    call tstOut t, 'v4='vGet('v4') 'o='vGetO('v4')

    call vPutO 'o0', o0
    call tstOut t, 'o o0='vGetO('o0')
    call tstOut t, 's o0='vGet('o0')
    fSt0 = 'fSt0'
    fRe0 = 'fRe0'
    m.o0 = 'rexx o0-value'
    m.o0.fSt0 = 'rexx o0.fSt0'
    m.o0.fRe0 = s2o('rexx o0.fRe0')
    call tstOut t, 'o o0='vGetO('o0')
    call tstOut t, 's o0='vGet('o0')
    call tstOut t, 'o0&fSt0='vGet('o0&fSt0') 'o='vGetO('o0&fSt0')
    call tstOut t, 'o0&fRe0='vGet('o0&fRe0') 'o='vGetO('o0&fRe0')
    call tstOut t, 'o0&='vGet('o0&') 'o='vGetO('o0&')

    call vPut 'o0&', 'put o0-value'
    call vPut 'o0&fSt0', 'put o0.fSt0'
    call vPutO 'o0&fRe0', s2o('putO o0.fRe0')
    call tstOut t, 'o o0='vGetO('o0')
    call tstOut t, 's o0='vGet('o0')
    call tstOut t, 'o0&fSt0='vGet('o0&fSt0') 'o='vGetO('o0&fSt0')
    call tstOut t, 'o0&fRe0='vGet('o0&fRe0') 'o='vGetO('o0&fRe0')
    call tstOut t, 'o0&='vGet('o0&') 'o='vGetO('o0&')

    call tstEnd t
    call tst t, "tstEnvVars1"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'

    call vPutO 'o1', o1
    call vPut 'o1&', 'put-o1-value'
    call vPut 'o1&fStr', 'put-o1.fStr'
    call vPutO 'o1&fRef', vGetO('o0')
    call tstOut t, 'm.o1='m.o1 'm.o1.fStr='mGet(o1'.fStr'),
         'm.o1.fRef='mGet(o1'.fRef')
    call tstOut t, 'o o1='vGetO('o1') 's o1='vGet('o1')
    call tstOut t, 'o1&fStr='vGet('o1&fStr') 'o='vGetO('o1&fStr')
    call tstOut t, 'o1&='vGet('o1&') 'o='vGetO('o1&')

    call tstOut t, 'o1&fRef='vGet('o1&fRef') 'o='vGetO('o1&fRef')
    call tstOut t, 'o1&fRef&fSt0='vGet('o1&fRef&fSt0') ,
        'o='vGetO('o1&fRef&fSt0')
    call tstOut t, 'o1&fRef&fRe0='vGet('o1&fRef&fRe0'),
         'o='vGetO('o1&fRef&fRe0')

    call vPut 'o1&fNest.fSt0', 'put-o1.fNest.fSt0'
    call vPutO 'o1&fNest.fRe0', s2o('put-o1&fNest.fRe0')
    call tstOut t, 'm.o1&fNest.fSt0=' mGet(o1'.fNest.fSt0') ,
            'm.o1&fNest.fRe0=' mGet(o1'.fNest.fRe0')
    call tstOut t, 'o1&fNest.fSt0='vGet('o1&fNest.fSt0'),
         'o='vGetO('o1&fNest.fSt0')
    call tstOut t, 'o1&fNest&fRe0='vGet('o1&fNest.fRe0'),
         'o='vGetO('o1&fNest.fRe0')
    call tstEnd t

    call tst t, "tstEnvVars2"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'
    call vPutO 'o2', o2
    call vPutO 'o2&fRef', vGetO('o1')
    call tstOut t, 'o2='o2 'getO(o2)='vGetO('o2'),
        'getO(o2&fRef)='vGetO('o2&fRef')

    call tstOut t, 'o2&fRef&fStr='vGet('o2&fRef&fStr'),
         'o='vGetO('o2&fRef&fStr')
    call tstOut t, 'o2&fRef&='vGet('o2&fRef&'),
     'o='vGetO('o2&fRef&')

    call tstOut t, 'o2&fRef&fRef='vGet('o2&fRef&fRef') ,
        'o='vGetO('o2&fRef&fRef')
    call tstOut t, 'o2&fRef&fRef&fSt0='vGet('o2&fRef&fRef&fSt0') ,
        'o='vGetO('o2&fRef&fRef&fSt0')
    call tstOut t, 'o2&fRef&fRef&fRe0='vGet('o2&fRef&fRef&fRe0'),
         'o='vGetO('o2&fRef&fRef&fRe0')
    call tstOut t, 'o2&fRef&fNest.fSt0='vGet('o2&fRef&fNest.fSt0'),
         'o='vGetO('o2&fRef&fNest.fSt0')
    call tstOut t, 'o2&fRef&fNest&fRe0='vGet('o2&fRef&fNest.fRe0'),
         'o='vGetO('o1&fNest.fRe0')
    call tstEnd t

    cS = classNew('n? TstEnvVarsS u f fStS v,f fStV s v, f fStR s r',
        ', f fNeS s TstEnvVars0, f = s v')
    oS = oNew(cS)
    call vPutO 'oS', oS
    oT = oNew(cS)
    call tst t, "tstEnvVarsS"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>',
        , oS '<oS>', oT '<oT>'
    call mPut oS'.fStS', '<put oS.fStS>'
    call tstOut t, 'oS='vGetO('oS') 'oS&fStS='vGet('oS&fStS')
    call mPut oS'.fStV.1', '<put oS.fStV.1>'
    call mPut oS'.fStV.0', 1
    call tstOut t, 'oS&fStV.0='vGet('oS&fStV.0'),
         'oS&fStV.1='vGet('oS&fStV.1')
    call mPut oS'.fStR.1', s2o('<put oS.fStR.1>')
    call mPut oS'.fStR.2', s2o('<put oS.fStR.2>')
    call mPut oS'.fStR.0', 2
    call tstOut t, 'm.oS.fStR.0='mGet(oS'.fStR.0'),
     '.2='mGet(oS'.fStR.2')
    call tstOut t, 'oS&fStR.0='vGet('oS&fStR.0'),
         '.1='vGet('oS&fStR.1') '.2='vGet('oS&fStR.2')
    call mPut oS'.1234', '<put oS.1234>'
    call mPut oS'.0', 9876
    call mPut oS'.fStR.0', 2
    call tstOut t, 'm.oS.0='mGet(oS'.0'),
     '.1234='mGet(oS'.1234')
    call tstOut t, 'oS&0='vGet('oS&0'),
         '.12='vGet('oS&12') '.1234='vGet('oS&1234')
    call tstEnd t
    return
endProcedure tstEnvVars

tstvWith: procedure expose m.
/*
$=/tstEW2/
    ### start tst tstEW2 ##############################################
    tstK1             TSTEW1
    tstK1&            get1 w
    tstK1&f1          get1.f1 v
    tstK1&f2          get1.f2 w
    tstK1&F3          get1.f3 v
    ttstK1&F3.FEINS   get1.f3.fEins v
    tstK1&F3.FZWEI    get1.f3.fZwei w
    tstK1&F3.FDREI o  !get1.f3.fDrei w
    tstK1&F3.FDREI    get1.f3.fDrei w
    tstK1&F3.1        get1.f3.1 w
    tstK1&F3.2        TSTEW1
    tstK1&F3.2&F1     get1.f1 v
    tstK1&F3.2&F3.2&F2 get1.f2 w
    *** err: missing key in mapGet(V, F1)
    *** err: no class found for object 0
    *** err: o2String did not return
    F1          0
    F1          get1.f1 v
    f2          get1.f2 w
    F3          get1.f3 v
    F3.FEINS    get1.f3.fEins v
    F3.FZWEI    get1.f3.fZwei w
    F3.FDREI o  !get1.f3.fDrei w
    F3.1        get1.f3.1 w
    pu1 F1      get1.f1 v
    pu2 F1      get2.f1 v
    po-2 F1     get1.f1 v
    *** err: missing key in mapGet(V, F1)
    *** err: no class found for object 0
    *** err: o2String did not return
    po-1 F1     0
$/tstEW2/  */
    call pipeIni
    c0 = classNew('n? TstEW0 u f FEINS v,f FZWEI w, f FDREI r,v,s r')
    call oClaMet c0, 'oFlds' /* new would do it, but we donot use it */
    cl = classNew('n? TstEW u r TstEW0, f F1 v, f F2 r, f F3 TstEW0')
    call oClaMet cl, 'oFlds' /* new would do it, but we donot use it */
    call oMutate tstEW1, cl
    m.tstEW1    = s2o('get1 w')
    m.tstEW1.f1 = 'get1.f1 v'
    m.tstEW1.f2 = s2o('get1.f2 w')
    m.tstEW1.f3 = 'get1.f3 v'
    m.tstEW1.f3.fEins = 'get1.f3.fEins v'
    m.tstEW1.f3.fZwei = s2o('get1.f3.fZwei w')
    m.tstEW1.f3.fDrei = s2o('get1.f3.fDrei w')
    m.tstEW1.f3.0 = 3
    m.tstEW1.f3.1 = s2o('get1.f3.1 w')
    m.tstEW1.f3.2 = tstEW1
    m.tstEW1.f3.3 = s2o('get1.f3.3 w')
    call oMutate tstEW2, cl
    m.tstEW2    = s2o('get2 w')
    m.tstEW2.f1 = 'get2.f1 v'
    m.tstEW2.f2 = s2o('get2.f2 w')
    call vPutO 'tstK1', tstEW1

    call tst t, 'tstEW2'
    call tstOut t, 'tstK1            ' vGetO('tstK1')
    call tstOut t, 'tstK1&           ' vGet('tstK1&')
    call tstOut t, 'tstK1&f1         ' vGet('tstK1&F1')
    call tstOut t, 'tstK1&f2         ' vGet('tstK1&F2')
    call tstOut t, 'tstK1&F3         ' vGet('tstK1&F3')
    call tstOut t, 'ttstK1&F3.FEINS  ' vGet('tstK1&F3.FEINS')
    call tstOut t, 'tstK1&F3.FZWEI   ' vGet('tstK1&F3.FZWEI')
    call tstOut t, 'tstK1&F3.FDREI o ' vGetO('tstK1&F3.FDREI')
    call tstOut t, 'tstK1&F3.FDREI   ' vGet('tstK1&F3.FDREI')
    call tstOut t, 'tstK1&F3.1       ' vGet('tstK1&F3.1')
    call tstOut t, 'tstK1&F3.2       ' vGetO('tstK1&F3.2')
    call tstOut t, 'tstK1&F3.2&F1    ' vGet('tstK1&F3.2&F1')
    call tstOut t, 'tstK1&F3.2&F3.2&F2' ,
                                vGet('tstK1&F3.2&F3.2&F2')
    call tstOut t, 'F1         ' vGet('F1')
    call vWith '+', tstEW1
    call tstOut t, 'F1         ' vGet('F1')
    call tstOut t, 'f2         ' vGet('F2')
    call tstOut t, 'F3         ' vGet('F3')
    call tstOut t, 'F3.FEINS   ' vGet('F3.FEINS')
    call tstOut t, 'F3.FZWEI   ' vGet('F3.FZWEI')
    call tstOut t, 'F3.FDREI o ' vGetO('F3.FDREI')
    call tstOut t, 'F3.1       ' vGet('F3.1')
    call tstOut t, 'pu1 F1     ' vGet('F1')
    call vWith '+', tstEW2
    call tstOut t, 'pu2 F1     ' vGet('F1')
    call vWith '-'
    call tstOut t, 'po-2 F1    ' vGet('F1')

    call vWith '-'
    call tstOut t, 'po-1 F1    ' vGet('F1')
    call tstEnd t
/*
$=/tstEW3/
    ### start tst tstEW3 ##############################################
    .          s c3&F1          = v(c3&f1)
    *** err: vGet flag f
    .          s c3&F1&FEINS    = 0
    *** err: vAdr null at c3&F3& in name c3&F3&FEINS
    *** err: vGet flag f
    .          s c3&F3&FEINS    = 0
    .          s c3&F3.FEINS    = val(c3&F3.FEINS)
    *** err: vGet flag f
    .          s c3&FEINS       = 0
    getO c3&
    aft Put   s c3&&FEINS      = v&&fEins
    Push c3   s F3.FEINS       = val(c3&F3.FEINS)
    aftPut=   s F3.FEINS       = pushPut(F3.FEINS)
    push c4   s F1             = v(c4&f1)
    put f2    s F2             = put(f2)
    put ..    s F3.FEINS       = put(f3.fEins)
    popW c4   s F1             = v(c3&f1)
    *** err: missing key in mapGet(V, F1)
    *** err: no class found for object 0
    *** err: o2String did not return
    popW c3   s F1             = 0
    .          s F222           = f222 pop stop
$/tstEW3/
*/
    call tst t, 'tstEW3'
    c3 = oNew('TstEW')
    call mAdd t.trans, c3 '<c3>'
    m.c3.f1 = 'v(c3&f1)'
    call vPutO 'c3', c3
    call tstEnvSG , 'c3&F1'
    call tstEnvSG , 'c3&F1&FEINS'
    call tstEnvSG , 'c3&F3&FEINS'
    call vPut 'c3&F3.FEINS', 'val(c3&F3.FEINS)'
    call tstEnvSG , 'c3&F3.FEINS'
    call tstEnvSG , 'c3&FEINS'
    call tstOut t,  'getO c3&', vGetO('c3&')
    call vPutO 'c3&', oNew('TstEW0')
    call vPut 'c3&&FEINS', 'v&&fEins'
    call tstEnvSG 'aft Put', 'c3&&FEINS'
    call vWith '+', c3
    call tstEnvSG 'Push c3', 'F3.FEINS'
    call vPut 'F3.FEINS', 'pushPut(F3.FEINS)'
    call tstEnvSG 'aftPut=', 'F3.FEINS'

    c4 = oNew('TstEW')
    call mAdd t.trans, c4 '<c4>'
    m.c4.f1 = 'v(c4&f1)'
    call vPut f222, 'f222 no stop'
    call vWith '+',  c4
    call tstEnvSG 'push c4', f1
    call vPut f2, 'put(f2)'
    call tstEnvSG 'put f2', f2
    call vPut f222, 'f222 stopped', 1
    call vPut 'F3.FEINS', 'put(f3.fEins)'
    call tstEnvSG 'put .. ', 'F3.FEINS'
    call vWith '-'
    call tstEnvSG 'popW c4', f1
    call vWith '-'
    call vPut f222, 'f222 pop stop'
    call tstEnvSG 'popW c3', f1
    call tstEnvSG          , f222
    call tstEnd t
    return
endProcedure tstvWith

tstEnvSG: procedure expose m. t
parse arg txt, nm
    call tstOut t, left(txt,10)'s' left(nm, 15)'=' vGet(nm)
    return

tstPipeLazy: procedure expose m.
    call pipeIni
/*
$=/tstPipeLazy/
    ### start tst tstPipeLazy #########################################
    a1 vor pipeBegin loop lazy 0 writeNow *** <class TstPipeLazyBuf>
    bufOpen <
    bufClose
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    a5 vor 2 writeNow in inIx 4
    a2 vor writeNow jBuf
    jBuf line 1
    jBuf line 2
    a3 vor writeNow in inIx 1
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    a4 vor barLast inIx 4
    a6 vor barEnd inIx 4
    a7 nach barEnd lazy 0 writeNow ***
    b1 vor barBegin lazy 0 writeNow *** <class TstPipeLazyRdr>
    RdrOpen <
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    rdrClose
    b4 vor writeNow
    b2 vor writeNow rdr inIx 2
    jRead lazyRdr
    tst in line 3 drei .schluss..
    jRead lazyRdr
    b3 vor barLast inIx 4
    b5 vor barEnd inIx 4
    b6 nach barEnd lazy 0 writeNow ***
    a1 vor pipeBegin loop lazy 1 writeAll *** +
        .<class TstPipeLazyBuf>
    a5 vor 2 writeAll in inIx 0
    a2 vor writeAll jBuf
    bufOpen <
    jBuf line 1
    jBuf line 2
    bufClose
    a3 vor writeAll in inIx 0
    #jIn 1# tst in line 1 eins ,
    tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    #jIn eof 4#
    a4 vor barLast inIx 0
    a6 vor barEnd inIx 4
    a7 nach barEnd lazy 1 writeAll ***
    b1 vor barBegin lazy 1 writeAll *** <class TstPipeLazyRdr>
    b4 vor writeAll
    b2 vor writeAll rdr inIx 1
    RdrOpen <
    jRead lazyRdr
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    jRead lazyRdr
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    jRead lazyRdr
    #jIn eof 4#
    rdrClose
    b3 vor barLast inIx 1
    b5 vor barEnd inIx 4
    b6 nach barEnd lazy 1 writeAll ***
$/tstPipeLazy/ */
    call tst t, "tstPipeLazy"
    do lz=0 to 1
        if lz then
            w = 'writeAll'
        else
            w = 'writeNow'
        m.t.inIx = 1-lz
        ty = class4Name('TstPipeLazyBuf', '')
        if ty == '' then do
            ty = classNew('n TstPipeLazyBuf u JRWDeleg', 'm',
            , 'jOpen call tstOut "T", "bufOpen" opt;',
              'call jOpen m.m.deleg, opt',
            , 'jClose call tstOut "T", "bufClose";',
              'call jClose m.m.deleg')
            end
        if \ lz then
             call mAdd t'.TRANS', ty '<class TstPipeLazyBuf>'
        call out 'a1 vor pipeBegin loop lazy' lz w '***' ty
        call pipe '+N'
        call out 'a2 vor' w 'jBuf'
        b = oNew('TstPipeLazyBuf', jBuf('jBuf line 1','jBuf line 2'))
        interpret 'call pipe'w 'b'
        call out 'a3 vor' w 'in inIx' m.t.inIx
        interpret 'call pipe'w
        call out 'a4 vor barLast inIx' m.t.inIx
        call pipe 'P|'
        call out 'a5 vor 2' w 'in inIx' m.t.inIx
        interpret 'call pipe'w
        call out 'a6 vor barEnd inIx' m.t.inIx
        call pipe '-'
        call out 'a7 nach barEnd lazy' lz w '***'

        ty = class4Name('TstPipeLazyRdr', '')
        if ty == '' then
            ty = classNew('n TstPipeLazyRdr u JRW', 'm',
            , 'jOpen call tstOut "T", "RdrOpen" opt',
            , 'jRead call out "jRead lazyRdr";' ,
                  'return jRead(m.m.rdr, var);',
            , 'jClose call tstOut "T",  "rdrClose";')
        if \ lz then
             call mAdd t'.TRANS', ty '<class TstPipeLazyRdr>'

        r = oNew('TstPipeLazyRdr')
            m.r.rdr = m.j.in
        if lz then
            call mAdd t'.TRANS', r '<lazyRdr>'
     m.t.inIx = 2-lz
     call out 'b1 vor barBegin lazy' lz w '***' ty
     call pipe '+N'
     call out 'b2 vor' w 'rdr inIx' m.t.inIx
     interpret 'call pipe'w 'r'
     call out 'b3 vor barLast inIx' m.t.inIx
     call pipe 'P|'
        call out 'b4 vor' w
        interpret 'call pipe'w
        call out 'b5 vor barEnd inIx' m.t.inIx
        call pipe '-'
     call out 'b6 nach barEnd lazy' lz w '***'
        end
    call tstEnd t
    return
endProcedure tstPipeLazy

tstEnvClass: procedure expose m.
    call pipeIni
/*
$=/tstEnvClass/
    ### start tst tstEnvClass #########################################
    a0 vor pipeBegin loop lazy 0 writeNow *** TY
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    a5 vor writeNow
    a1 vor jBuf()
    a2 vor writeNow b
    tstR: @<o20 of TstEnvClass10> isA :TstEnvClass10
    tstR:  .f11 = .
    tstR:  .F12 = value F12 of o1 <o20 of TstEnvClass10>
    tstR:  .f13 = .
    WriteO o2
    tstR: @<oc0 of TstEnvClass20> isA :TstEnvClass20 = valueO2Lazy0
    tstR:  .f24 = .
    tstR:  .F25 = value F25 of o20 <o20 of TstEnvClass20>
    a3 vor writeNow
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    a4 vor barLast inIx 4
    a6 vor barEnd
    a7 nach barEnd lazy 0 writeNow ***
    a0 vor pipeBegin loop lazy 1 writeAll *** TY
    a5 vor writeAll
    a1 vor jBuf()
    a2 vor writeAll b
    tstR: @<o21 of TstEnvClass10> isA :TstEnvClass10
    tstR:  .f11 = .
    tstR:  .F12 = value F12 of o1 <o21 of TstEnvClass10>
    tstR:  .f13 = .
    WriteO o2
    tstR: @<oc1 of TstEnvClass20> isA :TstEnvClass20 = valueO2Lazy1
    tstR:  .f24 = .
    tstR:  .F25 = value F25 of o21 <o21 of TstEnvClass20>
    a3 vor writeAll
    #jIn 1# tst in line 1 eins ,
    tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    #jIn eof 4#
    a4 vor barLast inIx 0
    a6 vor barEnd
    a7 nach barEnd lazy 1 writeAll ***
$/tstEnvClass/ */

    call tst t, "tstEnvClass"
    t10 = classNew('n? TstEnvClass10 u f f11 v, f F12 v, f f13 v')
    t20 = classNew('n? TstEnvClass20 u v, f f24 v, f F25 v')
    do lz=0 to 1
        if lz then
            w = 'writeAll'
        else
            w = 'writeNow'
        m.t.inIx = 1-lz
        call out 'a0 vor pipeBegin loop lazy' lz w '***' ty
        call pipe '+N'
        call out 'a1 vor jBuf()'
        b = jOpen(jBuf(), m.j.cWri)
        o1 = oNew('TstEnvClass10')
        m.o1.F12 = 'value F12 of o1' o1
        call mAdd t'.TRANS', o1 '<o2'lz 'of TstEnvClass10>'
        call jWriteO b, o1
        call jWrite b, 'WriteO o2'
        o2 = oNew('TstEnvClass20')
        m.o2 = 'valueO2Lazy'lz
        m.o2.F25 = 'value F25 of o2'lz o2
        oc = oCopy(oCopy(o2))
        call mAdd t'.TRANS', o2 '<o2'lz 'of TstEnvClass20>'
        call mAdd t'.TRANS', oc '<oc'lz 'of TstEnvClass20>'
        call jWriteO b, oc
        call out 'a2 vor' w 'b'
        interpret 'call pipe'w jClose(b)
        call out 'a3 vor' w
        interpret 'call pipe'w
        call out 'a4 vor barLast inIx' m.t.inIx
        call pipe 'P|'
        call out 'a5 vor' w
        interpret 'call pipe'w
        call out 'a6 vor barEnd'
        call pipe '-'
        call out 'a7 nach barEnd lazy' lz w '***'
        end
    call tstEnd t
    m.t.trans.0 = 0
    return
endProcedure tstEnvClass

tstFile: procedure expose m.
    call catIni
/*
$=/tstFile/
    ### start tst tstFile #############################################
    write read 0 last 10 vor anfang
    write read 1 last 80  links1 1   und rechts |  ..
    write read 2 last 80 liinks2 2   und rechts |  ..
    write read 5 last 80 links5 5 rechts5
    write read 99 last 80 links99 99 rechts
    write read 100 last 80 links100 100 rechts
    write read 101 last 80 links101 101 rechts
    write read 999 last 80 links999 999 rechts
    write read 1000 last 80 links1000 1000 rechts
    write read 1001 last 80 links1001 1001 rechts
    write read 2109 last 80 links2109 2109 rechts
    out > eins 1                                                       +
    .             .
    out > eins 2 schluss.                                              +
    .             .
    buf eins
    buf zwei
    buf drei
    out > zwei mit einer einzigen Zeile                                +
    .             .
    . links1 1   und rechts |  .                                       +
    .              .
$/tstFile/ */
    call tst t, "tstFile"
    pds = tstFilename('lib', 'r')
    call tstFileWr pds, 0, ' links0', '  und rechts |  .  '
    call tstFileWr pds, 1, ' links1', '  und rechts |  .  '
    call tstFileWr pds, 2, 'liinks2', '  und rechts |  .  '
    call tstFileWr pds, 5, 'links5', 'rechts5'
    call tstFileWr pds, 99, 'links99', 'rechts'
    call tstFileWr pds, 100, 'links100', 'rechts'
    call tstFileWr pds, 101, 'links101', 'rechts'
    call tstFileWr pds, 999, 'links999', 'rechts'
    call tstFileWr pds, 1000, 'links1000', 'rechts'
    call tstFileWr pds, 1001, 'links1001', 'rechts'
    call tstFileWr pds, 2109, 'links2109', 'rechts'
    pd2 = tstFilename('li2', 'r')
    call pipeIni
    call pipe '+F', s2o(tstPdsMbr(pd2, 'eins'))
    call out tstFB('out > eins 1') /* simulate fixBlock on linux */
    call out tstFB('out > eins 2 schluss.')
    call pipe '-'
    call pipe '+F', s2o(tstPdsMbr(pd2, 'zwei'))
    call out tstFB('out > zwei mit einer einzigen Zeile')
    call pipe '-'
    b = jBuf("buf eins", "buf zwei", "buf drei")
    call pipe '+f', , s2o(tstPdsMbr(pd2, 'eins')), b,
                    ,jBuf(),
                    ,s2o(tstPdsMbr(pd2, 'zwei')),
                    ,s2o(tstPdsMbr(pds, 'wr0')),
                    ,s2o(tstPdsMbr(pds, 'wr1'))
    call pipeWriteNow
    call pipe '-'
    call tstEnd t
    return
endProcedure tstFile

/*--- simulate fixBlock 80 on linux ---------------------------------*/
tstFB: procedure expose m.
parse arg line, recL
    if m.err.os \== 'LINUX' then
        return line
    else if recL == '' then
        return left(line, 80)
    else
        return left(line, recL)
endProcedure tstFB

tstPdsMbr: procedure expose m.
parse arg pds, mbr
    if m.err.os = 'TSO' then
        return pds'('mbr') ::F'
    if m.err.os = 'LINUX' then
        return pds'.'mbr
    call err 'tstPdsMbr implement os' m.err.os
endProcedure tstPdsMbr

tstFileWR: procedure expose m.
parse arg dsn, num, le, ri
    io = file(tstPdsMbr(dsn, 'wr'num))
    call jOpen io, m.j.cWri
    do x = 1 to num /* simulate fixBlock 80 on LINUX*/
        call jWrite io, tstFB(le x ri)
        end
    call jClose io
    if num > 100 then
        call jReset io, tstPdsMbr(dsn, 'wr'num)

    call jOpen io, m.j.cRead
    m.vv = 'vor anfang'
    do x = 1 to num
        if \ jRead(io, vv) then
            call err x 'not jRead'
        else if m.vv <> le x ri then
            call err x 'read mismatch' m.vv
        end
    if jRead(io, vv) then
        call err x 'jRead but should be eof 1'
    if jRead(io, vv) then
        call err x'+1 jjRead but should be eof 2'
    call jClose io
    call tstOut t, 'write read' num 'last' length(m.vv) strip(m.vv,'t')
    return
endProcedure tstFileRW

tstFileList: procedure expose m.
    call catIni
/*
$=/tstFileList/
    ### start tst tstFileList #########################################
    empty dir
    filled dir
    <<pref 2 List>>eins
    <<pref 2 List>>zwei
    <<pref 2 List>>drei
    <<pref 2 List>>vier
    filled dir recursive
    <<pref 2 List>>eins
    <<pref 2 List>>zwei
    <<pref 2 List>>drei
    <<pref 2 List>>vier
    <<pref 1 vier>>eins
    <<pref 1 vier>>zwei
    <<pref 1 vier>>drei
$/tstFileList/ */
/*
$=/tstFileListTSO/
    ### start tst tstFileListTSO ######################################
    empty dir
    filled dir
    <<pref 2 LIST>>DREI
    <<pref 2 LIST>>EINS
    <<pref 2 LIST>>ZWEI
    filled dir recursive
    <<pref 2 LIST>>DREI
    <<pref 2 LIST>>EINS
    <<pref 1 VIER>>DREI
    <<pref 1 VIER>>EINS
    <<pref 1 VIER>>ZWEI
    <<pref 2 LIST>>ZWEI
$/tstFileListTSO/ */
    if m.err.os = 'TSO' then
        call tst t, "tstFileListTSO"
    else
        call tst t, "tstFileList"
    fi = file(tstFileName('FileList', 'r'))
    call fileMkDir fi
    fl = fileList(fi)
    call tstOut t, 'empty dir'
    call jWriteNow t, fl
    call tstFileListMake t, fi, 2
    call tstOut t, 'filled dir'
    call jWriteNow t, fl
    call tstOut t, 'filled dir recursive'
    call jWriteNow t, fileList(fi, 'r')
    call tstEnd t
    return
endProcedure tstFileList

tstFileListMake: procedure expose m.
parse arg t, fi, lev
    if \ fileIsDir(fi) then
        call fileMkDir fi
    call mAdd t'.TRANS', filePath(fi) || m.file.sep ,
                '<<pref' lev right(filePath(fi),4)'>>'
    call jCat fileChild(fi, 'eins','::F'),jBuf('zeile eins','eins' lev)
    call jCat fileChild(fi, 'zwei','::F'),jBuf('zeile zwei','zwei' lev)
    call jCat fileChild(fi, 'drei','::F'),jBuf('zeile drei','drei' lev)
    if lev > 1 then
        call tstFileListMake t, fileChild(fi, 'vier'), lev-1
    return
endProcedure tstFileListMake

tstMail: procedure expose m.
do i=1 to 3
    call mailHead xy, 'mail from walter''s rexx' time() i, A540769
    call mAdd xy, 'und hier kommt der text' ,
                , 'und zeile zwei timestamp' i':' date('s') time() ,
                , left('und eine lange Zeile 159', 156, '+')159 ,
                , left('und eine lange Zeile 160', 157, '+')160 ,
                , left('und eine lange Zeile 161', 158, '+')161 ,
                , left('und eine lange Zeile ', 200, '+')203 'y ',
                , 'und kurz' ,
                , 'und Schluss'
    call mailSend xy
    call sleep 60
    end
    return
endprocedure tstMail

tstF: procedure expose m.
/*
$=/tstF/
    ### start tst tstF ################################################
    f(1 23%c345%c67%%8, eins,  zwei ) =1 23eins345 zwei 67%8;
    f(1\S23%c345%S67%%8, eins,  zwei ) =1\S23eins345zwei67%8;
    f(1 23%C345%C67%%8, eins,  zwei ) =1 23eins345 zwei 67%8;
    f(1 23%c345%S67%%8, eins,  zwei ) =1 23eins345zwei67%8;
    f(1%S2%c3@2%S4@%c5, eins,  zwei ) =1eins2 zwei 3zwei4 zwei 5;
    f(1%-2C2%3C3@2%3.2C4, eins,  zwei ) =1ei2ei 3zwe4;
    f(1@F1%c2@f2%c3@F3%c4, eins,  zwei ) =1fEins2fZwei3fDrei4;
    tstF2 _ %-9C @%5I @%8I @%+8I @%-8I -----
    _ 0             0        0       +0 0       .
    _ -1.2         -1       -1       -1 -1      .
    _ 2.34          2        2       +2 2       .
    _ -34.8765    -35      -35      -35 -35     .
    _ 567.91234   568      568     +568 568     .
    _ -8901     -8901    -8901    -8901 -8901   .
    _ 23456     23456    23456   +23456 23456   .
    _ -789012   *****  -789012  -789012 -789012 .
    _ 34e6      ***** 34000000 ******** 34000000
    _ -56e7     ***** ******** ******** ********
    _ 89e8      ***** ******** ******** ********
    _ txtli     txtli    txtli    txtli txtli   .
    _ undEinLan undEi undEinLa undEinLa undEinLa
    tstF2 _ %-9C @%5.2I @%12.2I @%+12.2I @%-12.2I -----
    _ 0          0.00         0.00        +0.00 0.00        .
    _ -1.2      -1.20        -1.20        -1.20 -1.20       .
    _ 2.34       2.34         2.34        +2.34 2.34        .
    _ -34.8765  *****       -34.88       -34.88 -34.88      .
    _ 567.91234 *****       567.91      +567.91 567.91      .
    _ -8901     *****     -8901.00     -8901.00 -8901.00    .
    _ 23456     *****     23456.00    +23456.00 23456.00    .
    _ -789012   *****   -789012.00   -789012.00 -789012.00  .
    _ 34e6      *****  34000000.00 +34000000.00 34000000.00 .
    _ -56e7     ***** ************ ************ ************
    _ 89e8      ***** ************ ************ ************
    _ txtli     txtli        txtli        txtli txtli       .
    _ undEinLan undEi undEinLanger undEinLanger undEinLanger
    tstF2 _ %-9C @%7e @% 8E @% 9.3e @% 11.4E -----
    _ 0         0.00e00  0.00E00  0.000e00  0.0000E000
    _ -1.2      -1.2e00 -1.20E00 -1.200e00 -1.2000E000
    _ 2.34      2.34e00  2.34E00  2.340e00  2.3400E000
    _ -34.8765  -3.5e01 -3.49E01 -3.488e01 -3.4877E001
    _ 567.91234 5.68e02  5.68E02  5.679e02  5.6791E002
    _ -8901     -8.9e03 -8.90E03 -8.901e03 -8.9010E003
    _ 23456     2.35e04  2.35E04  2.346e04  2.3456E004
    _ -789012   -7.9e05 -7.89E05 -7.890e05 -7.8901E005
    _ 34e6      3.40e07  3.40E07  3.400e07  3.4000E007
    _ -56e7     -5.6e08 -5.60E08 -5.600e08 -5.6000E008
    _ 89e8      8.90e09  8.90E09  8.900e09  8.9000E009
    _ txtli       txtli    txtli     txtli       txtli.
    _ undEinLan undEinL undEinLa undEinLan undEinLange
    _ 8.76e-07  8.76e-7  8.76E-7  8.760e-7  8.7600E-07
    _ 5.43e-11  5.4e-11  5.4E-11  5.43e-11  5.4300E-11
    _ -8.76e-07 -8.8e-7 -8.76E-7 -8.760e-7 -8.7600E-07
    _ -5.43e-11 -5e-011 -5.4E-11 -5.43e-11 -5.4300E-11
$/tstF/ */
    call tst t, 'tstF'
    call tstF1 '1 23%c345%c67%%8'
    call tstF1 '1\S23%c345%S67%%8'
    call tstF1 '1 23%C345%C67%%8'
    call tstF1 '1 23%c345%S67%%8'
    call tstF1 '1%S2%c3@2%S4@%c5'
    call tstF1 '1%-2C2%3C3@2%3.2C4'
    call tstF1 '1@F1%c2@f2%c3@F3%c4'
    nums = '0 -1.2 2.34 -34.8765 567.91234 -8901 23456' ,
                '-789012 34e6 -56e7 89e8 txtli undEinLangerText?'
    call tstF2 '_ %-9C @%5I @%8I @%+8I @%-8I', nums
    call tstF2 '_ %-9C @%5.2I @%12.2I @%+12.2I @%-12.2I', nums
    num2 = ' 8.76e-07  5.43e-11 -8.76e-07  -5.43e-11'
    call tstF2 '_ %-9C @%7e @% 8E @% 9.3e @% 11.4E', nums num2
    call tstEnd t
    return
endProcedure tstF

tstF1: procedure expose m.
parse arg fmt
    e='eins'
    z=' zwei '
    f2 = 'f2'
    m.e.f1 = 'fEins'
    m.e.f2 = 'fZwei'
    m.e.f3 = 'fDrei'
    call out "f("fmt"," e"," z") ="f(fmt, e, z)";"
    return
endProcedure tstF1

tstF2: procedure expose m.
parse arg fmt, vals
    call out 'tstF2' fmt '-----'
    do vx=1 to words(vals)
        call out f(fmt, word(vals, vx))
        end
    return
endProcedure tstF2

tstFe: procedure expose m.
/*
$=/tstFe/
  ### start tst tstFe ###############################################
  .                   1 < 1.00e00> <1.00e00>
  .                   0 < 0.00e00> <0.00e00>
  .                -2.1 <-2.10e00> <-2.1e00>
  .                  .3 < 3.00e-1> <3.00e-1>
  .             -.45678 <-4.57e-1> <-4.6e-1>
  .                 901 < 9.01e02> <9.01e02>
  .               -2345 <-2.35e03> <-2.3e03>
  .              678e90 < 6.78e92> <6.78e92>
  .              123e-4 < 1.23e-2> <1.23e-2>
  .             567e-89 < 5.7e-87> <5.7e-87>
  .              12e456 < 1.2e457> <1.2e457>
  .             78e-901 < 8e-0900> <8e-0900>
  .           2345e5789 < 2e05792> <2e05792>
  .           123e-4567 < 1e-4565> <1e-4565>
  .          8901e23456 < 9e23459> <9e23459>
  .          -123e-4567 <-1e-4565> <-0e-999>
  .          567e890123 <********> <*******>
  .       45678e-901234 < 0e-9999> <0e-9999>
  .                kurz <    kurz> <kurz   >
  .       undLangerText <undLange> <undLang>
$/tstFe/
*/
    call tst t, 'tstFe'
    vAll = '1 0 -2.1 .3 -.45678 901 -2345 678e90 123e-4' ,
            '567e-89 12e456 78e-901 2345e5789  123e-4567 8901e23456' ,
            '-123e-4567 567e890123 45678e-901234' ,
            'kurz undLangerText'
    do vx=1 to words(vAll)
        v = word(vAll, vx)
        call tstOut t, right(v, 20)  '<'fe(v, 8, 2, 'e', ' ')'>' ,
                                     '<'fe(v, 7, 1, 'e', '-')'>'
        end
    call tstEnd t
    return
endProcedure

tstFTst: procedure expose m.
/*
$=/tstFTstS/
    ### start tst tstFTstS ############################################
    1956-01-29-23.34.56.987654     SS => 1956-01-29-23.34.56.987654|
    1956-01-29-23.34.56.987654     Ss => 1956-01-29-23.34.56|
    1956-01-29-23.34.56.987654     S  => 1956-01-29-23.34.56|
    1956-01-29-23.34.56.987654     SD => 19560129|
    1956-01-29-23.34.56.987654     Sd => 560129|
    1956-01-29-23.34.56.987654     SE => 29.01.1956|
    1956-01-29-23.34.56.987654     Se => 29.01.56|
    1956-01-29-23.34.56.987654     St => 23.34.56|
    1956-01-29-23.34.56.987654     ST => 23:34:56|
    1956-01-29-23.34.56.987654     SY => GB29|
    1956-01-29-23.34.56.987654     SM => B2923345|
    1956-01-29-23.34.56.987654     SH => C33456|
$/tstFTstS/
$=/tstFTsts/
    ### start tst tstFTsts ############################################
    2014-12-23-16.57.38            sS => 2014-12-23-16.57.38.000000|
    2014-12-23-16.57.38            ss => 2014-12-23-16.57.38|
    2014-12-23-16.57.38            s  => 2014-12-23-16.57.38|
    2014-12-23-16.57.38            sD => 20141223|
    2014-12-23-16.57.38            sd => 141223|
    2014-12-23-16.57.38            sE => 23.12.2014|
    2014-12-23-16.57.38            se => 23.12.14|
    2014-12-23-16.57.38            st => 16.57.38|
    2014-12-23-16.57.38            sT => 16:57:38|
    2014-12-23-16.57.38            sY => EM23|
    2014-12-23-16.57.38            sM => M2316573|
    2014-12-23-16.57.38            sH => B65738|
$/tstFTsts/
$=/tstFTstD/
    ### start tst tstFTstD ############################################
    23450618                       DS => 2345-06-18-00.00.00.000000|
    23450618                       Ds => 2345-06-18-00.00.00|
    23450618                       D  => 2345-06-18-00.00.00|
    23450618                       DD => 23450618|
    23450618                       Dd => 450618|
    23450618                       DE => 18.06.2345|
    23450618                       De => 18.06.45|
    23450618                       Dt => 00.00.00|
    23450618                       DT => 00:00:00|
    23450618                       DY => PG18|
    23450618                       DM => G1800000|
    23450618                       DH => A00000|
$/tstFTstD/
0----+----1----+----2----+----3
abcdefghijklmnopqrstuvwxyz
$=/tstFTstd/
    ### start tst tstFTstd ############################################
    120724                         dS => 2012-07-24-00.00.00.000000|
    120724                         ds => 2012-07-24-00.00.00|
    120724                         d  => 2012-07-24-00.00.00|
    120724                         dD => 20120724|
    120724                         dd => 120724|
    120724                         dE => 24.07.2012|
    120724                         de => 24.07.12|
    120724                         dt => 00.00.00|
    120724                         dT => 00:00:00|
    120724                         dY => CH24|
    120724                         dM => H2400000|
    120724                         dH => A00000|
$/tstFTstd/
$=/tstFTstE/
    ### start tst tstFTstE ############################################
    09.12.1345                     ES => 1345-12-09-00.00.00.000000|
    09.12.1345                     Es => 1345-12-09-00.00.00|
    09.12.1345                     E  => 1345-12-09-00.00.00|
    09.12.1345                     ED => 13451209|
    09.12.1345                     Ed => 451209|
    09.12.1345                     EE => 09.12.1345|
    09.12.1345                     Ee => 09.12.45|
    09.12.1345                     Et => 00.00.00|
    09.12.1345                     ET => 00:00:00|
    09.12.1345                     EY => PM09|
    09.12.1345                     EM => M0900000|
    09.12.1345                     EH => A00000|
$/tstFTstE/
$=/tstFTste/
    ### start tst tstFTste ############################################
    31.05.2467                     eS => 2024-05-31-00.00.00.000000|
    31.05.2467                     es => 2024-05-31-00.00.00|
    31.05.2467                     e  => 2024-05-31-00.00.00|
    31.05.2467                     eD => 20240531|
    31.05.2467                     ed => 240531|
    31.05.2467                     eE => 31.05.2024|
    31.05.2467                     ee => 31.05.2467|
    31.05.2467                     et => 00.00.00|
    31.05.2467                     eT => 00:00:00|
    31.05.2467                     eY => OF31|
    31.05.2467                     eM => F3100000|
    31.05.2467                     eH => A00000|
$/tstFTste/
$=/tstFTstt/
### start tst tstFTstt ############################################
    12.34.56                       tS => 0000-01-01-12.34.56.000000|
    12.34.56                       ts => 0000-01-01-12.34.56|
    12.34.56                       t  => 0000-01-01-12.34.56|
    12.34.56                       tD => 00000101|
    12.34.56                       td => 000101|
    12.34.56                       tE => 01.01.0000|
    12.34.56                       te => 01.01.00|
    12.34.56                       tt => 12.34.56|
    12.34.56                       tT => 12:34:56|
    12.34.56                       tY => ??01|
    12.34.56                       tM => ?0112345|
    12.34.56                       tH => B23456|
$/tstFTstt/
$=/tstFTstT/
    ### start tst tstFTstT ############################################
    23.45.06                       TS => 0000-01-01-23.45.06.000000|
    23.45.06                       Ts => 0000-01-01-23.45.06|
    23.45.06                       T  => 0000-01-01-23.45.06|
    23.45.06                       TD => 00000101|
    23.45.06                       Td => 000101|
    23.45.06                       TE => 01.01.0000|
    23.45.06                       Te => 01.01.00|
    23.45.06                       Tt => 23.45.06|
    23.45.06                       TT => 23.45.06|
    23.45.06                       TY => ??01|
    23.45.06                       TM => ?0123450|
    23.45.06                       TH => C34506|
$/tstFTstT/
$=/tstFTstY/
    ### start tst tstFTstY ############################################
    FE25                           YS => 2015-04-25-00.00.00.000000|
    FE25                           Ys => 2015-04-25-00.00.00|
    FE25                           Y  => 2015-04-25-00.00.00|
    FE25                           YD => 20150425|
    FE25                           Yd => 150425|
    FE25                           YE => 25.04.2015|
    FE25                           Ye => 25.04.15|
    FE25                           Yt => 00.00.00|
    FE25                           YT => 00:00:00|
    FE25                           YY => FE25|
    FE25                           YM => E2500000|
    FE25                           YH => A00000|
$/tstFTstY/
$=/tstFTstM/
    ### start tst tstFTstM ############################################
    I2317495                       MS => 0000-08-23-17.49.50.000000|
    I2317495                       Ms => 0000-08-23-17.49.50|
    I2317495                       M  => 0000-08-23-17.49.50|
    I2317495                       MD => 00000823|
    I2317495                       Md => 000823|
    I2317495                       ME => 23.08.0000|
    I2317495                       Me => 23.08.00|
    I2317495                       Mt => 17.49.50|
    I2317495                       MT => 17:49:50|
    I2317495                       MY => ?I23|
    I2317495                       MM => I2317495|
    I2317495                       MH => B74950|
$/tstFTstM/
$=/tstFTstH/
    ### start tst tstFTstH ############################################
    B23456                         HS => 0000-01-01-12.34.56.000000|
    B23456                         Hs => 0000-01-01-12.34.56|
    B23456                         H  => 0000-01-01-12.34.56|
    B23456                         HD => 00000101|
    B23456                         Hd => 000101|
    B23456                         HE => 01.01.0000|
    B23456                         He => 01.01.00|
    B23456                         Ht => 12.34.56|
    B23456                         HT => 12:34:56|
    B23456                         HY => ??01|
    B23456                         HM => ?0112345|
    B23456                         HH => B23456|
$/tstFTstH/
*/
    say "f('%t  ')" f('%t  ')
    allOut = 'Ss DdEetTYMH'
    allIn  = 'S1956-01-29-23.34.56.987654' ,
             's2014-12-23-16.57.38' ,
             'D23450618' ,
             'd120724'   ,
             'E09.12.1345' ,
             'e31.05.2467' ,
             't12.34.56'  ,
             'T23.45.06' ,
             'YFE25' ,
             'MI2317495' ,
             'HB23456'
    do ix=1 to words(allIn)
        parse value word(allIn, ix) with iF 2 iV
        call tst t, "tstFTst"iF
        do ox=1 to length(allOut)
            ft = iF || substr(allOut, ox, 1)
            call tstOut t, left(iV, 30) ft  '=>' f('%t'ft, iV)'|'
            if 0 & iF = 'Y' then
                say '???' ft '>>>' mGet('F_GEN.%t'ft)
            end
        call tstEnd t
        end
    return
endProcedure tstFTst

tstFmt: procedure expose m.
    call pipeIni
/*
$=/tstFmt/
    ### start tst tstFmt ##############################################
    =   a2i b3b   d4                       fl5          ex6
    -11 -11 b3    d4-11+d4++++    -111.1000000 -1.11000e-12
    -1  -10 b     d4-10+d4+++            null1        null3
    -    -9 b3b-9 d4-9+d4+++       -11.0000000 -1.11900e-10
    -8+  -8 b3b-  d4-8+d4++        -18.0000000 -1.18000e010
    -7   -7 b3b   d4-7+d4+          -7.0000000 -1.70000e-07
    -    -6 b3    d4-6+d4           -0.1111160 -6.00000e006
    -5+  -5 b     d4-5+d                 null2        null2
    -4   -4 b3b-4 d4-4+         -11114.0000000 -1.11140e008
    -    -3 b3b-  d4-3              -0.1130000 -1.13000e-04
    -2+  -2 b3b   d4-               -0.1200000 -1.20000e001
    -1   -1 b3    d4                -0.1000000 -1.00000e-02
    0     0 b     d                      null1        null1
    1+    1 b3    d4                 0.1000000  1.00000e-02
    2++   2 b3b   d42                0.1200000  1.20000e001
    3     3 b3b3  d43+               0.1130000  1.13000e-04
    4+    4 b3b4+ d44+d          11114.0000000  1.11140e008
    5++   5 b     d45+d4                 null2        null2
    6     6 b3    d46+d4+            0.1111160  1.11116e005
    7+    7 b3b   d47+d4++           0.1111117  7.00000e-08
    8++   8 b3b8  d48+d4+++          8.0000000  1.80000e009
    9     9 b3b9+ d49+d4++++         0.9000000  1.19000e-08
    10   10 b     d410+d4++++            null1        null3
    11+  11 b3    d411+d4+++++       0.1110000  1.00000e-12
    1    12 b3b   d412+d4++++++  11112.0000000  2.00000e012
    13   13 b3b1  d               1111.3000000  1.13000e-12
    14+  14 b3b14 d4            111111.0000000  1.40000e013
    1    15 b     d41                    null2        null1
    16   16 b3    d416               6.0000000  1.16000e003
    17+  17 b3b   d417+              0.7000000  1.11170e-03
    1    18 b3b1  d418+d            11.0000000  1.11800e003
    19   19 b3b19 d419+d4            0.1190000  9.00000e-05
    20+  20 b     d420+d4+               null1        null2
    2    21 b3    d421+d4++         11.1210000  1.11000e-05
    22   22 b3b   d422+d4+++     11111.2000000  2.00000e007
    23+  23 b3b2  d423+d4++++        0.1111123  1.11230e-09
    c3L      a2i drei  d4                 fl5          ex6
    -11 -1.10E01 b3    d4-11+d   -111.1000000 -1.11000e-12
    -1  -1.00E01 b     d4-10+d          null1        null3
    -   -9.00E00 b3b-9 d4-9+d4    -11.0000000 -1.11900e-10
    -8+ -8.00E00 b3b-  d4-8+d4    -18.0000000 -1.18000e010
    -7  -7.00E00 b3b   d4-7+d4     -7.0000000 -1.70000e-07
    -   -6.00E00 b3    d4-6+d4     -0.1111160 -6.00000e006
    -5+ -5.00E00 b     d4-5+d           null2        null2
    -4  -4.00E00 b3b-4 d4-4+   -11114.0000000 -1.11140e008
    -   -3.00E00 b3b-  d4-3        -0.1130000 -1.13000e-04
    -2+ -2.00E00 b3b   d4-         -0.1200000 -1.20000e001
    -1  -1.00E00 b3    d4          -0.1000000 -1.00000e-02
    0    0.00E00 b     d                null1        null1
    1+   1.00E00 b3    d4           0.1000000  1.00000e-02
    2++  2.00E00 b3b   d42          0.1200000  1.20000e001
    3    3.00E00 b3b3  d43+         0.1130000  1.13000e-04
    4+   4.00E00 b3b4+ d44+d    11114.0000000  1.11140e008
    5++  5.00E00 b     d45+d4           null2        null2
    6    6.00E00 b3    d46+d4+      0.1111160  1.11116e005
    7+   7.00E00 b3b   d47+d4+      0.1111117  7.00000e-08
    8++  8.00E00 b3b8  d48+d4+      8.0000000  1.80000e009
    9    9.00E00 b3b9+ d49+d4+      0.9000000  1.19000e-08
    10   1.00E01 b     d410+d4          null1        null3
    11+  1.10E01 b3    d411+d4      0.1110000  1.00000e-12
    1    1.20E01 b3b   d412+d4  11112.0000000  2.00000e012
    13   1.30E01 b3b1  d         1111.3000000  1.13000e-12
    14+  1.40E01 b3b14 d4      111111.0000000  1.40000e013
    1    1.50E01 b     d41              null2        null1
    16   1.60E01 b3    d416         6.0000000  1.16000e003
    17+  1.70E01 b3b   d417+        0.7000000  1.11170e-03
    1    1.80E01 b3b1  d418+d      11.0000000  1.11800e003
    19   1.90E01 b3b19 d419+d4      0.1190000  9.00000e-05
    20+  2.00E01 b     d420+d4          null1        null2
    2    2.10E01 b3    d421+d4     11.1210000  1.11000e-05
    22   2.20E01 b3b   d422+d4  11111.2000000  2.00000e007
    23+  2.30E01 b3b2  d423+d4      0.1111123  1.11230e-09
$/tstFmt/ */

    call tst t, "tstFmt"
    b = jBuf()
    st = b'.BUF'
    call pipe '+F', b
    call tstDataClassOut '. c3 a2i i b3b c5 d4 c13 fl5 f8n2 ex6 e9n3',
        , -11, + 23
    call pipe '-'
    call fTabAuto fTabReset(abc, 1), b
    call fTabReset abc, 1
    call fTabAddDetect abc,      , st   , 'c3L'
    call fTabAdd       abc, 'a2i', ' % 8E'
    call fTabAddDetect abc, 'b3b', st   , 'drei'
    call fTabAdd       abc, 'd4', ' %-7C'
    call fTabAddDetect abc, 'fl5', st
    call fTabAddDetect abc, 'ex6', st
    call fTab abc, b
    call tstEnd t
    return
endProcedure tstFmt

tstFTab: procedure expose m.
    call pipeIni
/*
$=/tstFTab/
    ### start tst tstFTab #############################################
    testData begin
    ..---------a2i-b3b------------------d4------fl5-----ex6---
    -11       -11 b3           -11+d4++++ -111.100 -1e-012
    -1        -10 b            4-10+d4+++    null1   null3
    -          -9 b3b-9        d4-9+d4+++  -11.000 -1e-010
    -8+        -8 b3b-          d4-8+d4++  -18.000 -1.2e10
    -7         -7 b3b            d4-7+d4+   -7.000 -1.7e-7
    -          -6 b3              d4-6+d4   -0.111 -6.0e06
    -5+        -5 b                d4-5+d    null2   null2
    -4         -4 b3b-4             d4-4+ ******** -1.1e08
    -          -3 b3b-               d4-3   -0.113 -1.1e-4
    -2+        -2 b3b                 d4-   -0.120 -1.2e01
    -1         -1 b3                   d4   -0.100 -1.0e-2
    0           0 b                     d    null1   null1
    1+          1 b3                   d4    0.100 1.00e-2
    2++         2 b3b                 d42    0.120 1.20e01
    3           3 b3b3               d43+    0.113 1.13e-4
    4+          4 b3b4+             d44+d ******** 1.11e08
    5++         5 b                d45+d4    null2   null2
    6           6 b3              d46+d4+    0.111 1.11e05
    7+          7 b3b            d47+d4++    0.111 7.00e-8
    8++         8 b3b8          d48+d4+++    8.000 1.80e09
    9           9 b3b9+        d49+d4++++    0.900 1.19e-8
    10         10 b            410+d4++++    null1   null3
    11+        11 b3           11+d4+++++    0.111 1.0e-12
    1          12 b3b          2+d4++++++ ******** 2.00e12
    13         13 b3b1                  d 1111.300 1.1e-12
    14+        14 b3b14                d4 ******** 1.40e13
    1          15 b                   d41    null2   null1
    16         16 b3                 d416    6.000 1.16e03
    17+        17 b3b               d417+    0.700 1.11e-3
    1          18 b3b1             d418+d   11.000 1.12e03
    19         19 b3b19           d419+d4    0.119 9.00e-5
    20+        20 b              d420+d4+    null1   null2
    2          21 b3            d421+d4++   11.121 1.11e-5
    22         22 b3b          d422+d4+++ ******** 2.00e07
    23+        23 b3b2         423+d4++++    0.111 1.11e-9
    ..---------a2i-b3b------------------d4------fl5-----ex6---
    testData end
$/tstFTab/ */

    call tst t, "tstFTab"
    b = jBuf()
    st = b'.BUF'
    call pipe '+F', b
    call tstDataClassOut '. c3 a2i i b3b c5 d4 c13 fl5 f8n2 ex6 e9n3',
        , -11, + 23
    call pipe 'P|'
    call fTabReset ft, 2 1, 1 3, '-'
    call fTabAdd   ft, '='   , '%-6C' , '.', 'testData begin',
                                            , 'testData end'
    call fTabAdd   ft, 'a2i' , ' %6i'
    call fTabAdd   ft, 'b3b' , ' %-12C'
    call fTabAdd   ft, 'd4'  , ' %10C'
    call fTabAdd   ft, 'fl5' , ' %8.3I'
    call fTabAdd   ft, 'ex6' , ' %7e'
    call fTab ft
    call pipe '-'
    call tstEnd t
    return
endProcedure tstFTab


tstfUnits: procedure
/*
$=/tstfUnits/
    ### start tst tstfUnits ###########################################
    .            .3 ==>  0s30 ++>   0s30 -+> -0s30 -->  -0s30
    .            .8 ==>  0s80 ++>   0s80 -+> -0s80 -->  -0s80
    .             1 ==>  1s00 ++>   1s00 -+> -1s00 -->  -1s00
    .           1.2 ==>  1s20 ++>   1s20 -+> -1s20 -->  -1s20
    .            59 ==> 59s00 ++>  59s00 -+> -59s0 --> -59s00
    .         59.07 ==> 59s07 ++>  59s07 -+> -59s0 --> -59s07
    .        59.997 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .            60 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .          60.1 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .           611 ==> 10m11 ++>  10m11 -+> -10m1 --> -10m11
    .        3599.4 ==> 59m59 ++>  59m59 -+> -59m5 --> -59m59
    .        3599.5 ==>  1h00 ++>   1h00 -+> -1h00 -->  -1h00
    .          3661 ==>  1h01 ++>   1h01 -+> -1h01 -->  -1h01
    .         83400 ==> 23h10 ++>  23h10 -+> -23h1 --> -23h10
    .         84700 ==> 23h32 ++>  23h32 -+> -23h3 --> -23h32
    .         86400 ==>  1d00 ++>   1d00 -+> -1d00 -->  -1d00
    .         89900 ==>  1d01 ++>   1d01 -+> -1d01 -->  -1d01
    .       8467200 ==> 98d00 ++>  98d00 -+> -98d0 --> -98d00
    .    8595936.00 ==> 99d12 ++>  99d12 -+> -99d1 --> -99d12
    .    8638704.00 ==>  100d ++>   100d -+> -100d -->  -100d
    .       8640000 ==>  100d ++>   100d -+> -100d -->  -100d
    .     863913600 ==> 9999d ++>  9999d -+> ----d --> -9999d
    .     863965440 ==> ++++d ++> 10000d -+> ----d --> -----d
    .     8.6400E+9 ==> ++++d ++> +++++d -+> ----d --> -----d
    .            .3 ==>   0.300 ++>    0.300 -+>  -0.300 -->   -0.300
    .            .8 ==>   0.800 ++>    0.800 -+>  -0.800 -->   -0.800
    .             1 ==>   1.000 ++>    1.000 -+>  -1.000 -->   -1.000
    .           1.2 ==>   1.200 ++>    1.200 -+>  -1.200 -->   -1.200
    .            59 ==>  59.000 ++>   59.000 -+> -59.000 -->  -59.000
    .         59.07 ==>  59.070 ++>   59.070 -+> -59.070 -->  -59.070
    .        59.997 ==>  59.997 ++>   59.997 -+> -59.997 -->  -59.997
    .            60 ==>  60.000 ++>   60.000 -+> -60.000 -->  -60.000
    .          60.1 ==>  60.100 ++>   60.100 -+> -60.100 -->  -60.100
    .           611 ==> 611.000 ++>  611.000 -+> -611.00 --> -611.000
    .        3599.4 ==>   3k599 ++>    3k599 -+>  -3k599 -->   -3k599
    .        3599.5 ==>   3k600 ++>    3k600 -+>  -3k600 -->   -3k600
    .          3661 ==>   3k661 ++>    3k661 -+>  -3k661 -->   -3k661
    .         83400 ==>  83k400 ++>   83k400 -+> -83k400 -->  -83k400
    .     999999.44 ==> 999k999 ++>  999k999 -+> -999k99 --> -999k999
    .      999999.5 ==>   1M000 ++>    1M000 -+>  -1M000 -->   -1M000
    .    567.6543E6 ==> 567M654 ++>  567M654 -+> -567M65 --> -567M654
    .    .9999991E9 ==> 999M999 ++>  999M999 -+> -999M99 --> -999M999
    .    .9999996E9 ==>   1G000 ++>    1G000 -+>  -1G000 -->   -1G000
    .   .9999991E12 ==> 999G999 ++>  999G999 -+> -999G99 --> -999G999
    .   .9999996E12 ==>   1T000 ++>    1T000 -+>  -1T000 -->   -1T000
    .   567.6543E12 ==> 567T654 ++>  567T654 -+> -567T65 --> -567T654
    .   .9999991E15 ==> 999T999 ++>  999T999 -+> -999T99 --> -999T999
    .   .9999996E15 ==>   1P000 ++>    1P000 -+>  -1P000 -->   -1P000
    .   .9999991E18 ==> 999P999 ++>  999P999 -+> -999P99 --> -999P999
    .   .9999996E18 ==>   1E000 ++>    1E000 -+>  -1E000 -->   -1E000
    .   567.6543E18 ==> 567E654 ++>  567E654 -+> -567E65 --> -567E654
    .   .9999991E21 ==> 999E999 ++>  999E999 -+> -999E99 --> -999E999
    .   .9999996E21 ==>   1000E ++>    1000E -+>  -1000E -->   -1000E
    .   .9999992E24 ==> 999999E ++>  999999E -+> ------E --> -999999E
    .   .9999995E24 ==> ++++++E ++> 1000000E -+> ------E --> -------E
    .    10.6543E24 ==> ++++++E ++> +++++++E -+> ------E --> -------E
$/tstfUnits/ */
    call jIni
    call tst t, "tstfUnits"
    d = 86400
    lst = .3 .8 1 1.2 59 59.07 59.997 60 60.1 611 ,
          3599.4 3599.5 3661 d-3000 d-1700 d d+3500 ,
          d * 98 d * 99.49 d * 99.985 d*100 d * 9999 d * 9999.6 ,
          d * 1e5
    do wx=1 to words(lst)
        call tstOut t, right(word(lst, wx), 14) ,
                 '==>' fTime(     word(lst, wx)   ) ,
                 '++>' fTime(     word(lst, wx), 1),
                 '-+>' fTime('-'word(lst, wx),    ),
                 '-->' fTime('-'word(lst, wx), 1)
        end
    lst = subword(lst, 1, 14) 999999.44 999999.5,
        567.6543e6 .9999991e9 .9999996e9 .9999991e12 .9999996e12 ,
        567.6543e12 .9999991e15 .9999996e15 .9999991e18 .9999996e18 ,
        567.6543e18 .9999991e21 .9999996e21 .9999992e24 .9999995e24 ,
         10.6543e24
    do wx=1 to words(lst)
        call tstOut t, right(word(lst, wx), 14) ,
                 '==>' fDec(      word(lst, wx)   ) ,
                 '++>' fDec(      word(lst, wx), 1),
                 '-+>' fDec('-'word(lst, wx),     ),
                 '-->' fDec('-'word(lst, wx), 1)
        end
    call tstEnd t
    return
endProcedure tstfUnits

tstSb: procedure expose m.
/*
$=/tstSb/
    ### start tst tstSb ###############################################
    end        : 0
    char  3    : 1 abc
    lit   d?   : 0 .
    lit   de   : 1 de
    lit   de ? fg fgh: 1 fg
    while HIJ  : 0 .
    end        : 0
    while Jih  : 1 hi
    while ? klj: 1 jklkl ?
    end        : 1
    while ? klj: 0 .
    char  3    : 0 .
    lit        : 0 .
    until cba  : 0 .
    until ?qd  : 1 abc
    until ?qr  : 1 defdef .
    until ?qr  : 0 .
    strEnd ?   : 1 ?
    strEnd ?   : 0 .
    strEnd ?   : 1 ab??cd????gh?
    strEnd ") ": 1 ab) .
    strEnd ") ": 1 cd) ) gh) .
$/tstSb/ */
    call tst t, 'tstSb'
    call scanSBSrc s, 'abcdefghijklkl ?'
    call out 'end        :' scanSBEnd(s)
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit   d?   :' scanLit(s, 'd?') m.s.tok
    call out 'lit   de   :' scanLit(s, 'de') m.s.tok
    call out 'lit   de ? fg fgh:',
            scanLit(s, 'de', '?', 'fg', 'fgh') m.s.tok
    call out 'while HIJ  :' scanWhile(s, 'HIJ') m.s.tok
    call out 'end        :' scanSBEnd(s)
    call out 'while Jih  :' scanWhile(s, 'Jih') m.s.tok
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'end        :' scanSBEnd(s)
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit        :' scanLit(s) m.s.tok
    call scanSBSrc s, 'abcdefdef ?'
    call out 'until cba  :' scanUntil(s, 'cba') m.s.tok
    call out 'until ?qd  :' scanUntil(s, '?qd') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSBSrc s, 'ab??cd????gh?ijk'
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSBSrc s, 'ab) cd) ) gh) jk) )'
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call tstEnd t
    return
endProcedure tstSb

tstSb2: procedure expose m.
/*
$=/tstSb2/
    ### start tst tstSb2 ##############################################
    end        : 0
    char  3    : 1 abc
    lit   d?   : 0 .
    lit   de   : 1 de
    lit   de ? fg fgh: 1 fg
    while HIJ  : 0 .
    end        : 0
    while Jih  : 1 hi
    while ? klj: 1 jklkl ?
    end        : 1
    while ? klj: 0 .
    char  3    : 0 .
    lit        : 0 .
    until cba  : 0 .
    until ?qd  : 1 abc
    until ?qr  : 1 defdef .
    until ?qr  : 0 .
    strEnd ?   : 1 ?
    strEnd ?   : 0 .
    strEnd ?   : 1 ab??cd????gh?
    strEnd ") ": 1 ab) .
    strEnd ") ": 1 cd) ) gh) .
$/tstSb2/ */
    call tst t, 'tstSb2'
    call scanIni
    call scanSrc s, 'abcdefghijklkl ?'
    call out 'end        :' scanEnd(s)
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit   d?   :' scanLit(s, 'd?') m.s.tok
    call out 'lit   de   :' scanLit(s, 'de') m.s.tok
    call out 'lit   de ? fg fgh:',
            scanLit(s, 'de', '?', 'fg', 'fgh') m.s.tok
    call out 'while HIJ  :' scanWhile(s, 'HIJ') m.s.tok
    call out 'end        :' scanEnd(s)
    call out 'while Jih  :' scanWhile(s, 'Jih') m.s.tok
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'end        :' scanEnd(s)
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit        :' scanLit(s) m.s.tok
    call scanSrc s, 'abcdefdef ?'
    call out 'until cba  :' scanUntil(s, 'cba') m.s.tok
    call out 'until ?qd  :' scanUntil(s, '?qd') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab??cd????gh?ijk'
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab) cd) ) gh) jk) )'
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call tstEnd t
    return
endProcedure tstSb2

tstScan: procedure expose m.
/*
$=/tstScan.1/
    ### start tst tstScan.1 ###########################################
    scan src a034,Und hr123sdfER"st1" 'str2''mit''apo''s' .
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    scan n tok 3: Und key  val .
    scan v tok 1:   key  val .
    scan n tok 10: hr123sdfER key  val .
    scan q tok 5: "st1" key  val st1
    scan v tok 1:   key  val st1
    scan a tok 19: 'str2''mit''apo''s' key  val str2'mit'apo's
    scan v tok 1:   key  val str2'mit'apo's
$/tstScan.1/ */
    call scanIni
    call tst t, 'tstScan.1'
    call tstScan1 ,'l"litEins"l"litZwei"ndv" "aq1' ,
        ,"a034,Und hr123sdfER""st1"" 'str2''mit''apo''s' "
    call tstEnd t

/*
$=/tstScan.2/
    ### start tst tstScan.2 ###########################################
    scan src a034,Und hr123sdfER"st1" 'str2''mit''apo''s' .
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    scan n tok 3: Und key  val .
    scan b tok 1:   key  val .
    scan n tok 10: hr123sdfER key  val .
    scan " tok 5: "st1" key  val st1
    scan b tok 1:   key  val st1
    scan ' tok 19: 'str2''mit''apo''s' key  val str2'mit'apo's
    scan b tok 1:   key  val str2'mit'apo's
$/tstScan.2/ */
    call tst t, 'tstScan.2'
    call tstScan1 , 'ndsb1' ,
        ,"a034,Und hr123sdfER""st1"" 'str2''mit''apo''s' "
    call tstEnd t

/*
$=/tstScan.3/
    ### start tst tstScan.3 ###########################################
    scan src a034,'wie 789abc
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    *** err: scanErr ending Apostroph(') missing
    .    e 1: last token  scanPosition 'wie 789abc
    .    e 2: pos 6 in string a034,'wie 789abc
    scan ' tok 1: ' key  val .
    scan n tok 3: wie key  val .
    scan s tok 1:   key  val .
    *** err: scanErr illegal number end after 789
    .    e 1: last token 789 scanPosition abc
    .    e 2: pos 14 in string a034,'wie 789abc
    scan d tok 3: 789 key  val .
    scan n tok 3: abc key  val .
$/tstScan.3/ */
    call tst t, 'tstScan.3'
    call tstScan1 , 'nds1' ,
        ,"a034,'wie 789abc"
    call tstEnd t

/*
$=/tstScan.4/
    ### start tst tstScan.4 ###########################################
    scan src litEinsefr 23 sdfER'str1'litZwei "str2""mit quo"s .
    scan l tok 7: litEins key  val .
    scan n tok 3: efr key  val .
    scan b tok 1:   key  val .
    scan d tok 2: 23 key  val .
    scan b tok 1:   key  val .
    scan n tok 5: sdfER key  val .
    scan a tok 6: 'str1' key  val str1
    scan l tok 7: litZwei key  val str1
    scan b tok 1:   key  val str1
    scan q tok 15: "str2""mit quo" key  val str2"mit quo
    scan n tok 1: s key  val str2"mit quo
    scan b tok 1:   key  val str2"mit quo
$/tstScan.4/ */
    call tst t, 'tstScan.4'
    call tstScan1 , 'l"litEins"l"litZwei"ndbaq1' ,
           ,"litEinsefr 23 sdfER'str1'litZwei ""str2""""mit quo""s "
    call tstEnd t

/*
$=/tstScan.5/
    ### start tst tstScan.5 ###########################################
    scan src  aha;+-=f ab=cdEf eF='strIng' .
    scan b tok 1:   key  val .
    scan k tok 4:  no= key aha val def
    scan ; tok 1: ; key aha val def
    scan + tok 1: + key aha val def
    scan - tok 1: - key aha val def
    scan = tok 1: = key aha val def
    scan k tok 4:  no= key f val def
    scan k tok 4: cdEf key ab val cdEf
    scan b tok 1:   key ab val cdEf
    scan k tok 8: 'strIng' key eF val strIng
    scan b tok 1:   key eF val strIng
$/tstScan.5/ */
    call tst t, 'tstScan.5'
    call tstScan1 , 'bk1'," aha;+-=f ab=cdEf eF='strIng' "
    call tstEnd t
    return
endProcedure tstScan

tstScanRead: procedure expose m.
/*
$=/tstScanRead/
    ### start tst tstScanRead #########################################
    name erste
    space
    name Zeile
    space
    nextLine
    nextLine
    space
    name dritte
    space
    name Zeile
    space
    name schluss
    space
$/tstScanRead/ */
    call scanReadIni
    call tst t, 'tstScanRead'
    b = jBuf('erste Zeile  ',,'  dritte Zeile  schluss  ')
    s = jOpen(scanRead(b), m.j.cRead)
    do while \scanEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanVerify(s, ' ') then call tstOut t, 'space'
        else if scanReadNL(s) then      call tstOut t, 'nextLine'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        end
    call jClose s
    call tstEnd t

/*
$=/tstScanReadMitSpaceLn/
    ### start tst tstScanReadMitSpaceLn ###############################
    name erste
    spaceLn
    name Zeile
    spaceLn
    name dritte
    spaceLn
    name Zeile
    spaceLn
    name schluss
    spaceLn
$/tstScanReadMitSpaceLn/ */
    call tst t, 'tstScanReadMitSpaceLn'
    s = scanOpen(scanRead(b))
    do forever
        if scanName(s) then         call out 'name' m.s.tok
        else if scanSpace(s) then call out 'spaceLn'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        else                        leave
        end
    call scanClose s
    call tstEnd t

/*
$=/tstScanJRead/
    ### start tst tstScanJRead ########################################
    1 jRead n tok erste val .
    2 jRead s tok   val .
    3 jRead n tok Zeile val .
    4 jRead s tok   val .
    5 jRead n tok dritte val .
    6 jRead s tok   val .
    7 jRead n tok Zeile val .
    8 jRead s tok   val .
    9 jRead n tok schluss val .
    10 jRead s tok   val .
    11 jRead 0 tok 1 val 1
    12 jRead s tok   val 1
    13 jRead + tok + val 1
    14 jRead s tok   val 1
    15 jRead 0 tok 2. val 2..
    16 jRead s tok   val 2..
    17 jRead + tok + val 2..
    18 jRead . tok . val 2..
    19 jRead s tok   val 2..
    20 jRead 0 tok +.3 val +.3
    21 jRead 0 tok -45e-3 val -45E-3
    22 jRead s tok   val -45E-3
    23 jRead " tok "a""b" val a"b
    24 jRead s tok   val a"b
    25 jRead ' tok 'c''d' val c'd
    className 1: ScanRes 18: ScanRes
$/tstScanJRead/ */

    call tst t, 'tstScanJRead'
    call jWrite jOpen(b,'>>'), '1  + 2. +. +.3-45e-3 "a""b"' "'c''d'"
    s = jOpen(scanRead(jClose(b)), '<')
    do x=1 while ass('v', jReadO(s))  \== ''
        call out x 'jRead' m.v.type 'tok' m.v.tok 'val' m.v.val
        v.x = v
        end
    call jClose s
    call out 'className 1:' className(objClass(v.1)),
                    '18:' className(objClass(v.18))
    call tstEnd t
    return
endProcedure tstScanRead

tstScanUtilInto: procedure expose m.
/*
$=/tstScanUtilIntoL/
TEMPLATE P3
     DSN('DBAF.DA540769.A802A.P00003.BV5I3NRN.REC')
     DISP(OLD,KEEP,KEEP)
TEMPLATE P4
     DSN('DBAF.DA540769.A802A.P00004.BV5I3NTK.REC')
     DISP(OLD,KEEP,KEEP)
LOAD DATA        LOG NO RESUME NO REPLACE COPYDDN(TCOPYD)
           STATISTICS INDEX(ALL) REPORT NO UPDATE ALL
 EBCDIC  CCSID(00500,00000,00000)
 SORTKEYS
  -- ENFORCE NO
  SORTDEVT DISK
  SORTNUM 160
  WORKDDN(TSYUTD,TSOUTD)
  INTO TABLE OA1P.TWB981 PART 1 INDDN TREC134
 WORKDDN(TSYUTS,TSOUTS)
 INTO TABLE "A540769"
   ."TWK802A1"
 PART 00001 INDDN P0
 WHEN(00001:00002) = X'0041'
 ( "DE1"
  POSITION(  00003:00010) DECIMAL
 , "CH2"
  POSITION(  00011:00015) CHAR(00005)
 , "TS3"
  POSITION(  00016:00041) TIMESTAMP EXTERNAL
 , "TI4"
  POSITION(  00042:00049) TIME EXTERNAL
 , "DA5"
  POSITION(  00050:00059) DATE EXTERNAL
 , "IN6"
  POSITION(  00060:00063) INTEGER
 , "RE7"
  POSITION(  00064:00067) FLOAT(21)
 )
 INTO TABLE "A540769"."TWK802A1"
 PART 00002 INDDN P0
 WHEN(00001:00002) = X'0041'
 ( "DE1"
  POSITION(  00003:00010) DECIMAL
 , "CH2"
  POSITION(  00011:00015) CHAR(00005)
 )
 dobido
$/tstScanUtilIntoL/
$=/tstScanUtilInto/
  ### start tst tstScanUtilInto #####################################
  -- 1 scanUtilInto
  . ( "DE1"
  .  POSITION(  00003:00010) DECIMAL
  . , "CH2"
  .  POSITION(  00011:00015) CHAR(00005)
  . , "TS3"
  .  POSITION(  00016:00041) TIMESTAMP EXTERNAL
  . , "TI4"
  .  POSITION(  00042:00049) TIME EXTERNAL
  . , "DA5"
  .  POSITION(  00050:00059) DATE EXTERNAL
  . , "IN6"
  .  POSITION(  00060:00063) INTEGER
  . , "RE7"
  .  POSITION(  00064:00067) FLOAT(21)
  . ) .
  .  -- table OA1P.TWB981 part 00001
  -- 2 scanUtilInto
  . ( "DE1"
  .  POSITION(  00003:00010) DECIMAL
  . , "CH2"
  .  POSITION(  00011:00015) CHAR(00005)
  . ) .
  .  -- table A540769.TWK802A1 part 00002
  -- 3 scanUtilInto
$/tstScanUtilInto/ */

    call scanReadIni
    b = jBuf()
    call mAddst b'.BUF', mapInline('tstScanUtilIntoL')
    call tst t, 'tstScanUtilInto'
    s = jOpen(scanUtilReset(ScanRead(b)), '<')
    do ix=1
        call out '--' ix 'scanUtilInto'
        if \ scanUtilInto(s) then
            leave
        call out '  -- table' m.s.tb 'part' m.s.part
        end
    call tstEnd t
    return
endProcedure tstSCanUtilInto

tstScanWin: procedure expose m.
/*
$=/tstScanWin/
    ### start tst tstScanWin ##########################################
    info 0: last token  scanPosition erste     Zeile                 dr+
    itteZe\npos 1 in line 1: erste     Zeile
    name erste
    spaceNL
    name Zeile
    spaceNL
    name dritteZeeeile
    info 5: last token dritteZeeeile scanPosition    zeile4            +
    .    fuenfueberSechs\npos 1 in line 4:    zeile4
    spaceNL
    name zeile4
    spaceNL
    name fuenfueberSechsUnddSiebenUNDundUndUAcht
    spaceNL
    info 10: last token   scanPosition undZehnueberElfundNochWeiterZwoe+
    lfundim1\npos 9 in line 10:         undZehn
    name undZehnueberElfundNochWeiterZwoelfundim13
    spaceNL
    name Punkt
    infoE 14: last token Punkt scanPosition \natEnd after line 13: im13+
    .      Punkt
$/tstScanWin/ */
    call scanWinIni
    call tst t, 'tstScanWin'
    b = jBuf('?erste     Zeile?',,'?  dritteZeeeile?', '?   zeile4 ',
       ,'?          fuenf?', '?ueberSechsUnddS?', '?iebenUNDundUndU?',
       ,'?Acht           ?', '?               ?', '?        undZehn?',
       ,'?ueberElfundNoch?', '?WeiterZwoelfund?', '?im13      Punkt?')
    s = jOpen(scanWin(b, , , 2, 15), m.j.cRead)
    call tstOut t, 'info 0:' scanInfo(s)
    do sx=1 while \scanEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        if sx // 5 = 0 then
            call tstOut t, 'info' sx':' scanInfo(s)
        end
    call tstOut t, 'infoE' sx':' scanInfo(s)
    call tstEnd t

/*
$=/tstScanWinRead/
    ### start tst tstScanWinRead ######################################
    info 0: last token  scanPosition erste     Zeile                z3 +
    com Ze\npos 1 in line 1: erste     Zeile
    name erste
    spaceNL
    name Zeile
    spaceNL
    name z3
    info 5: last token z3 scanPosition  com Zeeeile z4 come4          f+
    uenf\npos 4 in line 3:  z3 com Zeeeile
    spaceNL
    name z4
    spaceNL
    name fuenf
    spaceNL
    info 10: last token   scanPosition com    Sechs  com  sieben   comA+
    cht  com\npos 15 in line 5:     fuenf     c
    name com
    spaceNL
$/tstScanWinRead/ */
    call tst t, 'tstScanWinRead'
    call mAdd t.cmp,
       ,  "name Sechs",
       ,  "spaceNL",
       ,  "name com",
       ,  "info 15: last token com scanPosition   sieben   comAcht  c",
       || "om com    com\npos 2 in line 7: m  sieben   com" ,
       ,  "spaceNL",
       ,  "name sieben",
       ,  "spaceNL",
       ,  "name Acht",
       ,  "spaceNL",
       ,  "info 20: last token   scanPosition ueberElfundNochWeit com ",
       || "elfundim13\npos 1 in line 11: ueberElfundNoch",
       ,  "name ueberElfundNochWeit",
       ,  "spaceNL",
       ,  "name im13",
       ,  "spaceNL",
       ,  "name Punkt",
       ,  "info 25: last token Punkt scanPosition \natEnd after line ",
       || "13: im13      Punkt",
       ,  "infoE 26: last token Punkt scanPosition \natEnd after line",
       || " 13: im13      Punkt"
    b = jBuf('?erste     Zeile?',,'? z3 com Zeeeile?', '? z4 come4 ',
       ,'?    fuenf     c?', '?om    Sechs  co?', '?m  sieben   com?',
       ,'?Acht  com com  ?', '?  com          ?', '?  com   undZehn?',
       ,'?ueberElfundNoch?', '?Weit com elfund?', '?im13      Punkt?')
    s = scanWin(b, , , 2, 15)
    call scanOpts s, , , 'com'
    call tstOut t, 'info 0:' scanInfo(jOpen(s, m.j.cRead))
    do sx=1 while \scanEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        if sx // 5 = 0 then
            call tstOut t, 'info' sx':' scanInfo(s)
        end
    call tstOut t, 'infoE' sx':' scanInfo(s)
    call tstEnd t
    return
endProcedure tstScanWin

tstjCatSql: procedure expose m.
/*
$=/tstJCatSql/
    ### start tst tstJCatSql ##########################################
    cmd1 select     current time                 stamp from s.1
    cmd2 update ";--""'/*"
    cmd3 delete '*/''"' / 3 - 1
    cmd4 .
$/tstJCatSql/ */
    call tst t, 'tstJCatSql'
    b = jBuf('select -- /* c1', ' /* c1 */ current/* c2 " '' ',
       ,'c3"', '  c4   */time', 'stamp-- c5', 'from s.1; /* c6 */ ;' ,
       ,';update ";--""''/*";;       del123',
       , 'ete ''*/''''"'' / 3 - 1  -- c7', '/*c8 */   ')
    call jCatSqlReset tstJCat, , jOpen(b, '<'), 30
    do sx=1 until nx = ''
        nx = jCatSqlNext(tstJCat, ';')
        call tstOut t, 'cmd'sx nx
        end
    call jClose b
    call tstEnd t
    return
endProcedure tstJCatSql

tstScanSql: procedure expose m.
    call scanWinIni
/*
$=/tstScanSqlId/
    ### start tst tstScanSqlId ########################################
    sqlId ABC
    spaceNL
    sqlId AB__345EF
    spaceNL
$/tstScanSqlId/ */
    call tst t, 'tstScanSqlId'
    b = jBuf('abc  --  kommentar', right('ab_', 72), '_345ef-- kom')
    s = jOpen(scanSql(b), m.j.cRead)
    do sx=1 while \scanEnd(s)
        if scanSqlID(s) then       call tstOut t, 'sqlId' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlDelimited/
    ### start tst tstScanSqlDelimited #################################
    sqlDeId ABC
    spaceNL
    sqlDeId AB_3F
    spaceNL
    sqlDeId abc
    spaceNL
    sqlDeId ab_Ef
    spaceNL
$/tstScanSqlDelimited/ */
    call tst t, 'tstScanSqlDelimited'
    b = jBuf('abc  --  kommentar',,'  -- ',,right('ab_', 72),'3F-- kom',
           , '"abc"  --  ko', ' -- ',,right('"ab_', 72),'Ef"-- kom')
    s = jOpen(scanSql(b), m.j.cRead)
    do sx=1 while \scanEnd(s)
        if scanSqlDeID(s) then       call tstOut t, 'sqlDeId' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlQualified/
    ### start tst tstScanSqlQualified #################################
    sqlQuId ABC 1 ABC
    sqlQuId AB_3F 1 AB_3F
    sqlQuId abc 1 abc
    sqlQuId ab_Ef 1 ab_Ef
    sqlQuId EINS.Zwei.DREI 3 EINS
    sqlQuId vi er.fu  enf 2 vi er
$/tstScanSqlQualified/ */
    call tst t, 'tstScanSqlQualified'
    b = jBuf('abc  --  kommentar',,'  -- ',,right('ab_', 72),'3F-- kom',
           , '"abc"  --  ko', ' -- ',right('"ab_', 72),'Ef"-- kom',
           , 'eins."Zwei', '" -- com', ' . -- com', '  -- com',
           , 'drei -- ko', '"vi er"."fu  enf   " -- co')
    s = jOpen(scanSql(b), m.j.cRead)
    do sx=1 while \scanEnd(s)
        if scanSqlQuID(s) then
            call tstOut t, 'sqlQuId' m.s.val m.s.val.0 m.s.val.1
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlNum/
    ### start tst tstScanSqlNum #######################################
    sqlNum 1
    spaceNL
    sqlNum 2..
    spaceNL
    sqlNum .3
    spaceNL
    sqlNum 4.5
    spaceNL
    sqlNum +6
    spaceNL
    sqlNum +7.03
    spaceNL
    sqlNum -8
    spaceNL
    sqlNum -.9
    spaceNL
    sqlNum 1E2
    spaceNL
    sqlNum -2.E-2
    spaceNL
    sqlNum +.3E+3
    spaceNL
$/tstScanSqlNum/ */
    call tst t, 'tstScanSqlNum'
    b = jBuf('1 2. .3 4.5 +6 + --kom', , ' -- com ', , ' 7.03 -8 -  .9',
             '1e2 - 2.e-2 + .3e+3')
    s = jOpen(scanSql(b), m.j.cRead)
    do sx=1 while \scanEnd(s)
        if scanSqlNum(s) then
            call tstOut t, 'sqlNum' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlNumUnit/
    ### start tst tstScanSqlNumUnit ###################################
    sqlNumUnit 1 KB
    spaceNL
    sqlNumUnit .3 MB
    sqlNumUnit .5
    sqlNumUnit +6.E-5 B
    spaceNL
    sqlNumUnit -7
    char *
    spaceNL
    sqlNumUnit -.8
    char T
    char B
    spaceNL
    *** err: scanErr scanSqlNumUnit after +9. bad unit TB
    .    e 1: last token Tb scanPosition .
    .    e 2: pos 41 in line 1: 1 kb .3mB.5 +   6.e-5B -7* -.8 TB + 9.T+
    b
    sqlNumUnit +9..
    spaceNL
$/tstScanSqlNumUnit/ */
    call tst t, 'tstScanSqlNumUnit'
    b = jBuf('1 kb .3mB.5 +   6.e-5B -7* -.8 TB + 9.Tb')
    s = jOpen(scanSql(b), m.j.cRead)
    do sx=1 while \scanEnd(s)
        if scanSqlNumUnit(s, 0, 'B KB MB') then
            call tstOut t, 'sqlNumUnit' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else if scanChar(s ,1) then call tstOut t, 'char' m.s.tok
        else call scanErr s, 'cannot scan'
        end
    call tstEnd t
    return
endProcedure tstScanSql

/*--- one single test scan with lines to scan in stem ln -------------*/
tstScan1:
parse arg sc, classs, ln
    if sc == '' then do
        call tstOut t, 'scan src' ln
        call scanSrc scanOpts(s), ln
        end
    else do
        call tstOut t, 'scan scanner' sc
        s = sc
        end
    m.s.key = ''
    m.s.val = ''
    do forever
        x = tstScanType(s, classs)
        if x == '' then
           leave
        call tstOut t, 'scan' x 'tok' length(m.s.tok)':' m.s.tok ,
                  'key' m.s.key 'val' m.s.val
        end
    return
endProcedure tstScan1

tstScanType: procedure expose m.
parse arg s, opt
    cx = 1
    a2 = ''
    res = 0
    do while cx <= length(opt)
        f = substr(opt, cx, 1)
        cx = cx + 1
        if pos(substr(opt, cx, 1), "'""") > 0 then do
            m.tstScanType.src = opt
            m.tstScanType.pos = cx
            call scanString tstScanType
            a2 = m.tstScanType.val
            cx = m.tstScanType.pos
            end
        if      f == 'a' then
            res = scanString(s, "'")
        else if f == 'b' then
            res = scanSpace(s)
        else if f == 'c' then
            res = scanChar(s, a2)
        else if f == 'd' then
            res = scanNat(s, a2)
        else if f == 'k' then
            res = scanKeyValue(s, 'def')
        else if f == 'l' then
            res = scanLit(s, a2)
        else if f == 'q' then
            res = scanString(s, '"')
        else if f == 'v' then
            res = scanVerify(s, a2)
        else if f == 'w' then
            res = scanWord(s)
        else if f == 'y' then
            res = scanVerify(s, a2, 'm')
        if res then
            return f
        end
    return scanType(s)
endProcedure tstScanType

/* copx tstBase end   *************************************************/

/* copx tst begin ****************************************************
      test infrastructure
***********************************************************************/
/*--- migrate all compares to new ones:
        tstCI input compare
        tstCO ouput migrated compares
        tstCIO input and output -------------------------------------*/
tstUtTime: procedure expose m.
    say 'begin' utTime()  sysvar('sysnode')
    do 3000000
       end
    call sleep 1
    say 'end  ' utTime()
return

tstUtc2d: procedure expose m.
/*
$=/tstUtc2d/
    ### start tst tstUtc2d ############################################
    .             ff            255
    .           ffff          65535
    .          10000          65536          65536 = 1 * 16 ** 4
    .          10001          65537
    .         ffffff       16777215
    .        1000000       16777216       16777216 = 1 * 16 ** 6
    .        1000001       16777217
    .        20000FF       33554687
    .      100000000     4294967296     4294967296 = 1 * 16 ** 8
    .      300000000    12884901888    12884901888 = 3 * 16 ** 8
    .      3020000EF    12918456559
$/tstUtc2d/
*/
    numeric digits 33
    call tst t, 'tstUtc2d'
    all = 'ff ffff 10000 10001 ffffff 1000000 1000001 20000FF' ,
           '100000000 300000000 3020000EF'
    do ax = 1 to words(all)
        a = word(all, ax)
        if substr(a, 2) = 0 then
            b = right(left(a, 1) * 16 ** (length(a)-1), 15) ,
              '=' left(a, 1) '* 16 **' (length(a)-1)
        else
            b = ''
        call out right(a, 15)right(utc2d(x2c(a)), 15)b
        end
    call tstEnd t
    return
endProcedure tstUtc2d

tstCI: procedure expose m.
parse arg m, nm
    m.m.CIO = 0
    signal tstCIwork
tstCIO: procedure expose m.
parse arg m, nm
    m.m.CIO = 1
tstCIwork:
    m.m.name = nm
    m.m.cmp.1 = left('### start tst' nm '', 67, '#')

    do ix=2 to arg()-1
        m.m.cmp.ix = arg(ix+1)
        end
    m.m.cmp.0 = ix-1
    if m.m.CIO then
        call tstCO m
    return

tstCO: procedure expose m.
parse arg m
    call tst2dpSay m.m.name, m'.CMP', 68
    return
/*--- initialise m as tester with name nm
        use inline input nm as compare lines -----------------------*/
tstReset: procedure expose m.
parse arg m, nm
    call tstIni
    m.m.name = nm
    m.m.inIx  = 0
    m.m.out.0 = 0
    m.m.err   = 0
    m.err.count = 0
    m.tst.act = m
    if \ datatype(m.m.trans.0, 'n') then
        m.m.trans.0 = 0
    m.m.trans.old = m.m.trans.0
    return
endProcedure tstReset

tst: procedure expose m.
parse arg m, nm, cmpSt
    call tstReset m, nm
    m.tst.tests = m.tst.tests+1
    if cmpSt == '' then do
        cmpSt = mCut(t'.CMP', 0)
        call tst4dp cmpSt, mapInline(nm)
        end
    m.m.cmp = cmpSt
    m.m.moreOutOk = 0
    call mAdd mCut(m'.IN', 0), 'tst in line 1 eins ,'    ,
                     , 'tst in line 2 zwei ;   ' ,
                     , 'tst in line 3 drei .schluss.'
    call tstOut m, left('### start tst' nm '', 67, '#')
    call errReset 'h', 'return tstErrHandler(ggTxt)'
    m.m.errCleanup = m.err.cleanup
    if m.tst.ini.j \== 1 then do
/*      call err implement outDest 'i', 'call tstOut' quote(m)', msg'
*/      end
    else do
        call oMutatName m, 'Tst'
        call oMutatName m'.IN', 'Tst'
        m.m.jReading = 1
        m.m.jWriting = 1
        m.m.jUsers = 0
        m.m.in.jReading = 1
        m.m.in.jWriting = 1
        m.m.in.jUsers = 0
        if m.tst.ini.e \== 1 then do
            m.m.oldJin = m.j.in
            m.m.oldOut = m.j.out
            m.j.in = m
            m.j.out = m
            end
        else do
            if m.pipe.0 <> 2 then
                call tstErr m, 'm.pipe.0' m.pipe.0 '<> 2'
            call pipe '+Ff', m , m'.IN'
            end
        end
    return m
endProcedure tst

tstEnd: procedure expose m.
parse arg m, opt opt2
    cmp = m.m.cmp
    m.m.jReading = 0
    m.m.jWriting = 0
    if m.tst.ini.j == 1 then do
        m.m.jReading = 0
        m.m.jWriting = 0
           if m.tst.ini.e \== 1 then do
            m.j.in = m.m.oldJin
            m.j.out = m.m.oldOut
            end
        else do
            if m.j.in \== m'.IN' | m.j.out \== m then
                call tstErr m, m.j.in '\==' m '|' m.j.out '\==' m
            call pipe '-'
            if m.pipe.0 <> 2 then
                call tstErr m, 'm.pipe.0' m.pipe.0 '<> 2'
            end
        end
    if m.m.err = 0 then
        if m.m.errCleanup \= m.err.cleanup then
            call tstErr m, 'err.Cleanup' m.err.cleanup '<> old',
                        m.m.errCleanup
    if m.m.out.0 \= m.cmp.0 & \ (m.m.moreOutOk,
            &  m.m.out.0 > m.cmp.0) then do
        call tstErr m, 'old' m.cmp.0 'lines \= new' m.m.out.0
        do nx = m.m.out.0 + 1 to min(m.m.out.0+10, m.cmp.0)
            say 'old -  ' m.cmp.nx
            end
        end
    call errReset 'h'
    m.tst.act = ''
    soll = 0
    if opt = 'err' then do
        soll = opt2
        if m.m.err \= soll then
            call err soll 'errors expected, but got' m.m.err
        end
    if m.m.err \= soll then do
        say 'new lines:' (m.m.out.0 - 1)
           call tst2dpSay m.m.name, m'.OUT', 68
        end
    say left('###' m.m.name 'end with' m.m.err 'errors ', 67, '#')

    if 1 & m.m.err \= soll then
        call err 'dying because of' m.m.err 'errors'
    m.m.trans.0 = m.m.trans.old
    return
endProcedure tstEnd

tst2dp: procedure expose m.
parse arg st, dp, ml
    dx = m.dp.0
    do sx=1 to m.st.0
        li = m.st.sx
        cx = 1
        do until cx > length(li)
            c = substr(li, cx, 1)
            ou = left('.', strip(c) == '' | pos(c, '.+') > 0)
            ex = min(length(li),cx+ml-length(ou)-2)
            ou = ou || substr(li, cx, ex+1-cx)
            dx = dx + 1
            c = right(ou, 1)
            if ex < length(li) then
                m.dp.dx = ou || '+'
            else if strip(c) == '' | pos(c, '.+') > 0 then
                m.dp.dx = ou || '.'
            else
                m.dp.dx = ou
            cx = ex +1
            end
        end
    m.dp.0 = dx
    return
endProcedure tst2dp

tst2dpSay: procedure expose m.
parse arg name, st, ml
    say '$=/'name'/'
    call tst2dp st, mCut('TST.TMP', 0), 68
    do nx=1 to m.tst.tmp.0
           say '   ' m.tst.tmp.nx
           end
     say '$/'name'/'
return tst2dpSay

tst4dp: procedure expose m.
parse arg st, dp
    sx = m.st.0
    inData = 0
    data = ''
    do dx=1 to m.dp.0
        li = strip(m.dp.dx)
        if pos(left(li, 1), '.+') > 0 then
         li = substr(li, 2)
        if right(li, 1) == '+' then do
            inData = 1
            data = data || left(li, length(li)-1)
            iterate
            end
        if right(li, 1) == '.' then
            li = left(li, length(li)-1)
        sx = sx + 1
        m.st.sx = repAll(data || li, '$ä', '/*', '$ö', '*/')
        inData = 0
        data = ''
        end
    m.st.0 = sx
    if inData then
        call err 'end inData'
    return
endProcedure tst4dp

/*--- write to test: say lines and compare them ----------------------*/
tstWrite: procedure expose m.
parse arg m, arg
    call tstOut m, 'out:' arg
    return
endProcedure tstWrite

tstOut: procedure expose m.
parse arg m, arg
    do tx=m.m.trans.0 by -1 to 1
        arg = repAll(arg, word(m.m.trans.tx, 1),
            , subword(m.m.trans.tx, 2))
        end
    call mAdd m'.OUT', arg
    nx = m.m.out.0
    cmp = m.m.cmp
    c = m.cmp.nx
    if nx > m.cmp.0 then do
        if nx = m.cmp.0+1 & \ m.m.moreOutOK then
            call tstErr m, 'more new Lines' nx
        end
    else if c \== arg then do
        do cx=1 to min(length(c), length(arg)) ,
             while substr(c, cx, 1) == substr(arg, cx, 1)
             end
         msg = 'old line' nx '<> new overnext, firstDiff' cx',',
                 'len old' length(c)', new' length(arg)

        if cx > 10 then
            msg = overlay('|', msg, cx-10)
        call tstErr m, msg
        say c
        end
    say arg
    return 0
endProcedure tstOut

tstWriteO: procedure expose m.
parse arg m, var
    cl = objClass(var, '')
    if cl == '' then do
        if var == '' then
            call tstOut t, 'tstR: @ obj null'
        else
            call tstOut t, 'no class for' var 'in tstWriteO|'
        end
    else if abbrev(var, m.o.escW) then do
        call tstOut t, o2String(var)
        end
    else if cl == m.class.classV then do
        call tstOut t, m.var
        end
    else if oKindOf(var, 'JRW') then do
        call tstOut t, 'tstWriteO kindOf JRW jWriteNow begin <<<'
        call jWriteNow m, var
        call tstOut t, 'tstWriteO kindOf JRW jWriteNow end   >>>'
        end
    else if oKindOf(var, 'ORun') then do
        call tstOut t, 'tstWriteO kindOf ORun oRun begin <<<'
        call oRun var
        call tstOut t, 'tstWriteO kindOf ORun oRun end   >>>'
        end
    else do
        do tx=m.m.trans.0 by -1 to 1 ,
                while word(m.m.trans.tx, 1) \== var
            end
        if tx < 1 then
            call mAdd M'.TRANS', var 'tstWriteoV' || (m.m.trans.0+1)
        call classOut , var, 'tstR: '
        end
    return
endProcedure tstWriteO

tstReadO: procedure expose m.
parse arg m, arg
    if right(m, 3) == '.IN' then
       m = left(m, length(m)-3)
    else
        call err 'tstReadO bad m' m
    ix = m.m.inIx + 1
    m.m.inIx = ix
    if ix <= m.m.in.0 then do
        call tstOut m, '#jIn' ix'#' m.m.in.ix
        return s2o(m.m.in.ix)
        end
    call tstOut m, '#jIn eof' ix'#'
    return ''
endProcedure tstReadO

tstFilename: procedure expose m.
parse arg suf, opt
    if m.err.os == 'TSO' then do
        dsn = dsn2jcl('~tmp.tst.'suf)
        if opt = 'r' then do
            if sysDsn("'"dsn"'") \== 'DATASET NOT FOUND' then
                call adrTso "delete '"dsn"'"
            call csiOpen 'TST.CSI', dsn'.**'
            do while csiNext('TST.CSI', 'TST.FINA')
                say 'deleting csiNext' m.tst.fina
                call adrTso "delete '"m.tst.fina"'"
                end
            end
        return dsn
        end
    else if m.err.os == 'LINUX' then do
        if abbrev(suf, '/') then
            fn = suf
        else
            fn = .Stream%%new('~/tmp/tst/'suf)%%qualify /* full path */
        cx = lastPos('/', fn)
        if cx > 0 then do
            dir = left(fn, cx-1)
            if \sysIsFileDirectory(dir) then
                call adrSh "mkdir -p" dir
            if \sysIsFileDirectory(dir) then
                call err 'tstFileName could not create dir' dir
            end
        if opt \= 'r' then
            nop
        else if sysIsFile(fn) then
            call sysFileDelete fn
        else if sysIsFileDirectory(fn) then
            call adrSh 'rm -r' fn
        return fn
        end
    else
        call err 'tstFilename does not implement os' m.err.os
endProcedure tstFilename

/*--- say total errors and fail if not zero --------------------------*/
tstTotal: procedure expose m.
    say '######'
    say '######'
    say '######' m.tst.tests 'tests with' ,
                 m.tst.err 'errors in' m.tst.errNames
    say '######'
    say '######'
    if m.tst.err \== 0 then
        call err m.tst.err 'errors total'
    return
endProcedure tstTotal

/*--- test err: message, count it and continue -----------------------*/
tstErr: procedure expose m.
parse arg m, msg
    say '### error' msg
    m.m.err = m.m.err + 1
    m.tst.err = m.tst.err + 1
    nm = m.m.name
    if wordPos(nm, m.tst.errNames) < 1 then
        m.tst.errNames = m.tst.errNames nm
    return 0
endProcedure tstErr

/*--- tstErrHandler: intercept errors --------------------------------*/
tstErrHandler: procedure expose m.
parse arg ggTxt
    m = m.tst.act
    if m == '' then
        call err ggTxt
    m.err.count = m.err.count + 1
    call errMsg ' }'ggTxt
    call tstOut m.tst.act, '*** err:' m.err.1
        do x=2 to m.err.0
            call tstOut m, '    e' (x-1)':' m.err.x
            end
    return 0
endSubroutine tstErrHandler

tstTrc: procedure expose m.
parse arg msg
    m.tst.trc = m.tst.trc + 1
    say 'tstTrc' m.tst.trc msg
    return m.tst.trc
endProcedure tstTrc

/*--- tstIni: global initialization ----------------------------------*/
tstIni: procedure expose m.
    if m.tst.ini \== 1 then do
        m.tst.ini = 1
        call mapIni
        m.tst.err = 0
        m.tst.trc = 0
        m.tst.errNames = ''
        m.tst.tests = 0
        m.tst.act = ''
        end
    if m.tst.ini.j \== 1 & m.j.ini == 1 then do
        m.tst.ini.j = 1
          call classNew 'n Tst u JRWO', 'm',
             , "jReadO return tstReadO(m)",
             , "jWrite call tstOut m, line",
             , "jWriteO call tstWriteO m, var"
        end
    if m.tst.ini.e \== 1 & m.pipe.ini == 1 then do
        m.tst.ini.e = 1
        end
    return
endProcedure tstIni
/* copx tst    end   **************************************************/
/* copx tstData begin *************************************************/
tstData: procedure expose m.
parse arg ty 2 le 'n' nu, l, r, num
    abs = abs(num)
    if nu \== '' then do
        if abs // 5 = 0 then
            return 'null' || (abs % 5 // nu + 1)
        end
    if ty = 'c' then do
        if le = '' then
            le = 8
        le = abs // le + 1
        if r = '' then
            r = '+'
        return left(l || num || r, le, right(r, 1))
        end
    if pos(ty, 'ief') < 1 then
        call err 'bad type' ty
    nn = abs
    if abbrev(num, '-') | abbrev(num, '+') then
        parse var num si 2 nn
    else
        si = ''
    if ty == 'e' then
        ex = 'e' || left('-', abs // 2) || (abs // 15)
    else
        ex = ''
    if le \== '' then
        nn = right(nn, abs // max(1, le - length(si||ex)) + 1, 1)
    if ty \== 'i' & (abs // 4) \= 0 & length(nn) > 1 then
            nn = overlay('.', nn, length(nn) - abs // length(nn))
     return si || nn || ex
endProcedure tstData

tstDataClassFo: procedure expose m.
parse arg flds
    ty = ''
    do fx=1 by 2 to words(flds)
        if word(flds, fx) = '.' then
            ty = ty', v'
        else
            ty = ty', f' word(flds, fx) 'v'
        end
    t = classNew('n* tstData u' substr(ty, 2))
    fo = oNew(m.t.name)
    fs = oFlds(fo)
    do fx=1 to m.fs.0
        f = fo || m.fs.fx
        m.f = word(flds, 2*fx)
        end
    return fo
endProcedure tstDataClassFo


tstDataClassOut: procedure expose m.
parse arg flds, f, t
    fo = tstDataClassFo(flds)
    fs = oFlds(fo)
    do x=f to t
        o = oCopy(fo)
        do fx=1 to m.fs.0
            na = substr(m.fs.fx, 2)
            f = o || m.fs.fx
            m.f = tstData(m.f, na, '+'na'+', x)
            end
        call outO o
        end
    return
endProcedure tstDataClassOut
/* copx tstData end ***************************************************/
/* copy tstAll end   **************************************************/
/* copy time begin -----------------------------------------------------
 11.05.23 wk: numeric digits transparent: in jeder Procedure drin
              time2jul, tst externalisiert
----------------------------------------------------------------------*/
/*--- read timeZoneOffset and leapSeconds registers
        and set variables for uniq ----------------------------------*/
timeReadCvt: procedure expose m.
parse arg debug
    numeric digits 15
    /* offsets documented in z/OS Data Areas  Vol.1 */
    cvtOH      = '00000010'          /* cvt control block Address */
    cvtext2O   = x2d('00000560') /* offset  to extension 2    */
    cvtldtoO   = x2d('00000038') /* offset to timezone    */
    cvtlsoO    = x2d('00000050') /* offset to leapSeconds */

    /* CVT CB        address               + extention2   */
    cvtExt2A       = C2D(STORAGE(cvtOH,4)) + cvtext2O
    /* cvtLdto timeZone              address +offset      */
    m.timeZone     = c2d(STORAGE(d2x(cvtext2A+cvtldtoO, 8), 8))
    m.timeStckUnit = 1e-6 / 256 / 16
    /* cvtLso LeapSecs               address +offset      */
    m.timeLeap     = C2D(STORAGE(d2x(cvtext2A+cvtlsoO, 8), 8))
    m.timeUQDigits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345678'
                         /* find lrsn of day 0 */
    m.timeUQZero = timeGmt2Lrsn('2004-12-31-00.00.22.000000')
                         /* 0 out last 6 bits  */
    m.timeUQZero = b2x(overlay('000000', x2b(m.timeUQZero), 43))
    if debug == 1 then do
      say 'stckUnit          =' m.timeStckUnit
      say 'timeLeap          =' d2x(m.timeLeap,16) '=' m.timeLeap ,
                   '=' format(m.timeLeap * m.timeStckUnit, 9,3) 'secs'
      say 'timeZone          =' d2x(m.timeZone,16) '=' m.timeZone,
                   '=' format(m.timeZone  * m.timeStckUnit, 6,3) 'secs'
      say "cvtext2_adr       =" d2x(cvtExt2A, 8)
      say 'timeUQZero        =' m.timeUQZero
      say 'timeUQDigis       =' ,
                    length(m.timeUQDigits) 'digits' m.timeUQDigits
    end
    m.timeReadCvt = 1
    return
endSubroutine timeReadCvt

timestampParse:
    parse arg yyyy'-'mo'-'dd'-'hh'.'mm'.'ss'.'ffffff
    return

/*--- GMT timestamp yyyy-mm.... -> stck value char(8)
         BLSUXTID is described in z/OS V1R7.0 MVS IPCS Customization
         BLSUXTID format : mo/dd/yyyy hh:mm:ss.ffffff ---------------*/
timeGmt2Stck: procedure expose m.
    parse arg tst
    call timestampParse tst
    tDate = mo'/'dd'/'yyyy' 'hh':'mm':'ss'.'ffffff
    ACC=left('', 8, '00'x)
    ADDRESS LINKPGM "BLSUXTID TDATE ACC"
    RETURN acc
endProcedure timeGmt2Stck

/*--- GMT timestamp yyyy-mm.... -> stck value in hex(16) ------------*/
timeGmt2LRSN: procedure expose m.
    return c2x(left(timeGmt2Stck(arg(1)), 6))
endProcedure timeGmt2LRSN

/*--- LZT (Locale Zurich Tst -> stck value in hex(16) ---------------*/
timeLZT2LRSN: procedure expose m.
parse arg tst
    numeric digits 23
    if m.timeReadCvt \== 1 then
        call timeReadCvt
    return left(d2x(c2d(timeGmt2Stck(tst)) ,
                     - m.timeZone + m.timeLeap, 16), 12)
endProcedure timeLZT2LRSN
/*--- conversion from Stck Clock Value to GMT Timestamp
        BLSUXTOD is described in z/OS V1R7.0 MVS IPCS Customization
        input -> + leapseconds -> output ----------------------------*/
timeStck2Gmt: PROCEDURE expose m.
parse arg stck
  stck = left(stck, 8, '00'x)
  TDATE = COPIES('0' , 26)
  ADDRESS LINKPGM "BLSUXTOD stck TDATE"
  /* return format   : mo/dd/yyyy hh:mm:ss.ffffff */
  /* Timestamp format: yyyy-mm-dd-hh.mm.ss.ffffff */
  parse var TDATE mo '/' dd '/' yyyy hh ':' mm ':' ss '.' ffffff
RETURN yyyy'-'mo'-'dd'-'hh'.'mm'.'ss'.'ffffff
endProcedure timeStck2Gmt

/*--- conversion from Lrsn Clock Value to GMT Timestamp -------------*/
timeLrsn2Gmt:
    return timeStck2Gmt(x2c(arg(1)))
endProcedure timeLrsn2Gmt

/*--- conversion from Lrsn Clock Value to LZT Local Zurich Time -----*/
timeLrsn2LZT: procedure expose m.
parse arg lrsn
    numeric digits 23
    if m.timeReadCvt \== 1 then
        call timeReadCvt
    return timeStck2Gmt(d2c(x2d(left(lrsn, 16, 0)) ,
                           + m.timeZone-m.timeLeap))
endProcedure timeLrsn2LZT

/*--- timestamp to julian --------------------------------------------*/
time2jul: procedure expose m.
parse arg yyyy '-' mm '-' dd '-'
        /* date function cannot convert to julian, only from julian
            ==> guess a julian <= the correct and
                try the next values
        */
    j = trunc((mm-1) * 29.5) + dd
    yy = right(yyyy, 2)
    do j=j by 1
        j = right(j, 3, 0)

        d = date('s', yy || j, 'j')
        if substr(d, 3) = yy || mm || dd then
            return yy || j
        end
    return
endProcedure time2jul
/* convert a uniq variable to lrsn ************************************/
timeUniq2lrsn: procedure expose m.
parse arg uniq
    numeric digits 15
    if m.timeReadCvt \== 1 then
        call timeReadCvt
    uniq = left(uniq, 8, 'A')
    d42 = d2x(q2i(uniq, m.timeUQDigits))
    d48 = b2x('00'x2b(d42)'000000')
    lrsn = right(d2x(x2d(d48) + x2d(m.timeUQZero)), 12, 0)
    return lrsn
endProcedure uniq2lrsn
/*--- translate a number in q-system to decimal
       arg digits givs the digits corresponding to 012.. in the q sysem
       q = length(digits) --------------------------------------------*/
q2i: procedure expose m.
parse arg v, digits
    b = length(digits)
    i = 0
    do x = 1 to length(v)
        q = substr(v, x, 1)
        r = pos(q, digits)
        if r < 1 then
            call err 'bad digit' q 'in' v 'valid digits' digits
        i = i * b + r - 1
        end
    return i
endProcedure q2i
/*--- translate a decimal number to q-system - inverse of q2i --------*/
i2q: procedure expose m.
parse arg i, digits
    if i = 0 then
        return left(digits, 1)
    b = length(digits)
    v = ''
    do while i > 0
        v = substr(digits, 1 + (i // b), 1) || v
        i = i % b
        end
    return v
endProcedure i2q
/* copy time end -----------------------------------------------------*/
/* copy sort begin ****************************************************/
sort: procedure expose m.
parse arg i, o, cmp
    if cmp == '' then
        m.sort.comparator = "cmp = m.l.l0 <<= m.r.r0"
    else if length(cmp) < 6 then
        m.sort.comparator = "cmp = m.l.l0" cmp "m.r.r0"
    else if pos(';', cmp) < 1 then
        m.sort.comparator = "aLe = l'.'l0; aRi = r'.'r0; cmp =" cmp
    else
        m.sort.comparator = "aLe = l'.'l0; aRi = r'.'r0;" cmp
    call sort1 i, 1, m.i.0, o, 1, sort.work, 1
    m.o.0 = m.i.0
    return
endProcedure sort

sort1: procedure expose m.
parse arg i, i0, le, o, o0, w, w0
    if le <= 3 then do
        if le = 3 then do
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, w, w0
            call sortMerge i, i0+2, i0+3, w, w0, w0+2, o, o0
            end
        else if le = 2 then
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, o, o0
        else if le = 1 then
            m.o.o0 = m.i.i0
        return
        end
    h = (le + 1) % 2
    call sort1 i, i0,   h,    o, o0+le-h, w, w0
    call sort1 i, i0+h, le-h, w, w0,      o, o0
    call sortMerge o, o0+le-h, o0+le, w, w0, w0+le-h, o, o0
    return
endProcedure sort1

sortMerge: procedure expose m.
parse arg l, l0, le, r, r0, re, o, o0
    do while l0 < le & r0 < re
        interpret m.sort.comparator
        if cmp then do
            m.o.o0 = m.l.l0
            l0 = l0 + 1
            end
        else do
            m.o.o0 = m.r.r0
            r0 = r0 + 1
            end
        o0 = o0 + 1
        end
    do while l0 < le
        m.o.o0 = m.l.l0
        l0 = l0 + 1
        o0 = o0 + 1
        end
    do while r0 < re
        m.o.o0 = m.r.r0
        r0 = r0 + 1
        o0 = o0 + 1
        end
    return
endProcedure sortMerge
/* copy sort end   ****************************************************/
/* copy match begin ***************************************************/
/*--- 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 comp begin *****************************************************
    the shell compiler
         syntax and semantics of the shell language see javaDoc
***********************************************************************/
/***** initialisation *************************************************/
/*--- module initialisation ------------------------------------------*/
compIni: procedure expose m.
    if m.compIni = 1 then
        return
    m.compIni = 1
    call pipeIni
    call scanReadIni
    cc = classNew('n Compiler u')
    call mNewArea 'COMP.AST', '='

    m.comp.chOp   = '.-<@|?%^'
    m.comp.chKind = '.-=#@:'
    m.comp.chKiNO = '=#:'
    call mPut 'COMP.INFO..', "object"
    call mPut 'COMP.INFO.-', "string"
    call mPut 'COMP.INFO.=', "skeleton"
    call mPut 'COMP.INFO.#', "text"
    call mPut 'COMP.INFO.@', "rexxShell"
    call mPut 'COMP.INFO.:', "pureShell"

    m.comp.chDol = '$'
    m.comp.chSpa = ' 'x2c('09')
    call mPut 'COMP.EXTYPE.b', m.comp.chDol'{}'       /* braces */
    call mPut 'COMP.EXTYPE.d', m.comp.chDol            /* data */
    call mPut 'COMP.EXTYPE.s', m.comp.chDol            /* strip */
    call mPut 'COMP.EXTYPE.w', m.comp.chDol||m.comp.chSpa /* word */

    m.comp.idChars = m.ut_alfNum'@_'
    m.comp.wCatC = 'compile'
    m.comp.wCatO = 'out'
    m.comp.wCatS = 'do withNew with for forWith ct proc table'
    return
endProcedure compIni

compReset: procedure expose m.
parse arg m
    m.m.scan = scanRead(,,'0123456789')
    return m
endProcedure compReset

/*--- constructor of Compiler ----------------------------------------*/
comp: procedure expose m.
parse arg src
    nn = oNew('Compiler')
    m.nn.cmpRdr = j2rdr(src)
    return nn
endProcedure comp

/**** user interface **************************************************/
/*--- compile and run ------------------------------------------------*/
compRun: procedure expose m.
parse arg spec, inO, ouO, infoA
    cmp = comp(inO)
    r = compile(cmp, spec)
    if infoA \== '' then
        m.infoA = 'run'
    if ouO \== '' then
        call pipe '+F', ouO
    call oRun r
    if ouO \== '' then
        call pipe '-'
    return 0
endProcedure compRun

/*--- compile the source and return an oRunner with the code ---------*/
compile: procedure expose m.
parse arg m, spec
    call compReset m
    s = m.m.scan
    if spec = '' then
        spec = '@'
    else
        spec = strip(spec, 'l')
    if m.m.cmpRdr == '' then do
        call scanSrc s, spec
        end
    else do
        call scanReadReset s, m.m.cmpRdr
        call jOpen s, m.j.cRead, spec
        end
    m.m.out = ''
    m.m.end = ''
    res = compAst(m, '¢')
    eOld = m.err.count
    do forever
        one = ''
        if pos(scanLook(s, 1), m.comp.chKind'*') > 0 then do
            call scanChar s, 1
            ki = m.s.tok
            one = compileOne(m, ki)
            end
        else if \ scanName(s) then do
            call scanErr s, 'kind or hook expected after $#'
            end
        else if m.s.tok == 'out' then do
            m.m.out = scanPos(s)
            leave
            end
        else if m.s.tok == 'end' then do
            if m.m.end = '' then
                m.m.end = scanPos(s)
            one = compileOne(m)
            end
        else if m.s.tok == 'version' then do
            call scanSpace s
            if \ scanWord(s) | m.s.tok \== '2.0' then
                call scanErr s, 'only version 2.0 is supported'
            call scanNl s, 1
            end
        else do
            one = compileOne(m, , m.s.tok)
            end
        if one \== '' then
            call mAdd res, one
        if scanEnd(s) then
            leave
        if \ scanLit(s, '$#') then
            return scanErr(s, m.comp.info.ki "expected: compile",
                 ki "stopped before end of input")
        end
    if m.m.cmpRdr \== '' then
        call jClose s
    if m.err.count <> eOld then
        return ''
    if 0 then
        call compAstSay res, 0
    return oRunner(compAst2Rx(m, ';', res))
endProcedure compile

/*--- compile or use hook for one part from spec or input -----------*/
compileOne: procedure expose m.
parse arg m, ki, hook
    s = m.m.scan
    call compSpComment m
    if ki == '*' | m.m.end \== '' then do
        do until scanLook(s, 2) == '$#' | scanEnd(s)
            call scanNl s, 1
            end
        return ''
        end
    if ki \== '' then do
        call scanNl s
        return compUnit(m, ki, '$#')
        end
    say 'interpreting hook' hook':' strip(scanLook(s))
    interpret return 'wshHook_'hook'(m)'
endProcedure compileOne

/*--- parse the whole syntax of a unit ------------------------------*/
compUnit: procedure expose m.
parse arg m, ki, stopper
    s = m.m.scan
    if pos(ki, m.comp.chKind) < 1 then
        return scanErr(s, 'bad kind' ki 'in compUnit(...'stopper')')
    else if pos(ki, '.-=@:') > 0 then do
     a = compAst(m, '¢')
        do forever
         one = compPipe(m, ki)
         if one \== '' then
          call mAdd a, one
         if \ scanLit(m.m.scan, '$;') then
                return a
         end
        end
    else if ki == '#' then do
        res = compAST(m, '¢')
        call scanChar s
        if verify(m.s.tok, m.comp.chSpa) > 0 then
            call mAdd res, compAstAddOp(m,
                , compAst(m, '=', strip(m.s.tok, 't')), '$')
        do while scanNL(s, 1) & \ abbrev(m.s.src, stopper)
            call mAdd res, compAstAddOp(m,
                , compAst(m, '=', strip(m.s.src, 't')), '$')
            end
        return res
        end
    call err 'bad kind' ki 'in compUnit'
endProcedure compUnit

/*--- compile an expression, type d=data, b=block w=word, s=strip ---*/
compExpr: procedure expose m.
parse arg m, type, ki
    if symbol('m.comp.exType.type') \== 'VAR' then
        call err s, 'bad type' type 'in compExpr'
    s = m.m.scan
    if length(ki) \== 1 | pos(ki, '.-=@') < 1 then
        return scanErr(s, 'bad kind' ki 'in compExpr')

    res = compAST(m, translate(ki, '-', '='))
    astKi = translate(ki, 'os=c', '.-=@')
    laPrim = 0
    gotTxt = 0
    m.res.containsC = 0
    if pos(type, 'sb') > 0 then
        m.res.containsC = compSpComment(m) >= 2
    do forever
        if scanVerify(s, m.comp.exType.type, 'm') then do
            one = compAST(m, astKi, m.s.tok)
            if verify(m.s.tok, m.comp.chSpa) > 0 then
                gotTxt = 1
            end
        else do
            old = scanPos(s)
            if \ scanLit(s, m.comp.chDol) then
                leave
            one = compPrimary(m, ki)
            if one = '' then do
                call scanSetPos s, old
                leave
                end
            laPrim = m.res.0 + 1
            end
        call mAdd res, one
        if compComment(m) then
            m.res.containsC = 1
        end
    if pos(type, 'bs') > 0 then do
        do rx=m.res.0 by -1 to laPrim+1
            one = m.res.rx
            m.one.text = strip(m.one.text, 't')
            if length(m.one.text) <> 0 then
                leave
            call mFree one
            end
        m.res.0 = rx
        end
    m.res.containsD = laPrim > 0 | gotTxt
    return compAstFree0(res, '')
endProcedure compExpr

/*--- compile a primary and return code ------------------------------*/
compPrimary: procedure expose m.
parse arg m, ki, compOk
    s = m.m.scan
    ops = ''
    if scanLit(s, '.', '-') then
        ops = m.s.tok || compOpKi(m, m.s.tok)
    getOps = ops
    if getOps == '' then
        getOps = substr('-.', 1+(ki == '.'), 1)
    if scanString(s) then
        return compASTAddOp(m, compAST(m, '=', m.s.val), getOps)
    r = compVar(m, 'c')
    if r \== '' then do
        if m.r.var \== 'c' then
             return compASTAddOp(m, compAst(m,'&',m.r.var, r), getOps)
        o2 = ops
        do while pos(right(o2, 1) ,'.@') > 0
         o2 = left(o2, length(o2)-1)
            end
        if right(o2, 1) == '^' then
         o2 = left(o2, length(o2)-1)
        else if compOk \== 1 then
            call scanErr s, 'compile can only be used by ^'
           return compASTAddOp(m, compAst(m, 'M'), o2)
        end
    if ops == '' then
     return ''
     one = compBlock(m, ops)
    if one \== '' then
        return one
    return scanErr(s, 'primary not finished')
endProcedure compPrimary

/*--- scan an operator chain and a kind ------------------------------*/
compOpKi: procedure expose m.
parse arg m, defKi
    s = m.m.scan
    call scanVerify s, m.comp.chOp || m.comp.chKiNO
    op = m.s.tok
    kx = verify(op, m.comp.chKiNO, 'm')
    if kx = length(op) & op \== '' then
        return op
    else if kx \== 0 then
        call scanErr s, 'kind' substr(op, kx, 1) 'not after ops'
    else if pos(right(op, 1), m.comp.chKind'^%') > 0 then
        return op
    else if defKi == '<' | right(op, 1) == '<' then
        return op'='
    else if defKi \== '' then
        return op || defKi
    else
     call scanErr s, 'no kind after ops' op
endProcedure compOpKi

/*--- block or expression --------------------------------------------*/
compExprBlock: procedure expose m.
parse arg m, ki
    s = m.m.scan
    old = scanPos(s)
    pk = compOpKi(m, ki)
    kiFi = left('<', ki =='<')
    res = compBlock(m, kiFi || pk)
    if res \== '' then do
        if length(pk) \== 1 | ki == '<' then
            return res
        else
            return compAstAddOp(m, res, translate(pk, '--', '=#'))
        end
    if pk \== '' then
        lk = right(pk, 1)
    else
        lk = translate(ki, '.', '@')
    res = compExpr(m, 's', lk)
    if res \== '' then
        return compASTAddOp(m, res, kiFi || left(pk, length(pk)-1))
    call scanSetPos s, old
    return res
endProcedure compExprBlock

/*--- compile a pipe and return code ---------------------------------*/
compPipe: procedure expose m.
parse arg m, ki
    s = m.m.scan
    res = compAST(m, 'P', ' ', '', '')
    do forever
        one = compExprStmts(m, ki)
        if one \== '' then do
            if m.res.0 > 2 then  do;call compAstSay res, 0
                call scanErr s, '$| before statements needed' ; end
            call mAdd res, one
            end
        pre = left(m.comp.chDol, scanLit(s, m.comp.chDol))
        if scanLit(s, '<') then do
            if m.res.2 == '' then
                m.res.2 = compAst(m, '.')
            else
                call mAdd m.res.2, compAst(m, 'o', ', ')
            call mAdd m.res.2, compExprBlock(m, '<')
            m.res.text = m.res.text'f'
            end
        else if scanLit(s, '>>', '>') then do
            if m.res.1 <> '' then
                call scanErr s, 'duplicate output'
            m.res.text = if(m.s.tok == '>', 'F', 'A') ,
                ||substr(m.res.text, 2)
            m.res.1 = compExprBlock(m, '<')
            end
        else if scanLit(s, '|') then do
            if m.res.0 < 3 then
                call scanErr s, 'stmts expected before |'
            call compSpNlComment m
            call mAdd res, compCheckNE(m, compExprStmts(m, ki),
                , 'stmts or expressions after | expected')
            end
        else do
            call scanBack s, pre
            if m.res.0 > 3 | m.res.1 \== '' | m.res.2 \== '' then
                return res
            one = if(m.res.0 = 3, m.res.3)
            call mFree res
            return one
            end
        end
endProcedure compPipe

/*--- compile expressions and stmts ---------------------------------*/
compExprStmts: procedure expose m.
parse arg m, ki
    s = m.m.scan
    res = compAst(m, '¢')
       nlLe = 0 /* sophisticated logic using left and right NLs*/
    tb = ''
    do forever
        if tb \== '' then do
            call compSpComment m
            px = m.s.pos
            e1 = compExpr(m, 'w', '=')
            if e1 \== '' then do
                aa = compAst(m, '¢')
                fx = 0
                do until e1 == ''
                    do fx=fx+1 to m.tb.0 until px < m.ff.end
                        ff = m.tb.fx
                        end
                    if fx > m.tb.0 then
                        call scanErr s, 'right of all table fields'
                    if m.s.pos <= m.ff.pos then
                        call scanErr s, 'before table field' m.ff.name
                    call mAdd aa, compAst(m, 'A', ,
                        , compAst(m, '=', m.ff.name), e1)
                    call compSpComment m
                    px = m.s.pos
                    e1 = compExpr(m, 'w', '=')
                    end
                call mAdd res, compAst(m, 'F', 'with',
                    , compAst(m, 'o', "oNew('"m.tb.class"')"),
                    , aa, compAst(m, '*', '$.'))
                end
            nlRi = scanNL(s)
            end
        else if ki == ':' then do
            call compSpNlComment m, '*'
            nlRi = 0
            end
        else if ki == '@' then do
            call compSpNlComment m
            one = compExpr(m, 's', ki)
            if one == '' then
                nlRi = 0
            else if m.one.0 < 1 then
                call scanErr s, 'assert not empty' m.one.0
            else do
                do forever /* scan all continued rexx lines */
                    nlRi = 1
                    la = m.one.0
                    la = m.one.la
                    if m.la.kind \== 'c' then
                        leave
                    m.la.text = strip(m.la.text, 't')
                    if right(m.la.text, 1) \== ',' then
                        leave
                    m.la.text = strip(left(m.la.text,
                            , length(m.la.text)-1), 't')' '
                    call compSpNlComment m
                    cont = compExpr(m, 's', '@')
                    if cont == '' | m.cont.kind \== m.one.kind then
                        call scanErr s, 'bad rexx continuation'
                    call mAddSt one, cont
                    call mFree cont
                    end
                call mAdd res, one
                end
            end
        else do
            do cc=0 while compComment(m)
                end
            one = compExpr(m, 'd', ki)
            nlRi = scanNL(s)
            if one == '' then do
                if nlLe & nlRi & cc < 1 then
                    call mAdd res,compAstAddOp(m,compAst(m,'='),'$')
                end
            else if m.one.containsD | (nlLe & nlRi,
                      & \ (cc > 0 | m.one.containsC)) then do
                call mAdd res, compAstAddOp(m, one, '$')
                end
            else do
                call mFree one
                end
            end
        nlLe = nlRi
        if \ nlRi then do
            one = compStmt(m)
            if one = '' then
                return compAstFree0(res)
            if m.one.kind == 'T' then
                tb = one
            else
                call mAdd res, one
            end
        end
endProcedure compExprStmts

/*--- compile a single statement -------------------------------------*/
compStmt: procedure expose m.
parse arg m
    s = m.m.scan
    res = compAss(m)
    if res \== '' then
        return res
    pre = ''
    old = scanPos(s)
    if scanLit(s,m.comp.chDol'$',m.comp.chDol'@',m.comp.chDol,'@') then
        pre = m.s.tok
    if pre == m.comp.chDol'$' then
        return  compAstAddOp(m, compCheckNN(m, compExprBlock(m,'='),
            , 'block or expression expected after $$'), '$')
    wCat = compName(m, 'sv')
    fu = m.s.tok

    if right(pre, 1) == '@' & wCat \== 's' then do
        if wCat == 'v' then
         return compAstAddOp(m, compAst(m, '=', fu), '%.&')
        if compSpNlComment(m) then
                return compAstAddOp(m,
                , compCheckNE(m, compExpr(m, 's','@'),
                , 'block/primary/expr after $@'), ';')

        ops = compOpKi(m, '@')
        one = compBlock(m, ops)
        if one \== '' then
            return one
        one = compPrimary(m, right(ops, 1))
        if one \== '' then
            return compAstAddOp(m, one, ops)
        call scanSetPos s, old
        return ''
        end

    if wordPos(fu, 'for forWith with withNew') > 0 then do
        res = compAst(m, 'F', fu)
        call compSpComment m
        if fu \== 'with' then do
            b = compVar(m, left('o', fu == 'withNew'))
            end
        else do
            b = compAss(m, '.', 'o')
            if b == '' then
                b = compCheckNE(m, compExpr(m, 's', '.'),
                , "assignment or expression after with")
            end
        call compSpComment m
        st = compCheckNN(m, compStmt(m), "var? statement after" fu)
        if b = '' then do
            b = compBlockName(m, st)
            if b == '' then
                call scanErr s, "variable or named block after" fu
            b = compAst(m, '=', b)
            end
        call mAdd res, b, st
        if fu == 'withNew' then do
            vars = substr(compAstAssignedVars(m.res.2), 3)
            m.res.class = classNew('n* CompTable u' vars)
            m.res.1 = compAst(m, 'A', ,m.res.1, compAst(m, 'o',
                , "oNew('"m.res.class"')"))
            end
        if abbrev(fu, 'with') then do
            r1 = m.res.1
            if m.r1.kind == 'A' then do
                a1 = m.r1.1
                if m.a1.var == 'o' then do
                    call mAdd res, compAstAddOp(m, m.r1.2, '$.')
                    m.res.1 = m.r1.2
                    call mFree a1
                    end
                end
            end
        return res
        end

    if fu == 'do' then do
        call compSpComment m
        pre = compExpr(m, 's', '@')
        res = compAst(m, 'D', , pre)
        p1 = m.pre.1
        if pre \== '' then do
            txt = ''
            do px=1 to m.pre.0
                pC = m.pre.px
                if m.pC.kind \== 'c' then
                    leave
                txt = txt m.pC.text
                cx = pos('=', txt)
                if cx > 0 then do
                    m.res.text = strip(left(txt, cx-1))
                    leave
                    end
                end
            end
        call compSpComment m
        call mAdd res, compCheckNN(m, compStmt(m), 'stmt after do')
        return res
        end

    if fu == 'ct' then do
        call compSpComment m
        return compAst(m, 'C', , compCheckNN(m, compStmt(m),
            , 'ct statement'));
        end
    if fu == 'proc' then do
           call compSpComment m
        nm = ''
        if compName(m, 'v') == 'v' then do
            nm = m.s.tok
            call compSpComment m
            end
        st = compCheckNN(m, compStmt(m), 'proc statement')
        if nm == '' then do
            nm = compBlockName(m, st)
            if nm == '' then
                call scanErr s, 'var or namedBlock expected after proc'
            end
        return compAst(m, 'B', '', compAst(m, '=', nm), st)
        end
    if fu == 'table' then do
        call compSpComment m
        if scanNl(s) then
            call compSpComment m
        res = compAst(m, 'T', 'c')
        flds = ''
        do while compName(m, 'v') == 'v'
            f1 = compAst(m, 'T')
            m.f1.end = m.s.pos
            m.f1.pos = m.s.pos - length(m.s.tok)
            m.f1.name = m.s.tok
            m.f1.text = 'f' m.f1.name m.f1.pos m.f1.end
            call mAdd res, f1
            flds = flds', f' m.s.tok 'v'
            call compSpComment m
            end
        if \ scanNl(s) then
            call scanErr s, 'name or nl after table expected'
        if m.res.0 < 1 then
            call scanErr s, 'no names in table'
        m.res.class = classNew('n* CompTable u' substr(flds, 3))
        m.res.text = 'c' cl
        return res
        end
    call scanBack s, pre || fu
    return ''
endProcedure compStmt

compBlockName: procedure expose m.
parse arg m, a
    a1 = m.a.1
    if m.a.kind == '¢' then
         return m.a.text
    else if m.a.kind == '*' & m.a1.kind == '¢' then
        return m.a1.text
    return ''
endProcedure compBlockName

compVar: procedure expose m.
parse arg m, vk
    s = m.m.scan
    ty = compName(m, 'v' || vk)
    if ty \== '' then do
        r = compAst(m, '=', m.s.tok)
        m.r.var = ty
        return r
        end
    if \ scanLit(s, '{') then
        return ''
    call scanLit s, '?', '>'
    f = m.s.tok
    r = compCheckNE(m, compExpr(m, 'b', '='), 'var name')
    if \scanLit(s, '}') then
        call scanErr s, 'closing } missing after {'
    m.r.var = f
    return r
endProcedure compVar

compAss: procedure expose m.
parse arg m, ki, vk
    s = m.m.scan
    call scanLit s, m.comp.chDol'=', '='
    pr = m.s.tok
    if pr \== '' then
        call compSpComment m
    v = compVar(m, vk)
    if v \== '' then do
        call compSpComment m
        if \ scanLit(s, '=') then
            call scanErr s, '= expected in assignment after' pr 'var'
        end
    else if pr == '' then
        return ''
    else
        oldInfo = scanInfo(s)
    eb = compCheckNE(m, compExprBlock(m, if(ki=='', '=', ki)),
        , 'block or expression in assignment after' pr)
    if v == '' then do
        v = compBlockName(m, eb)
        if v == '' then
            call scanErr s, 'var or namedBlock expected',
                    'in assignment after' pr, oldInfo
        else
            v = compAst(m, '=', v)
        end
    return compAst(m, 'A', , v, eb)
endProcedure compAss

/*--- block deals with the correct kind and operators
      the content is parsed by compUnit ------------------------------*/
compBlock: procedure expose m.
parse arg m, ops
orOps = ops
    s = m.m.scan
    if \ scanLit(s, '¢', '/') then
        return ''
    start = m.s.tok
    if pos(right(ops, 1), '%^') > 0 then
        ops = ops'='
    if ops == '' | pos(right(ops, 1), m.comp.chKind) < 1 then
        return scanErr(s, 'bad kind' ops 'for block') /0
    ki = right(ops, 1)
    ops = left(ops, length(ops)-1)
    isCall = pos(right(ops, 1), '%^') > 0
    starter = start
    if start == '¢' then
        stopper = m.comp.chDol'!'
    else do
        call scanVerify s, '/', 'm'
        starter = '/'m.s.tok'/'
        stopper = m.comp.chDol || starter
        if \scanLit(s, '/') then
            call scanErr s, 'ending / after stopper' stopper 'expected'
        end
    if \ isCall then do
        res = compUnit(m, ki, stopper)
        end
    else do
        call compSpComment m
        res = compPrimary(m, '.', right(ops, 1) == '^')
        if m.res.kind == 'M' then
            ops = left(ops, length(ops)-1)
        else
            res = compAst(m, right(ops, 1), , res)
        args = compExpr(m, 's', ki)
        if args \== '' then
            call mAdd res, args
        end
    if \ scanLit(s, stopper, substr(stopper, 2)) then
           call scanErr s, 'ending' stopper 'expected after' starter
    if abbrev(starter, '/') then
        m.res.text = substr(starter, 2, length(starter)-2)
    return compAstAddOp(m, res, ops)
endProcedure compBlock

/**** lexicals ********************************************************/
/*--- skip a comment. return 0 if there is none ----------------------*/
compComment: procedure expose m.
parse arg m
    s = m.m.scan
    got = 0
    do forever
        if scanLit(s, m.comp.chDol'**') then
            m.s.pos = 1 + length(m.s.src) /* before next nl */
        else if scanLit(s, m.comp.chDol'*+') then
            call scanNL s, 1
        else if scanLit(s, m.comp.chDol'*(') then do
            do forever
                if scanVerify(s, m.comp.chDol, 'm') then iterate
                if scanNL(s) then iterate
                if compComment(m) then iterate
                if \ scanLit(s, m.comp.chDol) then
                    call scanErr s, 'source end in comment'
                if scanLit(s, '*)') then
                    return 1
                if scanLit(s, m.comp.chDol) then iterate
                if scanString(s) then iterate
                end
            end
        else
            return got
        got = 1
        end
endProcedure compComment

/*--- skip spaces and comments ---------------------------------------*/
compSpComment: procedure expose m.
parse arg m, xtra
    s = m.m.scan
    got = 0
    do forever
        if scanVerify(s, m.comp.chSpa) then
            got = bitOr(got, 1)
        else if compComment(m) then
            got = bitOr(got, 2)
        else if xtra == '' then
            return got
        else if \ scanLit(s, xtra) then
            return got
        else do
            got = bitOr(got, 4)
            m.s.pos = 1+length(m.s.src)
            end
        end
endProcedure compSpComment

/*--- skip spaces, NLs and comments ----------------------------------*/
compSpNlComment: procedure expose m.
parse arg m, xtra
    found = 0
    do forever
        if compSpComment(m, xtra) < 1 then
            if \ scanNL(m.m.scan) then
             return found
        found = 1
        end
endProcedure compSpNlComment
/*--- scan a name in one of the categories
        v=var, c=compile, s=stmt ----------------------------------*/
compName: procedure expose m.
parse arg m, cats
    s = m.m.scan
    if \ scanName(s) then
        return ''
    if wordPos(m.s.tok, m.comp.wCatS) > 0 then do
        if pos('s', cats) > 0 then
            return 's'
        end
    else if wordPos(m.s.tok, m.comp.wCatC) > 0 then do
        if pos('c', cats) > 0 then
            return 'c'
        end
    else if wordPos(m.s.tok, m.comp.wCatO) > 0 then do
        if pos('o', cats) > 0 then
            return 'o'
        end
    else if pos('v', cats) > 0 then do
        return 'v'
        end
    call scanBack s, m.s.tok
    return ''
endProcedure compName


compSpNlComment: procedure expose m.
/**** small helper routines ******************************************/
compInter: procedure expose m.
    interpret arg(1)
    return
endProcedure compInter

/*--- if va == null then issue an error with msg --------------------*/
compCheckNN: procedure expose m.
parse arg m, va, msg
    if va == '' then
        call scanErr m.m.scan, msg 'expected'
    return va
endProcedure compCheckNN

/*--- return true iff expression is empty ---------------------------*/
compIsEmpty: procedure expose m.
parse arg m, a
    if a == '' then
        return 1
    do while a \== '' & m.a.kind == '*'
        a = m.a.1
        end
    return a == '' | (m.a.kind \== '¢' & m.a.0 < 1)
endProcedure compIsEmpty

/*--- if va == null or empty then issue an error with msg -----------*/
compCheckNE: procedure expose m.
parse arg m, ex, msg
    e1 = left(ex, 1)
    if compIsEmpty(m, ex) then
        call scanErr m.m.scan, msg 'expected'
    return ex
endProcedure compCheckNE

/**** AST = Astract Syntax Graph ***************************************
          goal is to migrate to migrate to old codeGenerator to AST

------- atoms, no children
  =  string constant
----------- rexx fragments
  c  rexxStatement
  s  string
  o  object
  r  run
  f  file

------- containers
  ¢  block          ==>* c-.
  @  code = 1 stmt  ==>*
  -  string
  .  object
  *  operand chain  ==>1

------- molecules
  &  variable access ==>1 =-
  A  assignment     ==> 2
  B  proc           ==> 2
  C  ct             ==> 1
  D  do                ==> 2
  F  for + with     ==> 2
  P  Pipe           ==>* 1=input 2=output
  T  Table
  M  compile
  %  RunOut         ==> 1,2 (Run, arguments)
  ^  RunRet         ==> 1,2 (Run, arguments)
  I??  Input          ==>* .

***********************************************************************/
/*--- create a new AST -----------------------------------------------*/
compAST: procedure expose m.
parse arg m, ki, txt
    n = mNew('COMP.AST')
    if length(ki) <> 1 then
        call err 'compAST bad kind' ki / 0
    m.n.kind = ki
    m.n.text = txt
    if pos(ki, '¢@-.*&ABCDFPTM%^') > 0 then do
        do cx=1 to arg()-3
            m.n.cx = arg(cx+3)
            end
        m.n.0 = cx-1
        if txt \== '' & pos(ki, '&*FPT') < 1 then
            return err('kind' ki 'text='txt'|')/0
        end
    else if pos(ki, '=csorf') > 0  then do
        m.n.0 = 'kind'ki
        end
    else do
        call err "compAst kind '"ki"' not supported"
        1/0
        end
    return n
endProcedure compAST

/*--- free AST if empty ----------------------------------------------*/
compASTFree0: procedure expose m.
parse arg a, ret
    if m.a.0 > 0 then
        return a
    call mFree a
    return ret
endProcedure compAstFree0

/*--- add operandands to an AST -------------------------------------*/
compASTAddOp: procedure expose m.
parse arg m, a, ops
    do while right(ops, 1) == m.a.kind
        ops = left(ops, length(ops)-1)
        end
    if ops == '' then
        return a
    ops = translate(ops, '-', '=')
    if m.a.kind \== '*' then
        return compAst(m, '*', ops, a)
    m.a.text = ops || m.a.text
    return a
endProcedure compAstAddOp

/*--- return the kind of an AST --------------------------------------*/
compASTKind: procedure expose m.
parse arg m, a
    m.m.astOps = ''
    if \ abbrev(a, 'COMP.AST.') then
        return left(a, 1)
    c = a
    do while m.c.kind == 'ops'
        if m.c.text \== '' then
            return left(m.c.text, 1)
        c = m.c.1
        end
    if a == c then
        return '?'
    return compAstKind(m, c)
endProcedure compASTKind

compAstAssignedVars: procedure expose m.
parse arg a
    res = ''
    if m.a.kind == 'F' then
        return ''
    if m.a.kind == 'A' then do
        a1 = m.a.1
        if m.a1.kind == '=' & m.a1.var == 'v' then do
            if words(m.a1.text) \= 1 then
                call astErr a1 'bad var'
            a2 = m.a.2
            say m.a2.kind m.a2.text
            if pos(m.a2.kind, '-=s') > 0 ,
                | (m.a2.kind == '*' & abbrev(m.a2.text, '-')) then
                res = ', f' m.a1.text 'v'
            else if pos(m.a2.kind, '.<@o') > 0 ,
                | (m.a2.kind == '*' & abbrev(m.a2.text, '.<@')) then
                res = ', f' m.a1.text 'r'
            else
                call astErr a2, 'string or object????'
            end
        end
    if datatype(m.a.0, 'n') then
        do ax=1 to m.a.0
            res = res || compAstAssignedVars(m.a.ax)
            end
    return res
endProcedure compAstAssignedVars

compAstSay: procedure expose m.
parse arg a, lv
    if \ abbrev(a, 'COMP.AST.') then do
        if a \== '' then
            return err('bad ast' a)
        say left('', 19)': * empty ast'
        return
        end
    say lefPad(left('', lv) m.a.kind, 10) ,
        || rigPad(if(dataType(m.a.0, 'n'), m.a.0), 3),
        '@'rigPad(substr(a, 10), 4)':' m.a.text'|'
    if dataType(m.a.0, 'n') then do cx=1 to m.a.0
        call compAstSay m.a.cx, lv+1
        end
    return
endProcedure compAstSay

compAstErr: procedure expose m.
parse arg a, txt
    call errSay txt
    call compAstSay a, 0
    return err(txt)
endProcedure compAstErr

compAstOnlyOut: procedure expose m.
parse arg a, rec
    do ax=1 to m.a.0
        a1 = m.a.ax
        if m.a1.kind == '*' then
            if abbrev(m.a1.text, '$') then
                iterate
        if m.a1.kind = '¢' & rec \== 0 then
            if compAstOnlyOut(a1) then
                iterate
        return 0
        end
    return 1
endProcedure compAstOnlyOut

/*--- return the code for an AST with operand chain trg --------------*/
compCode2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if pos(o1, ';') > 0 then
        return compCode2rx(m, oR, strip(f))
    if pos(o1, '-.<|?@') > 0 then
        return compRun2rx(m, ops, quote(oRunner(f)))

    call err 'compCode2rx bad ops' ops
endProcedure compCode2rx

compCon2rx: procedure expose m.
parse arg m, ops, f, a
    do ox=length(ops) by -1 to 1 while pos(substr(ops,ox,1), '.-')>0
        end
    if substr(ops, ox+1, 1) == '.' then
        f = s2o(f)
    if length(f) < 20 then
        v = quote(f, "'")
    else if a \== '' & m.a.text == f then
        v = 'm.'a'.text'
    else
        v =    'm.'compAst(m, '=', f)'.text'
    if substr(ops, ox+1, 1) == '.' then
        return compObj2rx(m, left(ops, ox), v)
    else
        return compString2rx(m, left(ops, ox), v)
endProcedure compCon2rx

compString2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '-' then
        return compString2rx(m, oR, f)
    if o1 == '.' then
        return compObj2rx(m, oR, 's2o('f')')
    if pos(o1, '$') > 0 tOnlyOut then
        return compCode2rx(m, oR, 'call out' f';')
    if o1 == '&' then do
        o2 = substr('1'ops, length(ops), 1)
        if o2 \== '.' & o2 \== '<' then
            return compString2rx(m, oR, 'vGet('f')')
        else
            return compObj2rx(m, oR, 'vGetO('f')')
        end
    if o1 == '<' then
        return compFile2rx(m, oR, 'file('f')')
    call err 'compString2rx bad ops' ops
endProcedure compString2rx

compObj2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '.' then
        return compObj2rx(m, oR, f)
    if o1 == '-' then
        return compString2rx(m, oR, 'o2string('f')')
    if o1 == '$' then
        return compCode2rx(m, oR, 'call outO' f';')
    if o1 == '<' then
        return compFile2rx(m, oR, 'o2file('f')')
    if o1 == '@' then
        return compRun2Rx(m, oR, f)
    if pos(o1, ';%^') > 0 then
        return compRun2rx(m, ops, f)
    call err 'compObj2rx bad ops' ops
endProcedure compObj2rx

compRun2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '@' then
        return compRun2Rx(m, oR, f)
    if o1 == ';' | o1 == '%' then /*??? immer % verwenden ???*/
        return compCode2Rx(m, oR, 'call oRun' f';')
    return compObj2rx(m, ops, f)
endProcedure compObj2rx

compFile2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if pos(o1, '<.@') > 0 then
        return compFile2rx(m, oR, f)
    if o1 == '|' | o1 == '?' then
        return compObj2Rx(m, oR, 'jSingle('f ||if(o1=='?', ", ''")')')
    return compRun2rx(m, ops, f)
endProcedure compFile2rx

compAst2rx: procedure expose m.
parse arg m, ops, a
    ki = m.a.kind

    if ki == 'c' then
        return compCode2Rx(m, ops, m.a.text)
    if ki == 's' then
        return compString2rx(m, ops, m.a.text)
    if ki == 'o' then
        return compObj2Rx(m, ops, m.a.text)
    if ki == '=' then
        return compCon2Rx(m, ops, m.a.text, a)
    if ki == '*' then
        return compAst2Rx(m, ops || m.a.text, m.a.1)
    o1 = right(ops, 1)
    oR = left(ops, max(0, length(ops)-1))
    if ki == '-' then
        if m.a.0 = 1 then
            return compAst2rx(m, ops, m.a.1)
        else
            return compString2rx(m, ops, compCatRexxAll(m, a,,, ' || '))
    if ki == '.' then
        return compObj2Rx(m, ops, compCatRexxAll(m, a,,, ' || '))
    if ki == '@' then
        return compCode2Rx(m, ops, compCatRexxAll(m, a,,,' || ')';')
    if ki == '¢' then do
        a1 = m.a.1
        if m.a.0 == 1 & m.a1.kind == '¢' then
            return compAst2Rx(m, ops, a1)
        if o1 == '-' then do
            if \ compAstOnlyOut(a) then
                return compAst2Rx(m, ops';', a)
            res = ''
            do ax=1 to m.a.0
                a1 = m.a.ax
                if m.a1.kind == '*' & abbrev(m.a1.text, '$') then
                    b = compAst2rx(m, overlay('-',m.a1.text,1), m.a1.1)
                else if m.a1.kind == '¢' then
                    b = compAst2rx(m, '-', a1)
                else
                    call compAstErr a, 'onlyOut but' ax'='a1 ,
                        'kind='m.a1.kind 'text='m.a1.text
                res = compCatRexx(res, b, ' ')
                end
                    /* ???? only necessary if part of expression ????*/
            return compString2rx(m, oR, '('strip(res)')')
            end
        if o1 == '<' then
         return compAst2Rx(m, ops'@', a)
        if o1 == '.' then
            return compAst2Rx(m, oR'|', a)
        if o1 == '|' | o1 == '?' then
            if m.a.0 = 1 & compAstOnlyOut(a, 0) then do
                a1 = m.a.1
                if m.a1.kind \== '*' | \ abbrev(m.a1.text, '$') then
                    call compAstErr a, 'onlyOut but',
                        '1='a1 'kind='m.a1.kind 'text='m.a1.text
                return compAst2Rx(m, oR'.'substr(m.a1.text, 2), m.a1.1)
                end
            else
                return compFile2Rx(m, ops, compAst2rx(m, '<;', a))
        if pos(o1, '@;') > 0 then do
            if m.a.0 = 1 then  /*???pipe und code können multi stmts */
               return compCode2Rx(m, ops, 'do;' ,
                    compAst2rx(m,';',m.a.1) 'end;')
            res = ''
            do ax=1 to m.a.0
                res = res compAst2rx(m, ';', m.a.ax)
                end
            if res = '' then
                return compCode2Rx(m, ops, 'nop;')
            return compCode2Rx(m, ops, 'do;'res 'end;')
            end
        end
    if ki == '&' then do
        nm = compAst2Rx(m, '-', m.a.1)
        if m.a.text=='' | m.a.text=='v' then
            return compString2rx(m, ops'&', nm)
        else if m.a.text == '?' then
         return compString2rx(m, ops, 'vIsDefined('nm')')
        else if m.a.text == '>' then
         return compString2rx(m, ops, 'vReadO('nm')')
        else
         call compAstErr a, 'bad text' m.a.text 'in ast &'
        end
    if ki == '%' | ki == '^' then do
        c1 = compAst2Rx(m, '.', m.a.1)
        if m.a.0 = 1 then
            return compRun2Rx(m, ops, c1)
        args = compAst2Rx(m, '', m.a.2)
        if ki == '%' then
            return compCode2Rx(m, ops, 'call oRun' c1',' args';')
        call err 'implement'
        end
    if ki == 'A' then do /* assignment */
        nm = compAst2Rx(m, '-', m.a.1)
        vl = m.a.2
     if m.vl.kind == '=' | m.vl.kind == '-' ,
            | (m.vl.kind == '*' & right(m.vl.text, 1) == '-') then
         return compCode2Rx(m, ops,
                , 'call vPut' nm',' compAst2Rx(m, '-', vl)';')
        else
         return compCode2Rx(m, ops,
                , 'call vPutO' nm',' compAst2Rx(m, '.', vl)';')
        end
    if ki == 'B' then do /* proc */
        call vPutO compInter('return' compAst2Rx(m, '-', m.a.1)),
            , oRunner(compAst2Rx(m ,';', m.a.2))
        return ''
        end
    if ki == 'C' then do /* ct */
     call compInter compAst2Rx(m, ';', m.a.1)
        return ''
        end
    if ki == 'D' then do /* do */
        res = 'do' compAst2rx(m, '', m.a.1)';'
        if m.a.text \== '' then
            res = res "call vPut '"m.a.text"'," m.a.text";"
        return compCode2Rx(m, ops, res compAst2Rx(m, ';', m.a.2),
             "end;")
        end
    if ki == 'F' then do /* for... */
        a1 = m.a.1
         st = compAst2Rx(m, ';', m.a.2)
        if abbrev(m.a.text, 'with') then do
            if m.a1.kind \== 'A' then do
                v = compAst2Rx(m, '.', a1)
                end
             else do
                v = compAst2Rx(m, ,a1)
                if \ abbrev(v, 'call vPutO ') | right(v, 1) \==';' then
                    call scanErr s, 'bad vPutO' v
                v = 'vPutO('substr(v, 12, length(v)-12)')'
                end
            if m.a.0 <= 2 then
                return 'call vWith "+",' v';' st 'call vWith "-";'
            a3 = m.a.3
            if m.a3.kind == '*' then
                return 'call vWith "+",' v';' st,
                    compObj2Rx(m, m.a3.text, "vWith('-')")
            end
        v = compAst2Rx(m, '-', m.a.1)
        if m.a.text == 'forWith' then
            st = 'call vWith "=", vGetO('v');' st
        if abbrev(m.a.text, 'for') then
            st = 'do while vReadO('v');' st 'end;'
        if m.a.text == 'forWith' then
            st = 'call vWith "+";' st 'call vWith "-";'
        return compCode2Rx(m, ops, st)
        end
    if ki == 'P' then do /* pipe */
        if ((left(m.a.text, 1) == ' ') \== (m.a.1 == '')) ,
         | ((substr(m.a.text, 2) == '') \== (m.a.2 == '')) ,
         | (m.a.0 <= 3 & m.a.text == '') then
            call compAstErr a, 'bad/trivial astPipe'
        res = ''
        do ax=3 to m.a.0
            a1 = ''
            if ax < m.a.0 then /* handle output */
                t1 = 'N'
            else if m.a.1 == '' then
                t1 = 'P'
            else do
                t1 = left(m.a.text, 1)
                a1 = compAst2Rx(m, '.', m.a.1)
                end
            if ax == 3 then do /* handle input */
                t1 = '+'t1 || substr(m.a.text, 2)
                if m.a.2 \== '' then
                    a1 = a1',' compAst2Rx(m, '.', m.a.2)
                end
            else
                t1 = t1'|'
            res = res "call pipe '"t1"'," a1";" ,
                   compAst2Rx(m, ';', m.a.ax)
            end
        return compCode2Rx(m, ops, res "call pipe '-';")
        end
    if ki == 'R' then do /* Run with args */
        res = 'call oRun' compAst2Rx(m, , m.a.1)
        if m.a.0 > 1 then
            res = res',' compAst2Rx(m, , m.a.2)
        return compRun2rx(m, ops, res';')
        end
    if ki == 'M' then do
        if m.a.0 = 0 then
            args = ''
        else
            args = ',' compAst2Rx(m, , m.a.1)
        return compRun2rx(m, ops, 'compile(comp(j2Buf())' args')')
        end
    call compAstErr a, 'comAst2rx bad ops' ops 'kind' ki
endProcedure compAst2rx

compCatRexxAll: procedure expose m.
parse arg m, a, ops, mi, sep
    res = ''
    do ax=1 to m.a.0
        res = compCatRexx(res, compAst2rx(m, ops, m.a.ax), mi , sep)
        end
    return strip(res)
endProcedure compCatRexxAll

/*--- cat two rexx parts, avoid strange effects--------------------*/
compCatRexx: procedure expose m.
parse arg le, ri, mi, sep
    if mi \== '' then
        return le || mi || ri
    lr = right(le, 1)
    rl = left(ri, 1)
    if (lr == "'" | lr == '"') then do
        if rl == lr then                /* "a","b" -> "ab" */
            return left(le, length(le)-1) || substr(ri, 2)
        else if  rl == '(' then            /* "a",( -> "a" || ( */
            return le||sep||ri            /* avoid function call    */
        end
    else if pos(lr, m.comp.idChars) > 0 then
        if pos(rl, m.comp.idChars'(') > 0 then
            return le || sep || ri        /* a,b -> a || b */
    return le || ri
endProcedure compCatRexx
/* copy comp end ******************************************************/
/* copy scanSB begin ***************************************************
     Achtung: inc generiert SB aus scanSB, Aenderungen nur in scanSB|
ScanSB: basic scan
    scanLook(m,len) : returns next len chars, pos is not moved
    scanChar(m,len) : scans next len chars
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanWord(m,u)  : scan a space delimited word or a string,
                          if u=1 then uppercase non-strings
variable interface
    scanSrc(m, source) starts scanning a single line
    scanEnd(m)     : returns whether we reached end of input
    scanErr(m, txt): error with current scan location

    m is an address, to store our state
    if a scan function succeeds, the scan posititon is moved

returns: true if scanned, false otherwise
         m.m.tok  ==> last token
         m.m.val  ==> last value for scanString/Word
         m.m.pos ==> scan position
         m.m.src ==> scan source
***********************************************************************/
/*--- return the next len characters until end of src ----------------*/
scanLook: procedure expose m.
parse arg m, len
    if len == '' then
        return substr(m.m.src, m.m.pos)
    else
        return substr(m.m.src, m.m.pos,
                     , min(len, 1 + length(m.m.src) - m.m.pos))
endProcedure scanLook

/*--- scan len chararcters, atmost to end of src ---------------------*/
scanChar: procedure expose m.
parse arg m, len
    m.m.tok = scanLook(m, len)
    m.m.pos = m.m.pos + length(m.m.tok)
    return m.m.tok \== ''
endProcedure scanChar

/*--- scan the literal lit ------------------------------------------*/
scanLit: procedure expose m.
parse arg m
    do ax=2 to arg()
        if abbrev(substr(m.m.src, m.m.pos), arg(ax)) then do
            m.m.tok = arg(ax)
            m.m.pos = m.m.pos + length(arg(ax))
            return 1
            end
        end
    m.m.tok = ''
    return 0
endProcedure scanLit

/*--- scan with verify, vOpt is passed to verify ---------------------*/
scanVerify: procedure expose m.
parse arg m, alpha, vOpt, onlyIfMatch
    if arg() > 3 then
        call err 'deimplement onlyIfMatch???'
    if vOpt == '' then   /* empty string does not take default */
        nx = verify(m.m.src, alpha, , m.m.pos)
    else
        nx = verify(m.m.src, alpha, vOpt, m.m.pos)
    if nx = 0 then
        if onlyIfMatch == 1 then
            nx = m.m.pos
        else
            nx = length(m.m.src) + 1
    m.m.tok = substr(m.m.src, m.m.pos, nx - m.m.pos)
    m.m.pos = nx
    return m.m.tok \== ''
endProcedure scanVerify

/*--- scan while in charset ------------------------------------------*/
scanWhile: procedure expose m.
parse arg m, chSet
    return scanVerify(m, chSet, 'n')

/*--- scan until in charset ------------------------------------------*/
scanUntil: procedure expose m.
parse arg m, chSet
    return scanVerify(m, chSet, 'm')

/*--- scan until (and over) string End -------------------------------*/
scanStrEnd: procedure expose m.
parse arg m, sep
    px = m.m.pos
    do forever
        px = pos(sep, m.m.src, px)
        if px = 0 then do
            m.m.tok = ''
            return 0
            end
        px = px + length(sep)
        if \ abbrev(substr(m.m.src, px), sep) then do
            m.m.tok = substr(m.m.src, m.m.pos, px-m.m.pos)
            m.m.pos = px
            return 1
            end
        px = px + length(sep)
        end
endProcedure scanStrEnd

/*--- scan a string with quote char qu -------------------------------*/
scanString: procedure expose m.
parse arg m, prefs
    if prefs = '' then do
        call scanLit m, "'", '"'
        end
    else do
        do px=1 to words(prefs) until scanLit(m, word(prefs, px))
            end
        end
    if m.m.tok == '' then
        return 0
    t1 = m.m.tok
    qu = right(t1, 1)
    if \ scanStrEnd(m, qu) then do
        m.m.pos = m.m.pos - length(t1)
        return scanErr(m, 'ending Apostroph('qu') missing')
        end
    m.m.val = repAll(left(m.m.tok, length(m.m.tok)-1), qu||qu, qu)
    m.m.tok = t1 || m.m.tok
    return 1
endProcedure scanString

/*--- scan a word and put value into *.val
           a word is either delimited by space or stopper
                     or a string (with single or double quotes -------*/
scanWord: procedure expose m.
parse arg m, stopper, ucWord
    if scanString(m) then
        return 1
    if stopper == '' then
        stopper = ' '
    if \scanUntil(m, stopper) then
        return 0
    m.m.val = m.m.tok
    if ucWord == 1 then
        m.m.val = translate(m.m.val)
    return 1
endProcedure scanWord

/*--- skip, scan and return next word --------------------------------*/
scanSkWord: procedure expose m.
parse arg m, stopper, ucWord, eMsg
    if scanWord(scanSkip(m), stopper, ucWord) then
        return m.m.val
    else if eMsg == '' then
        return ''
    else
        call scanErr m, eMsg 'expected'
endProcedure scanSkWord

/*--- go back the current token --------------------------------------*/
scanBack: procedure expose m.
parse arg m, tok
    if m.m.pos <= length(tok) then
        call scanErr sc, 'cannot back "'tok'" length'/0
    cx = m.m.pos - length(tok)
    if substr(m.m.src, cx, length(tok)) \== tok then
        return scanErr(m, 'cannot back "'tok'" value')/0
    m.m.pos = cx
    return
endProcedure scanBack

/*--- set new src - allow scanning without open ----------------------*/
scanSBSrc: procedure expose m.
parse arg m, m.m.src
    return scanSBOpen(m)
endProcedure scanSBSrc

/*--- start scanning with a new single src ---------------------------*/
scanSBOpen: procedure expose m.
parse arg m
    m.m.pos = 1
    m.m.tok = ''
    return m
endProcedure scanSBOpen

/*--- start scanning with a new single src ---------------------------*/
scanSBClose: procedure expose m.
parse arg m
    m.m.pos = length(m.m.src) + 1
    m.m.tok = '--- closed ---'
    return m
endProcedure scanSBClose

scanSBSpace: procedure expose m.
parse arg m
    nx = verify(m.m.src, ' ', , m.m.pos)
    if nx = 0 then
        nx = length(m.m.src) + 1
    res = nx <> m.m.pos
    m.m.tok = left(' ', res)
    m.m.pos = nx
    return res
endProcedure scanSBSpace

/*--- skip over space, nl and comments and return m -----------------*/
scanSkip: procedure expose m.
parse arg m
    call scanSpace m
    return m
endProcedure scanSkip

scanErr: procedure expose m.
parse arg m, txt
    if arg() < 3 then
        return err('s}'txt'\n'scanInfo(m))
    else
        return err('scanErr' txt'\n'arg(3))
endProcedure scanErr

scanSBInfo: procedure expose m.
parse arg m
    return 'last token' m.m.tok 'scanPosition' ,
        strip(substr(m.m.src, m.m.pos, 40), 't') ,
        || '\npos' m.m.Pos 'in string' strip(m.m.src, 't')
endProcedure scanSBInfo

/*--- return position in simple format -------------------------------*/
scanSBPos: procedure expose m.
parse arg m
    return 'singleSrc' m.m.pos
    return if(m.m.pos > length(m.m.src), 'E', 'singleSrc' m.m.pos)

/*--- set position to position in arg to------------------------------*/
scanSetPos: procedure expose m.
parse arg m, to
    cur = scanPos(m)
    wc = words(cur)
    if wc <> words(to) ,
        | subWord(cur, 1, wc-1) <> subWord(to, 1, wc-1) then
        call scanErr m 'cannot back from' cur 'to' to
    m.m.pos = word(to, wc)
    return

/*--- return true if at end of src -----------------------------------*/
scanSBEnd: procedure expose m.
parse arg m
    return m.m.pos > length(m.m.src)
/* copy scanSB end ****************************************************/
/* copy scan begin ****************************************************
Scan: scan an input: with multiple lines
    ==> all of scanSB
    scanEnd(m)     : returns whether we reached end of input
    scanLit(m,lit) : scan Literal lit if present or return 0
    scanChar(m,n)  : scan next n characters
    scanName(m)    : scan a name
    ScanNat(m)     : scan a natural number (without sign)
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanKeyValue(m): scan a key = value clause (with spaces)
    scanWord(m,u)  : scan a space delimited word or a string,
                          if u=1 then uppercase non-strings
    scanErr(m, txt): error with current scan location

    m is an address, to store our state
    if a scan function succeeds, the scan posititon is moved

returns: true if scanned, false otherwise
         m.m.tok  ==> last token
         m.m.val  ==> last value for scanString/Word/KeyValue
         m.key    ==> key for scanKeyValue
         m.m.pos ==> scan position
         m.m.src ==> scan source
***********************************************************************/
scanIni: procedure expose m.
    if m.scan.ini = 1 then
        return
    m.scan.ini = 1
    call jIni
    ts = classNew('n ScanRes u f TOK v, f VAL v, f KEY v, f TYPE v')
    call classNew 'n ScanSB u JRWO', 'm',
        , 'scanEnd  return 1',
        , 'scanNL   m.m.tok = ""; return 0',
        , 'scanCom  m.m.tok = ""; return 0',
        , 'scanInfo return scanSBInfo(m)' ,
        , 'jReset call scanSbSrc m, arg;' ,
                  'call scanOpts m, arg2, arg3, arg(4)',
        , "jOpen call scanSBOpen scanOC(m, opt, 'ScanSBR')" ,
        , "jClose call scanSBClose scanOC(m, , 'ScanSB')",
        , 'scanPos return scanSBPos(m)'
    call classNew 'n ScanSBR u ScanSB', 'm',
        , "jReadO if scanType(m) == '' then return '';" ,
                    " else return oClaCopy('"ts"', m, '')"
    return
endProcedure scanIni
/*--- check open opt is read and mutate ------------------------------*/
scanOC: procedure expose m.
parse arg m, opt, cla
    if \ abbrev(m.j.cRead, opt) then
        call err 'scanOpen opt must be' m.j.cRead 'not' opt
    return oMutatName(m, cla)
endProcedure scanOC
/*--- start scanning with a new single src ---------------------------*/
scanSrc: procedure expose m.
parse arg m, src
    return scanSbSrc(oMutatName(m, 'ScanSB'), src)

scanOpen: procedure expose m.
parse arg m
    opt = ''
    interpret objMet(m, 'jOpen')
    return m
endProcedure scanOpen

scanClose: procedure expose m.
parse arg m
    interpret objMet(m, 'jClose')
    return m
endProcedure scanOpen

scanInfo: procedure expose m.
parse arg m
    interpret objMet(m, 'scanInfo')

/*--- return true if at end of src -----------------------------------*/
scanEnd: procedure expose m.
parse arg m
    if m.m.pos <= length(m.m.src) then
        return 0
    interpret objMet(m, 'scanEnd')

/*--- scan over white space, nl, comments ...-------------------------*/
scanSpace: procedure expose m.
parse arg m
    fnd = 0
    do while scanSBSpace(m) | scanCom(m) | scanNl(m)
        fnd = 1
        end
    m.m.tok = left(' ', fnd)
    return fnd
endProcedure scanSpace

/*--- scan next line -------------------------------------------------*/
scanNL: procedure expose m.
parse arg m, unCond
    interpret objMet(m, 'scanNL')

/*--- scan one comment -----------------------------------------------*/
scanCom: procedure expose m.
parse arg m
    interpret objMet(m, 'scanCom')

scanPos: procedure expose m.
parse arg m
    interpret objMet(m, 'scanPos')
endProcedure scanPos

scanOpts: procedure expose m.
parse arg m, m.m.scanName1, namePlus, m.m.scanComment
    if m.m.scanName1 == '' then
        m.m.scanName1 = m.ut_alfa
    if namePlus == '' then
        m.m.scanNameR = m.m.scanName1 || '0123456789'
    else
        m.m.scanNameR = m.m.scanName1 || namePlus
    return m
endProcedure scanOpts

/*--- begin scanning a single line -----------------------------------*/
scanSrc: procedure expose m.
parse arg m, m.m.src
    return scanOpen(m)
endProcedure scanSrc

/*--- scan a Name, first char in *.scanName1, rest in *.scanNameR ----*/
scanName: procedure expose m.
parse arg m
    if pos(substr(m.m.src, m.m.pos, 1),
                 , m.m.scanName1) <= 0 then do
        m.m.tok = ''
        return 0
        end
    return scanVerify(m, m.m.scanNameR)
endProcedure scanName

/*--- scan a natural number (no sign, decpoint ...) ------------------*/
scanNat: procedure expose m.
parse arg m, chEn
    if \ scanVerify(m, '0123456789') then
        return 0
    if chEn \== 0 then
        if pos(scanLook(m , 1), m.m.scanNameR) > 0 then
            call scanErr m, 'illegal number end after' m.m.tok
    return 1
endProcedure ScanNat

/*--- scan an integer (optional sign, no decpoint ...) ---------------*/
scanInt: procedure expose m.
parse arg m, chEn
    call scanLit m, '+', '-'
    si = m.m.tok
    if \ scanNat(m, chEn) then do
        m.m.pos = m.m.pos - si
        return 0
        end
    m.m.tok = si || m.m.tok
    return 1
endProcedure scanInt

/*--- scan a number (optional sign, decpoint, exponent) ------------*/
scanNum: procedure expose m.
parse arg m, chEn
    sx = m.m.pos
    call scanLit m, '+', '-'
    po = scanLit(m, '.')
    if \ scanNat(m, 0) then do
        m.m.pos = sx
        return 0
        end
    if \ po then
        if scanLit(m, '.') then
            call scanNat m, 0
        if scanLit(m, 'e', 'E') then
            if \ scanInt(m, 0) then
                call scanErr m, 'exponent expected after' ,
                             substr(m.m.src, sx, m.m.pos-sx)
    m.m.tok = substr(m.m.src, sx, m.m.pos-sx)
    m.m.val = translate(m.m.tok)
    if chEn \== 0 then
        if pos(scanLook(m , 1), m.m.scanNameR) > 0 then
            call scanErr m, 'illegal number end after' m.m.tok
    return 1
endProcedure scanNum

scanType: procedure expose m.
parse arg m, opt
    m.m.tok = ''
    if scanName(m) then
        m.m.type = 'n'
    else if scanNum(m) then
        m.m.type = 0
    else if scanString(m) then
        m.m.type = left(m.m.tok, 1)
    else if scanSpace(m) then
        m.m.type = 's'
    else do
        call scanChar m, 1
        m.m.type = m.m.tok
        end
    return m.m.type
endProcedure scanType

/*--- scan a key = word phrase
          put key into m.key and word into m.m.val -------*/
scanKeyValue: procedure expose m.
parse arg m, def
    if \ scanName(m) then
        return 0
    m.m.key = m.m.tok
    if \ scanLit(scanSkip(m), '=') then do
        m.m.val = def
        m.m.tok = ' no='
        end
    else if \scanWord(scanSkip(m)) then
        return scanErr(m, 'word expected after' m.m.key '=')
    return 1
endProcedure scanKeyValue

/*--- emit an error with current scan pos ----------------------------*/
/* copy scan end   ****************************************************/
/* copy scanRead begin ************************************************/
scanReadIni: procedure expose m.
    if m.scanRead.ini = 1 then
        return
    m.scanRead.ini = 1
    call scanIni
 /* ts = classNew('n Scan u f TOK v, f VAL v, f KEY v, f TYPE v') */
    call classNew 'n ScanRead u ScanSB', 'm',
        , 'scanEnd return m.m.atEnd' ,
        , 'scanNL return scanReadNL(m, unCond)',
        , 'scanCom return scanReadCom(m)',
        , 'scanInfo return scanReadInfo(m)',
        , 'scanPos return scanReadPos(m)',
        , "jOpen   call scanReadOpen scanOC(m, opt, 'ScanReadR')",
                    ", ,arg()>=3, arg(3)" ,
        , "jClose  call scanReadClose scanOc(m, , 'ScanRead')"
    call classNew 'n ScanReadR u ScanRead', 'm',
        , 'jReadO' oClaMet(class4Name('ScanSBR'), 'jReadO')
    call classNew "n EditRead u JRW", "m",
        , "jRead  return editRead(m, var)",
        , "jOpen" ,
        , "jReset m.m.linex = arg - 1"
    return
endProcedure scanReadIni

/*--- begin scanning the lines of a reader ---------------------------*/
scanRead: procedure expose m.
parse arg rdr, n1, np, co
    return scanReadReset(oNew('ScanRead'), rdr, n1, np, co)

scanReadReset: procedure expose m.
parse arg m, m.m.rdr, n1, np, co
    call oMutatName m, 'ScanRead'
    call scanOpts m, n1, np, co
    return m
endProcedure scanReadReset

scanReadOpen: procedure expose m.
parse arg m, o, hasLine0, line0
    m.m.atEnd = 0
    m.m.lineX = 0
    m.m.val = ''
    m.m.key = ''
    call jOpen m.m.rdr, '<'
    if hasLine0 then do
        m.m.pos = 1
        m.m.src = line0
        end
    else
        call scanReadNL m, 1
    return m
endProcedure scanReadOpen

scanReadClose: procedure expose m.
parse arg m
    call jClose m.m.rdr
    m.m.atEnd = 'closed'
    return scanSBClose(m)
endProcedure scanReadClose

/*--- scan over next newLine
        if unCond \== 1 only if we are already at endOfLine
         return true if we scanned a NL ------------------------------*/
scanReadNL: procedure expose m.
parse arg m, unCond
    if unCond \== 1 then
        if m.m.pos <= length(m.m.src) then
            return 0
    if m.m.atEnd then
        return 0
    m.m.atEnd = \ jRead(m.m.rdr, m'.SRC')
    if m.m.atEnd then do
        m.m.pos = 1 + length(m.m.src)
        return 0
        end
    m.m.pos = 1
    m.m.lineX = m.m.lineX + 1
    return 1
endProcedure scanReadNl

scanReadCom: procedure expose m.
parse arg m
    m.m.tok = ''
    if m.m.scanComment == '' then
        return 0
    if \ abbrev(substr(m.m.src, m.m.pos), m.m.scanComment) then
        return 0
    m.m.pos = 1 + length(m.m.src)
    m.m.tok = ' '
    return 1
endProcedure scanReadCom

scanReadPos: procedure expose m.
parse arg m, msg
    return m.m.lineX m.m.pos
endProcedure scanReadPos

scanReadInfo: procedure expose m.
parse arg m
    msg = 'last token' m.m.tok 'scanPosition' ,
        strip(substr(m.m.src, m.m.pos, 40), 't')
    if scanEnd(m) then
        msg = msg'\natEnd after'
    else
        msg = msg'\npos' m.m.pos 'in'
    return msg 'line' m.m.lineX':' strip(m.m.src, 't')
endProcedure scanReadInfo

/*--- use scan sqlEdit macro --> temporarily here --------------------*/
/*--- read next line from edit data ----------------------------------*/
editRead: procedure expose m.
parse arg m, var
    m.m.lineX = m.m.lineX + 1
    if adrEdit('(ll) = line' m.m.lineX, 12) \= 0 then
        return 0
    m.var = ll
    return 1
endProcedure editRead
/*--- search loop in edit macro --------------------------------------*/
scanSqlSeekId: procedure expose m.
parse arg m, lx, cmd, opts
    if opts = '' then
        opts = word
                     /* line 1 col 0, otherwise first word is skipped*/
    if adrEdit("cursor =" max(trunc(lx), 1) 0, 12) = 12 then
        return -1
    do while adrEdit("seek" cmd opts, 4) = 0 /* find each command*/
        call adrEdit "(fx) = cursor"
        if m.debug then do
            call adrEdit "(LI) = LINE" fx
            call debug 'scanSqlSeekId after' lx 'found' cmd 'at' fx li
            end
        call jReset m.m.rdr, fx
        call jOpen m, '<'
        m.m.lineX = fx
        do while word(scanPos(m), 1) <= fx & scanSqlClass(m)
            if m.m.sqlClass = 'i' & m.m.val == cmd then
                return fx
            end
        call jClose m
        end
    return -1
endProcedure scanSqlSeekId
/* copy scanRead end **************************************************/
/* copy scanWin begin *************************************************
     scan the the concatenation of the lines of a reader
         any token my be split over several line
         except the end-of-line-comment-token
***********************************************************************/
scanWinIni: procedure expose m.
    if m.scanWin.ini = 1 then
        return
    m.scanWin.ini = 1
    call scanReadIni
    call classNew 'n ScanWin u ScanSB', 'm',
        , 'jReset call scanWinReset m, arg, arg2, arg3',
        , "jOpen call scanWinOpen scanOC(m, opt, 'ScanWinR'), arg(3)",
        , "jClose call scanReadClose scanOC(m, , 'ScanWin')",
        , 'scanNL return scanWinNl(m, unCond)',
        , 'scanCom return scanWinCom(m)',
        , 'scanInfo return scanWinInfo(m)',
        , 'scanPos  return scanWinPos(m)'
    call classNew 'n ScanWinR u ScanWin', 'm',
        , 'jReadO' oClaMet(class4Name('ScanSBR'), 'jReadO')
    return
endProcedure scanWinIni

/*--- instanciate a new window scanner, open rdr ---------------------*/
scanWin: procedure expose m.
parse arg rdr, wiSz, wiBa, cuPo, cuLe
    return scanWinOpts(oNew('ScanWin', rdr), wiSz, wiBa, cuPo, cuLe)

/*--- set the reader and attributes of window scanner m -------------*/
scanWinReset: procedure expose m.
parse arg m, m.m.rdr, wiSz, wiGa, cuPo, cuLe
    m.m.atEnd = 'closed after reset'
    return scanWinOpts(scanOpts(m), wiSz, wiGa, cuPo, cuLe)
endProcedure scanWinReset

/*--- set the attributes of window scanner m ------------------------*/
scanWinOpts: procedure expose m.
parse arg m, wiSz, wiGa, cuPo, cuLe
    wiSz = word(wiSz 5, 1)
    wiGa = word(wiGa 1, 1)
    m.m.cutPos = word(cuPo 1, 1)
    m.m.cutLen = word(cuLe 72, 1)
    m.m.winTot = (wiSz * 2 + wiGa) * m.m.cutLen
    m.m.posLim = (wiSz     + wiGa) * m.m.cutLen
    m.m.posOff =  wiGa * m.m.cutLen
    return m
endProcedure scanWinOpts

/*--- open reader and start scanning --------------------------------*/
scanWinOpen: procedure expose m.
parse arg m, lx
    m.m.atEnd = 0
    if lx = '' then
        m.m.lineX = 1
    else
        m.m.lineX = lx
    call scanSBOpen m
    m.m.val = ''
    m.m.key = ''
    m.m.pos = 1
    m.m.src = ''
    call jOpen m.m.rdr, m.j.cRead
    call scanWinRead m
    return m
endProcedure scanWinOpen

/*--- move the source window: cut left side and append at right side
      return number of characters cut at left ------------------------*/
scanWinRead: procedure expose m.
parse arg m
    dlt = 0
    if m.m.atEnd then
        return 0
    if m.m.pos >= m.m.posLim then do     /*  cut left side */
        dlt = m.m.pos - (m.m.pos // m.m.cutLen + m.m.posOff)
        m.m.src = substr(m.m.src, dlt+1)
        m.m.pos = m.m.pos - dlt
        m.m.lineX = m.m.lineX + dlt % m.m.cutLen
        end
    do while length(m.m.src) < m.m.winTot /* read and fill to len */
        if \ jRead(m.m.rdr, m'.'one) then do
            m.m.atEnd = 1
            return dlt
            end
        m.m.src = m.m.src || substr(m.m.one, m.m.cutPos, m.m.cutLen)
        end
    call assert 'length(m.m.src) = m.m.winTot', 'm.m.winTot m.m.src'
    return dlt
endProcedure scanWinRead

/*--- return position of next line start -----------------------------*/
scanWinNLPos: procedure expose m.
parse arg m
    return m.m.pos + m.m.cutLen - ((m.m.pos - 1) // m.m.cutLen)

/*--- scan comment ---------------------------------------------------*/
scanWinCom: procedure expose m.
parse arg m
    call scanWinRead m
    if scanLit(m, '/*') then do
        ex = pos('*/', m.m.src, m.m.pos+2)
        if ex <= m.m.pos then
            return scanErr(m, '*/ missing after /*')
        m.m.pos = ex+2
        call scanWinRead m
        end
    else do
        cl = length(m.m.scanComment)
        np = scanWinNlPos(m)
        if \ ( cl>0 & m.m.pos+cl <= np & m.m.scanComment ,
                == substr(m.m.src, m.m.pos, cl)) then do
            m.m.tok = ''
            return 0
            end
        m.m.pos = np
        end
    m.m.tok = ' '
    return 1
endProcedure scanWinCom

/*--- scan nl --------------------------------------------------------*/
scanWinNL: procedure expose m.
parse arg m, unCond
    call scanWinRead m
    if unCond \== 1 then
       return 0
    np = scanWinNLPos(m)
    if np \= m.m.pos then
        return 0
    m.m.pos = np
    return 1
endProcedure scanWinNl

/*--- return current position in input ------------------------------*/
scanWinPos: procedure expose m.
parse arg m
    if scanEnd(m) then
        return 'E'
    ps = m.m.pos - 1
    return (m.m.lineX + (ps % m.m.cutLen)) (ps // m.m.cutLen + 1)
endProcedure scanWinPos

/*--- return a description of the current scan position --------------*/
scanWinInfo: procedure expose m.
parse arg m
    p = scanWinPos(m)
    if p == 'E' then do
        res = 'atEnd after'
        p = m.m.lineX - 1 + length(m.m.src) % m.m.cutLen
        end
    else do
        res = 'pos' word(p, 2) 'in'
        p = word(p, 1)
        end
    return 'last token' m.m.tok 'scanPosition' ,
          strip(left(substr(m.m.src, m.m.pos), 40), 't') ,
        || '\n'res 'line' p':' strip(substr(m.m.src,
          , 1 + (p - m.m.lineX) * m.m.cutLen, m.m.cutLen), 't')
endProcedure scanWinInfo
/* copy scanWin end   *************************************************/
/* copy scanSql begin *************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanSql: procedure expose m.
parse arg inRdr
    return scanSqlReset(scanWin(inRdr), inRdr)

scanSqlReset: procedure expose m.
parse arg m, r, scanWin
    if scanWin \== 0 then
        call scanWinOpts m, 5, 2, 1, 72
    m.m.rdr = r
    return scanOpts(m, , '0123456789_' , '--')
endProcedure scanSqlReset

/*--- scan a sql token put class in m.sqlclass:
      'i': ordinary identifier   e.g. Name
      'd': delimited identifier  e.g. "Delimited"
      'q': qualified identifier  e.g. abc."efg"
      'u': integer units         e.g. 8G
      'n': number                e.g. -234 or .2e3
      's': string                e.g. 'abc''ef'
      '' : at end
         : any other character   e.g. ;
      ----------------------------------------------------------------*/
scanSqlClass: procedure expose m.
parse arg m, retSpace
    m.m.val = ''
    if scanSpace(m) & retSpace = 1 then do
        m.m.sqlClass = 'b'
        return 1
        end
    c2 = scanLook(m ,2)
    if scanString(m, "' x' X'") then do
        m.m.sqlClass = 's'
        if \abbrev(m.m.tok, "'") then
            m.m.val = x2c(m.m.val)
        end
    else if scanSqlQuId(m) then do
        if m.m.val.0 > 1 then
            m.m.sqlClass = 'q'
        else if abbrev(m.m.tok, '"') then
            m.m.sqlClass = 'd'
        else
            m.m.sqlClass = 'i'
        end
    else if scanSqlNum(m, 0)  then
        m.m.sqlClass = 'n'
    else if scanChar(m, 1) then
        m.m.sqlClass = m.m.tok
    else if scanEnd(m) then do
        m.m.sqlClass = ''
        return 0
        end
    else
        call scanErr m, 'cannot scan sql'
    return 1
endProcedure scanSqlClass

scanSqlSkipBrackets: procedure expose m.
parse arg m, br
    if br \== '' then
        nop
    else if scanLit(m, '(') then
        br = 1
    else
        return 0
    do while scanSqlClass(m) & m.m.sqlClass \== ';'
        if m.m.sqlClass = '('        then br = br + 1
        else if m.m.sqlClass \== ')' then iterate
        else if br > 1              then br = br - 1
        else                             return 1
        end
    call scanErr m, '; or eof, but' br 'closing ) expected'
endProcedure skipBrackets
/*--- scan an ordinary sql identifier e.g. abc, ef_12 ----------------*/
scanSqlId: procedure expose m.
parse arg m
    if \ scanName(m) then
        return 0
    m.m.val = translate(m.m.tok)
    return 1
endProcedure scanSqlId

/*--- scan a delimited or ordinay sql identifier ---------------------*/
scanSqlDeId: procedure expose m.
parse arg m
    if scanSqlId(m) then
        return 1
    if \ scanString(m, '"') then
        return 0
    m.m.val = strip(m.m.val, 't')
    return 1
endProcedure scanSqlDeId

/*--- scan a qualified sql identifier --------------------------------*/
scanSqlQuId: procedure expose m.
parse arg m
    res = ''
    rto = ''
    do qx=1
        if \ scanSqlDeId(m) then do
            if qx <> 1 then
                call scanErr m, 'id expected after .'
            return 0
            end
        m.m.val.qx = m.m.val
        res = res'.'m.m.val
        rto = rto'.'m.m.tok
        if \ scanLit(scanSkip(m), '.') then
            leave
        call scanSpace m
        end
    m.m.val.0 = qx
    m.m.val = substr(res, 2)
    m.m.tok = substr(rto, 2)
    return 1
endProcedure scanSqlQuId

/*--- scan a sql number ----------------------------------------------*/
scanSqlNum: procedure expose m.
parse arg m, checkEnd, noSp
    si = ''
    if noSp == 1 then
        call err 'deimplement noSp, use scanNum instead'
    if scanLit(m, '+', '-') then do
        si = m.m.tok
        call scanSpace m
        ch = scanLook(m, 2)
        if left(ch, 1) == '.' then
            ch = substr(ch, 2)
        if pos(left(ch, 1), '0123456789') < 1 then do
            call scanBack m, si
            m.m.val = ''
            return 0
            end
        end
    res = scanNum(m, checkEnd)
    m.m.val = si || m.m.val
    return res

endProcedure scanSqlNum

/*--- scan a sql number with a unit which may follow without space ---*/
scanSqlNumUnit: procedure expose m.
parse arg m, both, units
    if \ scanSqlNum(m, 0) then
        return 0
    nu = m.m.val
    sp = scanSpace(m)
    if scanSqlId(m) then do
        if units == '' | wordpos(m.m.val, units) > 0 then
            nu = nu m.m.val
        else if both | \ sp then
            call scanErr m, 'scanSqlNumUnit after' nu 'bad unit' m.m.val
        else
            call scanBack m, m.m.tok
        end
    else if both then
        call scanErr m, 'scanSqlNumUnit no unit after' nu
    else if \sp & pos(scanLook(m, 1), m.m.scanNameR) > 0 then
        call scanErr m, 'scanSqlNumUnit bad number end after' nu
    m.m.val = nu
    return 1
endProcedure scanSqlNumUnit

scan2Trgs: procedure expose m.
parse arg m, t1, t2
    cx = m.m.pos - 1
    do forever
        cx = verify(m.m.src, t1 || t2, 'm', cx + 1)
        if cx = 0 then do
            m.m.pos = length(m.m.src) + 1
            return ''
            end
        if pos(substr(m.m.src, cx, 1), t1) > 0 then do
            m.m.pos = cx
            return substr(m.m.src, cx, 1)
            end
        do ax=4 to arg()
            if arg(ax) == substr(m.m.src, cx, length(arg(ax))) then do
                m.m.pos = cx
                return arg(ax)
                end
            end
        end
endProcedure scan2Trgs

scanSql2Stop: procedure expose m.
parse arg m, sta, stop
    sta = substr(sta, 2)
    c1 = left(sta, 1)
    if c1 == 't' then do
        bx = m.m.pos
        c1 = scan2Trgs(m, '"'''stop, '-/', '--', '/*')
        if bx < m.m.pos then
            return 't'sta
        m.m.pos = m.m.pos + length(c1)
        c1 = left(c1, 1)
        sta = c1 || sta
        end
    if c1 == '/' then do
        bx = m.m.pos
        c1 = scan2Trgs(m, '"''', '-*', '--', '*/')
        if bx < m.m.pos then
            return '+'sta
        m.m.pos = m.m.pos + length(c1)
        if c1 == '*/' then
            return sta
        c1 = left(c1, 1)
        sta = c1 || sta
        end
    if abbrev(sta, "'") | abbrev(sta, '"') then do
         if scanStrEnd(m, c1) then
             return sta
         m.m.pos = 1 + length(m.m.src)
         return '+'sta
         end
    if pos(c1,  '-'stop) > 0 then do
        if c1 == '-' then
            m.m.pos = length(m.m.src) + 1
        return sta
        end
    if \ abbrev(sta, '/') then
        call err 'bad sta2' sta 'for scanSql2Stop'
    call err implement
        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
endProcedure scanSql2Stop

/* copy scanSql end   *************************************************/
/* copy scanUtil begin *************************************************
    scan db2 utility input statements using scan and a reader
**********************************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanUtilReader: procedure expose m.
parse arg m, inRdr
    call scanReader m, inRdr
    call scanOptions sc, , , '--'
    call scanUtilReset m
    return m
endProcedure scanUtilReader

scanUtilSql: procedure expose m.
parse arg inRdr
    m = scanSql(inRdr)
    call scanUtilReset m
    return m
endProcedure scanUtilReader

scanUtilReset: procedure expose m.
parse arg m
    m.m.utilBrackets = 0
    m.scanUtil =  'BACKUP CATENFM CATMAINT CHECK' ,
                  'COPY COPYTOCOPY DIAGNOSE EXEC LISTDEF LOAD' ,
                  'MERGECOPY MODIFY OPTIONS QUIESCE REBUILD' ,
                  'RECOVER REORG REPAIR REPORT RESTORE' ,
                  'RUNSTATS STOSPACE TEMPLATE UNLOAD'
    return m
endProcedure scanUtilReset
/*--- scan next token and put its class in m.sc.utilClass:
      'u' a utility name
      'n' a name
      '"' a quoted name
      "'" an apostroph'd string
      '.' a .
      ',' a ,
      'v' a value
      ''  at end
      ---------------------------------------------------------------*/
scanUtil: procedure expose m.
parse arg sc
    m.sc.utilSpace = scanSpace(sc)
    ty = '?'
    if scanLit(sc, '(') then do
        m.sc.utilBrackets = m.sc.utilBrackets + 1
        end
    else if scanLIT(sc, ')') then do
        m.sc.utilBrackets = m.sc.utilBrackets - 1
        if m.sc.utilBrackets < 0 then
           call scanErr sc, 'unmatched closing bracket )'
        end
    else if scanLit(sc, ',') then do
        end
    else if scanLit(sc, '.') then do
        end
    else if scanString(sc, "'") then do
        end
    else if scanString(sc, '"') then do
        end
    else if scanName(sc) then do
        m.sc.val = translate(m.sc.tok)
        if m.sc.utilBrackets > 0 then
            ty = 'n'
        else if 0 < wordPos(m.sc.val, m.scanUtil) then
            ty = 'u'
        else
            ty = 'n'
        end
    else if scanVerify(sc, ' (),''"', 'm') then do
        ty = 'v'
        m.sc.val = translate(m.sc.tok)
        end
    else if \scanEnd(sc) then do
            call scanErr sc, 'scanUtil stopped before end'
        end
    else do
        ty = ''
        m.sc.val = ''
        end
    if ty == '?' then
        m.sc.utilClass = left(m.sc.tok, 1)
    else
        m.sc.utilClass = ty
    return m.sc.utilClass
endProcedure scanUtil

/*--- scan a value or a bracketed list of values ---------------------*/
scanUtilValue: procedure expose m.
parse arg sc, remApo, nl
    if remApo = '' | rempApo = 0 then
        remApo = "nv"
    else if rempApo = 1 then
        remApo = "nv'"
    if '(' \== scanUtil(sc) then
         return scanUtilValueOne(sc, remApo)
    v = ''
    brx = m.sc.utilBrackets
    oLine = word(scanPos(sc), 1)
    do forever
        call scanUtil sc
        one = scanUtilValueOne(sc, remApo)
        if one == '' then
           call scanErr sc, 'eof in brackets'
        else if brx > m.sc.utilBrackets then
           return v
        nLine = word(scanPos(sc), 1)
        if \ m.sc.utilSpace then
            v = v || one
        else if nl \== '' & oLine <> nLine then
            v = v || nl || one
        else
            v = v' 'one
        oLine = nLine
        end
endProcedure scanUtilValue

scanUtilValueOne: procedure expose m.
parse arg sc, valTy
    if m.sc.utilClass == '' then
        return ''
    else if m.sc.utilClass == 'u' then
        call scanErr sc, 'util in scanUtilValueOne'
    if pos(m.sc.utilClass, valTy) > 0 then
        return m.sc.val
    else
        return m.sc.tok
endProcedure scanUtilValueOne

/*--- skip over nested brackets --------------------------------------*/
scanUtilSkipBrackets: procedure expose m.
parse arg m, br, doCat
    if br \== '' then
        lim = m.m.utilBrackets - br
    else if scanLit(m, '(') then do
        lim = m.m.utilBrackets
        m.m.utilBrackets = lim + 1
        end
    else
        return 0
    doCat = doCat == 1
    res = ''
    do while scanUtil(m) \== ''
        if m.m.utilBrackets <= lim then do
            if doCat then
                m.m.val = res
            return 1
            end
        if doCat then
            res = res m.m.tok
        end
    return scanErr(m, 'eof with' m.m.utilBrackets 'open (')
endProcedure skipBrackets

/*--- analyze a punch file write intoField to stdOut -----------------*/
scanUtilInto: procedure expose m.
parse arg m
    if m.m.utilBrackets \== 0 then
        call scanErr m, 'scanUtilInto with brackets' m.m.utilBrackets
  /*sc = scanUtilReader(m.j.in)
    call jOpen sc, 'r'
 */ do forever
        cl = scanUtil(m)
        if cl == '' then
            return 0
        if cl = 'n' & m.m.tok == 'INTO' then
            leave
        end
    if scanUtil(m) \== 'n' | m.m.tok \== 'TABLE' then
        call scanErr m, 'bad into table '
    if \ scanSqlQuId(scanSkip(m)) then
        call scanErr m, 'table name expected'
    if m.m.utilBrackets \== 0 then
        call scanErr m, 'into table in brackets' m.m.utilBrackets
    m.m.tb = m.m.val
    m.m.part = ''
    m.m.when = ''
    do forever
        cl = scanUtil(m)
        if cl == '' then
            call scanErr m, 'eof after into'
        if cl == 'n' & m.m.tok == 'PART' then do
            if scanUtil(m) == 'v' then
                m.m.part = m.m.val
            else
                call scanErr m, 'bad part'
            end
        else if cl == 'n' & wordPos(m.m.val, 'WHEN WORKDDN') > 0 then do
            call scanUtilSkipBrackets m
            end
        else if cl == '(' then do
           leave
           end
        end
    oX =  m.m.lineX
    oL =  overlay('', m.m.src, 1, m.m.pos-2)
    do while m.m.utilBrackets > 0
        call scanUtil m
        if oX \== m.m.lineX then do
            call out strip(oL, 't')
            oX =  m.m.lineX
            oL =  m.m.src
            end
        end
    call out left(oL, m.m.pos)
 /* call jClose sc
 */ return 1
endProcedure scanUtilInto
/* copy scanUtil end **************************************************/
/* copy pipe begin *****************************************************
***********************************************************************/
pipeIni: procedure expose m.
    if m.pipe.ini == 1 then
        return
    m.pipe.ini = 1
    call catIni
    call mapReset v
    m.v_with.0 = 0
    m.v_withMap = ''
    m.v_with.0.map = ''
    m.pipe.0 = 1
    m.pipe.1.in  = m.j.in
    m.pipe.1.out = m.j.out
    call pipe '+'
    return
endProcedure pipeIni

/*-------------------------------
  +-       push pop frame
  PYNFA    ouput: Parent saY Newcat File, Appendtofile
  psf|     input: parent string file oldOut
  old          --> new
  pipeBegin    --> pipe '+N'
  pipeBeLa f   --> pipe '+F'
  pipeLast     --> pipe 'P|'
  pipeLast f   --> pipe 'F|', f
  pipeEnd      --> pipe '-'
--------------------------------*/
pipe: procedure expose m.
parse arg opts, aO, aI
    ox = 1; oc = substr(opts, ox, 1)
    ax = m.pipe.0
    px = ax -1
    if oc == '-' then do
        if px < 2 then
            call err 'pipe pop empty'
        call jClose m.pipe.ax.out
        call jClose m.pipe.ax.in
        ax = px
        m.pipe.0 = ax
        px = ax-1
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if oc == '+' then do
        px = ax
        ax = ax+ 1
        m.pipe.0 = ax
        m.pipe.ax.in  = jOpen(m.pipe.px.in, '<')
        m.pipe.ax.out = jOpen(m.pipe.px.out, '>')
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    oOut = m.pipe.ax.out
    if pos(oc, 'NYPFA') > 0 then do
        call jClose oOut
        if oc == 'Y' then
            m.pipe.ax.out = jOpen(m.pipe.1.out, '>')
        else if oc == 'P' then
            m.pipe.ax.out = jOpen(m.pipe.px.out, '>')
        else if oc == 'N' then
            m.pipe.ax.out = jOpen(Cat(), '>')
        else if oc == 'F' then
            m.pipe.ax.out = jOpen(o2file(aO), '>')
        else if oc == 'A' then
            m.pipe.ax.out = jOpen(o2file(aO), '>>')
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if pos(oc, 's|fp') > 0 then do
        call jClose m.pipe.ax.in
        if oc == 'p' then
            m.pipe.ax.in = jOpen(m.pipe.px.in, '<')
        else if oc == '|' then
            m.pipe.ax.in = jOpen(oOut, '<')
        else if oc == 'f' then do
            if arg() <= 3 then
                m.pipe.ax.in = jOpen(o2file(aI), '<')
            else do
                ct = jOpen(Cat(), '>')
                do lx = 3 to arg()
                    call jWriteAll ct, arg(lx)
                    end
                m.pipe.ax.in = jOpen(jclose(ct), '<')
                end
            end
        else if arg() <= 3 then
            m.pipe.ax.in = jOpen(jBuf(aI), '<')
        else do
            bu = jOpen(jBuf(), '>')
            do lx = 3 to arg()
                call jWrite bu, arg(lx)
                end
            m.pipe.ax.in = jOpen(jclose(bu), '<')
            end
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if oc \== ' ' then
        call err 'implement' substr(opts, ox) 'in pipe' opts
    m.j.in  = m.pipe.ax.in
    m.j.out = m.pipe.ax.out
    return
endProcedure pipe

/*--- write all from rdr (rsp in) to out, not lazy ----------------*/
pipeWriteNow: procedure expose m.
    parse arg rdr
    call jWriteNow m.j.out, if(rdr == '', m.j.in, rdr)
    return
endProcedure pipeWriteNow

/*--- write all from rdr (rsp in) to out, possibly lazy -----------*/
pipeWriteAll: procedure expose m.
parse arg rdr
    call jWriteAll m.j.out, if(rdr == '', m.j.in, rdr)
    return
endProcedure pipeWriteAll

pipePreSuf: procedure expose m.
parse arg le, ri
    do while in(v)
        call out le || m.v || ri
        end
    return
endProcedure pipePreSuf

vIsDefined: procedure expose m.
parse arg na
    return   '' \== mapValAdr(v, na)
endProcedure vIsDefined

vWith: procedure expose m.
parse arg fun, o
    if fun == '-' then do
        tBe = m.v_with.0
        tos = tBe - 1
        if tos < 0 then
            call err 'pop empty withStack'
        m.v_with.0 = tos
        m.v_withMap = m.v_with.tos.map
        return m.v_with.tBe.obj
        end
    tos = m.v_with.0
    if fun == '=' then do
        if o == '' & m.v_with.tos.obj == '' then
            return
        if o \== '' & m.v_with.tos.obj \== '' then
            if objClass(o) == objClass(m.v_with.tos.obj) then do
                m.v_with.tos.obj = o
                return
                end
        par = tos - 1
        end
    else if fun == '+' then do
        par = tos
        tos = tos + 1
        m.v_with.0 = tos
        end
    m.v_with.tos.obj = o
    if par > 0 then
        key = m.v_with.par.classes
    else
        key = ''
    if o \== '' then
        key = strip(key objClass(o))
    m.v_with.tos.classes = key
    if symbol('m.v_withManager.key') == 'VAR' then do
        m.v_with.tos.map = m.v_withManager.key
        m.v_withMap = m.v_withManager.key
        return
        end
    m = mapNew()
    m.v_with.tos.map = m
    m.v_withMap = m
    m.v_withManager.key = m
    do kx=1 to words(key)
        c1 = word(key, 1)
        ff = oClaMet(c1, 'oFlds')
        do fx=1 to m.ff.0
            mNm = m || m.ff.fx
            f2cNm = 'F2C'm.ff.fx
            m.mNm = kx m.c1.f2cNm
            end
        do fx=1 to m.c1.stms.0
            mNm = m || c1.stms.fx
            m.mNm = kx m.class.classV
            end
        do fx=1 to m.c1.StFl.0
            f2cNm = 'F2C'm.c1.StFl.fx
            mNm = m || m.c1.StFl.fx
            m.mNm = kx m.c1.f2cNm
            end
        end
    return
endProcedure vWith

vGet: procedure expose m.
parse arg na
    parse value vAdr(na) with flag 2 adr
    if flag == 'm' then
        return o2string(mapGet(v, adr))
    else if flag == 's' then
        return m.adr
    else if flag == 'o' then
        return o2String(m.adr)
    return err('vGet flag' flag)
endProcedure vGet

vGetO: procedure expose m.
parse arg na
    parse value vAdr(na) with flag 2 adr
    if flag == 'm' then
        return mapGet(v, adr)
    else if flag == 's' then
        return s2o(m.adr)
    else if flag == 'o' then
        return m.adr
    return err('vGetO flag' flag)
endProcedure vGetO

vPut: procedure expose m.
parse arg na, val
    parse value vAdr(na) with flag 2 adr
    if flag == 'm' then
        call mapPut v, adr, s2o(val)
    else if flag == 's' then
        m.adr = val
    else if flag == 'o' then
        m.adr = s2o(val)
    else
        return err('vPut flag' flag)
    return val
endProcedure vPut

vPutO: procedure expose m.
parse arg na, val
    parse value vAdr(na) with flag 2 adr
    if flag == 'm' then
        call mapPut v, adr, val
    else if flag == 's' then
        m.adr = o2String(val)
    else if flag == 'o' then
        m.adr = val
    else
        return err('vPutO flag' flag)
    return val
endProcedure vPut

vAdr: procedure expose m.
parse arg na
    cx = pos('&', na)
    if cx = 1 | na = '' then
        call err 'vAdr bad name' na
    if cx < 1 then
        oNm = na
    else
        oNm = left(na, cx-1)
    if m.v_withMap \== '' then do
        mp = m.v_withMap
        if symbol('m.mp.oNm') == 'VAR' then do
            parse var m.mp.oNm wx cl
            end
        else do
            sNm = vStemPath(oNm)
            if symbol('m.mp.sNm') == 'VAR' then
                parse var m.mp.sNm wx cl
            end
        if symbol('wx') == 'VAR' then do
            o = m.v_with.wx.obj'.'oNm
            if cx < 1 then
                if cl == m.class.classV then
                    return 's'o
                else
                    return 'o'o
            end
        end
    if cx < 1 then
        return 'm'na
    if symbol('wx') \== 'VAR' then
        o = mapGet(v, oNm)
    cx = cx + 1
    do forever
        if o = '' then
            call err 'vAdr null at' left(na, cx-1) 'in name' na
        cy = pos('&', na, cx)
        if cy < cx then
            leave
        if cy = cx then
            a = o
        else
            a = o'.'substr(na, cx, cy-cx)
        if symbol('m.a') \== 'VAR' then
            call err 'vAdr undefined' a 'at' left(na, cy-1) 'in name' na
        o = m.a
        cx = cy+1
        end
    f = substr(na, cx)
    if f \== '' then
        f = '.'f
    a = o || f
    cl = objClass(o, '')
    if cl == '' then
        return = 'c'a
    f2cF = 'F2C'f
    if symbol('m.cl.f2cF') \== 'VAR' then do
        f2cF = 'F2C'vStemPath(f)
        if symbol('m.cl.f2cF') \== 'VAR' then
            return 'f'a
        end
    if m.cl.f2cF == m.class.classV then
        return 's'a
    else
        return 'o'a
endProcedure vAdr

/*--- resolve stem indexes within variable path ---------------------*/
vStemPath: procedure expose m.
parse arg f
    cx = 0
    do until cx < 1
     cy = verify(f, '0123456789', 'n', cx+1)
        if cy < 1 then do
            if substr(f, cx+1) \== 0 then
                f = left(f, cx)'*'
            return f
            end
        if substr(f, cy, 1) \== '.' | cy=cx+1 then do
            cx = pos('.', f, cy)
            end
        else if substr(f, cx+1, cy-cx) == 0 then do
            call err '0 within name' f
            end
        else do
            f = left(f,cx)'*'substr(f,cy)
            cx = pos('.', f, cx+2)
            end
        end
    return f
endProcedure vStemPath

vRead: procedure expose m.
parse arg na
    return in("V."na)

vReadO: procedure expose m.
parse arg na
    res = inO()
    if res == '' then
        return 0
    call vPutO na, res
    return 1
endProcedure vReadO

vHasKey: procedure expose m.
parse arg na
    return mapHasKey(v, na)

vRemove: procedure expose m.
parse arg na
    return mapRemove(v, na)
/* copy pipe end ******************************************************/
/* copy cat  begin ****************************************************
***********************************************************************/
/*--- create a new cat -----------------------------------------------*/
cat: procedure expose m.
    m = oNew('Cat') /* calls catReset */
    do ax=1 to arg()
        call catWriteAll m, arg(ax)
        end
    return m
endProcedure cat

catReset: procedure expose m.
parse arg m
    m.m.RWs.0 = 0
    m.m.catWr = ''
    m.m.catRd = ''
    m.m.catIx = -9e9
    m.m.catKeepOpen = ''
    return m
endProcedure catReset

catClose: procedure expose m.
parse arg m
    if m.m.catWr \== '' then do
        call jClose m.m.catWr
        call mAdd m'.RWS', m.m.catWr
        m.m.catWr = ''
        end
    if m.m.catRd \== '' then do
        call jClose m.m.catRd
        m.m.catRd = ''
        end
    m.m.catIx = -9e9
    return m
endProcedure catClose

catOpen: procedure expose m.
parse arg m, oo
    if oo == m.j.cRead then do
        m.m.catIx = 0
        call catNextRdr m
        m.m.jReading = 1
        end
    else if oo == m.j.cWri | oo == m.j.cApp then do
        if oo == m.j.cWri then
            m.m.RWs.0 = 0
        m.m.catIx = -9e9
        m.m.jWriting = 1
        end
    else do
        call err 'catOpen('m',' oo') bad opt'
        end
    return m
endProcedure catOpen

/*--- return and open next reader ------------------------------------*/
catNextRdr: procedure expose m.
parse arg m
    if m.m.catRd \== '' then
        call jClose m.m.catRd
    cx = m.m.catIx + 1
    m.m.catIx = cx
    if cx > m.m.RWs.0 then do
        m.m.catRd = ''
        return 0
        end
    m.m.catRd = m.m.RWs.cx
    if cx = word(m.m.catKeepOpen, 1) then
        m.m.catKeepOpen = subWord(m.catKeepOpen, 2)
    else
        call jOpen m.m.catRd , m.j.cRead
    return 1
endProcedure catNextRdr

catReadO: procedure expose m.
parse arg m
    do while m.m.catRd \== ''
        res = jReadO(m.m.catRd)
        if res \== '' then
            return res
        call catNextRdr m
        end
    return ''
endProcedure catReadO

catWrite: procedure expose m.
parse arg m, line
    if m.m.catWr == '' then
        m.m.catWr = jOpen(jBuf(), m.j.cWri)
    call jWrite m.m.catWr, line
    return
endProcedure catWrite

catWriteO: procedure expose m.
parse arg m, var
    if m.m.catWr == '' then
        m.m.catWr = jOpen(jBuf(), m.j.cWri)
    call jWriteO m.m.catWr, var
    return
endProcedure catWriteO

/*--- write contents of a reader to cat
          or keep it for later reading -------------------------------*/
catWriteAll: procedure expose m.
parse arg m
    if m.m.catWr \== '' then do
        call mAdd m'.RWS', jClose(m.m.catWr)
        m.m.catWr = ''
        end
    do ax=2 by 1 to arg()
        r = o2File(arg(ax))
        call mAdd m'.RWS', r
        if m.r.jReading then do
            m.m.catKeepOpen = m.m.rws.0 m.m.catKeepOpen
            call jOpen r, m.j.cRead
            end
        end
    return
endProcedure catWriteAll

/*--- create a reader/WriteO for an external file --------------------*/
file: procedure expose m.
parse arg str
    return oNew('File', str)
endProcedure file

fileChild: procedure expose m.
parse arg m, name, opt
    interpret objMet(m, 'fileChild')
endProcedure fileChild

fileRm: procedure expose m.
parse arg m
    interpret objMet(m, 'fileRm')
    return
endProcedure fileRm

filePath: procedure expose m.
parse arg m
    interpret objMet(m, 'filePath')
endProcedure filePath

fileIsFile: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsFile')
endProcedure fileIsFile

fileIsDir: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsDir')
endProcedure fileIsDir

fileMkDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileMkDir')
    return
endProcedure fileRm

fileRmDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileRmDir')
    return
endProcedure fileRm

/*--- create a reader/WriteO for the filelist of a directory----------*/
fileList: procedure expose m.
parse arg m, opt
    str = oIfStr(m, '')
    if str == '' then
        return oNew('FileList', filePath(m),  opt)
    else
        return oNew('FileList', dsn2Jcl(str),  opt)
endProcedure fileList

catIni: procedure expose m.
    if m.cat.ini == 1 then
        return
    m.cat.ini = 1
    call errIni
    call jIni
    call classNew "n Cat u JRWO", "m",
        , "jOpen  call catOpen m, opt",
        , "jReset call catReset m, arg",
        , "jClose call catClose m",
        , "jReadO return catReadO(m)",
        , "jWrite call catWrite m, line; return",
        , "jWriteO call catWriteO m, var; return",
        , "jWriteAll call catWriteAll m, rdr; return"

    call classAddMet m.class.classV, 'o2File return file(m.m)'
    call classAddMet m.class.classW, 'o2File return file(substr(m,2))'
    if m.err.os == 'TSO' then
        call fileTsoIni
    else if m.err.os == 'LINUX' then
        call fileLinuxIni
    else
        call err 'file not implemented for os' m.err.os
    return
endProcedure catIni
/* copy cat  end   ****************************************************/
/* copy mail begin ***************************************************/
mailHead: procedure expose m.
parse arg m, subj, rec, snd
    m.m.1 = 'SUBJECT   ' subj
    m.m.2 = 'RECPLIST  ' rec
    m.m.3 = 'SENDERID  ' if(snd=='', userid(), snd)
    m.m.4 = 'SENDERIDCC N'
    m.m.5 = 'DOCTEXT'
    m.m.0 = 5
    return m
endProcedure mailHead

mailSend: procedure expose m.
parse arg m, dsn
    call mAdd m, 'DOCTEXTEND'
    if dsn == '' then
        call dsnAlloc 'dd(iemapi) new  ::v255'
    else
        call dsnAlloc 'dd(iemapi) shr' dsn
    call writeDD iemapi, 'M.'m'.'
    call tsoClose iemapi
    call dsnAlloc 'dd(iemlog) sysout(*)'
    call adrTso "call *(os3550)", '*'
    if rc \== 0 then
        say 'error os3550 rc='rc 'mail not sent|||||'
    call tsoFree iemlog iemapi
    return 0
endProcedure mailSend
/* copy mail end *****************************************************/
/* copy fiLinux begin *************************************************/
/*--- send ggShCmd to shell bash,
        fail if rc <> 0 or not listed in ggRet -----------------------*/
adrSh: procedure expose m.  /* really no need for variables???? */
    parse arg ggShCmd, ggRet
    address 'bash' ggShCmd
    if rc == 0                     then return 0
    else if ggRet == '*'           then return rc
    else if wordPos(rc, ggRet) > 0 then return rc
    else
        call err 'adrSh rc' rc 'for' ggShCmd
endProcedure adrSh

fileLinuxReset: procedure expose m.
parse arg m, nm
    m.m.spec = nm
    if abbrev(nm, '&') then do
        if nm == '&in' then do
            m.m.stream = .input
            m.m.jReading = 1
            end
        else if nm == '&out' then do
            m.m.stream = .output
            m.m.jWriting = 1
            end
        else do
            call err 'bad spec' nm
            end
        end
    else do
        m.m.stream = .Stream%%new(nm)
        m.m.stream%%init(m.m.stream%%qualify)
        end
    return m
endProcedure fileLinuxReset

fileLinuxOpen: procedure expose m.
parse arg m, opt
    if opt == m.j.cRead then do
        res = m.m.stream%%open(read shareread)
        m.m.jReading = 1
        end
    else do
        if opt == m.j.cApp then
            res = m.m.stream%%open(write append)
        else if opt == m.j.cWri then
            res = m.m.stream%%open(write replace)
        else
            call err 'fileLinuxOpen('m',' opt') with bad opt'
        m.m.jWriting = 1
        end
    if res \== 'READY:' then
        call err 'fileLinuxOpen fails' res':' opt ,
        "'"m.m.stream%%qualify"'"
    return m
endProcedure fileLinuxOpen

fileLinuxClose:
parse arg m
    res = m.m.stream%%close
    if res \== 'READY:' then
        call err 'fileLinuxClose' res':' m.m.stream%%qualify
    return m
endProcedure fileLinuxClose

fileLinuxRead: procedure expose m.
parse arg m, var
    res = m.m.stream%%lineIn
    if res == '' then
        if m.m.stream%%state \== 'READY' then
            return 0
    m.var = res
    m.o.o2c.var = m.class.classV
    return 1
endProcedure fileLinuxRead

fileLinuxWrite: procedure expose m.
parse arg m, line
    if m.m.stream%%lineOut(line) then
        call err 'fileLinuxWrite'
    return
endProcedure fileLinuxWrite

fileLinuxRmDir: procedure expose m.
parse arg m, opt
    if opt == '' then
        return adrSh('rmdir' m.m.spec)
    else if opt == '-r' then
        return adrSh('rm -r' m.m.spec)
    else
        call err 'bad opt' opt 'in fileLinuxRmDir'
endProcedure fileLInuxRmDir

fileLinuxListReset: procedure expose m.
parse arg m, m.m.spec, o
    if o == 'r' then
        m.m.opt = 'S'
    else if o == '' then
        m.m.opt = ''
    else
        call err 'bad opt' o 'in fileLinuxListReset'
    m.m.rx = 'closed'
    return m
endProcedure fileLinuxListReset

fileLinuxListOpen: procedure expose m.
parse arg m
    if m \== translate(m) then
        call err 'bad m for fileLinuxList:' m
    if m.m.opt == '' then
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST')
    else
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST', m.m.opt)
    if rc \== 0 then
        call err 'rc' rc 'in sysFileTree('m.m.spec', m.'m'.LIST)'
    m.m.rx = 0
    m.m.jReading = 1
    return m
endProcedure fileLinuxListOpen

fileLinuxListRead: procedure expose m.
parse arg m, var
    x = m.m.rx + 1
    if x > m.m.list.0 then
        return 0
    m.var = substr(m.m.list.x, 43)
    m.m.rx = x
    call oMutate var, m.class.classV
    return 1
endProcedure fileLinuxListRead

fileLinuxIni: procedure expose m.
    if m.fileLinux.ini == 1 then
        return
    m.fileLinux.ini = 1
    m.file.sep = '/'
    call jIni
    call classNew "n File u JRW", "m",
        , "jReset call fileLinuxReset m, arg",
        , "jOpen  call fileLinuxOpen m, opt",
        , "jClose call fileLinuxClose m",
        , "jRead return fileLinuxRead(m, var)",
        , "jWrite call fileLinuxWrite m, line",
        , "jWriteO call jWrite m, o2String(var)",
        , "filePath return m.m.stream%%qualify",
        , "fileIsFile return sysIsFile(m.m.stream%%qualify)" ,
        , "fileIsDir return sysIsFileDirectory(m.m.stream%%qualify)" ,
        , "fileChild return file(m.m.stream%%qualify'/'name)",
        , "fileRm return adrSh(m.m.spec)",
        , "fileMkDir return adrSh('mkdir' m.m.stream%%qualify)" ,
        , "fileRmDir return fileLinuxRmDir(m, opt)"
    call classNew "n FileList u JRW", "m",
        , "jReset call fileLinuxListReset m, arg, arg2",
        , "jOpen  call fileLinuxListOpen m, opt",
        , "jClose m.m.rx = 'closed'",
        , "jRead return fileLinuxListRead(m, var)"
    return
endProcedure fileLinuxIni
/* copy fiLinux end   *************************************************/
/* copy fileTso begin *************************************************/
fileTsoReset: procedure expose m.
parse arg m, sp
    m.m.readIx = 'c'
    if symbol('m.m.defDD') \== 'VAR' then do
        m.m.defDD = 'CAT*'
        m.fileTso.buf = m.fileTso.buf + 1
        m.m.buf = 'FILETSO.BUF'm.fileTso.buf
        m.m.spec = sp
        end
    if sp \== '' then do
        m.m.spec = dsnSpec(sp)
        rr = translate(subword(m.m.spec, 4))
        m.m.stripT = \ (pos(':V', rr) > 0 | pos('RECFM(V', rr) > 0)
        end
    return m
endProcedure fileTsoReset

fileTsoOpen: procedure expose m.
parse arg m, opt
    buf = m.m.buf
    if opt == m.j.cRead then do
        aa = dsnAlloc(m.m.spec, 'SHR', m.m.defDD)
        aDD = word(aa, 1)
        aDsn = m.tso_dd.aDD
        if pos('(', aDsn) > 0 & pos('/', aDsn) < 1 then
            /* avoid messy tso message when read nonExistant member */
            if sysDsn("'"m.tso_dd.aDD"'") <> 'OK' then
                call err 'cannot read' m.tso_dd.aDD':',
                               sysDsn("'"m.tso_dd.aDD"'")
        call tsoOpen word(aa, 1), 'R'
        m.m.jReading = 1
        m.buf.0 = -1
        m.m.readIx = 0
        end
    else do
        if opt == m.j.cApp then
            aa = dsnAlloc(m.m.spec, 'MOD', m.m.defDD)
        else if opt == m.j.cWri then
            aa = dsnAlloc(m.m.spec, 'OLD', m.m.defDD)
        else
            call err 'fileTsoOpen('m',' opt') with bad opt'
        call tsoOpen word(aa, 1), 'W'
        m.m.jWriting = 1
        m.buf.0 = 0
        m.m.readIx = 'w'
        end
    parse var aa m.m.dd m.m.free
    m.m.dsn = mGet('TSO_DD.'m.m.dd)
    return m
endProcedure fileTsoOpen

fileTsoClose: procedure expose m.
parse arg m
    buf = m.m.buf
    if m.m.readIx \== 'c' then do
        if m.m.readIx == 'w' & m.buf.0 > 0 then
            call writeDD m.m.dd, 'M.'BUF'.'
        call tsoClose m.m.dd
        call tsoFree  m.m.free
        end
    m.buf.0 = 'closed'
    m.m.readIx = 'c'
    m.m.free  = ''
    m.m.dd    = ''
    return m
endProcedure fileTsoClose

fileTsoRead: procedure expose m.
parse arg m, var
    ix = m.m.readIx + 1
    buf = m.m.buf
    if ix > m.buf.0 then do
        res = readDD(m.m.dd, 'M.'buf'.')
        if \ res then
            return 0
        ix = 1
        end
    m.m.readIx = ix
    m.var = m.buf.ix
    call oMutate var, m.class.classV
    return 1
endProcedure fileTsoRead

fileTsoWrite: procedure expose m.
parse arg m, var
    buf = m.m.buf
    ix = m.buf.0 + 1
    m.buf.0 = ix
    if m.m.stripT then
        m.buf.ix = strip(var, 't')
    else
        m.buf.ix = var
    if ix > 99 then do
        call writeDD m.m.dd, 'M.'buf'.'
        m.buf.0 = 0
        end
    return
endProcedure fileTsoWrite

fileTsoWriteO: procedure expose m.
parse arg m, var
    if objClass(var, m.class.classV) == m.class.classV then do
        call fileTsoWrite m, m.var
        return
        end
    call err 'fileTsoWriteO('m',' var') cannot write objects of class',
                              objClass(var)
endProcedure fileTsoWriteO

fSub: procedure expose m.
    return file('.sysout(T) writer(intRdr)')
endProcedure fSub

fEdit: procedure expose m.
parse arg spec, vw
    if spec == '' then
        spec = 'new ::f'
    else if abbrev(spec, '::') then
        spec = 'new' spec
    else if abbrev(spec, ':') then
        spec = 'new' ':'spec
    f  = oNew('FileEdit', spec)
    m.f.editType = if(abbrev(translate(vw), 'V'), 'view', 'edit')
    return f
endProcedure fEdit

fileTsoEditClose: procedure expose m.
parse arg m
    dsn = m.m.dsn
    if dsn \== '' then do
        call fileTsoClose m
        call adrIsp m.m.editType "dataset('"dsn"')", 4
        return
        end
    fr = m.m.free
    dd = m.m.dd
    m.m.free = ''
    call fileTsoClose m
    call adrIsp "LMINIT DATAID(lmmId) ddName("dd") ENQ(SHRW)"
    eRc = adrIsp(m.m.editType "dataid("lmmId")", '*')
    lRc = adrIsp("LMFree DATAID("lmmId")", '*')
    call tsoFree fr
    if (eRc \== 0 & eRc \== 4) | lRc \== 0 then
        call err m.m.editType 'rc' eRc', lmFree rc' lRc
    return
endProcedure fileTsoEditClose

fileTsoIni: procedure expose m.
    if m.fileTso.ini == 1 then
        return
    m.fileTso.ini = 1
    m.file.sep = '.'
    m.fileTso.buf = 0
    call jIni
    um = "call err 'for tso undefined method'"
    call classNew "n File u JRW", "m",
        , "jOpen  call fileTsoOpen m, opt",
        , "jReset call fileTsoReset m, arg",
        , "jClose call fileTsoClose m",
        , "jRead return fileTsoRead(m, var)",
        , "jWrite call fileTsoWrite m, line",
        , "jWriteO call fileTsoWriteO m, var",
        , "filePath return word(m.m.spec, 1)"           ,
        , "fileIsFile" um "'fileIsFile'"      ,
        , "fileIsDir   return 1"              ,
        , "fileChild   return file(word(m.m.spec, 1)'.'name opt)",
        , "fileRm"     um "'fileRm'"          ,
        , "fileMkDir"  ,
        , "fileRmDir"  um "'fileRmDir'"
 /*     , "filePath return m.m.stream%%qualify",
        , "fileIsFile return sysIsFile(m.m.stream%%qualify)" ,
        , "fileIsDir return sysIsFileDirectory(m.m.stream%%qualify)" ,
        , "fileChild return file(m.m.stream%%qualify'/'name)",
        , "fileRm return adrSh(m.m.spec)",
        , "fileMkDir return adrSh('mkdir' m.m.stream%%qualify)" ,
        , "fileRmDir return fileLinuxRmDir(m, opt)" */
    call classNew "n FileList u JRW", "m",
        , "jReset if arg2 == 'r' then m.m.dsnMask=arg'.**';",
                                "else m.m.dsnMask=arg'.*';",
        , "jOpen  call csiOpen m, m.m.dsnMask",
        , "jClose" ,
        , "jRead return csiNext(m, var)"
    call classNew "n FileEdit u File", "m",
        , "jClose call fileTsoEditClose m"
    return
endProcedure fileTsoIni
/* copy fileTso end   *************************************************/
/* copy sqlDiv begin **************************************************/
/*--- generate the format m for a sql cx as specified in sp
          use the information from the sqlDa -------------------------*/
sqlFTabReset: procedure expose m.
parse arg ff, cx, tBef, tAft, m.ff.maxChar, m.ff.blobMax, m.ff.maxDec
    if m.ff.maxChar == '' then
        m.ff.maxChar = 32
    if m.ff.blobMax == '' then
        m.ff.blobMax = 200
    bf = '%-'max(m.ff.blobMax, 4)'C'
    m.ff.flds = ''
    m.ff.sqlX = cx
    call fTabReset ff, tBef, tAft, '-'
    m.ff.sql2fmt.384 = '%-10C' /* date    */
    m.ff.sql2fmt.388 = '%-8C'  /* time    */
    m.ff.sql2fmt.392 = '%-26C' /* timestamp */
    m.ff.sql2fmt.400 = 'c'     /* graphic string */
    m.ff.sql2fmt.404 = bf      /* BLOB           */
    m.ff.sql2fmt.408 = bf      /* CLOB           */
    m.ff.sql2fmt.412 = bf      /* DBCLOB         */
    m.ff.sql2fmt.448 = 'c'     /* varchar        */
    m.ff.sql2fmt.452 = 'c'     /* char           */
    m.ff.sql2fmt.452 = 'c'     /* long varchar   */
    m.ff.sql2fmt.460 = 'c'     /* null term. string */
    m.ff.sql2fmt.464 = 'c'     /* graphic varchar   */
    m.ff.sql2fmt.468 = 'c'     /* graphic char      */
    m.ff.sql2fmt.472 = 'c'     /* long graphic varchar   */
    m.ff.sql2fmt.480 = '%-7e'  /* float                  */
    m.ff.sql2fmt.484 = 'd'     /* packed decimal         */
    m.ff.sql2fmt.492 = '%20i'  /* bigInt                 */
    m.ff.sql2fmt.496 = '%11i'  /* int                    */
    m.ff.sql2fmt.500 = '%6i'   /* smallInt               */
    m.ff.sql2fmt.904 = '%-34H' /* rowID 17 Byte Binary   */
    return
endProcedure sqlFTabReset
/*--- set a defaultFormat for type tx in fTab ff ---------------------*/
sqlFTabDef: procedure expose m.
parse arg ff, tx, m.ff.sql2fmt.tx
return ff

sqlFTabAdd: procedure expose m.
parse arg m, c1 aDone, f1, l1
    if symbol('m.m.set.c1') == 'VAR' then do
        sx = m.m.set.c1
        if word(m.m.set.sx, 1) == c1 & sx <= m.m.set.0 then do
            parse var m.m.set.sx c1 aDone
            f1 = m.m.set.sx.fmt
            l1 = m.m.set.sx.label
            end
        end
    cx = m.m.sqlX
    kx = sqlCol2kx(cx, c1)
    if kx == '' then
        call err 'colName not found' c1
    do tx=2 to arg()-3
        if arg(tx+3) \== '' then
            call fTabAddTit m, tx, arg(tx+3)
        end
    if f1 \== '' then do
        if right(f1, 1) \== ' ' then
            f1 = f1' '
        return fTabAdd(m, c1 aDone, f1, l1)
        end
    ty = m.sql.cx.d.kx.sqlType
    le = m.sql.cx.d.kx.sqlLen
    withNulls = ty // 2
    ty = ty - withNulls
    if symbol('m.m.sql2fmt.ty') <> 'VAR' then
        call err 'sqlType' ty 'col' c1 'not supported'
    f2 = m.m.sql2fmt.ty
    if f2 == 'c' then
        f2 = '%-'min(le, m.m.maxChar)'C'
    else if f2 == 'd' then do
        trace ?r
        pr =  le % 256
        de =  le // 256
        f2 = '%'pr'.'de'i'
        end
    if \ abbrev(f2, '%') then
        call err 'sqlType' ty 'col' c1 'bad format' f2
    return fTabAdd(m, c1 aDone, f2' ', l1)
endProcedure sqlFTabAdd

sqlFTabOthers: procedure expose m.
parse arg m, doNot
    cx = m.m.sqlX
    do kx=1 to m.sql.cx.d.sqlD
        c1 = m.sql.cx.col.kx
        wx = wordPos(c1, m.m.cols)
        if (wx < 1 | m.m.wx.done \== 1) & wordPos(c1, doNot) < 1 then
            call sqlFTabAdd m, m.sql.cx.col.kx
        end
    return
endProcedure sqlFTabOthers

sqlFTab: procedure expose m.
parse arg m
    call fTabBegin m
    do while sqlRxFetch(m.m.sqlX, 'sqlFTab')
        call out f(m.m.fmt, 'sqlFTab')
        end
    return fTabEnd(m)
endProcedure sqlFTab

sqlFTabCol: procedure expose m.
parse arg m
    do rx=1 while sqlRxFetch(m.m.sqlX, 'sqlFTab')
        call out left('--- row' rx '', 100, '-')
        call fTabCol m, 'sqlFTab'
        end
    call out left('--- end of' (rx-1) 'rows ', 100, '-')
    return
endProcedure sqlFTabCol

sqlCatTb: procedure expose m.
parse arg ty gOnly, wh, ord, fTab, paPlus
    tb = tkrTable(, ty)
    if gOnly == 1 then
        edFun = ''
    else
        edFun = tkrTable(, ty, 'e')
    cx = 1
    ft = 'ft'm.tb.alias
    call sqlFTabReset ft, cx, 'c 1', '1 c', 12, if(fTab, , 2000)
    call sqlFTabDef      ft, 492, '%7e'
    call FTabSet         ft, 'CONTOKEN'  , '%-16H'
    call FTabSet         ft, 'DCONTOKEN'  , '%-16H'
    call FTabSet         ft, 'DBNAME'    , '%-8C', 'db'
    call FTabSet         ft, 'DSNAME'    , '%-44C'
    call FTabSet         ft, 'DSNUM'     , '%5i'
    call FTabSet         ft, 'PARTITION' ,'%5i' , 'part'
    call FTabSet         ft, 'PIT_RBA'   , '%-12H'
    call FTabSet         ft, 'RBA1'      , '%-12H'
    call FTabSet         ft, 'RBA2'      , '%-12H'
    call FTabSet         ft, 'START_RBA' ,'%-12H'
    call FTabSet         ft, 'TSNAME'    , '%-8C', 'ts'
    call FTabSet         ft, 'VERSION'   , '%-28C'
    if edFun \== '' then do
        interpret 'sq =' edFun'(ft, tb, wh, ord)'
        end
    else do
        cl = sqlColList(m.tb.table, m.ft.blobMax)
        sq = 'select' cl tkrTable( , tb, 'f') wh ,
             'order by' if(ord=='', m.tb.order, ord)
        call sqlQuery cx, sq
        call sqlFTabOthers ft
        call sqlCatTbVl ft, tb
        end
    if fTab then
        call sqlFTab ft
    else
        call sqlFTabCol ft
    call sqlClose cx
    call sqlCatTbTrailer space(m.TKR.path paPlus, 1), sq
    return 0
endProcedure sqlCatTb

sqlCatTbVlsep:
    return '+++'

sqlCatTbVl: procedure expose m.
parse arg ft, tb, sep
    if sep == '' then
        sep = sqlCatTbVLsep()
    if m.tb.vlKey == '' then
        return
    ky = m.tb.vlKey
    ff = ''
    tt = ''
    do kx=1 to m.ky.0
        tt = tt || sep || m.ky.kx.col
        ff = ff || sep'@'m.ky.kx.col'%S'
        end
    call fTabAdd ft, substr(tt,length(sep)+1), substr(ff,length(sep)+1)
    return
endProcedure sqlCatTbVl

sqlCatTbTrailer: procedure expose m.
parse arg pa, sq
    ox = lastPos(' order by ', sq)
    if ox < 1 then
        call err 'order by not found in' sq
    ord = substr(sq, ox+10)
    sq = left(sq, ox-1)
    sqUp = translate(sq)
    call out ''
    call out 'dbSys:' m.sql_dbSys
    call out 'path:' pa
    int = ''
    iNx = '  '
    br = ''
    cx = 1
    stops = '(select from where'
    do while cx < length(sq)
        nx = -1
        do sx=1 to words(stops)
            n2 = pos(word(stops, sx), sq, cx+1)
            if n2 > cx & (nx < 1 | n2 < nx) then
                nx = n2
            end
        if nx < 0 then
            leave
        call out int || substr(sq, cx, nx-cx)
        int = iNx
        if substr(sq, nx, 3) = '(se' then do
            iNx = iNx'  '
            br = left(br, length(int))')'
            end
        cx = nx
        end
    ll =  strip(substr(sq, cx))
    bq = strip(br)
    do while bq <> ''
        if right(bq, 1) \== ')' | right(ll, 1) \== ')' then
           call err 'missing ) bq:' bq', ll:' ll
        ll = strip(left(ll, length(ll) - 1))
        bq = strip(left(bq, length(bq) - 1))
        end
    call out int || ll
    if br <> '' then
        call out br
    if ord <> '' then
        call out '  order by' ord
    return
endProcedure sqlCatTbTrailer

sqlCatIxKeys: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select ikK.colSeq, ikK.colName, ikK.ordering, ikK.period' ,
             ', ik.creator, ik.name, ik.tbCreator, ik.tbName, ikC.*'  ,
          tkrTable(, tb ,'f') wh,
          'order by' if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, CREATOR, '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME   , '%-16C','index'
    call sqlFTabAdd      ft, colSeq  , '%5i',  'coSeq'
    call sqlFTabAdd      ft, colName, '%-16C', 'column'
    call sqlFTabAdd      ft, ordering
    call sqlFTabAdd      ft, period
    call sqlFTabAdd      ft, COLNO
    call sqlFTabAdd      ft, COLTYPE
    call sqlFTabAdd      ft, LENGTH
    call sqlFTabAdd      ft, SCALE
    call sqlFTabAdd      ft, NULLS
    call sqlFTabOthers ft, 'COL9 COL10 COL11 COL47'
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatIxKeys

sqlCatIXStats: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select *' tkrTable( , tb, 'f') wh ,
         'order by' if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, CREATOR, '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME   ,       , 'index'
    call sqlFTabAdd      ft, INSTANCE   , '%1i' , 'i'
    call sqlFTabAdd      ft, PARTITION , , 'part'
    call sqlFTabOthers ft
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatIXStats

sqlCatTables: procedure expose m.
parse arg ft, tb, wh, ord
    al = m.tb.alias
    sq = 'select' al'.*, tsX.type tsType, tsX.partitions',
            ', tsX.pgSize, tsX.dsSize' ,
            ', timestamp(rba1 || x''0000'') rba1Tst' ,
            ', timestamp(rba2 || x''0000'') rba2Tst' ,
          'from' m.tb.table 'left join sysibm.sysTablespace tsX',
            'on' al'.dbName = tsx.dbName and' al'.tsName = tsX.name',
            'where' m.tb.cond wh ,
            'order by'  if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, creator   , '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME      , '%-24C', 'table'
    call sqlFTabAdd      ft, type
    call sqlFTabAdd      ft, dbNAME    , '%-8C', 'db'
    call sqlFTabAdd      ft, tsNAME    , '%-8C', 'ts'
    call sqlFTabAdd      ft, tsType
    call sqlFTabAdd      ft, partitions,       , 'parts'
    call sqlFTabAdd      ft, pgSize
    call sqlFTabAdd      ft, dsSize
    call sqlFTabOthers ft, 'RBA1 RBA1TST RBA2 RBA2TST'
    call sqlFTabAdd      ft, rba1      , '%-12H'
    call sqlFTabAdd      ft, rba1Tst   ,       , 'rba1Timestamp:GMT'
    call sqlFTabAdd      ft, rba2      , '%-12H'
    call sqlFTabAdd      ft, rba2Tst   ,       , 'rba2Timestamp:GMT'
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatTables

sqlCatTSStats: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select' m.tb.alias'.*' ,
           tkrTable( , tb, 'f') wh ,
           'order by' if(ord == '', m.tb.order , ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, DBNAME, '%-8C', 'db'
    call sqlFTabAdd      ft, NAME   , '%-8C', 'ts'
    call sqlFTabAdd      ft, INSTANCE   , '%1i' , 'i'
    call sqlFTabAdd      ft, PARTITION , , 'part'
    call sqlFTabAdd      ft, NACTIVE   , , 'nActive'
    call sqlFTabAdd      ft, NPAGES    , , 'nPages'
    call sqlFTabAdd      ft, SPACE       , , 'spaceKB'
    call sqlFTabAdd      ft, TOTALROWS   , , 'totRows'
    call sqlFTabAdd      ft, DATASIZE         , , 'dataSz'
    call sqlFTabAdd      ft, LOADRLASTTIME    , , 'loadRLasttime'
    call sqlFTabAdd      ft, REORGLASTTIME    , , 'reorgLasttime'
    call sqlFTabAdd      ft, REORGINSERTS     , , 'inserts'
    call sqlFTabAdd      ft, REORGDELETES     , , 'deletes'
    call sqlFTabAdd      ft, REORGUPDATES     , , 'updates'
    call sqlFTabAdd      ft, REORGUNCLUSTINS  , , 'unClIns'
    call sqlFTabAdd      ft, REORGDISORGLOB   , , 'disorgL'
    call sqlFTabAdd      ft, REORGMASSDELETE  , , 'massDel'
    call sqlFTabAdd      ft, REORGNEARINDREF  , , 'nearInd'
    call sqlFTabAdd      ft, REORGFARINDREF   , , 'farInd'
    call sqlFTabAdd      ft, REORGCLUSTERSENS , , 'cluSens'
    call sqlFTabAdd      ft, REORGSCANACCESS  , , 'scanAcc'
    call sqlFTabAdd      ft, REORGHASHACCESS  , , 'hashAcc'
    call sqlFTabAdd      ft, STATSLASTTIME    , , 'statsLasttime'
    call sqlFTabAdd      ft, STATSINSERTS     , , 'inserts'
    call sqlFTabAdd      ft, STATSDELETES     , , 'deletes'
    call sqlFTabAdd      ft, STATSUPDATES     , , 'updates'
    call sqlFTabAdd      ft, STATSMASSDELETE  , , 'massDel'
    call sqlFTabAdd      ft, COPYLASTTIME     , , 'copyLasttime'
    call sqlFTabAdd      ft, COPYUPDATETIME   , , 'copyUpdatetime'
    call sqlFTabAdd      ft, COPYUPDATELRSN   , '%-12H', 'updateLRSN'
    call sqlFTabAdd      ft, COPYUPDATEDPAGES , , 'updaPgs'
    call sqlFTabAdd      ft, COPYCHANGES      , , 'changes'
    call sqlFTabOthers ft
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatTSStats

sql4obj: procedure expose m.
parse arg m, tb
    call out 'insert into' tb '--' className(objClass(m))
    line = ''
    ff = oFlds(m)
    pr = '   ('
    do fx=1 to m.ff.0
        call sql4ObjOut substr(m.ff.fx, 2)
        end
    call sql4ObjOut , 1
    call out '   ) values '
    pr = '   ('
    do fx=1 to m.ff.0
        f1 = substr(m.ff.fx, 2)
        v = strip(m.m.f1, 't')
        if dataType(v, n) then
            call sql4ObjOut v
        else do qx=1 until v == ''
            vx = verify(v, m.ut_alfPrint)
            if vx = 0 then do
                l1 = min(60, length(v))
                w = quote(left(v, l1), "'")
                end
            else if vx > 29 | vx = 0 then do
                l1 = min(60, vx)
                w = quote(left(v, l1), "'")
                end
            else do
                l1 = min(29, length(v))
                w = 'x'quote(c2x(left(v, l1)), "'")
                end
            if qx == 1 then
                call sql4ObjOut w
            else do
                if qx = 2 then
                    call sql4ObjOut , 1
                call out '   ||' w
                end
            v = substr(v, l1+1)
            end
        end
    call sql4ObjOut , 1
    call out '   ) ; '
    return
endProcedure
sql4objOut:
parse arg t1, force
    if (force == 1 & line \== '') | length(line t1) > 65 then do
        call out pr  substr(line, 3)
        pr = '   ,'
        line = ''
        end
    if force \== 1 then
        line = line',' t1
    return
endProcedure sql4objOut
/*--- -dis db interface ---------------------------------------------*/
/*--- do one -dis db... and insert it into stem  --------------------*/
sqlDisDb: procedure expose m.
parse upper arg o, cc
    do cx=1
        mid = strip(left(m.cc.cx, 10))
        if words(mid) > 1 then
            call err 'bad msgId' mid 'line:' m.cc.cx
        if mid == '' | wordPos(mid, 'DSNT360I DSNT361I DSNT365I') ,
                > 0 then
            iterate
        if mid == 'DSN9022I' then
            if cx = m.cc.0 then
                return m.o.0
            else
                call err 'not at end' cx':' m.cc.cx
        if mid \== 'DSNT362I' then
            call err 'DSNT362I expected not line:' m.cc.cx
        dx = pos('DATABASE =', m.cc.cx)
        sx = pos('STATUS ='  , m.cc.cx)
        if dx < 1 | sx <= dx then
            call err 'bad DSNT362I line' cx':' m.cc.cx
        db = word(substr(m.cc.cx, dx+10), 1)
        sta = strip(substr(m.cc.cx, sx+8))
        call sqlDisDbAdd o, db, ,0, 0, 'DB', sta
        do cx=cx+1 while abbrev(m.cc.cx, '   ')
            end
        if abbrev(m.cc.cx, 'DSNT397I ') then do
            cx = cx + 1
            if \ abbrev(space(m.cc.cx, 1),
                 , 'NAME TYPE PART STATUS ') then
                call err 'NAME TYPE PART STATUS mismatch' cx m.cc.cx
            txNa = pos('NAME', m.cc.cx)
            txTy = pos('TYPE', m.cc.cx)
            txPa = pos('PART', m.cc.cx)
            txSt = pos('STAT', m.cc.cx)
            txEn = verify(m.cc.cx, ' ', 'n', txSt+6)
            if 0 then say 'title' txNa txTy txPa txSt txEn cx m.cc.cx
            cx=cx+1
            do forever
                do while abbrev(m.cc.cx, '----')
                    cx = cx + 1
                    end
                if abbrev(m.cc.cx, '*') then
                    leave
                parse var m.cc.cx sp =(txTy)  ty . =(txPa)  paFr . ,
                                       =(txSt) sta   =(txEn)
                sp = strip(sp)
                if words(sp) \= 1 | wordPos(ty, 'TS IX') < 0 then
                    call err 'bad name or type' cx':'m.cc.cx
                if paFr == '' | paFr == 'L*' then
                    paFr = 0
                else if abbrev(paFr, 'D') | abbrev(paFr, 'L') then
                    paFr = substr(paFr, 2)
                if \ datatype(paFr, 'n') then
                    call err 'part not numeric' cx':'m.cc.cx
                paTo = paFr
                cw = cx
                cx = cx + 1
                if abbrev(m.cc.cx, '    -THRU ') then do
                    parse var m.cc.cx =(txPa)  paTo . =(txSt)
                    if \ datatype(paTo, 'n') then
                        call err '-thru part not numeric' cx':'m.cc.cx
                    cx = cx + 1
                    end
                call sqlDisDbAdd o, db, sp, paFr, paTo, ty, sta
                end
            end
        if m.cc.cx = '******** NO SPACES FOUND' then
            cx = cx + 1
        if abbrev(m.cc.cx, '******* DISPLAY OF DATABASE ') ,
          & word(m.cc.cx,5) == db then
            if word(m.cc.cx,6) == 'ENDED' then
                iterate
            else if word(m.cc.cx,6) == 'TERMINATED' then
                call err 'db display overflow' cx':' m.cc.cx
        call err 'database' db 'ENDED mismatch' cx':' m.cc.cx
        end
endProcedure sqlDbDis

/*--- insert one tuple into tDbState ---------------------------------*/
sqlDisDbAdd: procedure expose m.
    if arg(7) == '' | arg(7) == 'RW' then
         return
parse arg o
    m.o.0 = m.o.0 + 1
    q = o'.'m.o.0
parse arg , m.q.db, m.q.sp, m.q.paFr, m.q.paTo, m.q.ty, m.q.sta
 /* say added q m.q.db'.'m.q.sp':'m.q.paFr'-'m.q.paTo m.q.ty':'m.q.sta*/
    ky = m.q.db'.'m.q.sp
    if symbol('m.o.ky') \== 'VAR' then
        m.o.ky = m.o.0
    return
endProceedure sqlDisDbAdd

/*--- get index in o for db sp part ----------------------------------*/
sqlDisDbIndex: procedure expose m.
parse arg st, d, s, pa
     if symbol('m.st.d.s') \== 'VAR' then
         return 0
     ix = m.st.d.s
     if ix > m.st.0 | d \== m.st.ix.db | s \== m.st.ix.sp then
         return 0
     if pa == '' then
         return ix
     do ix=ix to m.st.0 while d == m.st.ix.db & s == m.st.ix.sp
         if pa < m.st.ix.paFr then
             return 0
         else if pa <= m.st.ix.paTo then
             return ix
         end
     return 0
endProcedure sqlDisDbIndex

/*--- dsn Command, return true if continuation needed ----------------*/
sqlDsnCont: procedure expose m.
parse arg cc, ssid, cmd
    say '???dsnCont' cmd
    cont = sqlDsn(cc, ssid, cmd, 12) <> 0
    if cont then do
        cz = m.cc.0
        cy = cz - 1
        if \ abbrev(m.cc.cy, DSNT311I) ,
                | \ abbrev(m.cc.cz, 'DSN9023I') then
            call err 'sqlDsn rc=12 for' cmd 'out='cz ,
                     '\n'cy'='m.cc.cy'\n'cz'='m.cc.cz
        m.cc.0 = cz-2
        end
    return cont
endProcedure sqlDsnCont
/* copy sqlDiv end   **************************************************/
/* copy db2Cat begin **************************************************/
catTbLastCol: procedure expose m.
parse upper arg cr, tb
    return sql2one( ,
          "select strip(char(colcount)) || ' ' || strip(c.name) one"  ,
              "from sysibm.sysTables t left join sysibm.sysColumns c" ,
                  "on c.tbCreator = t.creator and c.tbName = t.name"  ,
                       "and c.colNo = t.colCount"                     ,
               "where t.creator = '"cr"' and t.name = '"tb"'", ,'')
endProcedure catTbLastCol

catTbCols: procedure expose m.
parse upper arg cr, tb
    if sql2St("select strip(name) name "     ,
          "from sysibm.sysColumns " ,
          "where tbcreator = '"cr"' and tbname='"tb"'",
          "order by colNo", ggSt) < 1 then
        return ''
    res = m.ggst.1.name
    do cx=2 to m.ggst.0
        res = res m.ggst.cx.name
        end
    return res
endProcedure catTbCols

catTbColsTrunc: procedure expose m.
parse upper arg cr, tb, maxL
    if sql2St("select strip(name) name, colType, length, length2"     ,
          "from sysibm.sysColumns " ,
          "where tbcreator = '"cr"' and tbname='"tb"'",
          "order by colNo", ggSt) < 1 then
        return ''
    res = ''
    do cx=1 to m.ggst.0
        ty = m.ggSt.cx.colType
        if pos('LOB', ty) > 0 then
            res = res', substr('m.ggSt.cx.name', 1,' ,
                 min(maxL, m.ggSt.cx.length2)') 'm.ggSt.cx.name
        else if pos('CHAR', ty) > 0 & m.ggSt.cx.length > maxL then
            res = res', substr('m.ggSt.cx.name', 1,' maxL')',
                 m.ggSt.cx.name
        else
            res = res',' m.ggSt.cx.name
        end
    return substr(res, 3)
endProcedure catTbColsTrunc

catIxKeys: procedure expose m.
parse upper arg cr, ix
    sql = "select colSeq, colName, ordering"                          ,
              "from sysibm.sysKeys"                                   ,
               "where ixCreator = '"cr"' and ixName = '"ix"'" ,
               "order by colSeq"
    call sqlQuery 1, sql, '?sq col ord'
    res = ''
    drop d
    do kx=1 while sqlFetch(1, d)
        if m.d.sq \= kx then
            call err 'expected' kx 'but got colSeq' m.d.sq ,
                     'in index' cr'.'ix'.'m.d.col
        res = res || strip(m.d.col) || translate(m.d.ord, '<>?', 'ADR')
        end
    call sqlClose 1
    return res
endProcedure catIxKeys

catColCom: procedure expose m.
parse upper arg fCr, fTb, tCr, tTb
    sql = "select t.name, t.colType, t.nulls, t.""DEFAULT"""        ,
                    ", coalesce(f.nulls, 'new')"                    ,
              "from sysibm.sysColumns t"                            ,
                "left join sysibm.sysColumns f"                     ,
                  "on f.tbCreator = '"fCr"' and f.tbName = '"fTb"'" ,
                    "and f.name = t.name"                           ,
              "where t.tbCreator = '"tCr"' and t.tbName = '"tTb"'"  ,
              "order by t.colNo"
    call sqlQuery 1, sql, 'na ty nu de nn'
    pr = ' '
    do kx=1 while sqlFetch(1)
        /* say kx m..na m..ty m..nu m..de 'nn' m..nn */
        if pos('CHAR', m..ty) > 0 then
            dv = "''"
        else if pos('INT' ,m..ty) > 0 ,
                | wordPos(m..ty, 'REAL FLOAT') > 0 then
            dv = 0
        else if m..ty == 'TIMESTMP' then
            dv = '0001-01-01-00.00.00'
        else if pos('LOB', m..ty) > 0 then
            dv = m..ty"('')"
        else
            dv = '???'
        if m..nu = 'Y' then
            dv = 'case when 1=0 then' dv 'else null end'
        r = '???'
        if m..ty = 'ROWID' then do
            r = '--'
            end
        else if m..nn == 'new' then do
            if m..de = 'Y' then
                r = '--'
            else if m..nu == 'N' then
                r = dv
            else
                r = 'case when 1=0 then' dv 'else null end'
            end
        else do
            if m..nu = 'Y' | (m..nu = m..nn) then
                r = ''
            else
                r = 'coalesce('m..na',' dv')'
            end
        if abbrev(r, '--') then do
            r = ' ' r
            end
        else do
            r = pr r
            pr = ','
            end
        if pos('???', r) > 0 then
            call err 'no default for type' m..ty 'in' tCr'.'tTb'.'m..na
        call out r m..na
        end
    call sqlClose 1
    return
endProcedure catColCom
/* copy db2Cat end   **************************************************/
/* copy sqlO   begin ***************************************************
    sql interface  mit  o und j Anbindung
***********************************************************************/
sqlIni: procedure expose m.
    if m.sqlO_ini == 1 then
        return
    call sqlRxIni
    m.sqlO_ini = 1
    call jIni
    m.sqlO.cursors  = left('', 200)
    call classNew 'n SqlRdr u JRWO', 'm',
        , "jReset m.m.src = arg; m.m.type = arg2;",
        , "jOpen  call sqlRdrOpen m, opt",
        , "jClose call sqlRdrClose m",
        , "jReadO return sqlRdrReadO(m)"
    call classNew 'n SqlResRdr u JRWO', 'm',
        , "jReset m.m.cursor = arg; m.m.type = arg2;",
        , "jOpen  call sqlRdrO2 m" ,
        , "jClose call sqlClose m.m.cursor" ,
        , "jReadO return sqlRdrReadO(m)"
    call classNew 'n SqlRxConnection u', 'm',
        , "sqlQuery  return sqlRxQuery(cx, src, feVa, 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, feVa, 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, feVa, retOK)",
        , "sqlFetch  return sqlCsmFetch(cx, dst, retOk)",
        , "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, feVa, retOk)",
        , "sqlFetch  return sqlCsmFetch(m.cx.cursor, dst, retOk)",
        , "sqlClose  return 0",
        , "sqlUpdate call err 'implement sqlCsmUpdate'"   ,
        , "sqlCall   call err 'implement sqlCsmCall'"
    return 0
endProcedure sqlIni

/*--- connect to DB2 dsnRexx or csm ----------------------------------*/
sqlConnect: procedure expose m.
parse upper arg sys, retOk
    call sqlIni
    if sys == '' then
        sys = sqlDefaultSys()
    if pos('/', sys) <= 0 then do
        call  sqlRxConnect sys
        m.sql_connection = oNew('SqlRxConnection')
        end
    else do
        parse var sys m.sql_csmHost '/' m.sql_dbSys
        m.sql_connection = oNew('SqlCsmConnection')
        end
    return 0
endProcedure sqlConnect

/*--- disconnect from db2 --------------------------------------------*/
sqlDisconnect: procedure expose m.
parse arg retOk
    if m.sql_csmHost == '' then
        call sqlRxDisconnect
    else
        m.sql_csmHost = ''
    m.sql_dbSys = ''
    m.sql_connection = 'notConnected'
    return 0
endProcedure sqlDisonnect

/*--- execute a query from sql, with one resultset -------------------*/
sqlQuery: procedure expose m.
parse arg cx, src, feVa, retOk
    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')
endProcedue sqlUpdate

/*-- 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')
endProcedure sqlCall

/*--- return a free cursor -------------------------------------------*/
sqlGetCursor: procedure expose m.
parse arg rng
    if rng == '' then
        return sqlGetCursorRng(rng, 10, 48)
    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

/*--- execute stmts with options -------------------------------------*/
sqlStmtsOpt: procedure expose m.
parse arg src, opts
    upper opts
    sub = ''
    o = ''
    retOk = ''
    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
            retOk = retOk w
        else
            call err 'bad opt' w 'in opts' opts 'not -sql? -o or subsys'
        end
    return sqlStmts(src, strip(retOk), strip(o))
endProcedure sqlStmtsOpt

/*** execute sql's in a stream (separated by ;)
       opt: 'o' ==> write objects, otherwise fTabAuto
            'sql72' ==> spufi formatting (window 72) else linebreaks */
sqlStmts: procedure expose m.
parse arg sqlSrc, retOk, opt
   dlm = ';'
   isStr = oStrOrObj(sqlSrc, 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 scanSrc 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
       if translate(left(s1, 10)) == 'TERMINATOR' then do
            dlm = strip(substr(s1, 11))
            if length(dlm) \== 1 then
                call scanErr sqlStmts, 'bad terminator' dlm
            iterate
            end
       call outSt(splitNl(sqlTmp, sqlStmt(s1, retOk, opt)))
       end
   if \ isStr then
       call jClose fi
   return 0
endProcedure sqlStmts

/*--- execute a single sql statement --------------------------------*/
sqlStmt: procedure expose m.
parse arg src, retOk, opt
    cx = sqlGetCursor()
    r1 = sqlExecute(cx, src, retOK)
    res = m.sql_HaHi || sqlMsgLine(r1, m.sql.cx.updateCount, src)
    if m.sql.cx.resultSet \== '' then do
        rdr = jReset(oMutate('SQL_RDR.'cx, 'SqlResRdr'), cx)
        if pos('o', opt) > 0 then
            call pipeWriteAll rdr
        else
            call fTabAuto fTabReset(sql_StmtFmt, 1), rdr
        res = sqlMsgLine(m.rdr.rowCount 'rows fetched', , src)
        end
    call sqlFreeCursor cx
    return res
endProcedure sqlStmt

/*--- execute the given sql plus a commit
         until no more rows are updated -----------------------------*/
sqlUpdComLoop: procedure expose m.
parse arg src, retOk, opt
    src = inp2Str(src)
    crs = sqlGetCursor()
    upds = 0
    if retOk == '' then
        retOk = 100
    do coms=0
        cd = sqlExecute(crs, src, retOk)
        if m.sql.crs.updateCount < 1 then do
            call sqlFreeCursor(crs)
            return sqlMsgLine( , upds, src, coms 'commits')
            end
        upds = upds + m.sql.crs.updateCount
        call sqlCommit
        if coms // 20 = 19 then
            say sqlMsgLine(time(), upds, src, (coms+1) 'commits')
        end
endProcedure sqlUpdComLoop

/*--- sql call statement ---------------------------------------------*/
sqlStmtCall: procedure expose m.
parse arg src, retOk, 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.ut_alfDot) 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 fTabAuto 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

/*--- prepare and open cursor
      generate type and fetchList ------------------------------------*/
sqlRdr: procedure expose m.
parse arg src, type
     return oNew('SqlRdr', inp2str(src, '-sql'), type)
endProcedure sqlRdr

sqlRdrOpen: procedure expose m.
parse arg m, opt
    if opt\== m.j.cRead then
        call err 'opt not' m.j.cRead 'sqlRdrOpen('m',' opt')'
    m.m.cursor = sqlGetCursor()
    if m.m.type == '' then
        ff = ''
    else do
        ff = mCat(oClaMet(m.m.type, 'oFlds'), ' ')
        if abbrev(ff, '. ') then
            call err 'sqlRdrOpen implement value for type' m.m.type
        ff = space(translate(ff, ' ', '.'), 1)
        end
    call sqlQuery m.m.cursor, m.m.src, ff
    call sqlRdrO2 m
    return
endProcedure sqlRdrOpen

sqlRdrO2: procedure expose m.
parse arg m
    if m.m.type = '' then do
        ff = mCat('SQL.'m.m.cursor'.COL', ' v, f ')
        m.m.type = classNew('n* SQL u f' ff 'v')
        end
    m.m.rowCount = 0
    return
endProcedure sqlRdrO2

/*--- close sql Cursor -----------------------------------------------*/
sqlRdrClose: procedure expose m.
parse arg m, v
    call sqlClose m.m.cursor
    call sqlFreeCursor m.m.cursor
    m.m.cursor = ''
    return m
endProcedure sqlRdrClose

/*--- read next from cursor, return as object ------------------------*/
sqlRdrReadO: procedure expose m.
parse arg m
    v = oNew(m.m.type)
    if \ sqlFetch(m.m.cursor, v) then
        return mFree(v)
    m.m.rowCount = m.m.rowCount + 1
    return v
endProcedure sqlRdrReadO

/*--- select and write all to stdOut ---------------------------------*/
sqlSel: procedure expose m.
parse arg src, type
     s = sqlRdr(src, type)
     call pipeWriteAll s
     return m.s.rowCount
endProcedure sqlSel
/* copy sqlO   end   **************************************************/
/* copy sqlCsm begin **************************************************/
/*--- send an sql to csm and handle sqlCode --------------------------*/
sqlCsmExe:
parse arg cx, ggSqlStmt, ggRetOk
    sql_HOST =  m.sql_csmhost
    SQL_DB2SSID = m.sql_dbSys
    sql_query = ggSqlStmt
    address tso "CSMAPPC START PGM(CSMASQL)"
    if \ (rc = 0 |  rc = 4) then
        return 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\nsql =' ggSqlStmt
        else
            call errSay 'w}'sqlMsg(sqlCA2rx(sqlCa))
    return sqlCode
endProcedure sqlCsmExe

/*--- execute a query from sql, with one resultset -------------------*/
sqlCsmQuery: procedure expose m.
parse arg cx, sqlSrc, feVa, retOk, dst
    res = sqlCsmExe(cx, sqlSrc, 100 retOk)
    if res < 0 then
        return res
    if dst == '' then
        dst = 'SQL.'cx'.CSMDATA'
    m.dst.0 = 0
    m.dst.laIx = 0
    st = 'SQL.'cx'.COL'
    call sqlRemVars st
    feVa = translate(feVa, ' ', '?')
    if feVa = '' then do kx=1 to sqlD
        call sqlAddVar st, SQLDA_REXXNAME.kx, 1
        end
    else if abbrev(feVa, ':')  then
        return err('fetchVars with : for csm:' feVa)
    else if words(feVa) <> sqlD then
        return err('csm sqlD='sqlD', but' words(feVa) 'vars:' feVa)
    else do kx=1 to words(feVa)
        call sqlAddVar st, word(feVa, kx), 1
        end
    do kx=1 to sqlD
        rxNa = SQLDA_REXXNAME.kx
        cn = m.st.kx
        do rx=1 to sqlRow#
            if substr(sqlIndicator.rx, kx ,1) == 'ff'x then
                m.dst.rx.cn = m.sqlNull
            else
                m.dst.rx.cn = value(rxNa'.'rx)
            end
        end
    m.dst.0 = sqlRow#
    return 0
endProcedure sqlCsmQuery

sqlCsmFetch: procedure expose m.
parse arg cx, dst
    src = 'SQL.'cx'.CSMDATA'
    rx = m.src.laIx + 1
    if rx > m.src.0 then
        return 0
    m.src.laIx = 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 sqlRx  begin ***************************************************
       Achtung: inc generiert sql aus sqlRx, Aenderungen nur in sqlRx|
    sql interface
***********************************************************************/
/*--- initialize sqlRx -----------------------------------------------*/
sqlRxIni: procedure expose m.
    if m.sqlRx_ini == 1 then
        return
    m.sqlRx_ini = 1
    m.sqlNull = '---'
    m.sqlInd = 'sqlInd'
    m.sql_defCurs= 49
    m.sqlCAMsg = 0
    m.sqlSuMsg = 2
    m.sql_dbSys = ''
    m.sql_csmhost = ''
    isInProd = wordPos(sysvar(sysNode), 'RZ2') > 0
    m.sqlRetOK = 'dne' copies('rod', \ isInProd)
    return 0
endProcedure sqlRxIni

/*--- get default db2System ------------------------------------------*/
sqlDefaultSys: procedure expose m.
parse arg
    if sysvar(sysnode) == 'RZ1' then
        return 'DBAF'
    else if sysvar(sysnode) == 'RZ4' then
        return 'DP4G'
    else
        call err 'no default subsys for' sysvar(sysnode)
endProcedure sqlDefaultSys

/*--- 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
    if sys == '' then
        sys = sqlDefaultSys()
    m.sql_dbSys = sys
    ggSqlStmt =  'connect' sys
    address dsnRexx ggSqlStmt
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, ggRetOk, ggSqlStmt)
endProcedure sqlRxConnect

/*--- diconnect from db2 ---------------------------------------------*/
sqlRxDisconnect: procedure expose m.
parse arg retOk
    if m.sql_dbSys == '' then
        return 0
    ggSqlStmt =  'disconnect'
    m.sql_dbSys = ''
    m.sql_csmHost = ''
    address dsnRexx ggSqlStmt
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, retOk, ggSqlStmt)
endProcedure sqlRxDisconnect

/*--- reset sql cursor 'c'cx fields ----------------------------------*/
sqlReset: procedure expose m.
parse arg cx
     m.sql.cx.updateCount = ''
     m.sql.cx.resultSet   = ''
     m.sql.cx.d.sqlD = 'noSqlDA'
     m.sql.cx.i.sqlD = 'noDescInp'
     m.sql.cx.fetchVars = ''
     m.sql.cx.type  = ''
     call sqlRemVars 'SQL.'cx'.COL'
     return
endProcedue sqlReset

/*--- execute a query from sql, with one resultset -------------------*/
sqlRxQuery: procedure expose m.
parse arg cx, src, feVa, retOk
     call sqlReset cx
     src = inp2str(src, '%,%c ')
     s1 = ''
     if pos(left(feVa, 1), '?:') < 1 then
         s1 = 'into :M.SQL.'cx'.D'
     res = sqlExec('prepare s'cx s1 'from :src', retOk)
     if res < 0 then
         return res
     call sqlRxFetchVars cx, feVa
     call sqlExec 'declare c'cx 'cursor for s'cx
     res = sqlExec('open c'cx, retOk)
     if res < 0 then
         return res
     m.sql.cx.updateCount = sqlErrd.3
     m.sql.cx.resultSet = cx
     return res
endProcedure sqlRxQuery

/*--- prepare a query from sql, with one resultset -------------------*/
sqlQueryPrep: procedure expose m.
parse arg cx, src, feVa, retOk
     call sqlReset cx
     src = inp2str(src, '%,%c ')
     s1 = ''
     if feVa == '' | feVa = 'd' then
         s1 = 'into :M.SQL.'cx'.D'
     res = sqlExec('prepare s'cx s1 'from :src', retOk)
     if res < 0 then
         return res
     call sqlRxFetchVars cx, feVa
     call sqlExec 'declare c'cx 'cursor for s'cx
     return sqlExec('describe input s'cx 'into :M.SQL.'cx'.I')
endProcedure sqlQueryPrep

sqlQueryArgs: procedure expose m.
parse arg cx retOk
    do ix=1 to arg()-1
        call sqlDASet cx , 'I', ix, arg(ix+1)
        end
     res = sqlExec('open c'cx 'using descriptor :M.SQL.'cx'.I', retOk)
     if res < 0 then
         return res
     m.sql.cx.updateCount = sqlErrd.3
     m.sql.cx.resultSet = cx
     return res
endProcedure sqlQueryArgs

/*--- fetch next row to m.dst.* at end return false ------------------*/
sqlRxFetch: procedure expose m.
parse arg cx, dst, retOk
    fetCode = sqlExec('fetch c'cx 'into' m.sql.cx.fetchVars, 100 retOk)
    if fetCode == 100 then
        return 0
    if fetCode < 0 then
        return fetCode
    interpret m.sql.cx.fetchCode
    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 sqlExec('execute immediate :src', retOk)
        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, retOk)
        end
    if fun == 'DECLARE'  then do
        if 'GLOBAL' == translate(word(substr(src, bx), 2)) then
            return sqlExec('execute immediate :src', retOk)
        end
    res = sqlExec(src, retOk)
    if wordPos(fun, 'DELETE INSERT UPDATE') > 0 then
        m.sql.cx.updateCount = sqlErrd.3
    return res
endProcedure sqlRxUpdate

/*-- prepare an update -----------------------------------------------*/
sqlUpdPrep: procedure expose m.
parse arg cx, src, retOk
    res = sqlExec('prepare s'cx 'from :src', retOk)
    return sqlExec('describe input s'cx 'into :M.SQL.'cx'.I')
endProcedure sqlUpdPrep

/*-- execute a prepared update with the given arguments --------------*/
sqlUpdArgs: procedure expose m.
parse arg cx retOk
    do ix=1 to arg()-1
        call sqlDASet cx , 'I', ix, arg(ix+1)
        end
    res = sqlExec('execute s'cx 'using descriptor :M.SQL.'cx'.I',
                  , retOk)
    m.sql.cx.updateCount = sqlErrd.3
    return res
endProcedure sqlRxUpdArgs

/*-- execute a query, update or call ---------------------------------*/
sqlExecute: procedure expose m.
parse arg cx, src, retOk
    src = inp2Str(src, '-sql')
    f = translate(word(src, 1))
    bx = pos('(', f)
    if bx > 0 then
        f = left(f, max(1, bx-1))
    m.sql.cx.fun = f
    if f == 'SELECT' | f == 'WITH' | f == '(' 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

/*-- fetch all rows to stem and close --------------------------------*/
sqlFetch2St: procedure expose m.
parse arg cx, dst, retOK
    do sx=1 while sqlFetch(cx, dst'.'sx)
       end
    res = sx-1
    m.dst.0 = sx-1
    call sqlClose cx
    return m.dst.0
endProcedure sqlFetch2St

/*-- execute a query, copy result to stem ----------------------------*/
sql2St: procedure expose m.
parse arg src, dst, feVa, retOK
    cx = m.sql_defCurs
    res = sqlQuery(cx, src, feVa, retOk)
    return sqlFetch2St(cx, dst, retOk)
endProcedure sql2St

/*-- return first column of the only row and close -------------------*/
sqlFetch2One: procedure expose m.
parse arg cx, dst, retNone
    f1 = sqlFetch(cx, dst)
    if f1 then
        f2 = sqlFetch(cx, dst)
    call sqlClose cx
    if \ f1 then
        if retNone \== '' then
            return substr(retNone, 2)
        else
            call err 'sqlFetch2One: no row returned'
    else if f2 then
        call err 'sqlFetch2One: more than 1 row'
    c1 = m.sql.cx.col.1
    return m.dst.c1
endProcedure sqlFetch2One

/*-- fxecute a query and return first row of the only colun
           if > 1 row fail, if 0 rows return arg(3) or fail ----------*/
sql2One: procedure expose m.
parse arg src, dst, feVa, retOk, retNone
    cx = m.sql_defCurs
    call sqlQuery cx, src, feVa, retOk
    return sqlFetch2One(cx, dst, retNone)
endProcedure sql2One

/*--- describe table and return sqlDA --------------------------------*/
sqlDescribeTable: procedure expose m.
parse upper arg tb, force
    if force == 1 | \ datatype(m.sql.table.tb.sqlD, 'n') then
         call sqlExec 'describe table :tb into :M.SQL.TABLE.'tb
    return 'SQL.TABLE.'tb
endProcedure sqlDescribeTable

/*--- return select column list for table tb
      omit Blobs (blobMax < 0) or substr(blob, 1, blobMax) ----------*/
sqlColList: procedure expose m.
parse arg tb al, blobMax
    sd = sqlDescribeTable(tb)
    bs = ''
    lst = ''
    if al \== '' & right(al, 1) \== '.' then
        al = al'.'
    do sx=1 to m.sd.sqld
        if wordPos(m.sd.sx.sqlType, '404 405 408 409 412 413') < 1 then
            lst = lst',' al || m.sd.sx.sqlName
        else do
            bs = bs m.sd.sx.sqlName
            if blobMax >= 0 then
                lst = lst', length('al || m.sd.sx.sqlName')' ,
                                          m.sd.sx.sqlName'Len' ,
                     || ', substr('al  || m.sd.sx.sqlName ,
                     || ', 1,' blobMax')' m.sd.sx.sqlName
            end
        end
    m.sd.colList = substr(lst, 3)
    m.sd.blobs = strip(bs)
    return substr(lst, 3)
endProcedure sqlColList

/*--- use describe output to generate column names,
        ''         use names from ca (rexxified)
        nms+       use names, check ca for null values
        ?('?'?nm)+ use names, check for null if preceeded by ?
        :...       use string as is
                fetchVariables and sqlNull names ---------------------*/
sqlRxFetchVars: procedure expose m.
parse arg cx, src, cd
    m.sql.cx.fetchCode = cd
    st = 'SQL.'cx'.COL'
    call sqlRemVars st
    if abbrev(src, ':') then do
        m.sql.cx.fetchVars = src
        return
        end
    m.sql.cx.fetchVars = ''
    vrs = ''
    sNu = ''
    if abbrev(src, '?') then do
        r = substr(src, 2)
        do wx=1 to words(src)
            cn = word(src, wx)
            if abbrev(cn, '?') then
                call sqlRexxAddVar substr(cn, 2), 0, 1
            else
                call sqlRexxAddVar cn, 0, 0
            end
        end
    else if src <> '' then do kx=1 to words(src)
        cn = word(src, kx)
        call sqlRexxAddVar cn, 0, m.sql.cx.d.kx.sqlType // 2
        end
    else do kx=1 to m.sql.cx.d.sqlD
        call sqlRexxAddVar m.sql.cx.d.kx.sqlName, 1 ,
                       , m.sql.cx.d.kx.sqlType // 2
        end
    m.sql.cx.fetchVars = substr(vrs, 3)
    m.sql.cx.fetchCode = sNu cd
    return
endProcedure sqlRxFetchVars

sqlRexxAddVar:
parse arg nm, nicify, hasNulls
     nm = sqlAddVar(st, nm, nicify)
     if \ hasNulls then
          vrs = vrs', :m.dst.'nm
     else do
         vrs = vrs', :m.dst.'nm ':m.dst.'nm'.sqlInd'
         sNu = sNu 'if m.dst.'nm'.sqlInd < 0 then' ,
                    'm.dst.'nm '= m.sqlNull;'
         end
    return
endSubroutine sqlRexxAddVar

sqlCol2kx: procedure expose m.
parse arg cx, nm
    if symbol('M.SQL.CX.COL.NM') \== 'VAR' then
        return ''
    kx = m.sql.cx.col.nm
    if m.sql.cx.col.kx \== nm then
        call err 'sqlCol2kx' nm '==>' kx 'but' m.sql.cx.col.kx
    return kx
endProcedure sqlCol2kx

sqlRemVars: procedure expose m.
parse arg st
    if symbol('m.st.0') == 'VAR' then do
        do sx=1 to m.st.0
            nm = m.st.sx
            drop m.st.nm m.st.sx
            end
        end
    m.st.0 = 0
    return
endProcedure sqlRemVars

sqlAddVar: procedure expose m.
parse arg st, sNa, nicify
    sx = m.st.0 + 1
    if 1 | nicify then do
        cx = verifId(sNa)
        if cx > 0 then /* avoid bad characters for classNew| */
            sNa = left(sNa, cx-1)
        upper sNa
        if sNa == '' | symbol('m.st.sNa') == 'VAR' then
            sNa = 'COL'sx
        end
    m.st.0 = sx
    m.st.sx = sNa
    m.st.sNa = sx
    return sNa
endProcedure sqlAddVar

/*--- 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

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

/*--- execute sql thru the dsnRexx interface -------------------------*/
sqlExec: /* no procedure, to keep variables sql... */
    parse arg ggSqlStmt, ggRetOk
    m.sql_HaHi = ''
    address dsnRexx 'execSql' ggSqlStmt
    /* say 'sqlCode' sqlCode 'rc' rc 'for' ggSqlStmt ggNo */
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, ggRetOk, ggSqlStmt)
endProcedure sqlExec

sqlErrorHandler: procedure expose m. ,
                   sqlCode sqlErrMc sqlErrP sqlErrD. sqlWarn. sqlState
parse arg drC, retOk, verb rest
    if drC == 0 then
        return 'return 0'
    if wordPos(drC, '1 -1') < 0 then
        return "call err 'dsnRexx rc" drC"' sqlmsg()"
    if pos('-', retOK) < 1 then
        retOK = retOk m.sqlRetOk
    if pos('*', retOK) > 0 | wordPos(sqlCode, retOK) > 0 then do
        if sqlCode < 0 & pos('say', retOK) > 0 then
            return "call outSt errMsg(' }'sqlMsg()); return" sqlCode
        else
            return "return" sqlCode
        end
    upper verb
    if verb == 'DROP' then do
        if (sqlCode == -204 | sqlCode == -458) ,
                       & wordPos('dne', retok) > 0 then
            return 'return' sqlCode
        if sqlCode = -672 & wordPos('rod', retok) > 1 then do
            hahi = m.sql_HaHi ,
                 || sqlMsgLine(sqlCode, 'tb='sqlErrMc ,verb rest)'\n'
            call sqlExec 'alter table' SqlErrMc ,
                    'drop restrict on drop'
            hahi = hahi || m.sql_HaHi ,
                        || sqlMsgLine(sqlCode, , ggSqlStmt)'\n'
            call sqlExec verb rest
            m.sql_HaHi = hahi
            return 'return' sqlCode
            end
        end
    if drC < 0 then
         return "call err sqlmsg(); return" sqlCode
    if (sqlCode <> 0 | sqlWarn.0 ^==' ') & pos('w',retOK) < 1 then
        return "call outSt errMsg(' }'sqlMsg()); return" sqlCode
    return 'return' sqlCode
endProcedure sqlErrorHandler

sqlMsgLine: procedure expose m. sqlErrD.
parse arg res, cnt, src, plus
    verb = translate(word(src, 1))
    if datatype(res, 'n') then
        res = 'sqlCode' res
    if cnt \== '' then do
        res = res',' cnt
        vx = wordPos(translate(word(src,1)), 'DELETE INSERT UPDATE')
        if datatype(cnt, 'n') then
            if vx > 0 then
               res = res 'rows' word('deleted inserted updated', vx)
            else if cnt <> 0 then
                res = res 'rows updated'
        end
    if plus \== '' then
        res = res',' plus
    if abbrev(res, ', ') then
        res = substr(res, 3)
    if src \== '' then do
        ll = 75 - length(res)
        aa = strip(src)
        if length(aa) > ll then
            aa = space(aa, 1)
        if length(aa) > ll then
           aa = left(aa,  ll-3)'...'
        res = res':' aa
        end
    return res
endProcedure sqlMsgLine

/*--- 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

/*--- 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
    ggFrom = 'ggSqlStmt'
    ggW1 = translate(word(ggSqlStmt, 1))
    ggW2 = translate(word(ggSqlStmt, 2))
    if ggW1 == 'PREPARE' then
        ggFrom = sqlHostVarFind(ggSt, 'FROM')
    else if ggW1 ggW2 == 'EXECUTE IMMEDIATE' then
        ggFrom = sqlHostVarFind(ggSt, 1)
    ggPos = 0
    if datatype(sqlErrd.5, 'n') & sqlErrd.5 > 0 then do
        ggPos = sqlErrd.5
        ggRes = ggRes || sqlMsgSrcPos(value(ggFrom), sqlErrd.5)
        end
    if ggFrom == 'ggSqlStmt' then do
        ggRes = ggRes'\nsql =' sqlShorten(ggSqlStmt, 2000, ggPos)
        end
    else do
        ggRes = ggRes'\nsql =' sqlShorten(value(ggFrom), 2000, ggPos)
        ggRes = ggRes'\nstmt =' sqlShorten(ggSqlStmt, 2000)
        end
    ggPref = '\nwith'
    do ggXX=1 to m.ggSt.0
        if ggFrom = m.ggVa.ggXX then
            iterate
        ggRes = ggRes || ggPref m.ggBe.ggXX ':'m.ggVa.ggXX ,
                      '=' sqlShorten(value(m.ggVa.ggXX), 210)
        ggPref = '\n    '
        end
    if m.sqlSuMsg == 1 | (m.sqlSuMsg == 2 & m.sql_csmhost \== '') then
        ggRes = ggRes'\nsubsys =' m.sql_dbSys ,
             || ', host =' m.sql_csmhost
    return  ggRes
endSubroutine sqlMsg

sqlShorten: procedure expose m.
parse arg txt, maxL, pos
    if length(txt) <= maxL then
        return txt
    if \ datatype(pos, 'n') | pos < 1 then
        pos = 1
    ex = pos + min(60, maxL%7)
    if ex <= maxL - 4 then
        return left(txt, maxL-4) '...'
    if ex >= length(txt) then
        return left(txt, 67) '...\n'substr(txt, length(txt)-maxL+72)
    else
        return left(txt, 67) '...\n'substr(txt, ex-maxL+76, maxL-75) ,
                       '...'
endProcedure sqlShorten
/*--- 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 nop
    else if rc = 4 then say 'warn linkPgm dsnTiar rc' rc 'sqlCa' ca
    else                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: procedure expose m. ,
                   sqlCode sqlErrMc sqlErrP sqlErrD. sqlWarn. sqlState
    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 = 1
    do cnt
        cx = pos(':', src, cx) + 1
        if cx < 2 then
           leave
        if pos(substr(src, cx, 1), m.ut_rxN1) > 0 then
            iterate
        ex = verify(src, m.ut_rxDot, 'n', cx)
        if ex < 1 then
            m.st.var.sx = substr(src, cx)
        else
            m.st.var.sx = substr(src, cx, ex - cx)
        if m.st.var.sx == '' | length(m.st.var.sx) > 100 then
            iterate
                       /* 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.ut_alfa) > 0
            end
        if bB < bE & bB >= 0 then
            m.st.bef.sx = substr(src, bB+1, bE-bB)
        else
            m.st.bef.sx = ''
        sx = sx + 1
        end
    m.st.0 = sx-1
    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 sqlRx  end   **************************************************/
/* copy csi begin    ***************************************************
     csi interface: see dfs managing catalogs chapt. 11
         returncode/reason see message IDC3009I
**********************************************************************/
/*--- specify dsn mask and fields to start a csi catalog search --------
      arguments:
          m       objectPointer
          dsnMask specifies the dsns with wildcards:
              %  1 character
              *  0 - n character in one level
              ** 0 - n levels
          fields a (space separated) list of field names -------------*/
csiOpen: procedure expose m.
parse arg m, dsnMask, fields
    m.m.fld.0 = words(fields)
    ffix = d2c(m.m.fld.0, 2)
    do x=1 to m.m.fld.0
        m.m.fld.x = translate(word(fields, x))
        ffix = ffix || left(m.m.fld.x, 8)
        end
    if dsnMask \== '' & right(dsnMask, 1) \== ' ' ,
          & pos('*', dsnMask) < 1 & length(dsnMask) < 42 then
        dsnMask = dsnMask'.**'
    m.m.filt = left(dsnMask, 149) ,
             || left('Y', 3) ,        /* resume offset 149      */
             || ffix                  /* csiNumEn offset 152    */

    WORKLEN = 1024 * 64
    m.m.work = D2C(WORKLEN,4) || COPIES('00'X,WORKLEN-4)
    m.m.pos = workLen + 1
    return
endProcedure csiOpen

/*--- put the next dsn into m.o and m.o.* (for other fields)
      return 1 if next dsn exists 0 otherwise ------------------------*/
csiNext: procedure expose m.
parse arg m, o
    usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET AMOUNT OF WORK AREA USED */
    px = m.m.pos
    do forever
        if px > usedL then do
            if substr(m.m.filt, 150, 1) \== 'Y' then do
                m.m.pos = px
                m.o = ''
                return 0
                end
            reason = left('', 4)
            ADDRESS LINKPGM 'IGGCSI00  reason m.'m'.filt  m.'m'.work'
            if rc == 0 & substr(reason, 3, 2) == '0000'x then
                nop
            else if rc == 4 & substr(reason, 3, 2) == '0464'x then
                say 'data set entry with error'
            else
                call err 'call csi returns' rc,
                             'rc' c2d(substr(reason, 4,1)),
                             'reason' c2d(substr(reason, 3,1)),
                             'module' substr(reason, 1,2)
            usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET WORK AREA USED */
            numFD = C2D(SUBSTR(m.m.work,13,2)) /* no flds + 1 */
            if numFd <> m.m.fld.0 + 1 then
                call err 'csiNumFd' numFd 'not' m.m.fld.0 '+' 1
            px = 15
            iterate
            end
        eType =  substr(m.m.work, px+1, 1)
        m.o = strip(substr(m.m.work, px+2, 44), 't')
        flag = substr(m.m.work, px, 1)
        /* say 'eType' eType m.o 'flag' c2x(flag) */
        if eType == '0' then do
            if flag \== '00'x & flag \== '40'x then
                call err 'flag' c2x(flag) 'for catalog' m.o
            px = px + 50    /* length of catalog entry  */
            iterate
            end
        else do
            if \ abbrev(x2b(c2x(flag)), '101') then
                call err 'call csi entry flag' x2b(c2x(flag)),
                             'rc' c2d(substr(m.m.work, px+49,1)),
                             'reason' c2d(substr(m.m.work, px+48,1)),
                             'module' substr(m.m.work, px+46, 2),
                             'for entry' m.o,
                             'see qw IDC3009I'
            py = px + 46
            tl = c2d(substr(m.m.work, py, 2))
            pl = py + 4
            pf = py + m.m.fld.0 * 2 + 4
            do fx = 1 to m.m.fld.0
                fi = m.m.fld.fx
                fl = c2d(substr(m.m.work, pl, 2))
                m.o.fi = substr(m.m.work, pf, fl)
                if fi = 'MGMTCLAS' then
                    m.o.fi = substr(m.o.fi, 3, c2d(left(m.o.fi ,2)))
                else if wordPos(fi, 'COMUDSIZ NOBYTTRK') > 0 then
                    m.o.fi = utc2d(m.o.fi)
                pf = pf + fl
                pl = pl + 2
                end
            if py + tl <> pf then
                call err 'length mismatch for entry' m.o
            m.m.pos = pf
            return 1
            end
        end
endProcedure csiNext
/*--- if dsn is arcived return 'arcive'
      if dsn is tape return 'tape'
      otherwise return managment class ------------------------------*/
csiArcTape: procedure expose m.
parse arg vo, cl, dt, dsn
        if vo = '' then
            say err '||| no volume for dsn' dsn
        else if vo = 'ARCIVE' then
            res = 'arcive'
        else if cl <> '' then
            res = cl
        else if abbrev(vo, 'SHR') then
            res = 'SHR'
        else
            res = 'tape'
        if   res = 'arcive' then
            return res
      /*if   abbrev(res, 'ar') \= abbrev(dt, '00'x) , */
        if   abbrev(res, 'ta') \= abbrev(c2x(left(dt, 1)), '7') ,
           | (left(res, 1) >= 'A') \= abbrev(dt, '30'x) then
           say '||| mismatch cl' cl 'vo' vo 'dt' c2x(dt) 'dsn' dsn
        return res
endProcedure csiArcTape
/* copy csi end ******************************************************/
/* copy csm begin ******************************************************
    interface to csm,
        it is integrate with adrTso, eg. dsnAlloc , 'RZ3/..' uses csm
***********************************************************************/
/*--- execute a single csmExec command ------------------------------*/
adrCsm:
    if wordPos(translate(word(arg(1), 1)), 'COPY MBRLIST') > 0 then
       ggTO = ''
    else if symbol('m.csm_timeOut') == 'VAR' then
        ggTO = 'timeout('m.csm_timeOut')'
    else
        ggTO = 'timeout(30)'
    ggStart = time()
    if adrTso('csmExec' arg(1) ggTO, '*') == 0 then
        return 0
    if pos('CSMXC43E NO CONNECTION TO S', m.tso_trap) > 0 then
               /* CSMXC43E NO CONNECTION TO SYSPLEX:RR2 AVAILABLE */
        m.csm_err = 'noConn'
    else if pos('IKJ56225I', m.tso_trap) > 0             ,
               & ( pos('ALREADY IN USE', m.tso_trap) > 0 ,
                 | pos('CATED TO ANOTH', m.tso_trap) > 0) then
               /* 5 IKJ56225I DATA SET A540769.TMP.TEXT ALREADY IN USE,
                  6 IKJ56225I DATA SET IS ALLOCATED TO ANOTHER JOB */
        m.csm_err = 'inUse'
    else
        m.csm_err = ''
    if pos('*', arg(2)) > 0 then
        return m.tso_rc
    else if wordPos(m.tso_rc, arg(2)) > 0 then
        return m.tso_rc
    else
        call err strip('csmExec' m.csm_err) 'rc='m.tso_rc ,
            '\nstmt='subWord(m.tso_stmt, 2) m.tso_trap ,
            '\nend of csmExec, time='ggStart '-' time()
endProcedure adrCsm

csmDel: procedure expose m.
parse arg rz, dsn
    if dsnGetMbr(dsn) == '' then do
        call adrCsm "allocate system("rz") dataset('"dsn"')" ,
                         "disp(del) ddname(del1)"
        call adrTso 'free dd(del1)'
        end
    else do
        rr = adrCsm("mDelete system("rz") dataset('"dsnSetMbr(dsn)"')",
                          "member("dsnGetMbr(dsn)")", 8)
        if rr <> 0 then
            if pos('CSMEX77E Member:', m.tso_trap) < 1 ,
              | pos(' not found', m.tso_trap) < 1 then
                  call err 'rc='rr 'csm mDelete' rz'/'dsn':'m.tso_trap
        end
    return
endProcedure csmDel

csmCopy: procedure expose m.
parse arg aFr, aTo, retOk
    if dsnGetMbr(csnTo) \= '' & dsnGetMbr(csnTo) \= '' then do
        if dsnGetMbr(csnFr) <> dsnGetMbr(csnTo) then
            call err 'member rename' csnFr 'to' csnTo
        csnTo = dsnSetMbr(csnTo)
        end
    fr = csmSysDsn(aFr)
    frMbr = dsnGetMbr(fr)
    frDD = tsoDD('csmFr*', 'a')
    to = csmSysDsn(aTo)
    toMbr = dsnGetMbr(to, '=')
    toDD = tsoDD('csmTo*', 'a')
    call csmAlloc dsnSetMbr(fr) frDD 'shr'
    if frMbr == '' & m.tso_ddDsOrg.frDD == 'PO' then
        if toMbr \== '=' then
            call err 'csmCopy from' fr'(*) to ps' to
        else
            frMbr = '*'
    if frMbr == '' & (toMbr \== '' & toMbr \== '=') then
        psOrLib = 'dsorg(po) dsntype(library)'
    else if frMbr \== '' & toMbr == '' then
        psOrLib = 'dsorg(ps)'
    else
        psOrLib = ''
    call csmAlloc dsnSetMbr(to) toDD 'shr ::D'frDD psOrLib
    c = 'indd('frDD') outDD('toDD')'
    if frMbr \== '*' then do
        if frMbr \== '' then
            c = c 'member('frMbr')'
        if toMbr \== '' & toMbr \== '=' then
            c = c 'newName('toMbr')'
        call adrCsm 'copy' c
        end
    else do
        call adrCsm "mbrList ddName("frDD") index(' ') short"
        do mx=1 to mbr_mem#
            call adrCsm 'copy' c 'member('mbr_name.mx')'
            end
        end
    call tsoFree frDD toDD
    return
endProcedure csmCopy

csmAlloc: procedure expose m.
parse arg dsn dd disp rest ':' nn, retRc
    sys = ''
    a2 = ''
    upper dd disp
    m.tso_dd.dd = csmSysDsn(dsn)
    parse var m.tso_dd.dd sys '/' dsn
    if disp = '' then
        disp = 'shr'
    a1 = "SYSTEM("sys") DDNAME("dd")"
    if dsn == 'INTRDR' then do
        a1 = a1 'sysout(T) writer(intRdr)'
        end
    else do
        if dsn <> '' then do
            a1 = a1 "DATASET('"dsnSetMbr(dsn)"')"
            mbr = dsnGetMbr(dsn)
            if mbr <> '' then
                a1 = a1 'MEMBER('mbr')'
            end
        if abbrev(disp, 'SYSOUT(') then
            a1 = a1 disp
        else
            a1 = a1 "DISP("disp")"
        end
    nAtts = wordPos(disp, 'NEW MOD CAT') > 0 & nn \== ''
    if nAtts then
        rest = dsnCreateAtts( , nn) rest
    cx = pos(' UCOUNT(', ' 'translate(rest))
    if cx > 0 then do
         rest = left(rest, cx) || 'nitCnt(' || substr(rest, cx+7)
         say '???uCount ==>' rest
         end
    cx = pos(' RECFM(', ' 'translate(rest))
    if cx > 0 then do
        cy = pos(')', rest, cx)
        rest = left(rest, cx+5) || space(substr(rest,cx+6,cy-cx-6), 0) ,
                                || substr(rest,cy)
        say '???recfm ==>' rest
        end
    cx = pos(' CYLINDERS ', ' 'translate(rest)' ')
    if cx > 0 then do
        rest = delStr(rest, cx+8, 1)
        say '???cylinders ==>' rest
        end
    cx = pos(' CYL ', ' 'translate(rest)' ')
    if cx > 0 then do
        rest = insert('inder', rest, cx+2)
        say '???cyl ==>' rest
        end
    if retRc <> '' | nAtts | nn == '' then do
        alRc = adrCsm('allocate' a1 rest, retRc)
        m.tso_ddDsOrg.dd = subsys_dsOrg
        return alRc
        end
    alRc = adrCsm('allocate' a1 rest, '*')
    if alRc = 0 then
        return 0
    say 'csmAlloc rc' alRc 'for' a1 rest '...trying to create'
    call csmAlloc m.tso_dd.dd dd 'CAT' rest ':'nn
    call adrTso 'free  dd('dd')'
    return adrCsm('allocate' a1 rest)
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

csmLikeAtts: procedure expose m.
parse upper arg rz '/' dsn
    lc = adrCsm('dslist system('rz') dsnMask('dsn')')
    if stemsize <> 1 then
        call err 'csmLikeAtts stemSize='stemsize 'for dsn='dsn
    if abbrev(dsOrg.1, 'PO') then
        r = 'dsorg(po) dsnType(library)'
    else
        r = 'dsorg('dsOrg.1')'
    r = r 'mgmtClas('mgmtClas.1')'                       ,
       /* 'dataClas('dataClas.1')'   */                  ,
          'recFM('strip(translate('1 2 3', recFm.1, '123'))')'  ,
          'lRecl('lRecl.1')'                         ,
          'space('tracksused.1','  tracks.1') tracks'
    /*    if \ datatype(tracksused.1, 'n') then do
              if \ datatype(tracks.1, 'n') then
                  r = r 'space('tracks.1',' tracks.1')'
              if \ datatype(tracks.1, 'n') then
                  tracks.1 = tracksUsed.1   */

    say '???csmLike' rz'/'dsn '==>' r
    return r
endProcedure csmLikeAtts
/*--- execute a rexx (under tso) in another rz
           here we use rexx TPSYSIKJ which was written for
           jcl procedure RM@IKJ01
arguments
rz   which rz to run rexx
proc the (remote) procedure library to use
opt  options
cmd  the tso command to execute
----------------------------------------------------------------------*/
/*--- execute a rexx (under tso) in another rz
          directly (without TPSYSIKJ) --------------------------------*/
csmExRx: procedure expose m.
parse arg rz, proc, opt, cmd
    pStem = opt
    if pStem = '' then
        pStem ='CSMEXRX'
    do cx=1 to (length(cmd)-1) % 68
        cmd.cx = substr(cmd, 68*cx-67,68)'-'
        end
    cmd.cx = substr(cmd, 68*cx-67)
    cmd.0 = cx
    timeout = 11
    if 0 then do
        call adrTso  'free ed(rmtSys)'  ,'*'
        call tsoFree tsoDD(rmtsPrt, 'a')
        call adrTso  'free dd(rmtsIn)','*'
        call adrTso  'free dd(sysproc)' ,'*'
        end
    call dsnAlloc rz"/"proc "dd(rmSyPro) rmtDDn(sysProc)"
    call dsnAlloc rz"/tmp.tsin new dd(rmTsIn) rmtDdn(sysTsIn) ::f "
    call tsoOpen rmTsIn, 'w'
    call writeDD rmTsIn, cmd.
    call tsoClose rmtsin
    call dsnAlloc rz"/tmp.prt new dd(rmtsprt) rmtDdn(sysTsPrt)",
                    "::f133"
    call dsnAlloc rz"/tmp.rmt new dd(rmtSys) timeout("timeout")"
    call adrtso "csmappc start pgm(csmexec)" ,
           "parm('select tsocmd(''csmappc allocate plu(*.rmtSys)" ,
                 "tpname(sysikj) dealloc '')')",
           "timeout("timeOut")", '*'
    if rc <> 0 | appc_rc <> 0 then do
        ee = 'csm tso exec rc='rc 'appc_rc='appc_rc
        say ee
        say '  rexx rz='rz 'proc='proc 'opt='opt
        say '  cmd='cmd
        call csmappcRcSay ggTsoCmd
        call readDD 'rmTsPrt', 'M.'pStem'.'
        call tsoClose rmtsPrt
        say m.pStem.0 'tso output lines'
        do px=1 to m.pStem.0
            say ' ' strip(m.pStem.px, 't')
            end
        call err ee
        end
    if opt <> '' then do
        call readDD 'rmTsPrt', 'M.'pStem'.'
        call tsoClose rmtsPrt
        end
    call tsoFree rmSyPro rmtsPrt rmtSys rmtsIn
    return
 endProcedure csmExRx

/*--- sys the re and result variables from csmAppcRc -----------------*/
csmappcRcSay: procedure expose appc_rc appc_reason appc_msg. ,
           appc_state_c appc_state_f
 parse arg cmd
     say 'rc='appc_rc 'reason='appc_reason ,
         'state_c='appc_state_c appc_state_f
     say '  for' cmd
     do ix=1 to appc_msg.0
         say ' ' appc_msg.ix
         end
     return appc_rc
 endProcedure csmappcRcSay

/*--- execute a single csmAppc command ------------------------------*/
csmAppc:
    return adrTso('csmAppc' arg(1), arg(2))
endProcedure csmAppc
/* copy csm end *******************************************************/
/* copy timing begin *************************************************/
timing: procedure expose m.
parse arg typ, c2, txt
    e1 = time('E')
    c1 = strip(sysvar('syscpu'))
    s1 = sysvar('syssrv')
    if typ \== '' then do
        if symbol('m.timing_ela') \== 'VAR' then
            call err 'timing('typ',' c2',' txt') ohne ini'
        if symbol('m.timing.typ.ela') \== 'VAR' then do
            m.timing.typ.ela = 0
            m.timing.typ.cpu = 0
            m.timing.typ.su  = 0
            m.timing.typ.cnt = 0
            m.timing.typ.cn2 = 0
            if symbol('m.timing_types') == 'VAR' then
                m.timing_types = m.timing_types typ
            else
                m.timing_types = typ
            if symbol('m.timing_say') \== 'VAR' then
                m.timing_say = 0
            end
        m.timing.typ.ela = m.timing.typ.ela + e1 - m.timing_ela
        m.timing.typ.cpu = m.timing.typ.cpu + c1 - m.timing_cpu
        m.timing.typ.su  = m.timing.typ.su  + s1 - m.timing_su
        m.timing.typ.cnt = m.timing.typ.cnt + 1
        if c2 \== '' then
           m.timing.typ.cn2 = m.timing.typ.cn2 + c2
        end
    m.timing_ela = e1
    m.timing_cpu = c1
    m.timing_su  = s1
    if m.timing_say then
        say left(typ, 10)right(c2, 10) 'ela='e1 'cpu='c1 'su='s1 txt
    return
endProcedure timing

timingSummary: procedure expose m.
    say 'timing summary' time()
    do tx = 1 to words(m.timing_types)
        typ = word(m.timing_types, tx)
        say left(typ, 10)right(m.timing.typ.cnt,  7)       ,
                      || right(m.timing.typ.cn2,  7)       ,
                         'cpu='right(m.timing.typ.cpu, 10) ,
                         'su='right(m.timing.typ.su, 10)
        end
    return
endProcedure timingSummary
/* copy timing end ***************************************************/
/* copy ii begin ********* Installation Info *************************/
iiini: procedure expose m.
    if m.ii_ini == 1 then
        return
    m.ii_ini = 1
    m.ii_rz = ''
    m.ii.rzC = ''
    i = 'RZ1 1 S1 DBTF T DTF DVTB V DTB DBOC C DOC' ,
        'RZ2 2 S2 DBOF F DOF DVBP P DBP DP2G Q DP2' ,
        'RR2 R R2 DBOF F DOF DVBP P DBP DP2G Q DP2' ,
        'RZ4 4 S4 DBOL O DOL DP4G U DP4' ,
        'RZX X X2 DE0G E DE0 DEVG M DEV DPXG I DPX DX0G X DX0' ,
        'RZY Y Y2 DE0G E DE0 DEVG M DEV DPYG Y DPY' ,
        'RZZ Z Z2 DE0G E DE0 DEVG M DEV DPZG N DPZ'
    m.ii_rz = ''
    m.ii_rzC = ''
    do wx=1 by 3 to words(i)
        parse value subWord(i, wx, 3) with w1 w2 w3
        if abbrev(w1, 'R') & length(w1) == 3 then do
           rz = w1
           m.ii_DbSys.rz = ''
           m.ii_rz = strip(m.ii_rz rz)
           m.ii_rzC = m.ii_rzC || w2
           call iiA1 ii_sys2rz, w3, rz
           call iiA1 ii_rz2c, rz, w2
           end
        else if abbrev(w1, 'D') & length(w1) == 4 then do
           m.ii_DbSys.rz = strip(m.ii_DbSys.rz w1)
           call iiA1 ii_db2c, w1, w2
           call iiA1 ii_mbr2db, w3, w1
           end
        else
            call err 'bad w1' w1 w2 w3
        end
    return
endProcedure iiIni

iiA1: procedure expose m.
parse arg st, key ,val
    if symbol('m.st.key') \== 'VAR' then
        m.st.key = val
    else if m.st.key \== val then
        call err 'already <> defined' st'.'key'='m.st.key 'val='val
    return
endProcedure iiA1

iiMbr2DbSys: procedure expose m.
parse upper arg mbr
    return iiLazy(ii_mbr2db, left(mbr, 3), 'member')

iiRz2C: procedure expose m.
parse upper arg rz
    return iiLazy(ii_rz2c, rz, 'rz')

iiRz2Dsn: procedure expose m.
parse upper arg rz
    return overlay('Z', rz, 2)

iiDBSys2C: procedure expose m.
parse upper arg db
    return iiLazy(ii_db2c, db, 'dbSys')

iiSys2RZ: procedure expose m.
parse upper arg sys
    return iiLazy(ii_sys2rz, left(sys, 2), 'sys')

iiLazy: procedure expose m.
parse arg st, key, txt
    if symbol('m.st.key') == 'VAR' then
        return m.st.key
    if m.ii_ini == 1 then
       return err('no' txt'='key 'in ii' st)
    call iiIni
    return iiLazy(st, key, txt)
endProcedure iiRz2C
/* copy ii end   ********* Installation Info *************************/
/* 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 tsoOpen grp, 'R'
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 tsoClose 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 m.tso_stmt, ggRet
    call outtrap m.tso_trap.
    address tso m.tso_stmt
    m.tso_rc = rc
    call outtrap off
    if m.tso_rc == 0 then
        return 0
    m.tso_trap = ''
    do ggXx=1 to min(7, m.tso_trap.0)
        m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
        end
    if m.tso_trap.0 > 7 then do
        if m.tso_trap.0 > 14 then
            m.tso_trap = m.tso_trap'\n............'
        do ggXx=max(8, m.tso_trap.0-6) to m.tso_trap.0
            m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
            end
        end
    if ggRet == '*' then
        return m.tso_rc
    else if wordPos(m.tso_rc, ggRet) > 0 then
        return m.tso_rc
    else
        call err 'adrTso rc=' m.tso_rc 'stmt='m.tso_stmt m.tso_trap
endSubroutine 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 arg(2)
     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:  tsoOpen...'R', readDD*,  tsoClose
        write: tsoOpen...'W', writeDD*, tsoClose

        readDD returns true if data read, false at eof
        do not forget that open is mandatory to write empty file|
***********************************************************************/

/*--- open dd for read (rw='R') or write (rw='W') --------------------*/
tsoOpen: procedure expose m.
parse upper arg dd, rw
    return adrTso('execio' 0 'disk'RW dd '(open)')
return /* end tsoOpen */

/*--- close dd -----------------------------------------------------*/
tsoClose: procedure expose m.
    parse upper arg dd
    return adrTso('execio 0 diskR' dd '(finis)')
endProcedure tsoClose

/*--- 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 */

/*--- 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

/*--- 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
    parse value dsnAlloc('dd('m.m.dd')' m.m.dsn) with m.m.dd m.m.free
    call tsoOpen m.m.dd, 'R'
    return m
endProcedure readDDNxBegin

/*--- return the stem of the next line, or '' at end -----------------*/
readNx: procedure expose m.
parse arg m
    m.m.cx = m.m.cx + 1
    if m.m.cx <= m.m.0 then
        return m'.'m.m.cx
    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 stem of the curr line, '' at end --------------------*/
readNxCur: procedure expose m.
parse arg m
    if m.m.cx <= m.m.0 then
        return m'.'m.m.cx
    else
        return ''
endProcedure readNxCur

/*--- 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 tsoClose m.m.dd
    call tsoFree m.m.free
    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 pos('(', w) > 0 then
            leave
        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.tso_trap.1 = ''
        m.tso_trap.2 = ''
        m.tso_trap.3 = ''
        res = dsnAlloc(spec, pDi, pDD, '*')
        if \ datatype(res, 'n') then
            return res
        msg = m.tso_trap.1'\n'm.tso_trap.2'\n'm.tso_trap.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 dd == '-' & pDD \== '' then
        dd = pDD
    if dd == '-' then
        dd = 'DD*'
    dd = tsoDD(dd, 'a')
    m.tso_dd.dd = ''
    if na == '-' & di == '-' & rest = '' then
        return dd
    if di = '-' then
        if pDi == '' then
            di = 'SHR'
        else
            di = pDi
    if pos('(', na) < 1 then
        nop
    else if di = 'MOD' then
        call err 'disp mod for' na
    else
        di = 'SHR'
    if lastPos('/', na, 6) > 0 then
        rx = csmAlloc(na dd di rest, retRc)
    else
        rx = tsoAlloc(na dd di rest, retRc)
    if rx = 0 then
        return dd dd
    else
        return rx
endProcedure dsnAlloc

/*--- find a free dd with prefix dd ----------------------------------*/
tsoDD: procedure expose m.
parse upper arg dd, f, noErr
    if symbol('m.tso_ddAll') \== 'VAR' then do
        call errIni
        m.tso_ddAll = ''
        end
    if f == '-' then do
        ax = wordPos(dd, m.tso_ddAll)
        if ax > 0 then
            m.tso_ddAll = delWord(m.tso_ddAll, ax, 1)
        else if noErr \== 1 then
            call err 'tsoDD dd' dd 'not used' m.tso_ddAll
        end
    else if f <> 'A' then
        call err 'tsoDD bad fun' f
    else do
        if right(dd, 1) = '*' then do
            d0 = left(dd, length(dd)-1) || m.err.screen
            dd = d0
            do dx=1 while wordPos(dd, m.tso_ddAll) > 0
                dd = d0 || dx
                end
            end
        else if pos('?', dd) > 0 then
            dd = repAll(dd, '?', m.err.screen)
        if wordPos(dd, m.tso_ddAll) < 1 then
            m.tso_ddAll = strip(m.tso_ddAll dd)
        end
    return dd
endProcedure tsoDD

tsoAlloc: procedure expose m.
parse arg na dd disp rest ':' nn, retRc
    dd = translate(dd)
    c = 'alloc dd('dd')' disp
    if na == '-' then
        m.tso_dd.dd = ''
    else if na \== 'INTRDR' then do
        c = c "DSN('"na"')"
        m.tso_dd.dd = na
        end
    else do
        c = c "sysout(*) writer(intRdr)"
        m.tso_dd.dd = '*intRdr'
        end
    if wordPos(disp, 'NEW MOD') > 0 & nn \== '' then
        c = c dsnCreateAtts(,nn)
    if adrTso(c rest, '*') = 0 then
        return 0
    if pos('IKJ56246I', m.tso_trap) > 0 then
        if pos('TED, FILE IN USE', m.tso_trap) > 0 then do
     /* IKJ56246I DATA SET A540769.WK.REXX NOT ALLOCATED, FILE IN USE */
        say 'tsoAlloc dd' dd 'already in use:' substr(m.tso_trap, 3)
        say '.... trying to free'
        call tsoFree dd, 1
        say '.... retrying to allocate' c rest
        if adrTso(c rest, '*') = 0 then
            return 0
        end
    if nn \= '' & wordPos(disp, 'OLD SHR') > 0 ,
          & pos('IKJ56228I', m.tso_trap) > 0 ,
          & pos(' NOT IN CATALOG ', m.tso_trap) > 0 then do
       /* IKJ56228I DATA SET A540769.XY.REXX NOT IN CATALOG OR CATAL*/
        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
    if pos('*', retRc) < 1 & wordPos(m.tso_rc, retRc) < 1 then
        call err 'tsoAlloc rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
    call saySt(splitNl(err, 'tsoAlloc rc='m.tso_rc ,
                  'tsoStmt='m.tso_stmt m.tso_trap))
    return m.tso_rc
endProcedure tsoAlloc

dsnLikeAtts: procedure expose m.
parse upper arg dsn, isFile
     if isFile then do
         ddDsn = m.tso_dd.dsn
         if lastPos('/', m.tso_dd.dsn, 4) < 1 then
              return tsoLikeAtts(dsn, 1)
         dsn = m.tso_dd.dsn
         end
     sx = lastPos('/', dsn, 4)
     if sx < 1 then
         return tsoLikeAtts(dsn, 0)
     else if abbrev(dsn, '*/') | abbrev(dsn, sysVar('SYSNODE')'/') then
         return tsoLikeAtts(substr(dsn, sx+1), 0)
     else
         return csmLikeAtts(dsn)
endProcedure dsnLikeAtts

tsoLikeAtts: procedure expose m.
parse arg dsn, isFile
    rc = listDsi("'"dsn"'" copies('FILE', isFile) "SMSINFO")
    if rc = 0 then
        r = ''
    else if rc = 4 & sysReason = 19 then do
        r = 'UCOUNT(30)' /* 7.8.13 corrected attribute */
        say 'creating' dsn 'with multi volume' mv
        end
    else if rc ^= 0 then
        call err 'listDsi rc' rc 'reason' sysReason,
                             sysMsgLvl1 sysMsgLvl2
    if right(sysDsSms, 7) == 'LIBRARY' | abbrev(sysDsSms, 'PDS') ,
                      | sysDsOrg = 'PO' then
         r = 'dsOrg(PO) DSNTYPE(LIBRARY)' r
    else
         r = "dsOrg("sysDSorg")" r
    if sysUnits = 'TRACK' then
        sysUnits = 'TRACKS'
    return r "MGMTCLAS("sysMgmtClass")",
        "DATACLAS("sysDataClass")" ,
        "RECFM("strip(translate('1 2 3', ' 'sysREcFM, '123'))")",
        "LRECL("SYSLRECL")",
        "SPACE("sysPrimary"," sysSeconds")" ,
            sysUnits || left('S', sysUnits == 'TRACK')
    /*  "blksize("sysBLkSIZE")" removed 3.4.13: let sms do the magic */
endProcedure tsoLikeAtts

tsoFree: procedure expose m.
parse arg ddList, tryClose
    do dx=1 to words(ddList)
        dd = word(ddList, dx)
        if adrTso('free dd('dd')', '*') <> 0 then do
            if pos('IKJ56861I', m.tso_trap) > 0 & tryClose == 1 then
              if pos('NOT FREED, DATA SET IS OPEN', m.tso_trap),
                    > 0 then do
                  /* IKJ56861I  FILE A1 NOT FREED, DATA SET IS OPEN */
                say 'dataset open:' substr(m.tso_trap, 3)
                say '.... trying to close'
                if adrTso('execio 0 diskR' dd '(finis)', '*') = 0 then
                   call adrTso 'free dd('dd')', '*'
                end
            if m.tso_rc \== 0 then
                call saySt(splitNl(err, 'rc='m.tso_rc ,
                            'tsoStmt='m.tso_stmt m.tso_trap))
            end
        call tsoDD dd, '-', 1
        end
    return
endProcedure tsoFree

dsnCreateAtts: procedure expose m.
parse arg dsn, atts
    res = ''
    if dsn \== '' then
        res = "dataset('"dsnSetMbr(dsn)"')"
    if abbrev(atts, ':') then do
        parse var atts a1 atts
        rl = substr(a1, 3)
        if abbrev(a1, ':F') then do
            if rl = '' then
                rl = 80
            res =  res "recfm("space(f b)") lrecl("rl")"
            end
        else if abbrev(a1, ':V') then do
            if rl = '' then
                rl = 32756
            res =  res "recfm("space(v b)") lrecl("rl")"
            end
        else if abbrev(a1, ':L') then
            res = res dsnLikeAtts(rl, 0)
        else if abbrev(a1, ':D') then
            res = res dsnLikeAtts(rl, 1)
        else
            call err 'csnCreateAtt bad :' a1
        end
    aU = ' 'translate(atts)
    hasOrg = pos(' DSORG(', aU) > 0 | pos(' DSNTYPE(', aU) > 0
    if \ hasOrg & pos('(', dsn) > 0 then do
        hasOrg = 1
        atts = atts 'dsorg(po) dsntype(library)'
        end
    if hasOrg then do
         cx = pos(' DSORG(', ' 'translate(res))
         if cx > 0 then do
             cy = pos(')', res, cx)
             res = delstr(res, cx, cy+1-cx)
             say '???? del dsnOrg ==>' res
             end
         cx = pos(' DSNTYPE(', ' 'translate(res))
         if cx > 0 then do
             cy = pos(')', res, cx)
             res = delstr(res, cx, cy+1-cx)
             say '???? del dsntype ==>' res
             end
         end
    res = res atts
    aU = ' 'translate(res)
    if pos(' MGMTCLAS(', aU) < 1 then
        res = res 'mgmtclas(COM#A091)'
    if pos(' SPACE(', aU) < 1 then
        res = res 'space(100, 500) cylinders'
    return res
endProcedure dsnCreateAtts
/*--- check if a dataset is archive ------------------------------------
          returns 'ok'                    if dataset on disk
                  'not'                   if dataset is not catalogued
                  'arc'                   if dataset archived
                  listDsi errorMsg        otherwise ------------------*/
dsnArc: procedure expose m.
parse upper arg dsn
    lc = listDsi("'"strip(dsn)"' noRecall")
    if lc = 0 then
        return 'ok'
    else if lc=4 & sysReason = 19 then  /* multiple volumes */
        return 'ok'
    else if lc=16 & sysReason = 5 then
        return 'notCat'
    else if lc=16 & sysReason = 9 then
        return 'arc'
    else
        return 'listDsi cc='lc', sysReason='sysReason ,
                          'm2='sysMsgLvl2', m1='sysMsgLvl1
endProcedure dsnArc
/*--- 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)'
    call tsoFree word(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)'
    call tsoFree word(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
    parse value dsnAlloc(frSpec, 'SHR', 'FRDD') with frDD frFr
    parse value dsnAlloc(toSpec, 'OLD', 'TODD') with toDD toFr
    call tsoOpen frDD, 'R'
    call tsoOpen toDD, 'W'
    cnt = 0
    do while readDD(frDD, r.)
        call writeDD toDD, r.
        cnt = cnt + r.0
        end
    call tsoClose frDD
    call tsoClose toDD
    call tsoFree frFr toFr
    if ggSay == 1 | m.debug == 1 then
       say cnt 'records copied from' frSpec 'to' to toSpec
    return
endSubroutine writeDsn
/* copy adrTso end ****************************************************/
/* copy csv begin *****************************************************/
csvIni: procedure expose m.
    if m.csv.ini == 1 then
        return
    m.csv.ini = 1
    call jIni
    call classNew "n CsvRdr u JRWO, f RDR r", "m",
        , "jReset m.m.rdr = arg",
        , "jOpen call csvRdrOpen m, opt",
        , "jClose call jClose m.m.rdr; call oMutatName m, 'CsvRdr'"
    call classNew "n CsvRdrR u CsvRdr", "m",
        , "jReadO return csvRdrReadO(m)"
    call classNew "n CsvWrt u JRW, f RDR r", "m",
        , "jReset m.m.rdr = arg",
        , "jOpen call csvWrtOpen m, opt",
        , "jClose call jClose m.m.rdr; call oMutatName m, 'CsvWrt'"
    call classNew "n CsvWrtR u CsvWrt", "m",
        , "jRead return csvWrtRead(m, var)"
    return
endProcedure csvIni

/*--- create a new csvRdr --------------------------------------------*/
csvRdr: procedure expose m.
parse arg rdr
    return jReset(oNew('CsvRdr'), rdr)
endProcedure csvRdr

/*--- open csvRdr: read first line and create dataClass --------------*/
csvRdrOpen: procedure expose m.
parse arg m
    call jOpen m.m.rdr, '<'
    if jRead(m.m.rdr, m'.LINE') then do
        ff = 'f' repAll(m.m.line, ',', ' v, f ') 'v'
        m.m.class = classNew("n* CsvF u" ff)
        end
    call oMutatName m, 'CsvRdrR'
    return
endProcedure csvRdrOpen

/*--- read next line and return derived object -----------------------*/
csvRdrReadO: procedure expose m.
parse arg m
    do until m.m.line <> ''

        if \ jRead(m.m.rdr, m'.LINE') then
            return ''
        end
    var = oNew(m.m.class)
    ff = oClaMet(m.m.class, 'oFlds')
    s = m'.SCAN'
    call scanSrc s, m.m.line
    do fx=1
        f1 = substr(m.ff.fx, 2)
        if scanString(s, '"') then
            m.var.f1 = m.s.val
        else do
            call scanUntil s, ','
            m.var.f1 = m.s.tok
            end
        if scanEnd(s) then
            leave
        if \ scanLit(s, ',') then
            call scanErr s, ',' expected
        end
    if fx <> m.ff.0 then
        call scanerr s, 'csv cla' m.ff.0 'fields but' cx 'in line'
    return var
endProcedure csvRdrReadO

/*--- create a new csvRdr --------------------------------------------*/
csvWrt: procedure expose m.
parse arg rdr
    return jReset(oNew('CsvWrt'), rdr)
endProcedure csvWrt

/*--- open csvRdr: read first line and create dataClass --------------*/
csvWrtOpen: procedure expose m.
parse arg m
    call jOpen m.m.rdr, '<'
    m.m.class = ''
    m.m.o1    = ''
    call oMutatName m, 'CsvWrtR'
    return
endProcedure csvWrtOpen

/*--- read next line and return derived object -----------------------*/
csvWrtRead: procedure expose m.
parse arg m, var
    if m.m.o1 == '' then
        i1 = jReadO(m.m.rdr)
    else do
        i1 = m.m.o1
        m.m.o1 = ''
        end
    if i1 == '' then
        return 0
    if m.m.class == '' then do
        m.m.class = objClass(i1)
        m.m.o1 = i1
        t = ''
        ff = oFlds(i1)
        do fx=1 to m.ff.0
            t = t','substr(m.ff.fx, 2)
            end
        m.var = substr(t, 2)
        return 1
        end
    else do
        t = ''
        ff = oFlds(i1)
        do fx=1 to m.ff.0
            f1 = i1 || m.ff.fx
            val = m.f1
            if pos(',', val) > 0 | pos('"', val) > 0 then
                t = t','quote(val, '"')
            else
                t = t','val
            end
        m.var = substr(t, 2)
        return 1
        end
endProcedure csvWrtRead

/* copy csv end   *****************************************************/
/* copy j begin *******************************************************
    the j framework
         jReset
         jOpen
         jClose
         jRead
         jWrite
***********************************************************************/
jRead: procedure expose m.
parse arg m, var
    met = objMet(m, 'jRead')
    if m.m.jReading then
        interpret met
    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'
    met = objMet(m, 'jReadO')
    if m.m.jReading then
        interpret met
    else
        return err('jReadO('m',' var') but not opened r')
endProcedure jReadO

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

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

jWriteAll: procedure expose m.
parse arg m, rdr
    rdr = o2file(rdr)
    met = objMet(m, 'jWriteAll')
    if \ m.m.jWriting then
        return err('jWriteAll('m',' rdr') but not opened w')
    interpret met
    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')')
    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
    met = objMet(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 met
            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 met
            m.m.jWriting = 1
            end
        end
    m.m.jUsers = oUsers + 1
    return m
endProcedure jOpen

jClose: procedure expose m.
parse arg m
    met = objMet(m, 'jClose')
    oUsers = m.m.jUsers
    if oUsers = 1 then do
        interpret met
        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))
    fmt = '@cat%('fmt'%)'
    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'%.1', m.line)
        end
    call jClose m
    return res || f(fmt'%.2')
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 m.m.src == '' then
            m.m.src = ' '
        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
    sta = 'tt'
    res = ''
    do forever
        do while scanSBEnd(m)
            if \ jCatSqlNl(m) then
                return strip(res)
            end
        bx = m.m.pos
        sta = scanSql2Stop(m, sta, stop)
        s1 = left(sta, 1)
        if pos(s1, stop) > 0 then do
            if res <> '' then
                return strip(res)
            end
        else if s1 == '-' | s1 == '/' then
            res = res' '
        else if pos('/', sta) = 0 then
            res = res || substr(m.m.src, bx, m.m.pos - bx)
        end
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 classIni
    am = "call err 'call of abstract method"
    c1 = classNew('n JRW u ORun, f JREADING v, f JWRITING v', 'm',
        , "new return jReset("m.class.basicNew", 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")
    m.class.forceDown.c1 = c1'#new'
    c2 = classNew('n JRWDeleg u JRW', 'm',
        , "new return jReset("m.class.basicNew", arg)",
        , "jRead return jRead(m.m.deleg, var)" ,
        , "jReadO return jReadO(m.m.deleg)" ,
        , "jWrite  call jWrite(m.m.deleg, line)" ,
        , "jWriteO call jWrite(m.m.deleg, var)" ,
        , "jWriteAll call jWriteAll m.m.deleg, rdr",
        , "jWriteNow call jWriteNow m.m.deleg, rdr",
        , "jReset    if arg \== '' then m.m.deleg = arg;",
                                   "else call jReset m.m.deleg;",
        , "jOpen     call jOpen m.m.deleg,' opt; return m" ,
        , "jClose    call jClose m.m.deleg; return m" )
    m.class.forceDown.c2 = c2'#new'
    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)
    m.j.out = jOpen(oNew('JSay'), '>')
    m.j.errRead  = "return err('jRead('m',' var') but not opened r')"
    m.j.errReadO = "return err('jReadO('m',' var') but not opened r')"
    m.j.errWrite = "return err('jWrite('m',' line') but not opened w')"
    m.j.errWriteO= "return err('jWriteO('m',' var') but not opened w')"
    call classNew "n JBuf u JRWO, f BUF s r", "m",
        , "jOpen call jBufOpen m, opt",
        , "jClose call oMutatName m, 'JBuf'",
        , "jReset call jBufReset m, arg",
        , "jRead" m.j.errRead ,
        , "jReadO" m.j.errReadO ,
        , "jWrite" m.j.errWrite ,
        , "jWriteO" m.j.errWriteO
    call classNew "n JBufOR u JBuf", "m",
        , "jRead return jBufORead(m, var)",
        , "jReadO return jBufOReadO(m)"
    call classNew "n JBufSR u JBuf", "m",
        , "jRead return jBufSRead(m, var)",
        , "jReadO return jBufSReadO(m)"
    call classNew "n JBufOW u JBuf", "m",
        , "jWrite call jBufOWrite m, line",
        , "jWriteO call jBufOWriteO m, var"
    call classNew "n JBufSW u JBuf", "m",
        , "jWrite call jBufSWrite m, line",
        , "jWriteO call jBufSWriteO 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

/*--- 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

/*--- open for input given rdr, or stdIn if missing ------------------*/
inOpen: procedure expose m.
parse arg rdr
    if rdr == '' then
        return jOpen(m.j.in, '<')
    else
        return jOpen(o2file(rdr), '<')
endProcedure

j2Buf: procedure expose m.
    parse arg rdr
    if oStrOrObj(rdr, m.j.in) then
        return jBuf(ggStr)
    if oClaInheritsOf(ggCla, '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

outStem: procedure expose m.
parse arg st
    do sx=1 to m.st.0
       call jWrite m.j.out, m.st.sx
       end
    return 0
endProcedure out


JRWDeleg: procedure expose m.
parse arg arg
    return oNew('JRWDeleg', arg)
endProcedure JRWDeleg

/*--- 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.allS = 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.allS = 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.allS = 1
    return m
endProcedure jBufReset

jBufOpen: procedure expose m.
parse arg m, opt
    if opt == m.j.cRead then do
        m.m.readIx = 0
        if m.m.allS then
            call oMutatName m, 'JBufSR'
        else
            call oMutatName m, 'JBufOR'
        return m
        end
    if opt == m.j.cWri then do
        m.m.buf.0 = 0
        m.m.allS = 1
        end
    else if opt \== m.j.cApp then
         call err 'jBufOpen('m',' opt') with bad opt'
    if m.m.allS then
        call oMutatName m, 'JBufSW'
    else
        call oMutatName m, 'JBufOW'
    return m
endProcedure jBufOpen

jBufOWrite: procedure expose m.
parse arg m, line
    call mAdd m'.BUF', s2o(line)
    return
endProcedure jBufOWrite
jBufSWrite: procedure expose m.
parse arg m, line
    call mAdd m'.BUF', line
    return
endProcedure jBufWrite

jBufWriteStem: procedure expose m.
parse arg m, st
    ax = m.m.buf.0
    if m.m.allS 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

jBufOWriteO: procedure expose m.
parse arg m, ref
    call mAdd m'.BUF', ref
    return
endProcedure jBufOWriteO

jBufSWriteO: procedure expose m.
parse arg m, ref
    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
    do ax=1 to m.m.buf.0
        m.m.buf.ax = s2o(m.m.buf.ax)
        end
    m.m.allS = 0
    call oMutatName m, 'JBufOW'
    call mAdd m'.BUF', ref
    return
endProcedure jBufWriteO

jBufOReadO: procedure expose m.
parse arg m
    nx = m.m.readIx + 1
    if nx > m.m.buf.0 then
        return ''
    m.m.readIx = nx
    return m.m.buf.nx
endProcedure jBufOReadO

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

jBufORead: 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
    m.var = o2String(m'.BUF.'nx)
    return 1
endProcedure jBufORead

jBufSRead: 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
    m.var = m.m.buf.nx
    return 1
endProcedure jBufRead

jBufTxtWriteO: procedure expose m.
parse arg m, ref
    if m.m.allS \== 1 then
        call err '1 \== allS' m.m.allS '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 = oFlds(ref)
        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

jSingle: procedure expose m.
parse arg m
    call jOpen m, '<'
    res = jReadO(m)
    two = jReadO(m)
    call jClose m
    if res == '' then
        if arg() < 2 then
             call err 'empty file in jSingle('m')'
        else
            res = arg(2)
    if two \== '' then
        call err '2 or more recs in jSingle('m')'
    return res
endProcedure jSingle
/* copy j end *********************************************************/
/* copy o begin ******************************************************
    an object is register for a class in o2c
    a class has a list of parents in cParent
    a class has a methodTable cMet with lazy initialization
        if the parent is class OLazyMet, a methof found there is
             a method generator
        otherwise an existing method is simply copied
***********************************************************************/
oIni: procedure expose m.
    if m.o.ini == 1 then
        return
    m.o.ini = 1
    call mIni
    m.o.escW = '!'
    m.o.lazyGen = 'OLazyMetGen' /* lazy method generator */
    call oAddCla m.o.lazyGen
    return
endProcedure oIni

/*--- return whether cl is a currently defined class ----------------*/
oIsCla: procedure expose m.
parse arg cl
    return symbol('m.o.cParent.cl') == 'VAR'
endProcedure oIsCla

/*--- add class cl with the given parents ---------------------------*/
oAddCla: procedure expose m.
parse arg cl, parents
    if verifId(cl, '.') > 0 | pos('.', cl) <> lastPos('.', cl) then
        call err 'bad class name' cl 'in oAddCla('cl',' parents')'
    if oIsCla(cl) then
        call err 'duplicate class' cl 'in oAddCla('cl',' parents')'
    do px=1 to words(parents)
        if \ oIsCla(word(parents, px)) then
            call err word(parents, px) 'is no class' ,
                    'in oAddCla('cl',' parents')'
        end
    m.o.cParent.cl = parents
    return
endProcedure oAddCla

/*--- add to class cl method met ------------------------------------*/
oAddMet: procedure expose m.
parse arg cl, met, cont
    if \ oIsCla(cl) then
        call err 'undefined class' cl 'in oAddMet('cl',' met',' cont')'
    if symbol('m.o.cMet.cl.met') == 'VAR' then
       call err 'duplicate method' met 'in oAddMet('cl',' met',' cont')'
    m.o.cMet.cl.met = cont
    return
endProcedure oAddMet
/*--- create an an object of the class className
        and call it's new method ------------------------------------*/
oNew: procedure expose m.
parse arg cl, arg, arg2, arg3
    if symbol('m.o.cParent.cl') \== 'VAR' then
        cl = class4name(cl)
    interpret oClaMet(cl, 'new')
endProcedure oNew

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

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

oClaInheritsOf: procedure expose m.
parse arg cl, sup    /* wkTst optimierung in classAdded */
    if symbol('m.o.cParent.cl') \== 'VAR' then
         cl = class4name(cl)
    if symbol('m.o.cParent.sup') \== 'VAR' then
         sup = class4name(sup)
    if cl == sup then
        return 1
    do sx=1 to words(m.o.cParent.cl)
        if oClaInheritsOf(word(m.o.cParent.cl, sx), sup) then
            return 1
        end
    return 0
endProcedure oClaInheritsOf
/*--- return the code of method me of object m
         set m to the address and ggClass to the class ---------------*/
objMet: procedure expose m. m ggClass
parse arg m, me
    if symbol('m.o.o2c.m') == 'VAR' then
         ggClass = m.o.o2c.m
    else if abbrev(m, m.o.escW) then
         ggClass = "w"
    else if arg() >= 3 then
        return arg(3)
    else
        return err('no class found for object' m)
    if symbol('m.o.cMet.ggClass.me') == 'VAR' then
       return m.o.cMet.ggClass.me
    code = oClaMet(ggClass, me, '---')
    if code \== '---' then
        return code
    else if arg() >= 3 then
         return arg(3)
    return err('no method' me 'in class' className(ggClass) ,
               'of object' m)
endProcedure objMet

oClaMet: procedure expose m.
parse arg cl, me
    if symbol('m.o.cMet.cl.me') == 'VAR' then
       return m.o.cMet.cl.me
    if \ oIsCla(cl) then do
        c2 = class4Name(cl, '')
        if c2 \== ''  & oIsCla(c2) then do
            cl = c2
            if symbol('m.o.cMet.cl.me') == 'VAR' then
                return m.o.cMet.cl.me
            end
        else do
            if arg() >= 3 then
                return arg(3)
            else
                return err('no class' cl 'in oClaMet('cl',' me')')
            end
        end
    code = oLazyMetGen(m.o.lazyGen, cl, me)
    do px = 1 to words(m.o.cParent.cl) while code == '---'
        code = oClaMet(word(m.o.cParent.cl, px), me, '---')
        end
    if code == '---' then do
        if arg() >= 3 then
            return arg(3)
        else
            return err('no met' me 'in class' cl)
        end
    m.o.cMet.cl.me = code
    return code
endProcedure oClaMet

oLazyMetGen: procedure expose m.
parse arg lg, cl, me
    if symbol('m.o.cMet.lg.me') \== 'VAR' then
        return '---'
    interpret m.o.cMet.lg.me
endProcedure oLazyMetGen

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


oClear: procedure expose m.
parse arg m
    interpret objMet(m, 'oClear')
    return m
endProcedure oClear

/*--- mutate object m to the class cl -------------------------------*/
oMutate: procedure expose m.
parse arg m, cl
    m.o.o2c.m = cl
    return m
endProcedure oMutate

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

/*--- copy object m of class cl to t --------------------------------*/
oClaCopy: procedure expose m.
parse arg cl, m, t
    interpret "drop cl;" oClaMet(cl, 'oCopy')
endProcedure oClaCopy

/*--- copy object m to t / create a new object if t=='' -------------*/
oCopy: procedure expose m.
parse arg m, t
    interpret objMet(m, 'oCopy')
endProcedure oCopy

/*--- 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 of object m No Procedure:
        ??? optimize: class only run ???
         use from caller unprotected---------------------------------*/
oRunNP: procedure expose m.
    interpret objMet(arg(1), 'oRun')
    return
endProcedure oRunNP

/*--- run method oRun and return output in new JBuf ------------------*/
oRun2File: procedure expose m.
parse arg rn
    b = jBuf()
    call pipe '+F' , b
    call oRun rn
    call pipe '-'
    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 arg() = 1 then
        fmt = ' '
    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 = oFlds(m)
        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.o.escW || str
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 address (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
    m.class.in2 = 0
    call oIni
    call mapIni
    call mNewArea 'CLASS', 'CLASS'
    call mapReset 'CLASS.N2C'  /* name to class */
    m.class.classV = classBasicNew('u', 'v')
    m.class.classW = classBasicNew('u', 'w')
    m.class.classO = classBasicNew('u', 'o')

    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')
    m.class.basicNew = "oMutate(mNew(cl), cl)"
    call oAddMet m.o.lazyGen, 'new', "return classGenNew(cl, me)"
    call oAddMet m.o.lazyGen,'oClear',"return classGenClear(cl, me)"
    call oAddMet m.o.lazyGen,'oFlds',"return classGenFlds(cl, me)"
    call oAddMet m.o.lazyGen, 'oCopy', "return classGenCopy(cl, me)"

    cr = mIterBegin('CLASS')
    do while assNN('cr', mIter(cr))
        call classFinish cr
        call oClaMet cr, 'oFlds' /* generate flds */
        end
    m.class.in2 = 1

    call oAddMet m.class.classV, 'oCopy', "return oCopyV(m, t)"
    call classAddMet m.class.classV, 'o2String return m.m'
    call classAddMet m.class.classW, 'o2String return substr(m, 2)'
    call 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)'

    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 className

/*--- 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
    if ty == 'f' & nm == '=' then
        nm = ''
    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
    if length(ty) \== 1 | pos(ty, 'ufscrm') < 1 then
        call err 'bad type' ty': classBasicNew('ty',' nm',' cl')'
    else if nm == '' & pos(ty, 'm') > 0 then
        call err 'empty name: classBasicNew('ty',' nm',' cl')'
    else if nm \== '' & ty \== 'c' & verifId(nm) > 0 ,
        & \ (ty == 'f' & nm == '=') 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 isNew & m.class.in2 then
        call classFinish n
    if nmTy == '' | nmTy == '*' then
        call mapAdd class.n2c, clEx, n
    return n
endProcedure classNew

/*--- to the finish for new class cl -------------------------------*/
classFinish: procedure expose m.
parse arg cl, force
    call oMutate cl, m.class.class
                        /* find super and sub classes */
    m.cl.sub = ''
    sups = ''
    do ux=1 to m.cl.0
        u1 = m.cl.ux
        if m.u1 \== 'u' then
            iterate
        if wordPos(u1, sups) > 0 then
            call err u1 'already in sups' sups': classSuperSub('cl')'
        sups = sups u1
        if wordPos(m.cl.SUB, cl) > 0 | symbol('m.u1.sub') \== 'VAR' then
            call err cl 'is already in' u1'.sub' u1.SUB  ,
                || ': classSuperSub('cl')'
        m.u1.sub = strip(m.u1.sub cl)
        end
    m.cl.super = sups
                        /* add class to o */
    call oAddCla cl, sups
    if pos(m.cl, 'mfrsv') < 1 then do
        allMets = ''
        forceMets = ''
        do cx=1 to m.cl.0
            ch = m.cl.cx
            if m.ch == 'm' then do
                call oAddMet cl, m.ch.name, m.ch.met
                allMets = allMets m.ch.name
                end
            else if symbol('m.class.forceDown.ch') == 'VAR' then
                forceMets = forceMets m.class.forceDown.ch
            end
        myForce = ''
        do fx=1 to words(forceMets)
            parse value word(forceMets, fx) with fCla '#' fMet
            if wordPos(fMet, allMets) < 1 then do
                call oAddMet cl, fMet, m.o.cMet.fCla.fMet
                myForce = myForce cl'#'fMet
                allMets = allMets fMet
                end
            end
        if myForce \== '' then
            m.class.forceDown.cl = strip(myForce)
        end
    if cl == m.class.class then
        call mAlias 'CLASS', cl
    else  /* object addresses */
        call mNewArea cl, 'O.'substr(cl,7)
    if m.cl \== 'u' | m.cl.name == '' then
        return
    call mAlias cl, m.cl.name
    return
endProcedure classFinish

classAddMet: 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')'
    call mAdd cl, classNew('m' met code)
    call oAddMet cl, met, code
    return cl
endProcedure classAddMet
/*--- 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

classGenNew: procedure expose m.
parse arg cl, met
     return  "m=" m.class.basicNew";" oClaMet(cl, 'oClear') ";" ,
             "return m"
endProcedure classGenNew

classGenFlds: procedure expose m.
parse arg cl, met
    m.cl.flds.0 = 0
    m.cl.stms.0 = 0
    m.cl.stFl.0 = 0
    call classGenFldsAdd cl, cl
    return cl'.FLDS'
endProcedure classGenFlds

/*--- add the the fields of class cl to stem f ----------------------*/
classGenFldsAdd: procedure expose m.
parse arg f, cl, nm
    f2CNm = 'F2C'nm
    if cl == m.class.classV | cl == m.class.classW | m.cl=='r' then do
        if symbol('m.f.f2cNm') \== 'VAR' then
               m.f.f2cNm = cl
           else if m.f.f2cNm \== cl then
               return  err('f2c mismatch f='f f2cNm 'cl='cl ,
                           'old='m.f.f2cNm)
        if nm == '' then do
            call mMove f'.FLDS', 1, 2
            m.f.flds.1 = ''
            end
        else if pos('*', nm) < 1 then
            call mAdd f'.FLDS', nm
        else
            call mAdd f'.STFL', nm
        return 0
        end
    if m.cl == 's' | (m.cl == 'u' & m.cl.name \== '') then do
        nestNm = 'NEST'nm
        if symbol('m.f.nestNm') \== 'VAR' then
            m.f.nestNm = cl
        else if wordPos(cl, m.f.nestNm) > 0 then
            call err 'classGenFldsAdd dup' cl',' f nestNm m.f.nestNm
        else
            m.f.nestNm = m.f.nestNm cl
        end
    if m.cl = 's' then do
        if m.cl.class == '' then
            call err 'stem null class'
        call mAdd f'.STMS', nm
        m.f.f2cNm.0 = m.class.classV
          return classGenFldsAdd(f, m.cl.class, nm'.*')
        end
    if m.cl = 'f' then
        if m.cl.name == '' then
               return classGenFldsAdd(f, m.cl.class, nm)
        else
               return classGenFldsAdd(f, m.cl.class, nm'.'m.cl.name)
    if m.cl.class \== '' then
        return classGenFldsAdd(f, m.cl.class, nm)
    do tx=1 to m.cl.0
        call classGenFldsAdd f, m.cl.tx, nm
        end
    return 0
endProcedure classGenFldsAdd

classGenClear: procedure expose m.
parse arg cl, met
    r = ''
    call oClaMet cl, 'oFlds'
    do fx=1 to m.cl.flds.0
        f1 = m.cl.flds.fx
        if f1 == '' then
            c1 = cl
        else do
            c1 = substr(f1, 2)
            c1 = m.cl.f2c.c1
            end
        if c1 == m.class.classW then
            r = r classGenStmt(f1, "m.m~ = '"m.o.escW"';")
        else
            r = r classGenStmt(f1,  "m.m~ = '';")
        end
    do sx=1 to m.cl.stms.0
        if pos('*', m.cl.stms.sx) < 1 then
            r = r classGenStmt(m.cl.stms.sx, "m.m~.0 = 0;")
        end
    return r
endProcedure classGenClear

classGenStmt: procedure expose m.
parse arg f, st, resWo
    isNice = translate(f) == f
    resWo = translate(resWo) 'GGFF M'
    fDod = '.'f'.'
    do wx=1 to words(resWo) while isNice
        isNice = pos('.'word(resWo, wx)'.', fDot) < 1
        end
    if isNice then
        return repAll(st, '~', f)
    else
        return "ggFF = '"substr(f, 2)"';" repAll(st, '~', '.ggFF')
endProceduer classGenAss

/*--- return true if src is a rexxVariable a, m.a.c etc. -------------*/
rxIsVar: procedure expose m.
parse arg src
    if pos(left(src, 1), m.ut_rxN1) > 0 then
        return 0
    else
        return verify(src, m.ut_rxId) = 0
endProcedure rxIsVar

/*--- return true if src is a rexxConstant rerpresenting its value ---*/
rxIsConst: procedure expose m.
parse arg src, vars                                                   c
    if \ rxIsVar(src) then
        return 0
    srU = translate(src)
    if srU \== src then
        return 0
    srU = '.'srU'.'
    if pos('.GG', srU) > 0 then
        return 0
    if vars == '' then
        return 1
    upper vars
    do vx=1 to words(vars)
        if pos('.'word(vars, vx)'.', vars) > 0 then
            return 0
        end
    return 1
endProcedure rxIsConst

/*--- return rexx code m.cc or mGet('cc') ----------------------------*/
rxMGet: procedure expose m.
parse arg v1, cc, vars
    if cc == '' then
        return 'm.'v1
    else if rxIsConst(cc, vars) then
        return 'm.'v1'.'cc
    else
        return 'mGet('v1 || quote('.'cc, "'")')'
endProcedure rxMGet
classGenCopy: procedure expose m.
parse arg cl, me
    r = repAll("if t == '' then t =" m.class.basicNew ";" ,
               "else call oMutate t, cl;", 'cl', "'"cl"'")
    ff = oClaMet(cl, 'oFlds')            /* build code for copy */
    do fx=1 to m.cl.flds.0
        r = r classGenStmt(m.cl.flds.fx, 'm.t~ = m.m~;', 't')
        end
    do fx=1 to m.cl.stms.0
        nm = m.cl.stms.fx
        sc = m.cl.stms.fx.class
        if nm == '' then
            st = ''
        else do
            r = r "st = '"substr(nm, 2)"';"
            st = '.st'
            end
        r = r "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
    return r 'return t;'
endProcedure classGenCopy

/*--- oCopy for classW ----------------------------------------------*/
oCopyW: procedure expose m.
trace ?r
parse arg m, t
    if t == '' then
        return m
    m.t = o2String(m)
    return oMutate(t, m.class.classV)
endProcedure oCopyW
/*--- 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 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.ut_alfDot, '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 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 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>.** and m.<mbr>_**: every rexx Module (copy) should only
               allocate these addresses to avoid address conficts
               with <mbr> the name of therexx 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
    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
    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

mNew: 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 mNew

mFree: procedure expose m.
parse arg m
    p = 'M.P2A.'left(m, lastPos('.', m)-1)
    area = m.p
    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

/*--- get m.a --------------------------------------------------------*/
mGet: procedure expose m.
parse arg a
    return m.a
endProcedure mGet

/*--- put value v into m.a -------------------------------------------*/
mPut: procedure expose m.
parse arg a, v
    m.a = v
    return v
endProcedure mPut

/*--- 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, fx , tx
    dx = m.dst.0
    if fx == '' then
        fx = 1
    if tx == '' then
        tx = m.src.0
    do sx = fx to tx
        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
    return mCatFT(st, 1, m.st.0, fmt)

mCatFT: procedure expose m.
parse arg st, fx, tx, fmt
    if tx < fx then
        return ''
    fmt = '@cat%('fmt'%)'
    res = f(fmt, m.st.fx)
    do sx=fx+1 to tx
        res = res || f(fmt'%.1', m.st.sx)
        end
    return res || f(fmt'%.2')
endProcedure mCatFT

fGenCat: procedure expose m.
parse arg s, ax
    do fx=1 until \ scanLit(s, '%,')
        f.fx = fGen(s)
        end
    if \ scanLit(s, '%)') then
        call scanErr s, 'no %) after @fGenCat%('
    if \ scanEnd(s) then
        call scanErr s, 'mGenCat not at end'
    if fx < 2 | f.2 == "''" then
        f.2 = fGen(scanSrc(f_u, '%c'))
    if fx < 3 then
        f.3 = "''"
    if fx < 4 then
        f.4 = "''"
    adr = m.s.src'%'
    if f.1 == "''" then
        m.f_gen.adr.1 = 'return' f.2
    else
        m.f_gen.adr.1 = 'return' f.1 '||' f.2
    m.f_gen.adr.2 = 'return' f.4
    if f.3 == "''" then
        return f.2
    else
        return f.3 '||' f.2
endProcedure fGenCat

mIni: procedure expose m.
    if m.m.ini == 1 then
        return
    m.m.ini = 1
    call utIni
    m.mBase64 = m.ut_alfUC || m.ut_alfLc || m.ut_digits'+-'
    m.m.area.0 = 0
    call mNewArea
    return
endProcedure mIni

/* copy m end *********************************************************/
/* copy fTab begin ****************************************************/
fTabReset: procedure expose m.
parse arg m, m.m.titBef, m.m.titAft, m.m.opt
    m.m.generated = ''
    m.m.0 = 0
    m.m.len = 0
    m.m.cols = ''
    m.m.tit.0 = words(m.m.titBef m.m.titAft) + 5
    m.m.set.0 = 0
    do tx=1 to m.m.tit.0
        m.m.tit.tx = ''
        end
    return m
endProcedure fTabReset

/* add a piece to title tx at current pos */
fTabAddTit: procedure expose m.
parse arg m, tx, t1
    m.m.generated = ''
    m.m.tit.tx = left(m.m.tit.tx, m.m.len) || t1
    return m
endProcedure fTabAddTit

/*--- set the infos for one column -----------------------------------*/
fTabSet: procedure expose m.
parse arg m, c1 aDone, f1, l1
    sx = m.m.set.0 + 1
    m.m.set.0 = sx
    m.m.set.sx = c1 aDone
    m.m.set.sx.fmt = f1
    m.m.set.sx.label = l1
    m.m.set.c1 = sx
    return
endProcedure fTabSet

fTabAdd: procedure expose m.
parse arg m, c1 aDone, f1, l1
    cx = m.m.0 + 1
    m.m.generated = ''
    m.m.0 = cx
    k1 = c1
    if c1 == '' then
        k1 = '='
    else if c1 == '=' then
        c1 = ''
    m.m.cols = m.m.cols k1
    if words(m.m.cols) <> cx then
        call err 'mismatch of column number' cx 'col' c1  / 0
    if length(aDone) > 1 | wordPos('<'aDone'>', '<> <0> <1>') < 1 then
        call err 'bad done' length(aDone) '<'aDone'> after c1' c1
    m.m.cx.col = c1
    m.m.cx.done = aDone \== 0
    if l1 == '' then
        m.m.cx.label = k1
    else
        m.m.cx.label = l1
    if cx == 1 then
        if abbrev(f1, ' ') then
           f1 = substr(f1, 2)
    px = pos('%', f1)
    ax = pos('@', f1)
    if px < 1 | (ax > 0 & ax < px) then
        m.m.cx.fmt = f1
    else
        m.m.cx.fmt = left(f1, px-1)'@.'c1 || substr(f1, px)
    call mPut 'F_TEMP'left('.', c1\=='')c1, m.m.cx.label
    t1 = f(m.m.cx.fmt, 'F_TEMP')
    if pos(strip(t1), m.m.cx.label) < 1 then
        t1 = left(left('', max(0, verify(t1, ' ') -1))m.m.cx.label,
           , length(t1))
    m.m.cx.len = length(t1)
    call fTabAddTit m, 1, t1
    do tx=2 to arg()-3
        if arg(tx+3) \== '' then
            call fTabAddTit m, tx, arg(tx+3)
        end
    m.m.len = m.m.len + length(t1)
    return m
endProcedure fTabAdd

fTabGenerate: procedure expose m.
parse arg m
    f = ''
    do kx=1 to m.m.0
        f = f || m.m.kx.fmt
        end
    do while right(f, 1) == ' ' & f \== ''
        f = left(f, length(f)-1)
        end
    m.m.fmt = fCache('%.', f)

    cSta = m.m.tit.0+3
    do cEnd=cSta until kx > m.m.0
        cycs = ''
        do cx=cSta to cEnd
            m.m.tit.cx = ''
            cycs = cycs cx
            end
        cx = cSta
        ll = 0
        do kx=1 to m.m.0 while length(m.m.tit.cx) < max(ll,1)
            m.m.tit.cx = left(m.m.tit.cx, ll)m.m.kx.col
            cx = cx + 1
            if cx > cEnd then
                cx = cSta
            ll = ll + m.m.kx.len
            end
        end
    m.m.cycles = strip(cycs)
    if pos('-', m.m.opt) > 0 then
        m.m.tit.1 = translate(lefPad(m.m.tit.1, m.m.len), '-',' ')'---'
    m.m.generated = m.m.generated't'
    return
endProcedure fTabGenerate

fTabColGen: procedure expose m.
parse arg m
    do kx=1 to m.m.0
        l = if(m.m.kx.label == m.m.kx.col, , m.m.kx.label)
        f = lefPad(l, 10) lefPad(m.m.kx.col, 18)
        if length(f) > 29 then
           if length(l || m.m.kx.col) < 29 then
               f = l || left('', 29 - length(l||m.m.kx.col))m.m.kx.col
           else
               f = lefPad(strip(l m.m.kx.col), 29)
        g = strip(m.m.kx.fmt)
        o = right(g, 1)
        if pos(o, 'dief') > 0 then
            f = f '@'m.m.kx.col'%12e @'m.m.kx.col'%18c'
        else if o = 'C' then
            f = f left(g, length(g)-1)'c'
        else
            f = f g
        m.m.kx.colFmt = f
        end
    m.m.generated = m.m.generated'c'
    return
endProcedure fTabColGen

fTab: procedure expose m.
parse arg m, rdr
    call fTabBegin m
    call fAll m.m.fmt, rdr
    return fTabEnd(m)
endProcedure fTab

fTabCol: procedure expose m.
parse arg m, i
    if pos('c', m.m.generated) < 1 then
        call fTabColGen m
    do cx=1 to m.m.0 / 0
        call out f(m.m.cx.colFmt, i)
        end
    return 0
endProcedure fTabCol

fTabBegin: procedure expose m.
parse arg m
    if pos('t', m.m.generated) < 1 then
        call fTabGenerate m
    return fTabTitles(m, m.m.titBef)

fTabEnd: procedure expose m.
parse arg m
    return fTabTitles(m, m.m.titAft)

fTabTitles: procedure expose m.
parse arg m, list
    list = repAll(list, 'c', m.m.cycles)
    do tx=1 to words(list)
        t1 = word(list, tx)
        call out m.m.tit.t1
        end
    return m
endProcedure fTabTitles

/*--- auto tables detect format from data ---------------------------*/
/*--- detect fmt from a rdr and write it formatted to stdOut --------*/
fTabAuto: procedure expose m.
parse arg m, rdr, wiTi
    if m == '' then
        m = fTabReset(f_auto, 1)
    i = j2Buf(rdr)
    if m.i.buf.0 <= 0 then
        return m
    call fTabDetect m, i'.BUF', wiTi
    return fTab(m, i)
endProcedure fTabAuto

/*--- generate format for all fields of a stem of objects -----------*/
fTabDetect: procedure expose m.
parse arg m, b, wiTi
    ff = oFlds(m.b.1)
    do fx=1 to m.ff.0
        call fTabAddDetect m, substr(m.ff.fx, 2), b
        end
    return
endProcedure fTabDetect

/*--- detect format for one field in stem st ------------------------*/
fTabAddDetect: procedure expose m.
parse arg m, c1 aDone, st, l1
    lMa = -1
    rMa = -1
    bMa = -1
    aDiv = 0
    nMi =  9e999
    nMa = -9e999
    eMi =  9e999
    eMa = -9e999
    eDa = 2
    dMa = -9e999
    suf = left('.', c1 \== '')c1
    do sx=1 to m.st.0
        v = mGet(m.st.sx || suf)
        lMa = max(lMa, length(strip(v, 't')))
        rMa = max(rMa, length(strip(v, 'l')))
        bMa = max(bMa, length(strip(v, 'b')))
        if \ dataType(v, 'n') then do
            if length(v) > 100 then
                aDiv = 99
            else if aDiv <=3 then
                if aDiv.v \== 1 then do
                    aDiv.v = 1
                    aDiv = aDiv + 1
                    end
            iterate
            end
        v = strip(v)
        nMi = min(nMi, v)
        nMa = max(nMa, v)
        ex = verify(v, 'eEfF', 'm')
        if ex > 0 then do
            eMa = max(eMa, substr(v, ex+1))
            eMi = min(eMi, substr(v, ex+1))
            v = left(v, ex-1)
            do while pos(left(v,1), '+-0') > 0
                v = substr(v, 2)
                end
            eDa = max(eDa, length(v) - (pos('.', v) > 0))
            end
        dx = pos('.', v)
        if dx > 0 then do
            do while right(v, 1) == 0
                v = left(v, length(v)-1)
                end
            dMa = max(dMa, length(v)-dx)
            end
        end
    if nMi > nMa | aDiv > 3 then
        newFo = '-'max(1, (lMa+0))'C'
    else if eMi <= eMa then do
        newFo = ' ' || (eDa+max(length(eMa), length(eMi))+3) ,
             || '.'||(eDa-1)'e'
        end
    else do
        be = max(length(trunc(nMi)), length(trunc(nMa)))
        if dMa <= 0 then
            newFo = max(be, bMa)'I'
        else
            newFo = max(be+1+dMa, bMa)'.'dMa'I'
        end
    call fTabAdd m, c1 aDone, ' %'newFo, l1
  /*  say c1 '????==> %'newFo */
   return newFo
endProcedure fTabAddDetect

/*--- format with units seconds ==> 3d13 oder 5.43e6 ==> 5m43 --------*/
fTime: procedure expose m.
parse arg s, signed
    return fUnits(s, 't', signed==1)
endProcedure fTime

fDec: procedure expose m.
parse arg s, signed
    return fUnits(s, 'd', signed==1)
endProcedure fDec


fUnits: procedure expose m.
parse arg s, scale, signed
    if f_units_ini \== 1 then do
        f_units_ini = 1
        call fUnitsIni 't', '?smhd', 0 100 60 60 24 100, 0.01, 2
        call fUnitsIni 'd', '?.kMGTPE', 0 copies('1000 ',8), 0.001, 3
        end
    if s >= 0 then
        res = fUnitsNN(s, scale, wi)
    else
        res = '-'fUnitsNN(abs(s), scale, wi)
    len = m.fmt.units.scale.f.length + signed
    if length(res) <= len then
       return right(res, len)
    if \ abbrev(res, '-') then
        return right(right(res, 1), len, '+')
    if length(res) = len+1 & datatype(right(res, 1), 'n') then
        return left(res, len)
    return right(right(res, 1), len, '-')
endProcedure fUnits

fUnitsNN: procedure expose m.
parse arg s, scale
    sf = 'FMT.UNITS.'scale'.F'
    sp = 'FMT.UNITS.'scale'.P'
    if m.sf \== 1 then do
        call fmtIni
        if m.sf \== 1 then
            call err 'fUnitsNN bad scale' scale
        end

    do q=3 to m.sp.0 while s >= m.sp.q
        end
    do forever
        qb = q-2
        qu = q-1
        r = format(s / m.sp.qb, ,0)
        if q > m.sf.0 then
            return r || substr(m.sf.units, qb, 1)
        if r < m.sf.q * m.sf.qu then
            return (r % m.sf.qu) || substr(m.sf.units, qu, 1) ,
                              || right(r //m.sf.qu, m.sf.width, 0)
            /* overflow because of rounding, thus 1u000: loop back */
        q = q + 1
        end
endProcedure fUnitsNN

fUnitsIni: procedure expose m.
parse arg sc, us, fact, prod, wi
    sf = 'FMT.UNITS.'sc'.F'
    sp = 'FMT.UNITS.'sc'.P'
    m.sf.0 = words(fact)
    if length(us) + 1 <> m.sf.0 then
        call err 'fUnitsIni mismatch' us '<==>' fact
    m.sf.1 = word(fact, 1)
    m.sp.1 = prod
    do wx=2 to m.sf.0
        wx1 = wx-1
        m.sf.wx = word(fact, wx)
        m.sp.wx = m.sp.wx1 * m.sf.wx
        end
    m.sp.0 = m.sf.0
    m.sf.units = us
    m.sf.width = wi
    m.sf.length= 2 * wi + 1
    m.sf = 1
    return
endProcedure fUnitsIni
/* copy fTab end   ****************************************************/
/* copy f begin *******************************************************/
/*--- format with the given format ggA1, ggA2, etc. -----------------*/
f: procedure expose m.
parse arg ggFmt, ggA1, ggA2
    if symbol('M.f_gen.ggFmt') \== 'VAR' then
        call fCache ggFmt, ggFmt
    interpret m.f_gen.ggFmt
endProcedure f

fImm: procedure expose m.
parse arg ggFmt, ggA1
    interpret m.ggFmt
endProcedure fImm

/*--- compile format fmt put in the cache with address a ------------*/
fCache: procedure expose m.
parse arg a, fmt
    if a == '%.' then do
        if symbol('m.f_gen0') == 'VAR' then
            m.f_gen0 = m.f_gen0 + 1
        else
            m.f_gen0 = 1
        a = '%.'m.f_gen0
        end
    else if symbol('M.f_gen.a') == 'VAR' then
        return a
    if symbol('m.f_s_0') \== 'VAR' | m.f_s_0 == 0 then
        m.f_s_0 = 1
    else do
        m.f_s_0 = m.f_s_0 + 1
        f_s = 'F_S_'m.f_s_0
        end
    call scanSrc f_s, fmt
    m.f_gen.a = 'return' fGen(f_s)
    m.f_s_0 = m.f_s_0 - 1
    if \ scanEnd(f_s) then
        call scanErr f_s, "bad specifier '"m.f_s.tok"'"
    return a
endProcedure fCache

/*--------------------------------------------------------------------
 Format generator    should be compatible with fPrint|
 <<<< + extension of fPrint, - in fPrint but not implemented

   %%  %@ the escaped char
   ('@' argN? '.'? field)?'%' flags? width? ('.' precision)? specifier

 specifier: is the most significant one and defines the type

 - c  Character rigPad or lefPad, prec ==> substr(..., prec)
 -  C Cut %-nC = left(v,n), %nC = right(v,n) %n.mC = substr(m, n)
 - hH Characters in hex
 - iI Signed decimal integer (padded or cut)
 - eE Scientific notation (mantissa/exponent) using e character 3.92e+2
 - S  Strip (both)
 - txy time date formatting from format x to format y
 - (,) call format generator routine with name 'fGen' || field
 Flags:
 - -  Left-justify within the given field width; Right is default
 - +  print '+' before non negative numbers
 -' ' print ' ' before non negative numbers
 - /  cut to length
----------------------------------------------------------------------*/
fGen: procedure expose m.
parse arg f_s
    ax = 0
    cd = ''
    do forever
        txt = fText(f_s)
        if txt \== '' then
            cd = cd '||' quote(txt, "'")
        if scanEnd(f_s) then
            leave
        if \ scanLit(f_s, '@') then do
            ax = ax + 1
            af = ''
            hasDot = 0
            end
        else do
            if scanWhile(f_s, '0123456789') then
                ax = m.f_s.tok
            else if ax < 1 then
                ax = 1
            hasDot = scanLit(f_s, '.')
            af = fText(f_s)
            end
        if \ scanLit(f_s, '%') then
            call scanErr f_s, 'missing %'
        call scanWhile f_s, '-+ /'
        flags = m.f_s.tok
        if scanWhile(f_s, '0123456789') then
            len = m.f_s.tok
        else
            len = 0
        if \ scanLit(f_s, '.') then
            prec  = ''
        else do
            call scanWhile f_s, '0123456789'
            prec = m.f_s.tok
            end
        call scanChar f_s, 1
        sp = m.f_s.tok
        if ax < 3 then
            aa = 'ggA'ax
        else
            aa = 'arg(' || (ax+1) || ')'
        if af \== '' | hasDot then
            aa = rxMGet(aa, af)
        if sp == 'c' then do
            if prec \== '' then
                aa = 'substr('aa',' prec')'
            if len < 1 then
                cd = cd '||' aa
            else if pos('-', flags) > 0 then
                cd = cd '|| lefPad('aa',' len')'
            else
                cd = cd '|| rigPad('aa',' len')'
            end
        else if sp == 'C' then do
            if prec \== '' then do
                cd = cd '|| substr('aa',' prec
                if len < 1 then
                    cd = cd')'
                else
                    cd = cd',' len')'
                end
            else if len < 1 then
                cd = cd '||' aa
            else if pos('-', flags) > 0 then
                cd = cd '|| left('aa',' len')'
            else
                cd = cd '|| right('aa',' len')'
            end
        else if sp == 'H' then
            cd = cd "|| fH("aa"," len',' (pos('-', flags) > 0)')'
        else if sp == 'h' then
            cd = cd "|| translate(fH("aa", '"siL"'), 'abcdef','ABCDEF')"
        else if sp == 'i' then
            cd = cd "|| fI("aa"," len", '"flags"'," word(prec 0, 1)")"
        else if sp == 'I' then
            cd = cd "|| fI("aa"," len", '/"flags"'," word(prec 0, 1)")"
        else if sp == 'E' | sp == 'e' then do
            if len = 0 then
                len = 8
            if prec = '' then
                prec = len - 6
            cd = cd "|| fE("aa"," len"," prec", '"sp"', '"flags"')"
            end
        else if sp = 'S' then
            cd = cd '|| strip('aa')'
        else if sp = 't' then do
            call scanChar f_s, 2
            cd = cd '||' fTstGen(m.f_s.tok, aa)
            end
        else if sp = '(' then do
            if af == '' | flags \== '' | len \== 0 | prec \== '' then
                call scanErr f_s, "bad call shoud be @sub%("
            interpret "cRes = fGen"af"(f_s, ax)"
            cd = cd '||' cRes
            if \ scanLit(f_s, '%)') then
                if \ scanEnd(f_s) then
                    call scanErr f_s, '%) to end call' af 'expected'
            end
        else do
            call scanBack f_s, '%'sp
            leave
            end
        end
    if cd \== '' then
        return substr(cd, 5)
    else
        return "''"
endProcedure fGen

fText: procedure expose m.
parse arg f_s
    res = ''
    do forever
        if scanUntil(f_s, '@%') then
            res = res || m.f_s.tok
        if \ scanLit(f_s, '%%', '%@') then
            return res
        res = res || substr(m.f_s.tok, 2)
        end
endProcedure fText

fAll: procedure expose m.
parse arg fmt, rdr
    i = inOpen(rdr)
    o = jReadO(i)
    do while o \== ''
        call out f(fmt, o)
        o = jReadO(i)
        end
    call jClose i
    return
endProcedure fAll

/*--- format character2hex (if not sql null) -------------------------*/
fH: procedure expose m.
parse arg v, l, leftJ
    if v \== m.sqlNull then
        v = c2x(v)
    if length(v) > l then
        return v
    else if leftJ \== 1 then
        return right(v, l)
    else
        return left(v, l)
endProcedure fH

/*--- format integer or fixPoint Decimal -----------------------------*/
fI: procedure expose m.
parse arg v, l, flags, d
    if \ datatype(v, 'n') then
        return fRigLeft(strip(v), l, flags)
    v = format(v, , d, 0)
    if pos('+', flags) > 0 then
        if \ abbrev(v, '-') then
            v = '+'v
    if length(v) > l then
        if pos('/', flags) > 0 then
            return left('', l, '*')
        else
            return v
    return fRigLefPad(v, l, flags)
endProcedure fI

/*--- format with exponent l=total output len
                           d=number of digits after . in mantissa
                           c=exponent character
                           flags: - to ouput text left justified
    differences: exponent is always printed but without +
                 overflow ==> expand exponent, *****, 0e-999 ---------*/
fE: procedure expose m.
parse arg v, l, d, c, flags
    if \ datatype(v, 'n') then
        return fRigLeft(v, l, flags)
    if pos(' ', flags) < 1 then
        if v >=  0 then
            if pos('+', flags) > 0 then
                return '+'substr(fE(v, l, d, c, ' 'flags), 2)
            else
                return substr(fE(v, l+1, d+1, c, ' 'flags), 2)
    x = format(v, 2, d, 7, 0)
    m = 2 + d + (d>0)
    call assert "length(x) == m+9", 'm x length(x)'
    if substr(x, m+1) = '' then
        return left(x, m)c || left('', l-m-1, 0)
    call assert "substr(x, m+1, 1) == 'E'","exponenent in x not at m"
    y = verify(x, '0', 'n',  m+3)
    call assert 'y>0'
    if substr(x, m+1, 2) == 'E+' then do
        if m+10-y <= l-m-1 then
             return left(x,m)c || right(x, l-m-1)
        z = l - 4 - (m+10-y)
        end
    else if substr(x, m+1, 2) == 'E-' then do
        if m+10-y <= l-m-2 then
             return left(x,m)c'-'right(x, l-m-2)
        z = l - 5 - (m+10-y)
        end
    else
        call err 'bad x' x
    if z >= -1 & max(0, z) < d then
        return fE(v, l, max(0, z), c, flags)
    else if substr(x, m+1, 2) == 'E-' then
        return left(x,1)'0'c'-'left('', l-4, 9)
    else
        return left('', l, '*')
endProcedure fE

/*--- right or left with truncation ----------------------------------*/
fRigLeft: procedure expose m
parse arg s, len, flags
    if length(s) = len then
        return s
    else if pos('-', flags) > 0 | length(s) > len then
        return left(s, len)
    else
        return right(s, len)
endProcedure fRigLefPad

/*--- right or left pad without truncation --------------------------*/
fRigLefPad: procedure expose m
parse arg s, len, flags
    if pos('-', flags) > 0 then
        if length(strip(s, 't')) >= len then
            return strip(s, 't')
        else
            return left(s, len)
    else
        if length(strip(s, 'l')) >= len then
            return strip(s, 'l')
        else
            return right(s, len)
endProcedure fRigLefPad

/*--- generate timestamp formats: from format c to format d ----------*/
fTstGen: procedure expose m.
parse arg c 2 d, s
    cd = c || d
    if symbol('m.f_tstFo.c') \== 'VAR' ,
         | symbol('m.f_tstFo.d') \== 'VAR' then do
         if m.f_tstIni == 1 then
             call err "bad timestamp from or to format '"cd"'"
        m.f_tstIni = 1
        a = 'F_TSTFO.'
                      /* Y: A = 2010, M:B=Januar,
                         H=Stunde A=0 B=10 C=20
                         jkl = julian day
                         yyyy-mm-dd-hh.mm.ss.ffffff    */
        call mPut a'S', 'yz34-56-78-hi.mn.st.abcdef'
        call mPut a's', 'yz34-56-78-hi.mn.st'
        call mPut a' '
        call mPut a'D', 'yz345678'
        call mPut a'd',  '345678'
        call mPut a't',            'hi.mn.st'
        call mPut a'T',            'hi:mn:st'
        call mPut a'E', '78.56.yz34'
        call mPut a'e', '78.56.34'
        call mPut a'Y',      'YM78'
        call mPut a'M',    'M78himns'
        call mPut a'A',    'A8himnst'
        call mPut a'H',           'Himnst'
        call mPut a'n', 'yz345678 hi:mn:st'
        return fTstGen(cd, s)
        end

    if pos('(', s) > 0 | length(s) > 20 then
        return "fImm('F_GEN."fCache('%t'cd, '%t'cd)"'," s")"
    if c == ' ' then
        return "fImm('F_GEN."fCache('%tn'd, '%tn'd)"',date('S') time())"
    f = m.f_tstFo.c
    if d = ' ' then
        d = 's'
    t = m.f_tstFo.d
    if f == t then
        return s
    r = "translate("quote(t, "'")"," s "," quote(f, "'")")"

    if pos('yz34', t) > 0 & pos('yz34', f) < 1 then do
        if pos('34', f) > 0 then
            r = "overlay('20'," r"," pos('yz', t)")"
        else if pos('Y', f) > 0 then
            r = fTstGY2Year(r, "substr("s"," pos('Y', f)", 1)", 4,
                , pos('yz34', t))
        else
            r = "overlay('0000'," r"," pos('yz34', t)")"
        end
    else if pos('34', t) > 0 & pos('34', f) < 1 then do
        if pos('Y', f) > 0 then
            r = fTstGY2Year(r, "substr("s"," pos('Y', f)", 1)", 2,
                   , pos('34', t))
        else
            r = "overlay('00'," r"," pos('34', t)")"
        end
    if pos('Y', t) > 0 & pos('Y', f) < 1 then do
        if pos('yz34', f) > 0 then
            r = fTstGYear2Y(r, "substr("s"," pos('yz34', f)", 4)" ,
                   , pos('Y', t))
        else if pos('34', f) > 0 then
            r = fTstGYear2Y(r, "substr("s"," pos('34', f)", 2)" ,
                   , pos('Y', t))
        else
            r = "overlay('?'," r"," pos('Y', t)")"
        end
    if pos('M', t) > 0 & pos('M', f) < 1 then do
        if pos('56', f) > 0 then
            r = fTstG02A(r, "substr("s"," pos('56', f)", 2)" ,
                   , pos('M', t))
        else
            r  = "overlay('?'," r"," pos('M', t)")"
        end
    if pos('56', t) > 0 & pos('56', f) < 1 then
        if pos('M', f) > 0 then
            r = fTstGA2N2(r, "substr("s"," pos('M', f)", 1)" , "-1",
                   , pos('56', t))
        else
            r = "overlay('01'," r"," pos('56', t)")"
    if pos('78', t) > 0 & pos('78', f) < 1 then
        r = "overlay('01'," r"," pos('78', t)")"
    if pos('A8', t) > 0 & pos('A8', f) < 1 then
        if pos('78', f) > 0 then
            r = fTstG02A(r, "substr("s"," pos('78', f)", 1)" ,
                   , pos('A8', t))
        else
            r = "overlay('A1'," r"," pos('A8', t)")"
    if pos('hi', t) > 0 & pos('hi', f) < 1 then do
        if pos('H', f) > 0 then
            r = fTstGA2N1(r, "substr("s"," pos('H', f)", 1)" , "-1",
                   , pos('hi', t))
        else
            r = "overlay('00'," r"," pos('hi', t)")"
        end
    if pos('i', t) > 0 & pos('i', f) < 1 then
        r = "overlay('0'," r"," pos('i', t)")"
    if pos('H', t) > 0 & pos('H', f) < 1 then do
        if pos('hi', f) > 0 then
            r = fTstG02A(r, "substr("s"," pos('hi', f)", 1)" ,
                   , pos('H', t))
        else
            r  = "overlay('A'," r"," pos('H', t)")"
        end
    if pos('mn', t) > 0 & pos('mn', f) < 1 then
        r = "overlay('00'," r"," pos('mn', t)")"
    if pos('st', t) > 0 then do
        if pos('st', f) < 1 then
            if pos('s', f) > 0 then
                r = "overlay('0'," r"," pos('t', t)")"
            else
                r = "overlay('00'," r"," pos('st', t)")"
        end
    else if pos('s', t) > 0 & pos('s', f) < 1 then
        r = "overlay('0'," r"," pos('s', t)")"
    if pos('abcdef', t) > 0 & pos('abcdef', f) < 1 then
        r = "overlay('000000'," r"," pos('abcdef', t)")"
    return r
endProcedure fTstGen

/*--- from Number overlay Character A=0, B=1 etc.. ------------------*/
fTstG02A: procedure expose m.
parse arg r, n, tx
     return "overlay(substr(m.ut_alfUc," n "+ 1, 1)," r"," tx")"

/*--- from year overlay Y-Character  A=2010 etc..--------------------*/
fTstGYear2Y: procedure expose m.
parse arg r, y, tx
     return fTstG02A(r, "("y "+ 10) // 20", tx)

/*--- from character A=0 overlay 1 digit number ---------------------*/
fTstGA2N1: procedure expose m.
parse arg r, y, dlt, tx
     return "overlay(right(pos("y", m.ut_alfUc)" dlt", 1)," r"," tx")"

/*--- from character A=0 overlay 2 digit number ---------------------*/
fTstGA2N2: procedure expose m.
parse arg r, y, dlt, tx
     return "overlay(right('00' || (pos("y", m.ut_alfUc)" dlt"), 2)",
                    "," r"," tx")"

/*--- from Y yearCharacter overlay 2 or 4 digit year ----------------*/
fTstGY2Year: procedure expose m.
parse arg r, y, len, tx
     if m.f_tstY2ini \== 1 then do  /* generate lookup table */
          m.f_tstY2ini = 1
          n = left(f('%t  '), 4)
          do i = n-15 to n+4   /* 20 values: from -15 years to + 4 */
              j = left(f('%tDY', i'0101'), 1)
              m.f_tstY24.j = i
              m.f_tstY22.j = right(i, 2)
           /* say j m.f_tstY22.j m.f_tstY24.j  */
              end
          end
     if wordPos(len, 2 4) < 1 then
         call err "fTstGYear2Y bad len" len
     return "overlay(mGet('F_TSTY2"len".'"y")," r"," tx")"
endProcedure fTstGY2Year

/* copy f end   *******************************************************/
/* copy err begin *** errorhandling, messages, help    ****************/
errIni: procedure expose m.
    if m.err.ini == 1 then
        return
    call utIni
    m.err.ini     = 1
    m.err.handler = ''
    m.err.cleanup = ';'
    m.err.opt     = ''
    parse source m.err.os .
    m.err.ispf    = 0
    m.err.screen  = 0
    if m.err.os \== 'LINUX' then do
        call adrTso 'profile MsgId'   /* brauchen wir in tsoAlloc| */
        if sysVar('sysISPF') = 'ACTIVE' then do
            m.err.ispf = 1
            address ispExec 'vget zScreen shared'
            m.err.screen = zScreen
            end
        end
    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 & m.err.ispf then
        address ispExec '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
        interpret m.err.handler
    call errSay 'f}'ggTxt
    call errCleanup
    if ggOpt == '' then
        ggOpt = m.err.opt
    upper ggOpt
    if pos('T', ggOpt) > 0  then do
        trace ?r
        say 'trace ?r in err'
        end
    if pos('H', ggOpt) > 0 & m.err.eCat == 'f' then do
        call errSay ' }errorhandler exiting with divide by zero' ,
                                   'to show stackHistory'
        x = 1 / 0
        end
    call errSay ' }errorhandler exiting with exit(12)'
    exit errSetRc(12)
endSubroutine err

/*--- set rc for ispf: -------------------------------------------------
    if a cmd is run by ispStart, its RC is ignored,
         but ISPF passes the value of the shared variable zIspfRc
         back as return code
----------------------------------------------------------------------*/
errSetRc: procedure expose m.
parse arg zIspfRc
    if m.err.ispf then
        address ispExec vput 'zIspfRc' shared
    return zIspfRc
endProcedure errSetRc

/*--- error routine: user message cleanup exit -----------------------*/
errAddCleanup: procedure expose m.
parse arg code
    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
    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
    errCleanup = m.err.cleanup
    if errCleanup <> ';' then do
        m.err.cleanup = ';'
        say 'err cleanup begin' errCleanup
        interpret errCleanup
        say 'err cleanup end' errCleanup
        end
    if symbol('m.tso_ddAll') == 'VAR' then
        call tsoFree m.tso_ddAll, 1
    return
endProcedure errCleanup

/*--- say an errorMessage msg with pref and linesplits --------------*/
errSay: procedure expose m.
parse arg msg
    return saySt(errMsg(msg))

/*--- prefix an errormessage with pref,
          split it into lines at \n to stem m.err -------------------*/
errMsg: procedure expose m.
parse arg msg
    m.err.eCat = 'f'
    do while substr(msg, 2, 1) == '}'
        parse var msg m.err.eCat '}' msg
        end
    res = msg
    if m.err.eCat <> '' then do
       pTxt = ',error,fatal error,input error,s-}scanErr,warning,'
 /*    pTxt = ',error,fatal error,input error,syntax error,warning,' */
       px = pos(','m.err.eCat, pTxt)
       if px < 1 then do
           m.err.eCat = 'f'
           px = pos(','m.err.eCat, pTxt)
           end
       res = substr(pTxt, px+1, pos(',', pTxt, px+2)-px-1)
       if substr(res, 3, 1) == '}' then
           parse var res 2 opt 3 br 4 res
       if opt == '-' then
           res = res msg
       else do
           parse source . . s3 .              /* current rexx */
           res = res 'in' s3':' msg
           end
       end
    return splitNl(err, res)           /* split lines at \n */
endProcedure errMsg

splitNL: procedure expose m.
parse arg st, msg
    bx = 1
    do lx=1 to 20
        ex = pos('\n', msg, bx)
        if ex < bx then
            leave
        m.st.lx = substr(msg, bx, ex-bx)
        bx = ex+2
        end
    m.st.lx = substr(msg, bx)
    m.st.0 = lx
    return st
endProcedure splitNL

/*--- say (part of) the lines of a stem ----------------------------*/
saySt: procedure expose m.
parse arg st, fx, tx
    do lx=word(fx 1, 1) to word(tx m.st.0, 1)
        say m.st.lx
        end
    return st
endProcedure saySt

/*--- out (part of) the lines of a stem ----------------------------*/
outSt: procedure expose m.
parse arg st, fx, tx
    do lx=word(fx 1, 1) to word(tx m.st.0, 1)
        call out m.st.lx
        end
    return st
endProcedure outSt

/*--- say a trace message if m.trace is set --------------------------*/
debug: procedure expose m.
parse arg msg
    if m.debug == 1 then
        say 'debug:' msg
    return
endProcedure debug

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

/*--- assert that the passed rexx expression evaluates to true -------*/
assert:
    interpret 'if ('arg(1)') == 1 then return 1'
    interpret 'call err' quote('assert failed' arg(1) ':' arg(2) '==>'),
                        arg(2)
endProcedure assert

/*--- abend with Message after displaying help -----------------------*/
errHelp: procedure expose m.
parse arg msg
    call errSay 'i}'msg
    call help 0
    call err 'i}'msg
endProcedure errHelp

/*--- display the first comment block of the source as help ----------*/
help: procedure expose m.
parse arg doClear
    if doClear \== 0 then
        address tso 'clear'
    parse source . . s3 .
    say right(' help for rexx' s3, 72, '*')
    do lx=1 while pos('/*', sourceLine(lx)) < 1
        if lx > 10 then
            return err('initial commentblock not found for help')
        end
    doInc = 1
    ho = m.err.helpOpt
    do lx=lx+1 to sourceline() while pos('*/', sourceline(lx)) = 0
        li = strip(sourceLine(lx), 't')
        cx = lastPos('{', li)
        if cx > 0 then do
            if length(ho) = 1 then
                doInc = cx = length(li) | pos(ho, li, cx+1) > 0
            li = left(li, cx-1)
            end
        if doInc then
            say li
        end
    say right(' end help for rexx' s3, 72, '*')
    return 4
endProcedure help

/* copy err end   *****************************************************/
/* copy ut begin  *****************************************************/
utIni: procedure expose m.
    if m.ut_ini == 1 then
        return
    m.ut_ini = 1
    m.ut_digits = '0123456789'
    m.ut_alfLC  = 'abcdefghijklmnopqrstuvwxyz'
    m.ut_alfUC  = translate(m.ut_alfLc)
    m.ut_Alfa   = m.ut_alfLc || m.ut_alfUC
    m.ut_alfNum = m.ut_alfa || m.ut_digits
    m.ut_alfDot = m.ut_alfNum || '.'
    m.ut_alfId  = m.ut_alfNum'_' /* avoid rexx allowed @ # $ ¬ . | ? */
    m.ut_alfIdN1 = m.ut_digits    /* not as first character */
    m.ut_rxId   = m.ut_AlfNum'@#$?' /* charset puff mit ¬*/
    m.ut_rxDot  = '.'m.ut_rxId
    m.ut_rxN1   = '.0123456789'
    m.ut_alfPrint = m.ut_alfNum'+-*/=()¢!{}<> .:,;?|''"%&#@$£\_'
    return
endProcedure utIni
/*--- 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 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 ------------------------------*/
utTime: procedure expose m.
    return time() 'ela='time('E') 'cpu='sysvar('syscpu'),
            'su='sysvar('syssrv')

/*--- 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

/*--- left without truncation ----------------------------------------*/
lefPad: procedure expose m
parse arg s, len
    if length(strip(s, 't')) >= len then
        return strip(s, 't')
    return left(s, len)
endProcedure lefPad

/*--- right without truncation ---------------------------------------*/
rigPad: procedure expose m
parse arg s, len
    if length(strip(s, 'l')) >= len then
        return strip(s, 'l')
    return right(s, len)
endProcedure rigPad

/*--- 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

/*--- translate to lowercase -----------------------------------------*/
ut2lc: procedure expose m.
parse arg s
    return translate(s, m.ut_alfLc, m.ut_alfUc)

/*--- verify an id ---------------------------------------------------*/
verifId: procedure expose m.
    parse arg src, extra
    if pos(left(src, 1), m.ut_alfIdN1) > 0 then
        return 1
    else
        return verify(src, m.ut_alfId || extra, 'n')

/*--- 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
    do ax = 2 by 2 to arg()
        src = repAl2(src, src, arg(ax), arg(ax+1))
        end
    return src
endProcedure repAll

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

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

utc2d: procedure expose m.
parse arg ch
    cx = length(ch) // 3
    if cx = 0 then
        cx = 3
    res = c2d(left(ch, cx))
    do cx=cx+1 by 3 to length(ch)
        res = res * 16777216 + c2d(substr(ch, cx, 3))
        end
    return res
/* copy ut end ********************************************************/
}¢--- A540769.WK.REXX(WSH4) cre=2015-08-21 mod=2016-07-29-09.44.28 A540769 -----
/* rexx **********end;**************************************************
  wsh: walter's rexx shell                                   version 4.1
  interfaces:                                                   18. 2.15
      edit macro: for adhoc evaluation or programming
              either block selection: q or qq and b or a
              oder mit Directives ($#...) im Text
      wsh i:  tso interpreter
      batch:  input in dd wsh
      docu:   http://chw20025641/host/db2wiki/pmwiki.php?n=Main.Wsh
      syntax: http://chw20025641/host/db2wiki/pmwiki.php?n=Main.WshSyn
|||achtung $@.sqlRdr() funktioniert nicht nur $@..¢sqlRdr() $!
|||    sqlSel schreib !$#out |||||
|||    einheitliches sql select/rdr syntax in wsh (mit ftab oder ohne|)
|||sql select aus rz2 muss wie csmExRx erfolgen (via WSH) ||||

--- history ------------------------------------------------------------
16. 1.15 f: get/put read/write in/out Object/Strings transparent weiter
*********/ /*** end of help ********************************************
17.11.14 f: iirz2p ==> plex Buchstaben
17.06.14 f: %tS %tT und %tN mit MicroSekunden
16.06.14 csmCopy auch für LoadModule usw.
30.05.14 fix sql4obj fuer rcm profex
14.04.14 class vor obj, mit lazy
19.03.14 ii = installation Info
 9.01.14 walter: redesign formatting (fmt eliminiert), csm.div.p0.exec
 3.12.13 walter: db2 interface radikal geputzt
 3.10.13 walter: uCount fuer TSO <-> unitCount fuer Csm
23. 9.13 walter: ws2 syntax
 6. 2.13 w.keller csmCopy aendert mgmtClas für neue lokale Files
23. 1.13 w.keller sqlErrHandler sowie sqlRx und sql
11. 6.12 w.keller sqlUpdComLoop
23. 5.12 w.keller fix sqlStmt: drop accepts -204
31. 3.12 w.keller sql Query interface incl. sql über CSM
10. 2.12 w.keller div catTb* und eLong
 2. 6.11 w.keller sql error with current location and dsnTiar
 2. 5.11 w.keller sqlStmt etc..
16. 3.11 w.keller basic new r '' ==> r m.class_O
28. 2.11 w.keller compFile by exprBlock, fix $@do forever, |
 7. 2.11 w.keller cleanup block / with sqlPush....
 2. 2.11 w.keller simplified syntax, ast for blocks, ? for help
19. 6.10 w.keller open/close stacked mit jUsers, framed eliminiert
19. 5.10 w.keller class wird direkt erzeugt ohne temporary
18. 4.10 w.keller scanUtilInto ==> testCase noch einbauen
17. 4.10 w.keller oMutate in fileTsoList eingebaut
14. 1.10 w.keller batch interface no longer dies on openIfNotYet
                  CSM.RZ1.P0.EXEC korrigiert
***********************************************************************/

/*--- main code wsh --------------------------------------------------*/
    call errReset 'hI'
    m.myLib  = 'DSN.DB2.EXEC'
    m.myVers = 'v41 18. 2.15'
    call wshLog
    parse arg spec
    isEdit = 0
    if spec = '' & m.err.ispf then do /* z/OS edit macro */
        isEdit = adrEdit('macro (spec) NOPROCESS', '*') == 0
        if isEdit then do
            call adrEdit '(d) = dataset'
            call adrEdit '(m) = member'
            m.editDsn = dsnSetMbr(d, m)
            if spec = '' & abbrev(m.editDsn, 'A540769.WK.REXX(WS') then
                spec = 't'
            end
        end
    if spec = '?' then
        return help()
    if translate(word(spec, 1)) == 'T' then
        return wshTst(subword(spec, 2))
    f1 = spec
    rest = ''
    inp = ''
    out = ''
    call wshIni
    if m.err.os == 'TSO' then do
        if isEdit then do
            parse value wshEditBegin(spec) with inp out
            end
        else if sysvar('sysEnv') = 'FORE' then do
            end
        else do
            inp = file('dd(wsh)')
            useOut = listDsi('OUT FILE')
            if \ (useOut = 16 & sysReason = 2) then
                out = file('dd(out)')
            end
        end
    else if m.err.os == 'LINUX' then do
        inp = file('&in')
        out = file('&out')
        end
    else
        call err 'implement wsh for os' m.err.os
    m.wshInfo = 'compile'
    m.wsh_exitCC = 0
    call compRun spec, inp, out, wshInfo
    if isEdit then
        call wshEditEnd
exit m.wsh_exitCC

wshLog: procedure expose m.
parse arg msg, st
    lNm = 'tss.ska.db2.wshlog'
    f1 = dsnAlloc('dd(log) mod' lNm '::f', , , '*')
    if datatype(f1, 'n') then do
        lN2 = lNm'.R' || ( random() // 19)
        f1 = dsnAlloc('dd(log) old' lN2 '::f', , , '*')
        if datatype(f1, 'n') then do
            say 'could not allocate log' lNm lN2
            return
            end
        end
    parse source . . s3 .
    o.1 = m.myLib'('s3')' word(m.myVers, 1) sysvar(sysnode) ,
            'j='mvsvar('symdef', 'jobname') ,
             'u='userid() date('s') time()
    if msg <> '' then
        o.2 = left(msg, 80)
    ox = 1 + (msg <> '')
    if st <> '' then do sx=1 to m.st.0
        ox = ox+1
        o.ox = left(m.st.sx, 80)
        end
    call writedd log, o., ox
    call tsoClose log
    call tsoFree log
    return
endProcedure wshLog
/*--- test hook ----------------------------------------------------*/
wshHook_T: procedure expose m.
parse arg cmp
trace ?r
    rest = strip(scanLook(m.cmp.scan))
    call jClose m.cmp.scan
    return wshTst(rest)
endProcedure wshHook_t

wshTst: procedure expose m.
parse arg rest
    m.tst_csm = 1
    if rest = '' then do /* default */
        say funits(3e7, 'd')
        call err tstEnd
        call csmcopy 'CMN.DIV.P0.DB2J.#000197.LLB' ,
                   , 'RZ1/A540769.TST.LXB'
        return 0
        call csmcopy 'RZ1/A540769.TST.PS' ,
                   , 'RZ4/A540769.TST.PO3(EINS)'
        return 0
        call csmcopy 'RZ1/A540769.TST.LCTL(BBB)',
                   , 'RZ4/A540769.TST.PS'
        return 0
        call csmcopy 'A540769.WK.LLB' ,
                   , 'RZ1/A540769.TST.LLB'
        call csmCopL 'RZ4/A540769.WK.JCL(QZ*)',
                   , 'RZ1/A540769.TST.yCL'
        return 0
        call tstfTst
        call sqlConnect DBAF
        call catColCom 'cmnBatch', 'DSN_PGROUP_TABLE',
                     , 'cmnBatch', 'DSN_PGROUP_TABLE_new'
        call sqlDisConnect DBAF
        return 0
        end
    c = ''
    do wx=1 to words(rest)
        c = c 'call tst'word(rest, wx)';'
        end
    if wx > 2 then
        c = c 'call tstTotal;'
    say 'wsh interpreting' c
    interpret c
    exit 0
endProcedure wshTst

/*--- i hook: interpret user input: rexx, expr, data or shell -------*/
wshHook_I: procedure expose m.
parse arg cmp
    inp = strip(scanLook(m.cmp.scan))
    call scanClose m.cmp.scan
    mode = '*'
    do forever
        if pos(left(inp, 1), '/;:*@.-=') > 0 then
            parse var inp mode 2 inp
        if mode == '/' then
            exit 0
        mode = translate(mode, ';', ':')
        if inp <> '' then do
            say 'as' mode 'interpreting' inp
            if mode = ';' then
                interpret inp
            else if mode = '*' then
                interpret 'say' inp
            else do
                call errReset 'h', 'say "******* intercepting error";',
                    'call errSay ggTxt; return "";'
                call oRun compile(comp(jBuf(inp)), mode)
                call errReset 'h'
                end
            end
        say 'enter' mode 'expression, / for end, ; or * for Rexx' ,
                                                 '@ . - =  for wsh'
        parse pull inp
        end
endProcedure wshInter

/*--- sql hook -----------------------------------------------------*/
wshHook_S: procedure expose m.
parse arg cmp
    s = m.cmp.scan
    ki = '='
    call scanVerify s, m.comp_chSpa
    if scanVerify(s, m.comp_chKind) then
        ki = left(m.s.tok, 1)
    call scanChar s
    rest = m.s.tok
    call scanNl s
    dbSy = word(rest, 1)
    if abbrev(dbSy, '-') | \ (length(dbSy) = 4 ,
                   | (length(dbsy) = 8 & substr(dbSy,4,1) == '/')) then
        dbSy = ''
    else
        rest = subWord(rest, 2)
    res = compAST(cmp, 'P', ' f', '',
        , compAstAddOp(cmp, compUnit(cmp, ki, '$#'), '@'))
    call mAdd res, compAst(cmp, 'c', "call sqlConnect '"dbSy"'",
        "; if \ sqlStmts( , 'rb ret', '"rest"') then m.wsh_exitCC=8" ,
        "; call sqlDisConnect;" )
     return res
endProcedure wshHook_s


wshEditBegin: procedure expose m.
parse arg spec
    dst = ''
    li = ''
    m.wsh.editHdr = 0
    pc = adrEdit("process dest range Q", 0 4 8 12 16)
    if pc = 16 then
        call err 'bad range must be q'
    if pc = 0 | pc = 8 then do
        call adrEdit "(rFi) = lineNum .zFrange"
        call adrEdit "(rLa) = lineNum .zLrange"
    /*  say 'range' rFi '-' rLa */
        end
    else do
        rFi = ''
    /*  say 'no range' */
        end
    if pc = 0 | pc = 4 then do
        call adrEdit "(dst) = lineNum .zDest"
    /*  say 'dest' dst */
        dst = dst + 1
        end
    else do
    /*  say 'no dest' */
        if adrEdit("find first '$#out' 1", 4) = 0 then do
            call adrEdit "(dst) = cursor"
    /*      say '$#out' dst   */
            call adrEdit "(li) = line" dst
            m.wsh.editHdr = 1
            end
        end
    m.wsh.editDst = dst
    m.wsh.editOut = ''
    if dst \== '' then do
        m.wsh.editOut = jOpen(jBufTxt(), '>')
        if m.wsh.editHdr then
            call jWrite m.wsh.editOut, left(li, 50) date('s') time()
        end
    if rFi == '' then do
        call adrEdit "(zLa) = lineNum .zl"
        if adrEdit("find first '$#' 1", 4) = 0 then do
            call adrEdit "(rFi) = cursor"
            call adrEdit "(li) = line" rFi
            if abbrev(li, '$#out') | abbrev(li, '$#end') then
                rFi = 1
            if rFi < dst & dst \== '' then
                rLa = dst-1
            else
                rLa = zLa
            end
        else do
            rFi = 1
            rLa = zLa
            end
        end
    /*  say 'range' c1 'rc' pc':' rFi '-' rLa   'after' dst */

    m.wsh.editIn = jOpen(jBuf(), m.j.cWri)
    do lx=rFi to rLa
        call adrEdit "(li) = line" lx
        call jWrite m.wsh.editIn, li
        end
    call errReset 'h',
             , 'return wshEditErrH(ggTxt, ' rFi',' rLa')'
    return jClose(m.wsh.editIn) m.wsh.editOut
endProcedure wshEditBegin

wshEditEnd: procedure expose m.
    call errReset 'h'
    if m.wsh.editOut == '' then
        return 0
    call jClose(m.wsh.editOut)
    lab = wshEditInsLinSt(m.wsh.editDst, 0, , m.wsh.editOut'.BUF')
    call wshEditLocate max(1, m.wsh.editDst-7)
    return 1
endProcedure wshEditEnd

wshEditLocate: procedure
parse arg ln
    call adrEdit '(la) = linenum .zl'
    call adrEdit 'locate '  max(1, min(ln, la - 37))
    return
endProcedure wshEditLocate

wshEditErrH: procedure expose m.
parse arg ggTxt, rFi, rLa
    call errCleanup
    call errReset 'h'
    call splitNl err, errMsg(' }'ggTxt)
    call mMove err, 1, 2
    isScan = 0
    if wordPos("pos", m.err.4) > 0 ,
        & pos(" in line ", m.err.4) > 0 then do
        parse var m.err.4 "pos " pos .     " in line " lin":"
        if pos = '' then do
            parse var m.err.4 " line " lin":"
            pos = 0
            end
        isScan = lin \== ''
        end
    m.err.1 = '***' m.wshInfo 'error ***'
    if m.wshInfo=='compile' & isScan then do
        do sx=1 to m.err.0
            call out m.err.sx
            end
        lab = rFi + lin
        if pos \= '' then
            lab = wshEditInsLin(lab, 'msgline', right('*',pos))
        lab = wshEditInsLinSt((rFi+lin),0, 'msgline', err)
        call wshEditLocate rFi+lin-25
        end
    else do
        if m.wsh.editOut \== '' then do
            do sx=1 to m.err.0
                call jWrite m.wsh.editOut, m.err.sx
                end
            lab = wshEditInsLinSt(m.wsh.editDst, 0, ,
                , m.wsh.editOut'.BUF')
            call wshEditInsLinSt m.wsh.editDst, m.wsh.editHdr,
                , msgline, err
            call wshEditLocate max(1, m.wsh.editDst-7)
            end
        else do
            do sx=1 to m.err.0
                say m.err.sx
                end
            end
        end
    call errCleanup
    exit
endSubroutine wshEditErrH

wshEditInsLinCmd: procedure
parse arg wh
    if dataType(wh, 'n')  then do
        if adrEdit("label" wh "= .a", 0 8 12) \= 12 then
            return 'line_before .a ='
        else
            return 'line_after .zl ='
        end
    else if left(wh, 1) == '.' then
        return 'line_before' wh '='
    else
        return wh
endProcedure wshEditInsLinCmd

wshEditInsLin: procedure
parse arg wh, type
    cmd = wshEditInsLinCmd(wh)
    do ax=3 to arg()
        li = strip(arg(ax), 't')
  /*    if li == '' then   ???? leere Zeilen doch anzeigen
            iterate                 ????? */
        if translate(type) = 'MSGLINE' then do while length(li) > 72
            sx = lastPos(' ', li, 72)
            if sx < 10 then
                sx = 72
            one = left(li, sx)
            li = '    'strip(substr(li, sx))
            call adrEdit cmd type "(one)"
            end
        call adrEdit cmd type "(li)", 0 4
        end
    return cmd
endProcedure wshEditInsLin

wshEditInsLinSt: procedure expose m.
parse arg wh, pl, type, st
    if wh == '' then do
        do ox=1 to m.st.0
            say m.st.ox
            end
        return ''
        end
    wh = wh + pl
    cmd = wshEditInsLinCmd(wh)
    do ax=1 to m.st.0
        call wshEditInsLin cmd, type, m.st.ax
        end
    return cmd
endProcedure wshEditInsLinSt


/*** end wsh, begin all copies ****************************************/
/* copy tstAll begin  *************************************************/
/*----------- neu, noch versorgen |||||-------------------------------*/
tstRts: procedure expose m.
    call wshIni
    call sqlConnect dbaf
    call sqlQuery 3, "select * from sysibm.sysTableSpaceSTats" ,
                    "where dbName = 'MF01A1A' and name = 'A150A'",
                    "order by partition  asc"
    do while sqlFetch(3, rr)
        say f('@.DBNAME%-8C.@NAME%-8C @PARTITION %4C' ,rr)
        end
    call sqlDisconnect
endProcedure tstRts

tstWiki:
    call mapReset docs, 'k'
    call addFiles docs, 'n', '/media/wkData/literature/notes'
    call addFiles docs, 'd', '/media/wkData/literature/docs'

    in = jOpen(file('wiki.old'), '<')
    out = jOpen(file('wiki.new'), '>')
    abc = '(:abc: %l%'
    do cx=1 to length(m.ut_alfLC)
        c1 = substr(m.ut_alfLC, cx, 1)
        abc = abc '¢¢#'c1 '|' c1'!!'
        end
    call jWrite out, abc ':)'
    inTxt = 0
    li = m.i
    do lx=1 while jReadVar(in, i)
        if 0 then
            say length(m.i) m.i
        if m.i = '' then
            iterate
        li = m.i
        do forever
            bx = pos('¢=', li)
            if bx < 1 then
                leave
            ex = pos('=!', li)
            if ex <= bx then
                call err '=! before ¢= in' lx li
            li = left(li, bx-1)substr(li,bx+2, ex-bx-2)substr(li,ex+2)
            end
        li = strip(li)
        if abbrev(li, '|') then do
            w = word(substr(li, 2), 1)
            call jWrite out, '¢¢#'w'!! {$:abc}'
            call jWrite out, '|||' substr(li, 2)
            inTxt=1
            iterate
            end
        if \ inTxt then do
            call jWrite out, li
            iterate
            end
        if \ (abbrev(li, '->') | abbrev(li, '#') ,
                | abbrev(li, '¢')) then do
            call jWrite out, '-<' li
            iterate
            end
        cx = 1
        if substr(li, cx, 2) == '->' then
            cx = verify(li, ' ', 'n', cx+2)
        hasCross = substr(li, cx, 1) == '#'
        if hasCross then
            cx = verify(li, ' ', 'n', cx+1)
        ex = verify(li, '!:\, ', 'm', cx)
        ex = ex - (substr(li, ex, 1) \== '!')
        hasBr = substr(li, cx, 1) == '¢'
        if \ hasBr then
            w = substr(li, cx, ex+1-cx)
        else if substr(li, ex, 1) == '!' then
            w = substr(li, cx+1, ex-1-cx)
        else
            call err 'br not closed' substr(w, cx+1, ex-1-cx) 'in' lx li
        hasPdf = right(w, 4) == '.pdf'
        if hasPdf then
            w = left(w, length(w)-4)
        if verify(w, '#?', 'm') > 0 then do
            w = translate(w, '__', '#?')
            say '*** changing to' w 'in' lx li
            end
        o = '-< {def+'w'}'
        o = '-< ¢¢'w'!!'
        k = translate(w)
        if k.k == 1 then
            say '*** doppelter key' k 'in:' lx left(li,80)
        k.k = 1
        dT = ''
        if mapHasKey(docs, k) then do
            parse value mapGet(docs, k) with dT dC dN
            call mapPut docs, k, dT (dC+1) dN
            do tx=1 to length(dT)
                t1 = substr(dT, tx, 1)
                o = o '¢¢Lit'translate(t1)':'word(dN, tx) '|' t1 '!!'
                end
            end
        qSeq = 'nd'
        qq = left(qSeq, 1)
        qx = 0
        do forever
            qx = pos('@'qq, li, qx+1)
            if qx < 1 then do
                qq = substr(qSeq, 1 + pos(qq, qSeq), 1)
                qx=0
                if qq = '' then
                    leave
                else
                    iterate
                end
            if pos(qq, dT) < 1 then do
                say '*** @'qq 'document not found:' lx li
                iterate
                end
            do qb = qx-1 by -1 while substr(li, qb, 1) == ' '
                end
            do qe = qx+2 by 1 while substr(li, qe, 1) == ' '
                end
            if substr(li, qb, 1) == '.' & substr(li, qe, 1) == '.' then
                li = left(li, qb)substr(li, qe+1)
            else
                li = left(li, qb) substr(li, qe)
            end
        o = o':' strip(substr(li, ex+1+(substr(li,ex+1,1)==':')))
        if 0 then say left(li, 30) '==>' left(o, 30)
        call jWrite out, o
        end
    dk = mapKeys(docs)
    do dx=1 to m.dk.0
        parse value mapGet(docs, m.dk.dx) with dT dC dN
        if dC < 1 then
            say '*** document not used:' dT dC dn
        end
    call jClose in
    call jClose out
    return
endProcedure tstWiki

addFiles: procedure expose m.
parse arg m, ty, file
    fl = jOpen(fileList(file(file)), '<')
    do while jRead(fl)
        nm = substr(m.fl, lastPos('/', m.fl)+1)
        k = translate(left(nm, pos('.', nm)-1))
        if \ mapHasKey(m, k) then do
            call mapAdd m, k, ty 0 nm
            end
        else do
            parse value mapGet(m, k) with dT dC dN
            call mapPut m, k, dT || ty 0 dN nm
            end
        end
    call jClose fl
    return
endProcedure addFiles

tstAll: procedure expose m.
    say 'tstAll ws2 25.2.13...............'
    call tstBase
    call tstComp
    call tstDiv
    if m.err.os = 'TSO' then do
        call tstZos
        call tstTut0
        end
    return 0
endProcedure tstAll

/****** tstZos ********************************************************/
tstZOs:
    call tstTime
    call tstII
    call sqlIni
    call tstSqlRx
    call tstSql
    if m.tst_csm \== 0 then
        call tstSqlCsm
    call scanIni
    call tstSqlC
    call tstSqlCsv
    call tstSqlRxUpd
    call tstSqlUpd
    call tstSqlUpdPre
    call tstSqlE
    call tstSqlB
    call tstSqlO1
    call tstSqlO2
    call tstSqlStmt
    call tstSqlStmts
    call tstSqlUpdComLoop
    call tstSqls1
    call tstSqlO
    call tstSqlFTab
    call tstSqlFTab2
    call tstSqlFTab3
    call tstSqlFTab4
    call tstsql4obj
    call tstTotal
    return
endProcedure tstZOs

tstWshBatch:
    call adrTso 'alloc dd(WSH) shr dsn(WK.TEXT(WSHBATCH))'
    call wshBatch
    return adrTso('free dd(WSH)')

tstLmdTiming:
parse arg lev
    say timing() lev
    call lmdBegin abc, lev
    c = 0
    do while lmdNext(abc, st.)
        c = c + st.0
        end
    call lmdEnd   abc
    say timing() lev 'with group - without reading' c
    call adrIsp 'lmdinit listid(lmdId) level('lev')'
    do c=0 while adrIsp('lmdlist listid(&lmdId) dataset(abc)', 8) = 0
        end
    call adrIsp 'lmdfree listid(&lmdId)'
    say timing() lev 'with list' c
    return
endProcedure lmdTiming

tstCsi: procedure expose m.
    if 0 then do
        call lmd 'A540769.*K'
        call tstCsiCla 'A540769.WK.REXX'
        call tstCsiCla 'A540769.AAA.DATASETS'
        call tstCsiCla 'A540769.RRR.DATASETS'
        end
    if 0 then do
        call tstCsiOpNx 'A540769.WK.*E*'
        call tstCsiOpNx 'A540769.AAA.DATASETS'
        call tstCsiOpNx 'A540769.RRR.DATASETS'
        end
    if 1 then do
        call tstCsiNxCl 'A540769.WK.**'
        call tstCsiNxCl 'DBTF.M*.**'
        call tstCsiNxCl 'DBTF.BE*.**'
        end
    return
tstCsi: procedure expose m.
 /* call lmd 'A540769.*K' */
    call tstCsi1   'A540769.WK.REXX'
    call tstCsi1   'A540769.AAA.DATASETS'
    call tstCsi1   'A540769.RRR.DATASETS'
    return

tstCsiCla:
parse arg ds
    say ds '-->' csiCla(ds)
    return

tstCsiOpNx: procedure expose m.
parse arg ds
    m = 'NUE123'
    s = 'res89'
    flds = 'devtyp volser mgmtclas comudsiz NOBYTTRK UDATASIZ HARBA'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    do while csiNext(m, s)
        say m.s     'dev' c2x(m.s.devTyp) ,
                    'vol' m.s.volSer 'cla' m.s.mgmtclas,
                    'comuDsiz' m.s.comuDsiz 'noBytTrk' m.s.noBytTrk,
                    'udatasiz' c2x(m.s.udatasiz) ,
                    'harba' c2x(m.s.harba)
        end
    return

tstCsiNxCl: procedure expose m.
parse arg ds
    m = 'ABC123'
    s = 'efg89'
    flds = 'devtyp volser mgmtclas'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    say timing() 'begin'
    do i=1 while csiNext(m, s)
        nn = csiArcTape(m.s.volser, m.s.mgmtClas, m.s.devtyp, m.s)
/*      oo = csiCla(strip(m.s))
        if oo <> nn then
            say nn '<>' oo m.s
 */     if i // 1000 = 0 then
            say timing() i nn m.s
        end
    say timing() (i-1) nn m.s
    return
endProcedure tstCsiNxCl

listCatClass: procedure expose m.
parse upper arg dsn
    rt = adrTso("listcat volume entry('"dsn"')", 4)
    /* say 'listct rc =' rt 'lines' m.tso_trap.0 */
    cl = ''
    vo = ''
    if word(m.tso_trap.1, 3) \== dsn then
        call out 'err ??? for dsn' dsn 'bad first line' m.tso_trap.1
    else if pos('NOT FOUND', m.tso_trap.1) > 0 then
        return 'notFound'
    else if word(m.tso_trap.1, 1)\== 'NONVSAM' then
        call out 'err ??? for dsn' dsn 'bad first line' m.tso_trap.1
    do tx=2 to m.tso_trap.0 while vo = '' ,
              & left(m.tso_trap.tx, 1) = ' '
     /* say m.tso_trap.tx */
        p = pos('MANAGEMENTCLASS-', m.tso_trap.tx)
        if p > 0 then
            vo = strip(word(substr(m.tso_trap.tx, p+16), 1), 'l', '-')
        p = pos('VOLSER--', m.tso_trap.tx)
        if p > 0 then
            vo = strip(word(substr(m.tso_trap.tx, p+6), 1), 'l', '-')
        p = pos('DEVCLASS--', m.tso_trap.tx)
            dt = strip(word(substr(m.tso_trap.tx, p+8), 1), 'l', '-')
        end
   /*  say 'lc' cl 'vo' vo 'dt' dt 'dsn' dsn */
    if vo = '' then
        call out '??? err no volume for dsn' dsn
    else if vo = 'ARCIVE' then
        res =  'arcive'
    else if cl = '' then
        res = 'tape'
    else
        res = cl
    if   abbrev(res, 'ar') \= abbrev(dt, "X'0") ,
       | abbrev(res, 'ta') \= abbrev(dt, "X'7") ,
       | (left(res, 1) >= 'A') \= abbrev(dt, "X'3") then
       call out 'err ??? mismatch cl' cl 'vo' vo 'dt' dt 'dsn' dsn
    return res
endProcedure listCatClass

/****** tstDiv ********************************************************/
tstDiv:
    call tstSort
    call tstMat
    call tstMatch
    call tstTotal
    return
endProcedure tstDiv


tstSort: procedure expose m.
    call tstSortComp
    call tstSortComp '<<='
    call tstSortComp 'm.aLe <<= m.aRi'
    call tstSortComp 'if 1 then cmp = m.aLe <<= m.aRi; else call err sd'
    return
endProcedure tstSort

tstSortComp: procedure expose m.
parse arg cmp
/*
$=/tstSort/
    ### start tst tstSort #############################################
    sort 29  c ACHT DREI DREIZEHN EINS ELF FUENF M.I.25 M.I.26 M.I.27 M+
    ..I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI ZWOELF 0 1 1 1 2 2+
    . 3 3 4 4
    sort 22  c ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27 M.I.29 NEUN VIERZ+
    EHN ZEHN ZWOELF 0 1 1 1 2 2 3 3 4 4
    sort 15  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1 1 2 2 3 3 4 4
    sort 8  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1
    sort 1  M.I.29
    sortWords(also als a 05 4 1e2, cmp) a als also 05 1e2 4
    sortWords(also als a 05 4, cmp) a als also 05 4
    sortWords(also als a 05, cmp) a als also 05
    sortWords(also als a, cmp) a als also
    sortWords(also als, cmp) als also
    sortWords(also, cmp) also
    sortWords(, cmp) .
    sortWords(also als a 05 4 1e2, <) a als also 4 05 1e2
    sortWords(also als a 05 4 1e2, >) 1e2 05 4 also als a
$/tstSort/ */
/*
$=/tstSortAscii/
    ### start tst tstSortAscii ########################################
    sort 29  0 1 1 1 2 2 3 3 4 4 ACHT DREI DREIZEHN EINS ELF FUENF M.I.+
    25 M.I.26 M.I.27 M.I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI Z+
    WOELF c
    sort 22  0 1 1 1 2 2 3 3 4 4 ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27+
    . M.I.29 NEUN VIERZEHN ZEHN ZWOELF c
    sort 15  0 1 1 1 2 2 3 3 4 4 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 8  0 1 1 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 1  M.I.29
$/tstSortAscii/ */
    say '### start with comparator' cmp '###'
    if m.err.os == 'LINUX' then
        call tst t, "tstSortAscii"
    else
        call tst t, "tstSort"
    call mAdd mCut(i, 0), eins, zwei, drei, vier, fuenf, sechs,
        ,sieben, acht, neun, zehn, elf, zwoelf, dreizehn, vierzehn
    call mAdd i, 1, 2, 3, 4, 4, 3, 2, 1, 0, 1,
        , 'M.I.25', 'M.I.26', 'M.I.27', 'c', 'M.I.29'
    do yy = m.i.0 by -1 to 1

        do x = 0 to yy
            m.i.0 = x
            call sort i, o, cmp
            m = ''
            la = ''
            if x <> m.o.0 then
                call err 'size mismatch' x '<>' m.o.0
            do y=1 to m.o.0
                m = m m.o.y
                if \ (la << m.o.y) then
                    call err 'sort mismatch' yy x y '\' la '<<' m.o.y
                end
            end
        if yy // 7 = 1 then
           call tstOut t, 'sort' yy m
        do x = 2 to yy
            x1 = x-1
            m.i.x1 = m.i.x
            end
        end
    wi = 'also als a 05 4 1e2'
    do l=words(wi) by -1 to 0
        call tstOut t, 'sortWords('subWord(wi, 1, l)', cmp)' ,
                        sortWords(subWord(wi, 1, l), cmp)
        end
    call tstOut t, 'sortWords('wi', <)' sortWords(wi, '<')
    call tstOut t, 'sortWords('wi', >)' sortWords(wi, '>')
    call tstEnd t
    return
endProcedure tstSort
tstMatch: procedure expose m.
/*
$=/tstMatch/
    ### start tst tstMatch ############################################
    match(eins, e?n*) 1 1 2,i,s trans(E?N*) EiNs
    match(eins, eins) 1 1 0 trans(EINS) EINS
    match(e1nss, e?n*) 1 1 2,1,ss trans(?*) 1ss
    match(eiinss, e?n*) 0 0 -9 trans(E?N*) .
    match(einss, e?n *) 0 0 -9 trans(E?N *) .
    match(ein s, e?n *) 1 1 2,i,s trans(E?N *) EiN s
    match(ein abss  , ?i*b*) 1 1 3,e,n a,ss   trans(?I*B*) eIn aBss  .
    match(ein abss wie gehtsssxdirx und auch , ) 0 0 -9 trans() .
    match(ies000, *000) 1 1 1,ies trans(*000) ies000
    match(xx0x0000, *000) 1 1 1,xx0x0 trans(*000) xx0x0000
    match(000x00000xx, 000*) 1 1 1,x00000xx trans(000*) 000x00000xx
    match(000xx, *0*) 1 1 2,00,xx trans(ab*cd*ef) ab00cdxxef
    match(abcdef, *abcdef*) 1 1 2,, trans(*ABCDEF*) ABCDEF
    match(abcdef, **abcdef***) 1 1 5,,,,, trans(**ABCDEF***) ABCDEF
    match(abcdef, *cd*) 1 1 2,ab,ef trans(*CD*) abCDef
    match(abcdef, *abc*def*) 1 1 3,,, trans(*ABC*DEF*) ABCDEF
    match(abcdef, *bc*e*) 1 1 3,a,d,f trans(*BC*E*) aBCdEf
    match(abcdef, **bc**ef**) 1 1 6,a,,d,,, trans(**BC**EF**) aBCdEF
$/tstMatch/
*/
    call tst t, "tstMatch"
    call scanIni
    call tstOut t, tstMatch1('eins', 'e?n*'                         )
    call tstOut t, tstMatch1('eins', 'eins'                         )
    call tstOut t, tstMatch1('e1nss', 'e?n*', '?*'                  )
    call tstOut t, tstMatch1('eiinss', 'e?n*'                       )
    call tstOut t, tstMatch1('einss', 'e?n *'                       )
    call tstOut t, tstMatch1('ein s', 'e?n *'                       )
    call tstOut t, tstMatch1('ein abss  ', '?i*b*'                  )
    call tstOut t, tstMatch1('ein abss wie gehtsssxdirx und auch ' )
    call tstOut t, tstMatch1('ies000', '*000'                       )
    call tstOut t, tstMatch1('xx0x0000', '*000'                     )
    call tstOut t, tstMatch1('000x00000xx', '000*'                  )
    call tstOut t, tstMatch1('000xx', '*0*', 'ab*cd*ef'             )
    call tstOut t, tstMatch1('abcdef', '*abcdef*'                   )
    call tstOut t, tstMatch1('abcdef', '**abcdef***'                )
    call tstOut t, tstMatch1('abcdef', '*cd*'                       )
    call tstOut t, tstMatch1('abcdef', '*abc*def*'                  )
    call tstOut t, tstMatch1('abcdef', '*bc*e*'                     )
    call tstOut t, tstMatch1('abcdef', '**bc**ef**'                 )
    call tstEnd t
return

tstMatch1:
parse arg w, m, m2
    r = 'match('w',' m')' match(w, m) matchVars(w, m, vv) m.vv.0
    do x=1 to m.vv.0
        r = r','m.vv.x
        end
    if m2 = '' then
        m2 = translate(m)
    r = r 'trans('m2')' matchRep(w, m, m2)
    return r
endProcedure tstMatch1

tstIntRdr: procedure expose m.
    i.1 = "//A540769J JOB (CP00,KE50),'DB2 REO',"
    i.2 = "//         MSGCLASS=T,TIME=1440,"
    i.3 = "//         NOTIFY=&SYSUID,REGION=0M,SCHENV=DB2"
    i.4 = "//*MAIN CLASS=LOG"
    i.5 = "//S1       EXEC PGM=IEFBR14"
    call writeDsn 'RR2/intRdr', i., 5, 1
    return
endProcedure tstIntRdr

tstII: procedure expose m.
/*
$=/tstII/
    ### start tst tstII ###############################################
    iiRz2C(RZ2)       2
    *** err: no rz=R?Y in ii II_RZ2C
    iiRz2C(R?Y)       0
    iiRz2C(RZY)       Y
    iiDbSys2C(de0G)   E
    *** err: no dbSys=D??? in ii II_DB2C
    iiDbSys2C(d???)   0
    iiDbSys2C(DBOF)   F
    iiSys2RZ(S27)     RZ2
    iiMbr2DbSys(DBP5) DVBP
    ii_rz             RZX RZY RZZ RQ2 RR2 RZ2 RZ4
    ii_rz2db.rzx      DE0G DEVG DX0G DPXG
    rr2/dvbp    RR2 R p=R d=RZ2, db DVBP P 1
    iiixVPut 1: RZ2 2 p=B d=RZ2, db DBOF F 0
    iiixVPut 1: RZ2 2 p=B d=RZ2, db DVBP P 1
    iiixVPut 1: RZ2 2 p=B d=RZ2, db DP2G Q 0
$/tstII/
*/
    call tst t, 'tstII'
    call tstOut t, 'iiRz2C(RZ2)      '  iiRz2C(RZ2)
    call tstOut t, 'iiRz2C(R?Y)      '  iiRz2C(R?Y)
    call tstOut t, 'iiRz2C(RZY)      '  iiRz2C(RZY)
    call tstOut t, 'iiDbSys2C(de0G)  '  iiDbSys2C('de0G')
    call tstOut t, 'iiDbSys2C(d???)  '  iiDbSys2C('d???')
    call tstOut t, 'iiDbSys2C(DBOF)  '  iiDbSys2C('DBOF')
    call tstOut t, 'iiSys2RZ(S27)    '  iiSys2RZ(S27)
    call tstOut t, 'iiMbr2DbSys(DBP5)'  iiMbr2DbSys(DBP5)
    call tstOut t, 'ii_rz            '  m.ii_rz
    call tstOut t, 'ii_rz2db.rzx     '  m.ii_rz2db.rzx
    call pipeIni
    call iiVPut 'rr2/ DvBp  '
    call tstOut t, 'rr2/dvbp   ' ,
             vGet('rz') vget('rzC') 'p='vget('rzP') 'd='vget('rzD'),
             || ', db' vGet('dbSys') vGet('dbSysC') vGet('dbSysElar')
    w1 = wordPos('RZ2/DBOF', m.ii_rzDb)
    do wx=w1 to w1+2
        call tstOut t, 'iiixVPut' iiIxVPut(wx)':' ,
             vGet('rz') vget('rzC') 'p='vget('rzP') 'd='vget('rzD'),
             || ', db' vGet('dbSys') vGet('dbSysC') vGet('dbSysElar')
        end
    call tstEnd t
    return
endProcedure tstII

tstTime: procedure
/*         Winterzeit dez 2011
$=/tstTime/
    ### start tst tstTime #############################################
    Lrsn2Lzt(C5E963363741) 2010-05-01-11.34.55.789008
    Achtung: output ist abhaengig von Winter/Sommerzeit und LeapSecs
    timeZone 3600.00000 leapSecs 25.0000000
    2jul(2011-03-31-14.35.01.234567)  11090
    Lrsn2Gmt(C5E963363741) 2010-05-01-10.35.20.789008
    Lrsn2Lzt(C5E963363741) 2010-05-01-11.34.55.789008
    gmt2Lrsn(2011-03-31-14.35.01.234567) C78D87B86E38
    lzt2Lrsn(2011-03-31-14.35.01.234567) C78D7A670B7C
    Lrsn2Gmt(gmt2Lrsn(2011-03-31-14.35.01.234567) 2011-03-31-14.35.01.2+
    34560
    gmt2Lrsn(Lrsn2Gmt(C5E963363741) C5E963363741
    Lrsn2LZt(LZt2Lrsn(2011-03-31-14.35.01.234567) 2011-03-31-14.35.01.2+
    34560
    LZt2Stc(Lrsn2LZt(C5E963363741) C5E963363741
    Lrsn2uniq(C5E963363741) CTNR6S7T back C5E963363740
$/tstTime/
*/
    call jIni
    call tst t, 'tstTime'
    t1 = '2011-03-31-14.35.01.234567'
    s1 = 'C5E963363741'
    call out 'Lrsn2Lzt('s1')' timeLrsn2Lzt(s1)
    call out ,
     'Achtung: output ist abhaengig von Winter/Sommerzeit und LeapSecs'
    call out 'timeZone' m.time_Zone * m.time_StckUnit ,
             'leapSecs' m.time_Leap * m.time_StckUnit
    call timeReadCvt 1
    call out '2jul('t1') ' time2jul(t1)
    call out 'Lrsn2Gmt('s1')' timeLrsn2Gmt(s1)
    call out 'Lrsn2Lzt('s1')' timeLrsn2Lzt(s1)
    call out 'gmt2Lrsn('t1')' timeGmt2Lrsn(t1)
    call out 'lzt2Lrsn('t1')' timeLzt2Lrsn(t1)
    call out 'Lrsn2Gmt(gmt2Lrsn('t1')' timeLrsn2Gmt(timeGmt2Lrsn(t1))
    call out 'gmt2Lrsn(Lrsn2Gmt('s1')' timeGmt2Lrsn(timeLrsn2Gmt(s1))
    call out 'Lrsn2LZt(LZt2Lrsn('t1')' timeLrsn2LZt(timeLZt2Lrsn(t1))
    call out 'LZt2Stc(Lrsn2LZt('s1')'  timeLZt2Lrsn(timeLrsn2LZt(s1))
    call out 'Lrsn2uniq('s1')' timeLrsn2Uniq(s1) ,
                        'back' timeUniq2Lrsn(timeLrsn2Uniq(s1))
    call tstEnd t
    return
endProcedure tstTime
tstMat: procedure expose m.
/*
$=/tstMat/
    ### start tst tstMat ##############################################
    .   0 sqrt  0 isPrime 0 nxPrime    3 permut 1 > 1 2 3 4 5
    .   1 sqrt  1 isPrime 0 nxPrime    3 permut 2 > 2 1 3 4 5
    .   2 sqrt  1 isPrime 1 nxPrime    3 permut 3 > 1 3 2 4 5
    .   3 sqrt  1 isPrime 1 nxPrime    3 permut 3 > 2 3 1 4 5
    .   4 sqrt  2 isPrime 0 nxPrime    5 permut 3 > 3 2 1 4 5
    .   5 sqrt  2 isPrime 1 nxPrime    5 permut 3 > 3 1 2 4 5
    .   6 sqrt  2 isPrime 0 nxPrime    7 permut 4 > 1 2 4 3 5
    .   7 sqrt  2 isPrime 1 nxPrime    7 permut 4 > 2 1 4 3 5
    .   8 sqrt  2 isPrime 0 nxPrime   11 permut 4 > 1 3 4 2 5
    .   9 sqrt  3 isPrime 0 nxPrime   11 permut 4 > 2 3 4 1 5
    .  10 sqrt  3 isPrime 0 nxPrime   11 permut 4 > 3 2 4 1 5
    .  11 sqrt  3 isPrime 1 nxPrime   11 permut 4 > 3 1 4 2 5
    .  12 sqrt  3 isPrime 0 nxPrime   13 permut 4 > 1 4 3 2 5
    .  13 sqrt  3 isPrime 1 nxPrime   13 permut 4 > 2 4 3 1 5
    .  14 sqrt  3 isPrime 0 nxPrime   17 permut 4 > 1 4 2 3 5
    .  15 sqrt  3 isPrime 0 nxPrime   17 permut 4 > 2 4 1 3 5
    .  16 sqrt  4 isPrime 0 nxPrime   17 permut 4 > 3 4 1 2 5
    .  17 sqrt  4 isPrime 1 nxPrime   17 permut 4 > 3 4 2 1 5
    .  18 sqrt  4 isPrime 0 nxPrime   19 permut 4 > 4 2 3 1 5
$/tstMat/
$/tstMat/
*/
    call tst t, 'tstMat'
    q = 'tst_Mat'
    do qx=1 to 20
        m.q.qx = qx
        end
    do i=0 to 18
        call permut q, i
        call tstOut t, right(i,4) 'sqrt' right(sqrt(i), 2) ,
        'isPrime' isPrime(i) 'nxPrime' right(nxPrime(i), 4) ,
            'permut' m.q.0 '>' m.q.1 m.q.2 m.q.3 m.q.4 m.q.5
        end
    call tstEnd t
    return
endProcedure tstMat

/****** tstSql ********************************************************/
tstSqlUtils: procedure expose m.
    call sqlConnect 'DBAF'
    id = 'A540769.dsnUtils'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "TEMPLATE TCOPYD",
            "DSN('&SSID..&JO..&US..P&PART(2)..D&DATE(3)..T&TIME.')",
            "DATACLAS(NULL12) MGMTCLAS(COM#A011) STORCLAS(FAR$N)",
            "SPACE (150,3750) TRK UNCNT 59;",
         "listdef abc include tablespace DA540769.A002* partlevel;",
         "listdef mf  include tablespace MF01A1A.A110A partlevel;",
         "copy list abc copyddn(tcopyd) shrlevel change;"
    st = translate(st)
    call sqlExec "call SYSPROC.DSNUTILS ( :id, :rst,",
                   ":st,:retcode, :ANY" copies(',:e,:e,:z',12) ")"
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.DSNUTILS'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    exit
endProcedure tstSqlUtils

tstSqlStored: procedure expose m.
    call sqlConnect 'DBAF'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "abc"
    call sqlExec "call SYSPROC.COMMAND ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.COMMAND'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStored

tstSqlStoredWK: procedure expose m.
    call sqlConnect 'DBAF'
 /* st = "direct wkUtiSub"
    rst = 'NO'
    say 'before call st='st 'rst='rst
    call sqlExec "call A540769.WKUTILSUB ( :st, :rst)"
    say 'after call st='st 'rst='rst
 */ rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    prc = 'DB2UTIL.DB2UTIL'
    st = "DA540769.A2*" /*  DA540769.A1*" */
    say  "call" prc "("st", ...)"
    call sqlExec "call" prc "(:st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE' prc
 /* say 'results' results  */
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say ''
    say '***** utility output'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say '***** end output'
    call sqlDisconnect
    return 0
endProcedure tstSqlStoredWK

tstSqlStoredSQL1: procedure expose m.
    call sqlConnect 'DBIA'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "DA540769.A2* DA540769.A1*"
    call sqlExec "call A540769.WKSQL1 ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE A540769.WKUTIL'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStoredSQL1

tstSqlTriggerTiming:
    parse upper arg tb ni
    m.noInsert = ni == 0
    cnt = 200000
    if tb = '' then
        TB = 'GDB9998.TWK511TRI'
    call sqlConnect dbaf
    say timing() 'noInsert' m.noInsert 'tb' tb
    call sql2St 'select max(pri) MX from' tb, cc
    if m.cc.1.mx == m.sqlNull then
        m.cc.1.mx = 0
    von = m.cc.1.mx + 1
    bis = m.cc.1.mx + cnt
    say m.cc.0 'max' m.cc.1.mx 'von' von 'bis' bis 'count' cnt tb
    if right(tb, 2) = 'A1' then do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long, par)' ,
                   'values (?, ?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    else do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long)' ,
                   'values (?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    /*  call sqlLn 5,,, 'select * from' tb 'where pri >=' von */
    call sqlCommit
    say timing()
    call sqlDisconnect
    return
endProcedure tstSqlTriggerTiming

tstSqlRx: procedure expose m.
/*
$=/tstSqlRx/
    ### start tst tstSqlRx ############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s7 into :M.SQL.7.D from :src
    .    e 3: with into :M.SQL.7.D = M.SQL.7.D
    fetchA 1 ab=m.abcdef.123.AB=abc ef=efg
    fetchA 0 ab=m.abcdef.123.AB=abc ef=efg
    fetchB 1 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchB 0 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
    fetchBT 1 SYSTABLES
    fetchBT 0 SYSTABLES
    fetchBi 1 SYSINDEXES
    fetchBi 0 SYSINDEXES
$/tstSqlRx/ */
    call jIni
    call tst t, "tstSqlRx"
    call sqlRxConnect
    cx = 7
    call sqlRxQuery cx, 'select * from sysdummy'
    call sqlRxQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlRxFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    call sqlRxClose cx
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlRxQuery cx, sql, 'AB CD EF GH'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlRxFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    call sqlRxClose cx
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlRxQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlRxFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    call sqlRxClose cx
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call sqlQueryPrepare cx, "select name" ,
                        "from sysibm.sysTables" ,
                        "where creator = 'SYSIBM' and name = ?",':m.nm'
    call sqlQueryExecute cx, 'SYSTABLES'
    call out 'fetchBT' sqlRxFetch(cx) m.nm
    call out 'fetchBT' sqlRxFetch(cx) m.nm
    call sqlRxClose cx
    call sqlQueryExecute cx, 'SYSINDEXES'
    call out 'fetchBi' sqlRxFetch(cx) m.nm
    call out 'fetchBi' sqlRxFetch(cx) m.nm
    call tstEnd t
    call sqlRxDisconnect
    return
endProcedure tstSqlRx

tstSql: procedure expose m.
/*
$=/tstSql/
    ### start tst tstSql ##############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s7 into :M.SQL.7.D from :src
    .    e 3: with into :M.SQL.7.D = M.SQL.7.D
    fetchA 1 ab=m.abcdef.123.AB=abc ef=efg
    fetchA 0 ab=m.abcdef.123.AB=abc ef=efg
    fetchB 1 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchB 0 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
    sql2St 1 st.0=1
    sql2St:1 a=a b=2 c=--- d=d
    sql2One a
    sql2One a=a b=2 c=--- d=d
    fetchBT 1 SYSTABLES
    fetchBT 0 SYSTABLES
    fetchBi 1 SYSINDEXES
    fetchBi 0 SYSINDEXES
$/tstSql/ */
    call jIni
    call tst t, "tstSql"
    call sqlConnect
    cx = 7
    call sqlQuery cx, 'select * from sysdummy'
    call sqlQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    call sqlClose cx
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlQuery cx, sql, 'AB CD EF GH'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    call sqlClose cx
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    call sqlClose cx
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call out 'sql2St' sql2St(sql, st) 'st.0='m.st.0
    do i=1 to m.st.0
        call out 'sql2St:'i ,
            'a='m.st.i.a 'b='m.st.i.b 'c='m.st.i.c 'd='m.st.i.d
        end
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    drop m.st.a m.st.b m.st.c m.st.d m.st.0
    call out 'sql2One' sql2One(sql, st)
    call out 'sql2One' ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
    drop m.st.a m.st.b m.st.c m.st.d m.st.0
    call sqlQueryPrepare cx, "select name" ,
                        "from sysibm.sysTables" ,
                        "where creator = 'SYSIBM' and name = ?",':m.nm'
    call sqlQueryExecute cx, 'SYSTABLES'
    call out 'fetchBT' sqlFetch(cx) m.nm
    call out 'fetchBT' sqlFetch(cx) m.nm
    call sqlClose cx
    call sqlQueryExecute cx, 'SYSINDEXES'
    call out 'fetchBi' sqlFetch(cx) m.nm
    call out 'fetchBi' sqlFetch(cx) m.nm
    call tstEnd t
    call sqlDisconnect
    return
endProcedure tstSql

tstSqlCsm: procedure expose m.
/*
$=/tstSqlCsm/
    ### start tst tstSqlCsm ###########################################
    *** err: SQLCODE = -204: S100447.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: subsys = DE0G, host = RZZ
    *** err: implement sqlCmsQuery fetchVars ? or : :m.dst.ab, :m.dst.ef
    fetchA 0 ab=m.abcdef.123.AB=M.abcdef.123.AB ef=M.abcdef.123.EF
    fetchA 0 ab=m.abcdef.123.AB=M.abcdef.123.AB ef=M.abcdef.123.EF
    fetchB 1 ab=a cd=2 ef=--- ind=M.abc.Def.123.EF.SQLIND gh=d ind=M.ab+
    c.Def.123.GH.SQLIND
    fetchB 0 ab=a cd=2 ef=--- ind=M.abc.Def.123.EF.SQLIND gh=d ind=M.ab+
    c.Def.123.GH.SQLIND
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
$/tstSqlCsm/ */
    call pipeIni
    call tst t, "tstSqlCsm"
    call sqlConnect 'RZZ/DE0G'
    cx = 7
    call sqlCsmQuery cx, 'select * from sysdummy'
    call sqlCsmQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlCsmFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlCsmQuery cx, sql, 'AB CD EF GH'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlCsmFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlCsmQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlCsmFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call tstEnd t
    call sqlDisconnect
    return
endProcedure tstsqlCsm

tstSqlCSV: procedure expose m.
/*
$=/tstSqlCSV/
    ### start tst tstSqlCSV ###########################################
    NAME,CREATOR,MITCOM,MITQUO,MITNU,COL6
    SYSTABLES,SYSIBM  ,"a,b","a""b",1,8
    SYSTABLESPACE,SYSIBM  ,"a,b","a""b",---,8
    SYSTABLESPACESTATS,SYSIBM,"a,b","a""b",---,6
$/tstSqlCSV/ */
    call csvIni
    call scanReadIni
    call sqlConnect
    call tst t, "tstSqlCSV"
    r = csvWrt(sqlRdr("select name, creator, 'a,b' mitCom",
         ", 'a""b' mitQuo" ,
         ", case when name='SYSTABLES' then 1 else null end mitNu" ,
         ",length(creator)" ,
              "from sysibm.sysTables" ,
              "where creator = 'SYSIBM' and name like 'SYSTABLES%'",
              "fetch first 3 rows only"))
    call pipeWriteAll r
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlCsv

tstSqlB: procedure expose m.
/*
$=/tstSqlB/
    ### start tst tstSqlB #############################################
    #jIn 1# select strip(name) "tb", strip(creator) cr
    #jIn 2# , case when name = 'SYSTABLES' then 1 else null end
    #jIn 3# from sysibm.sysTables
    #jIn 4# where creator = 'SYSIBM' and name like 'SYSTABLES%'
    #jIn 5# .
    #jIn 6# order by name
    #jIn 7# fetch first 3 rows only
    #jIn eof 8#
    dest1.fet: SYSTABLES SYSIBM 1
    dest2.fet: SYSTABLESPACE SYSIBM ---
    dest3.fet: SYSTABLESPACESTATS SYSIBM ---
$/tstSqlB/ */
    call pipeIni
    call tst t, "tstSqlB"
    cx = 9
    call sqlConnect
    call jIni
    call mAdd mCut(t'.IN', 0),
      , 'select strip(name) "tb", strip(creator) cr' ,
      ,     ", case when name = 'SYSTABLES' then 1 else null end" ,
      ,   "from sysibm.sysTables" ,
      ,   "where creator = 'SYSIBM' and name like 'SYSTABLES%'", ,
      ,   "order by name",
      ,   "fetch first 3 rows only"
     call sqlQuery cx, in2Str(,' ')
     do qx=1 while sqlFetch(cx, 'dest'qx'.fet')
         dst = 'dest'qx'.fet'
         call out dst':' m.dst.tb m.dst.cr m.dst.col3
         drop m.dst.tb m.dst.cr m.dst.col3
         end
     call sqlClose cx
     call sqlDisconnect
     call tstEnd t
     return
endProcedure tstSqlB

tstSqlFTab: procedure expose m.
/*
$=/tstSqlFTab/
    ### start tst tstSqlFTab ##########################################
    UPDATESTATSTIME----------------NACTIVE------NPAGES-EXTENT-LOADRLAST+
    TIME--------------REORGLASTTIME--------------REORGINSERT-REORGDELET+
    E-REORGUPDATE-REORGUNCLUS-REORGDISORG-REORGMASSDE-REORGNEARIN-REORG+
    FARIND-STATSLASTTIME--------------STATSINSERT-STATSDELETE-STATSUPDA+
    TE-STATSMASSDE-COPYLASTTIME---------------COPYUPDATED-COPYCHANGES-C+
    OPYUP-COPYUPDATETIME-------------I---DBID---PSID-PARTIT-INSTAN---SP+
    ACE-TOTALRO-DATASIZ-UNCOMPR-DBNAME-------NAME---------REORGCL-REORG+
    SC-REORGHA-HASHLASTUS-DRI-L-STATS01---
    --- modified
    allg vorher                     others vorher
    db-------ts---------part---inst-UPDATESTATSTIME----------------NACT+
    IVE------NPAGES-EXTENT-LOADRLASTTIME--------------REORGLASTTIME----+
    ----------REORGINSERT-REORGDELETE-REORGUPDATE-REORGUNCLUS-REORGDISO+
    RG-REORGMASSDE-REORGNEARIN-REORGFARIND-STATSLASTTIME--------------S+
    TATSINSERT-STATSDELETE-STATSUPDATE-STATSMASSDE-COPYLASTTIME--------+
    -------COPYUPDATED-COPYCHANGES-COPYUP-COPYUPDATETIME-------------I-+
    --DBID---PSID---SPACE-TOTALRO-DATASIZ-UNCOMPR-REORGCL-REORGSC-REORG+
    HA-HASHLASTUS-DRI-L-STATS01---
    db-------ts---------part---inst-UPDATESTATSTIME----------------NACT+
    IVE------NPAGES-EXTENT-LOADRLASTTIME--------------REORGLASTTIME----+
    ----------REORGINSERT-REORGDELETE-REORGUPDATE-REORGUNCLUS-REORGDISO+
    RG-REORGMASSDE-REORGNEARIN-REORGFARIND-STATSLASTTIME--------------S+
    TATSINSERT-STATSDELETE-STATSUPDATE-STATSMASSDE-COPYLASTTIME--------+
    -------COPYUPDATED-COPYCHANGES-COPYUP-COPYUPDATETIME-------------I-+
    --DBID---PSID---SPACE-TOTALRO-DATASIZ-UNCOMPR-REORGCL-REORGSC-REORG+
    HA-HASHLASTUS-DRI-L-STATS01---
    allg nachher                    others nachher
    DBNAME                   INSTANCE                                  +
    .    NPAGES                                        REORGLASTTIME   +
    .                                   REORGUPDATES                   +
    .     REORGMASSDELETE                     STATSLASTTIME            +
    .                          STATSUPDATES                            +
    .           COPYUPDATEDPAGES               COPYUPDATETIME          +
    .            PSID                   DATASIZE                REORGSC+
    ANACCESS            DRIVETYPE
    .         NAME                   UPDATESTATSTIME                   +
    .                 EXTENTS                                          +
    .            REORGINSERTS                        REORGUNCLUSTINS   +
    .                  REORGNEARINDREF                                 +
    .   STATSINSERTS                        STATSMASSDELETE            +
    .                        COPYCHANGES                               +
    .    IBMREQD         SPACE                   UNCOMPRESSEDDATASIZE  +
    .  REORGHASHACCESS        LPFACILITY
    .                  PARTITION                                NACTIVE+
    .                        LOADRLASTTIME                             +
    .                        REORGDELETES                        REORGD+
    ISORGLOB                      REORGFARINDREF                       +
    .              STATSDELETES                        COPYLASTTIME    +
    .                                   COPYUPDATELRSN                 +
    .     DBID                  TOTALROWS               REORGCLUSTERSEN+
    S        HASHLASTUSED     STATS01
$/tstSqlFTab/
*/
    call pipeIni
    call tst t, 'tstSqlFTab'
    call sqlConnect
    call sqlQuery 17, 'select * from sysibm.sysTablespaceStats' ,
                "where dbName = 'xxxDB06' and name = 'SYSTSTAB'"
    call sqlFTabOpts fTabReset(abc, 1, ,'-'), 17,  12
    call sqlFTabDef      abc, 492, '%7e'
    call sqlFTabOthers abc
    call sqlfTab abc
    call sqlClose 17
    call out '--- modified'
    call sqlQuery 17, 'select * from sysibm.sysTablespaceStats' ,
                "where dbName = 'xxxDB06' and name = 'SYSTSTAB'"
    call sqlFTabOpts fTabReset(abc, 2 1, 1 3 'c', '-'), 17,  12
    call sqlFTabDef      abc, 492, '%7e'
    call sqlFTabAdd      abc, DBNAME, '%-8C', 'db', 'allg vorher'  ,
                                                  , 'allg nachher'
    call sqlFTabAdd      abc, NAME  , '%-8C', 'ts'
    call sqlFTabAdd      abc, PARTITION , , 'part'
    call sqlFTabAdd      abc, INSTANCE  , , 'inst'
    ox = m.abc.0 + 1
    call sqlFTabOthers abc
    call fTabAddTit      abc, ox, 2,             'others vorher'
    call fTabAddTit      abc, ox, 3,             'others nachher'
    call sqlFTab abc
    call sqlClose 17
    call tstEnd t
    call sqlDisConnect
    return
endProcedure tstSqlFTab

tstSqlFTab2: procedure expose m.
/*
$=/tstSqlFTab2/
    ### start tst tstSqlFTab2 #########################################
    Und Eins Oder
    .          zw aber
    Und Eins---------------zw aber---
    . und eins                22223
    . und eins                22224
    Und Eins---------------zw aber---
    Und Eins Oder
    .          zw aber
    a-------------b---
    aaa         222
    a-------------b---
    --- row 1 ---------------------------------------------------------+
    -------------
    .           Und Eins Oder       und eins
    .           zw aber            2.2223000e04              22223
    --- row 2 ---------------------------------------------------------+
    -------------
    .           Und Eins Oder       und eins
    .           zw aber            2.2224000e04              22224
    --- end of 2 rows -------------------------------------------------+
    -------------
$/tstSqlFTab2/
*/
    call pipeIni
    call tst t, 'tstSqlFTab2'
    call sqlConnect
    sq1 = 'select '' und eins'' "Und Eins Oder"',
             ', 22222 + row_number() over() "zw aber" ',
            'from sysibm.sysTables fetch first 2 rows only'
    call sqlQuery 17, sq1
    call sqlFTab sqlFTabOthers(sqlfTabReset(tstSqlFtab2, 17))
    call sqlClose 17
    sq2 =             'select ''aaa'' "a", 222 "b"' ,
            'from sysibm.sysTables fetch first 1 rows only'
    call sqlQuery 17, sq2
    call sqlFTab sqlFTabOthers(sqlfTabReset(tstSqlFtab2, 17))
    call sqlClose 17
    call sqlQuery 15, sq1
    call sqlFTabCol sqlFTabOthers(sqlfTabReset(tstSqlFtab5, 15))
    call sqlClose 15
    call tstEnd t
    call sqlDisConnect
    return
endProcedure tstSqlFTab2
tstSqlFTab3: procedure expose m.
/*
$=/tstSqlFTab3/
    ### start tst tstSqlFTab3 #########################################
    Und Eins Oder
    .          zw aber
    Und Eins--z---
    . und eins 1
    . und eins 2
    Und Eins--z---
    Und Eins Oder
    .          zw aber
    a-----b---
    aaa 222
    a-----b---
$/tstSqlFTab3/
*/
    call pipeIni
    call tst t, 'tstSqlFTab3'
    call sqlConnect
    sq1 = 'select '' und eins'' "Und Eins Oder"',
             ', row_number() over() "zw aber" ',
            'from sysibm.sysTables fetch first 2 rows only'
    r = jOpen(sqlRdr(sq1), '<')
    f = sqlRdrfTabReset(r, 'tstSqFTab3')
    b = in2Buf(r)
    call sqlFTabDetect f, b'.BUF'
    call fTab f, b
    call jClose r
    sq2 =             'select ''aaa'' "a", 222 "b"' ,
            'from sysibm.sysTables fetch first 1 rows only'
    call sqlQuery 17, sq2
    f = sqlfTabReset('tstSqFTab3t', 17)
    st = 'tstSqFTab3st'
    call sqlFetch2St 17, st
    s2 = 'tstSqFTab3s2'
    do sx=1 to m.st.0
        m.s2.sx = st'.'sx
        end
    m.s2.0 = m.st.0
    call sqlFTabDetect f, s2
    call fTabBegin f
    do sx=1 to m.st.0
        call out f(m.f.fmt, st'.'sx)
        end
    call fTabEnd f
    call tstEnd t
    call sqlDisConnect
    return
endProcedure tstSqlFTab3

tstSqlFTab4: procedure expose m.
/*
$=/tstSqlFTab4/
    ### start tst tstSqlFTab4 #########################################
    a
    1
    1 rows fetched: select 1 "a" from sysibm.sysDummy1
    sqlCode -204: drop table gibt.EsNicht
    a
    2
    1 rows fetched: select 2 "a" from sysibm.sysDummy1
    *** err: SQLCODE = -104: ILLEGAL SYMBOL "Y". SOME SYMBOLS THAT MIGH+
    T
    .    e 1:     BE LEGAL ARE: , FROM INTO
    .    e 2: src select x frm y
    .    e 3:   >              <<<pos 14 of 14<<<
    .    e 4: sql = select x frm y
    .    e 5: stmt = prepare s10 into :M.SQL.10.D from :src
    .    e 6: with into :M.SQL.10.D = M.SQL.10.D
    sqlCode -104: select x frm y
    a
    3
    1 rows fetched: select 3 "a" from sysibm.sysDummy1
    dy  => 1
    a
    1
    1 rows fetched: select 1 "a" from sysibm.sysDummy1
    sqlCode -204: drop table gibt.EsNicht
    a
    2
    1 rows fetched: select 2 "a" from sysibm.sysDummy1
    SQLCODE = -104: ILLEGAL SYMBOL "Y". SOME SYMBOLS THAT MIGHT
    .    BE LEGAL ARE: , FROM INTO
    src select x frm y
    .  >              <<<pos 14 of 14<<<
    sql = select x frm y
    stmt = prepare s10 into :M.SQL.10.D from :src
    with into :M.SQL.10.D = M.SQL.10.D
    sqlCode 0: rollback
    ret => 0
$/tstSqlFTab4/
*/
    call pipeIni
    call tst t, 'tstSqlFTab4'
    call sqlConnect
    b = jBuf('select 1 "a" from sysibm.sysDummy1;' ,
         , 'drop table gibt.EsNicht;' ,
         , 'select 2 "a" from sysibm.sysDummy1;',
         , ' select x frm y;',
         , 'select 3 "a" from sysibm.sysDummy1;')
    call tstout t, 'dy  =>' sqlsOut(scanSqlStmtRdr(b, 0))
    call tstout t, 'ret =>' sqlsOut(scanSqlStmtRdr(b, 0), 'rb ret')
    call tstEnd t
    call sqlDisConnect
    return
endProcedure tstSqlFTab4

tstSql4Obj: procedure expose m.
/*
$=/tstSql4Obj/
    ### start tst tstSql4Obj ##########################################
    tstR: @tstWriteoV2 isA :tstClass-1 = -11
    tstR:  .a2i = -11
    tstR:  .b3b = b3
    tstR:  .D4 = D4-11+D4++++.
    tstR:  .fl5 = -111.1
    tstR:  .ex6 = -.111e-11
    insert into cr.insTb -- tstClass-1
    .   ( , a2i, b3b, D4, fl5, ex6
    .   ) values .
    .   ( -11, -11, 'b3', 'D4-11+D4++++', -111.1, -.111e-11
    .   ) ; .
    insert into cr.insTbHex -- tstClass-1
    .   ( , a2i, b3b, D4, fl5, ex6
    .   ) values .
    .   ( -11, -11, 'b3', x'C40760F1F14EC4F44E4E4E4E', -111.1, -.111e-1+
    1
    .   ) ; .
    tstR: @tstWriteoV4 isA :tstClass-2
    tstR:  .c = c83
    tstR:  .a2i = 83
    tstR:  .b3b = b3b8
    tstR:  .D4 = D483+D4++++++++++++++++++++++++++++++++++++++++++++++++
    .++++++++++++++++++++++++++++++.
    tstR:  .fl5 = .183
    tstR:  .ex6 = .11183e-8
    insert into cr.insTb -- tstClass-2
    .   ( c, a2i, b3b, D4, fl5, ex6
    .   ) values .
    .   ( 'c83', 83, 'b3b8'
    .   , 'D483+D4++++++++++++++++++++++++++++++++++++++++++++++++++++++
    '
    .   || '++++++++++++++++++++++++'
    .   , .183, .11183e-8
    .   ) ; .
    insert into cr.insTbHex -- tstClass-2
    .   ( c, a2i, b3b, D4, fl5, ex6
    .   ) values .
    .   ( 'c83', 83, 'b3b8'
    .   , x'C407F8F34EC4F44E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E'
    .   || '++++++++++++++++++++++++++++++++'
    .   || x'314E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E'
    .   , .183, .11183e-8
    .   ) ; .
$/tstSql4Obj/
*/
    call pipeIni
    call tst t, 'tstSql4Obj'
    call pipe '+N'
    call tstDataClassOut '. c3 a2i i b3b c5 D4 c23 fl5 f8n2 ex6 e9n3',
        , -11, -11
    call tstDataClassOut 'c c3 a2i i b3b c5 D4 c93 fl5 f8n2 ex6 e9n3',
        , 83, 83
    call pipe 'P|'
    do cx=1 while in()
        i = m.in
        call mAdd t'.'trans, className(objClass(i)) 'tstClass-'cx
        call out i
        call sql4Obj i, 'cr.insTb'
        m.i.d4 = overlay('07'x, m.i.d4, 2)
        if length(m.i.d4) >= 62 then
            m.i.d4 = overlay('31'x, m.i.d4, 62)
        call sql4Obj i, 'cr.insTbHex'
        end
    call pipe '-'
    call tstEnd t
    return
endProcedure tstSql4Obj
tstSqlC: procedure expose m.
call sqlIni
/*
$=/tstSqlCRx/
    ### start tst tstSqlCRx ###########################################
    *** err: SQLCODE = -104: ILLEGAL SYMBOL "?". SOME SYMBOLS THAT MIGH+
    T
    .    e 1:     BE LEGAL ARE: . <IDENTIFIER> JOIN INNER LEFT RIGHT FU+
    LL CROSS ,
    .    e 2:     HAVING GROUP
    .    e 3: src select * from sysibm?sysDummy1
    .    e 4:   >    >>>pos 21 of 30>>>
    .    e 5: sql = select * from sysibm?sysDummy1
    .    e 6: stmt = prepare s9 into :M.SQL.9.D from :src
    .    e 7: with into :M.SQL.9.D = M.SQL.9.D
    *** err: SQLCODE = -204: NONONO.SYSDUMMY1 IS AN UNDEFINED NAME
    .    e 1: sql = select * from nonono.sysDummy1
    .    e 2: stmt = prepare s9 into :M.SQL.9.D from :src
    .    e 3: with into :M.SQL.9.D = M.SQL.9.D
    sys  ==> server CHSKA000DP4G    .
    fetched a1=abc, i2=12, c3=---
    .  I1 C2
    .   1 eins
    2222 zwei
$/tstSqlCRx/
$=/tstSqlCCsm/
    ### start tst tstSqlCCsm ##########################################
    *** err: SQLCODE = -104: ILLEGAL SYMBOL "?". SOME SYMBOLS THAT MIGH+
    T
    .    e 1:     BE LEGAL ARE: . <IDENTIFIER> JOIN INNER LEFT RIGHT FU+
    LL CROSS ,
    .    e 2:     HAVING GROUP
    .    e 3: src select * from sysibm?sysDummy1
    .    e 4:   >    >>>pos 21 of 30>>>
    .    e 5: sql = select * from sysibm?sysDummy1
    .    e 6: subsys = DE0G, host = RZZ
    *** err: SQLCODE = -204: NONONO.SYSDUMMY1 IS AN UNDEFINED NAME
    .    e 1: sql = select * from nonono.sysDummy1
    .    e 2: subsys = DE0G, host = RZZ
    sys RZZ/DE0G ==> server CHROI00ZDE0G    .
    fetched a1=abc, i2=12, c3=---
    .  I1 C2
    .   1 eins
    2222 zwei
$/tstSqlCCsm/ */
    sql1 = "select 1 i1, 'eins' c2 from sysibm.sysDummy1" ,
        "union all select 2222 i1, 'zwei' c2 from sysibm.sysDummy1"
    do tx=1 to 1 +  (m.tst_Csm \== 0)
        if tx = 1 then do
            call tst t, "tstSqlCRx"
            sys = ''
            end
        else do
            call tst t, "tstSqlCCsm"
            sys =  'RZZ/DE0G'
            end
        call sqlConnect sys
        cx = 9
        call sqlQuery cx, 'select * from sysibm?sysDummy1'
        call sqlQuery cx, 'select * from nonono.sysDummy1'
        call sqlQuery cx, "select 'abc' a1, 12 i2, current server srv",
                     ", case when 1=0 then 1 else null end c3",
                 "from sysibm.sysDummy1"
        do while sqlFetch(cx, dst)
            call out 'sys' sys '==> server' m.dst.srv
            call out 'fetched a1='m.dst.a1', i2='m.dst.i2', c3='m.dst.c3
            end
        call fTabAuto , sqlRdr(sql1)
        call sqlDisconnect
        call tstEnd t
        end
    return
endProcedure tstSqlC

tstSqlUpd: procedure expose m.
/*
$=/tstSqlUpd/
    ### start tst tstSqlUpd ###########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table (update session.dgtt  set c2 = 'u' +
    || c2)
    stmt = prepare s9 into :M.SQL.9.D from :src
    with into :M.SQL.9.D = M.SQL.9.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstSqlUpd/ */
    call tst t, "tstSqlUpd"
    cx = 9
    qx = 3
    call sqlConnect
    call sqlUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call sqlUpdate,"insert into session.dgtt" ,
                    "values(1, 'eins', '2012-04-01 06.07.08')"
    call sqlUpdate,"insert into session.dgtt" ,
                    "values(2, 'zwei', '2012-02-29 15:44:33.22')"
    call out 'insert updC' m.sql..updateCount
    call sqlUpdate,"insert into session.dgtt" ,
                      "select i1+10, 'zehn+'||strip(c2), t3+10 days",
                           "from session.dgtt"
    call out 'insert select updC' m.sql..updateCount
    call sqlQuery cx, 'select d.*' ,
               ', case when mod(i1,2) = 1 then 1 else null end grad' ,
               'from session.dgtt d'
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlClose cx
    call sqlQuery cx, "select * from final table (update session.dgtt",
                   " set c2 = 'u' || c2)"
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSqlUpd
tstSqlUpdPre: procedure expose m.
/*
$=/tstSqlUpdPre/
    ### start tst tstSqlUpdPre ########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table ( update session.dgtt set c2 = ? ||+
    . c2)
    stmt = prepare s5 into :M.SQL.5.D from :src
    with into :M.SQL.5.D = M.SQL.5.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstSqlUpdPre/ */
    call tst t, "tstSqlUpdPre"
    cx = 5
    qx = 3
    call sqlConnect
    call sqlUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call salUpdatePrepare 5, "insert into session.dgtt" ,
                                   "values (?, ?, ?)"
    call sqlUpdExecute 5, 1, 'eins', '2012-04-01 06.07.08'
    call sqlUpdExecute 5, 2, 'zwei', '2012-02-29 15:44:33.22'
    call out 'insert updC' m.sql.5.updateCount
    call salUpdatePrepare 5,"insert into session.dgtt" ,
                      "select i1+?, 'zehn+'||strip(c2), t3+? days",
                           "from session.dgtt"
    call sqlUpdExecute 5, 10, 10
    call out 'insert select updC' m.sql.5.updateCount
    call sqlQueryPrepare cx, 'select d.*' ,
               ', case when mod(i1,2) = ? then 0+? else null end grad' ,
               'from session.dgtt d'
    call sqlQueryExecute cx, 1, 1
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlClose cx
    call sqlQueryPrepare cx, "select * from final table (" ,
              "update session.dgtt set c2 = ? || c2)"
    call sqlQueryExecute cx, "u"
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSqlUpdPre
tstsqlRxUpd: procedure expose m.
/*
$=/tstsqlRxUpd/
    ### start tst tstsqlRxUpd #########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table (update session.dgtt set c2 = 'u' +
    || c2)
    stmt = prepare s9 into :M.SQL.9.D from :src
    with into :M.SQL.9.D = M.SQL.9.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstsqlRxUpd/ */
    call tst t, "tstsqlRxUpd"
    cx = 9
    qx = 3
    call sqlRxConnect
    call sqlRxUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call sqlRxUpdate,"insert into session.dgtt" ,
                    "values(1, 'eins', '2012-04-01 06.07.08')"
    call sqlRxUpdate,"insert into session.dgtt" ,
                    "values(2, 'zwei', '2012-02-29 15:44:33.22')"
    call out 'insert updC' m.sql..updateCount
    call sqlRxUpdate,"insert into session.dgtt" ,
                      "select i1+10, 'zehn+'||strip(c2), t3+10 days",
                           "from session.dgtt"
    call out 'insert select updC' m.sql..updateCount
    call sqlRxQuery cx, 'select d.*' ,
               ', case when mod(i1,2) = 1 then 1 else null end grad' ,
               'from session.dgtt d'
    do qx=qx+1 while sqlRxFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlRxClose cx
    call sqlRxQuery cx, "select * from final table",
                 "(update session.dgtt set c2 = 'u' || c2)"

    do qx=qx+1 while sqlRxFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlRxClose cx
    call sqlRxDisconnect
    call tstEnd t
    return
endProcedure tstsqlRxUpd

tstSqlE: procedure expose m.
/*
$=/tstSqlE/
    ### start tst tstSqlE #############################################
    *** err: SQLCODE = -713: THE REPLACEMENT VALUE FOR CURRENT SCHEMA I+
    S
    .    e 1:     INVALID
    .    e 2: sql = set current schema = 'sysibm'
    .    e 3: stmt = execute immediate :src
    -713 set schema ''
    0 set schema
    0 select
    fetch=1 SYSIBM
$/tstSqlE/
*/
    call sqlConnect
    call tst t, "tstSqlE"
    call tstOut t, sqlExecute(3, "set current schema = 'sysibm'") ,
                                 "set schema ''"
    call tstOut t, sqlExecute(3, " set current schema = sysibm ") ,
                                 "set schema"
    call tstOut t, sqlExecute(3, " select current schema c"      ,
                                      "from sysibm.sysDummy1") 'select'
    call tstOut t, 'fetch='sqlFetch(3, aa) m.aa.c
    call sqlClose 3
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlE
tstSqlO: procedure expose m.
/*
$=/tstSqlO/
    ### start tst tstSqlO #############################################
    sqlCode 0: set current schema = A540769
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s10 into :M.SQL.10.D from :src
    .    e 3: with into :M.SQL.10.D = M.SQL.10.D
    sqlCode -204: select * from sysdummy
    REQD=Y col=123 case=--- .sqlInd:-1 col5=anonym geburri=1956-04-01+
    -06.00.00.000000
$/tstSqlO/
*/
    call sqlConnect
    call scanWinIni
    call tst t, "tstSqlO"
    call sqlStmts 'set current schema = A540769';
    call sqlStmts 'select * from sysdummy';
    r = sqlRdr( ,
          "select d.*, 123, timestamp('01.04.1956','06:00:00')" ,
                             '"geburri walter",',
                    'case when 1=0 then 1 else null end caseNull,',
                    "'anonym'" ,
               'from sysibm.sysdummy1 d')
    call jOpen r, '<'
    do while jRead(r)
        o = m.r
        call out 'REQD='m.o.IBMREQD 'col='m.o.col2,
                  'case='m.o.CASENULL '.sqlInd:'m.o.caseNull.sqlInd,
                  'col5='m.o.col5,
                  'geburri='m.o.GEBURRI
        end
    call jClose r
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlO

tstSqlUpdComLoop: procedure expose m.
/*
$=/tstSqlUpdComLoop/
    ### start tst tstSqlUpdComLoop ####################################
    sqlCode 0: declare global temporary table session.dgtt (i1 int) on +
    commit ....
    sqlCode 0, 123 rows inserted: insert into session.dgtt select row_n+
    umber()....
    CNT
    123
    1 rows fetched: select count(*) cnt from session.dgtt
    123 rows deleted, 10 commits: delete from session.dgtt d where i1 i+
    n (sele....
    C
    0
    1 rows fetched: select count(*) cnt from session.dgtt
$/tstSqlUpdComLoop/ */
    call pipeIni
    call tst t, "tstSqlUpdComLoop"
    call sqlConnect
    call sqlsOut "declare global temporary table session.dgtt",
                           "(i1 int) on commit preserve rows"
    call sqlsOut "insert into session.dgtt",
       "select row_number() over() from sysibm.sysTables",
           "fetch first 123 rows only"
    call sqlsOut "select count(*) cnt from session.dgtt"
    call out sqlUpdComLoop("delete from session.dgtt d where i1 in",
       "(select i1 from session.dgtt fetch first 13 rows only)")
    call sqlsOut "select count(*) cnt from session.dgtt"
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlUpdComLoop

tstSqlO1: procedure expose m.
/*
$=/tstSqlO1/
    ### start tst tstSqlO1 ############################################
    tstR: @tstWriteoV2 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART
    tstR: @tstWriteoV3 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART_HIST
    tstR: @tstWriteoV4 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLES
    tstR: @tstWriteoV5 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLESPACE
    --- writeAll
    tstR: @tstWriteoV6 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART
    tstR: @tstWriteoV7 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART_HIST
    tstR: @tstWriteoV8 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLES
    tstR: @tstWriteoV9 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLESPACE
$/tstSqlO1/
*/
    call sqlConnect
    call tst t, "tstSqlO1"
    qr = sqlRdr("select strip(creator) cr, strip(name) tb",
                     "from sysibm.sysTables",
                     "where creator='SYSIBM' and name like 'SYSTABL%'",
                     "order by 2 fetch first 4 rows only")
    call jOpen qr, m.j.cRead
    call mAdd t.trans, className(m.qr.type) '<tstSqlO1Type>'
    do while jRead(qr)
        abc = m.qr
        if m.qr.rowCount = 1 then do
            cx = m.qr.cursor
            end
        call out abc
        end
    call jClose qr
    call out '--- writeAll'
    call pipeWriteAll qr
    call tstEnd t
    return 0
endProcedure tstSqlO1

tstSqlO2: procedure expose m.
/*
$=/tstSqlO2/
    ### start tst tstSqlO2 ############################################
    CR     TB                RR
    SYSIBM SYSTABLEPART       1
    SYSIBM SYSTABLEPART_HIST  4
    SYSIBM SYSTABLES          9
    SYSIBM SYSTABLESPACE     16
$/tstSqlO2/
*/
    call sqlConnect
    call pipeIni
    call tst t, "tstSqlO2"
    call pipe '+N'
    call out    "select strip(creator) cr, strip(name) tb,"
    call out         "(row_number()over())*(row_number()over()) rr"
    call out         "from sysibm.sysTables"
    call out         "where creator='SYSIBM' and name like 'SYSTABL%'"
    call out         "order by 2 fetch first 4 rows only"
    call pipe 'N|'
    call sqlSel
    call pipe 'P|'
    call fTabAuto fTabReset(abc, 1)
    call pipe '-'
    call tstEnd t
    return 0
endProcedure tstSqlO2
tstSqlS1: procedure expose m.
/*
$=/tstSqlS1/
    ### start tst tstSqlS1 ############################################
    select c, a from sysibm.sysDummy1
    tstR: @tstWriteoV2 isA :<cla sql c a>
    tstR:  .C = 1
    tstR:  .A = a
    select ... where 1=0
    tstR: @ obj null
$/tstSqlS1/
*/
    call sqlIni
    call tst t, "tstSqlS1"
    call sqlConnect
    s1 = jSingle( ,
        sqlRdr("select count(*) c, 'a' a from sysibm.sysdummy1"))
    call mAdd t.trans, className(objClass(s1)) '<cla sql c a>'
    call out 'select c, a from sysibm.sysDummy1'
    call tstWrite t, s1
    call out 'select ... where 1=0'
    call tstWrite t, jSingle( ,
        sqlRdr("select*from sysibm.sysdummy1 where 1=0"), '')
    call tstEnd t
    return
endProcedure tstSqlS1
tstSqlStmt: procedure expose m.
/*
$=/tstSqlStmt/
    ### start tst tstSqlStmt ##########################################
    *** err: SQLCODE = -713: THE REPLACEMENT VALUE FOR CURRENT SCHEMA I+
    S
    .    e 1:     INVALID
    .    e 2: sql = set current schema = 'sysibm'
    .    e 3: stmt = execute immediate :src
    sqlCode -713: set current schema = 'sysibm'
    sqlCode 0: set current schema =  sysibm
    tstR: @tstWriteoV2 isA :<sql?sc>
    tstR:  .C = SYSIBM
    tstR: @tstWriteoV3 isA :<sql?sc>
    tstR:  .C = SYSIBM
$/tstSqlStmt/
*/
    call sqlConnect
    call tst t, "tstSqlStmt"
    cn = className(classNew('n* Sql u f%v  C'))
    call mAdd t.trans, cn '<sql?sc>'
    call sqlStmts "set current schema = 'sysibm'"
    call sqlsOut "    set current schema =  sysibm "
    call sqlsOut "   select current schema c  from sysDummy1", , 'o'
    call sqlsOut "  (select current schema c from sysDummy1)", , 'o'
    call tstEnd t
    return
endProcedure tstSqlStmt
tstSqlStmts: procedure expose m.
/*
$=/tstSqlStmts/
   ### start tst tstSqlStmts #########################################
   *** err: SQLCODE = -104: ILLEGAL SYMBOL "BLABLA". SOME SYMBOLS THAT
   .    e 1:     MIGHT BE LEGAL ARE: <ERR_STMT> <WNG_STMT> GET SQL SAV+
   EPOINT HOLD
   .    e 2:     FREE ASSOCIATE
   .    e 3: src blabla
   .    e 4:   > <<<pos 1 of 6<<<
   .    e 5: sql = blabla
   sqlCode -104: blabla
   sqlCode 0: set current schema=  sysIbm
   c
   1
   1 rows fetched: select count(*) "c" from sysDummy1 with   ur
   c
   1
   1 rows fetched: select count(*) "c" from sysDummy1 with   ur
   #jIn 1# set current -- sdf
   #jIn 2# schema = s100447;
   #jIn eof 3#
   sqlCode 0: set current schema = s100447
$/tstSqlStmts/ */
    call jIni
    call sqlConnect
    call scanReadIni
    call scanWinIni
    call tst t, "tstSqlStmts"
    call sqlStmts "blabla ;;set current schema=  sysIbm "
    b = jBuf('select count(*) "c" from sysDummy1 --com' ,
             ,'with  /* comm */ ur;')
    call sqlStmts b
    call sqlStmts b
    call mAdd mCut(t'.IN', 0), 'set current -- sdf', 'schema = s100447;'
    call sqlStmts
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlStmts

tstCatTb:     /* ???????????????????? tkr kopieren und testen */
/*
$=/tstCatTb/
    ### start tst tstCatTb ############################################
    ..
    select * from sysibm.SYSDUMMY1  .
    IBMREQD
    I .
    Y .
    I .
    IBMREQD
$/tstCatTb/
*/
    call sqlConnect
    call tst t, 'tstCatTb'
    call sqlCatTb 'sysDummy1'
    call sqlCatTb 'SYSTableSpaceStats',
             , "name = 'A403A1' and dbName = 'DA540769'"
    call tstEnd t
    return
endProcedure tstCatTb

tstSqlDisDb: procedure expose m.
    call sqlDsn di, 'DP4G', '-dis db(*) sp(*)' ,
                    'restrict advisory limit(*)', 12
    m.oo.0 = 0
    call sqlDisDb oo, di
    say 'di.0' m.di.0 '==> oo.0' m.oo.0
    trace ?r
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE)
    say 'DB2PDB6.RR2HHAGE  ==>' ix m.oo.ix.sta
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE, 3)
    say 'DB2PDB6.RR2HHAGE.3  ==>' ix m.oo.ix.sta
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE,22)
    say 'DB2PDB6.RR2HHAGE.22 ==>' ix m.oo.ix.sta
    return
endProcedure tstSqlDisDb

/****** tstComp ********************************************************
    test the wsh compiler
***********************************************************************/
tstComp: procedure expose m.
    call compIni
    call tstCompDataConst
    call tstCompDataVars
    call tstCompShell
    call tstCompPrimary
    call tstCompExpr
    call tstCompFile
    call tstCompStmt
    call tstCompDir
    call tstCompObj
    call tstCompORun
    call tstCompORu2
    call tstCompORuRe
    call tstCompDataIO
    call tstCompPipe
    call tstCompPip2
    call tstCompRedir
    call tstCompComp
    call tstCompColon
    call tstCompTable
    call tstCompSyntax
    if m.err.os == 'TSO' then
        call tstCompSql
    call tstTotal
    return
endProcedure tstComp

tstComp1: procedure expose m.
parse arg ty nm cnt
    c1 = 0
    if cnt = 0 | cnt = '+' then do
        c1 = cnt
        cnt = ''
        end
    call jIni
    src = jBuf()
    call jOpen src, m.j.cWri
    do sx=2 to arg()
        call jWrite src, arg(sx)
        end
    call tstComp2 nm, ty, jClose(src), , c1, cnt
    return
endProcedure tstComp1

tstComp2: procedure expose m.
parse arg nm, spec, src, compSt
    call compIni
    call tst t, nm, compSt
    if src == '' then do
        src = jBuf()
        call tst4dp src'.BUF', mapInline(nm'Src')
        end
    m.t.moreOutOk = abbrev(strip(arg(5)), '+')
    oldErr = m.err.count
    cmp = comp(src)
    call out 'compile' spec',' (m.src.buf.0) 'lines:' m.src.buf.1
    r = compile(cmp, spec)
    noSyn = m.err.count = oldErr
    coErr = m.t.err
    say if( noSyn, "compiled", "*** syntaxed") r ":" objMet(r, 'oRun')

    cnt = 0
    do ax = 5 to max(arg(), 5) while m.t.err = coErr & noSyn
        a1 = strip(arg(ax))
        if a1 == '' & arg() >= 5 then
            iterate
        if abbrev(a1, '+') then do
            m.t.moreOutOk = 1
            a1 = strip(substr(a1, 2))
            end
        if datatype(a1, 'n') then
            cnt = a1
        else if a1 \== '' then
            call err 'tstComp2 bad arg('ax')' arg(ax)
        if cnt = 0 then do
            call mCut 'T.IN', 0
            call out "run without input"
            end
        else  do
            call mAdd mCut('T.IN', 0),
                ,"eins zwei drei", "zehn elf zwoelf?",
                , "zwanzig 21 22 23 24 ... 29|"
            do lx=4 to cnt
                call mAdd 'T.IN', left(lx "line" lx, lx+9, '-')';'
                end
            call out "run with" cnt "inputs"
            end
        m.t.inIx = 0
        call oRun r
        end
    call tstEnd t
    return
endProcedure tstComp2

tstCompDataConst: procedure expose m.
/*
$=/tstCompDataConst/
    ### start tst tstCompDataConst ####################################
    compile =, 8 lines:       Lline one, $** asdf
    run without input
    .      Lline one, .
    line two..
    line threecontinued on 4
    line five  fortsetzung
    line six   fortsetzung
$/tstCompDataConst/ */
    call tstComp1 '= tstCompDataConst',
        , '      Lline one, $** asdf',
        , 'line two.',
        , 'line three$*+ bla bla' ,
        , 'continued on 4',
        , 'line five $*( und so',
        , 'weiter $abc $? $''$*)'' $"$*)" und weiter $*) fortsetzung',
        , 'line six  $*( und $*( $** $*( so',
        , 'weiter $abc $? $*)'' $"$*)" und weiter $*) fortsetzung'

/*
$=/tstCompDataConstBefAftComm1/
    ### start tst tstCompDataConstBefAftComm1 #########################
    compile =, 3 lines:     $*(anfangs com.$*)       $*(plus$*) $** x
    run without input
    the only line;
$/tstCompDataConstBefAftComm1/ */
    call tstComp1 '= tstCompDataConstBefAftComm1',
        , '    $*(anfangs com.$*)       $*(plus$*) $** x',
        , 'the only line;',
        , '      $*(end kommentar$*)              '

/*
$=/tstCompDataConstBefAftComm2/
    ### start tst tstCompDataConstBefAftComm2 #########################
    compile =, 11 lines:     $*(anfangs com.$*)       $*(plus$*) $*+ x
    run without input
    the first non empty line;
    .      .
    befor an empty line with comments;
$/tstCompDataConstBefAftComm2/ */

    call tstComp1 '= tstCompDataConstBefAftComm2',
        , '    $*(anfangs com.$*)       $*(plus$*) $*+ x',
        , '    $*(forts Zeile com.$*)       $*(plus$*) $** x',
        , ' $*(comment 2. Zeile$*) $*(plus$*) $*+ x',
        , ' $*(forts Zeile com.$*) $*(plus$*) $** x',
        , 'the first non empty line;',
        , '      ',
        , 'befor an empty line with comments;',
        , ' $*(comment 4. Zeile$*) $*(plus$*) $*+ x',
        , ' $*(forts 4.Zeile com.$*) $*(plus$*) $** x',
        , '      $*(end kommentar$*)              $*+',
        , ' $*(forts end com.$*) $*(plus$*) $** x'
     return
endProcedure tstCompDataComm

tstCompDataVars: procedure expose m.
/*
$=/tstCompDataVars/
    ### start tst tstCompDataVars #####################################
    compile =, 5 lines:       Lline one, $** asdf
    run without input
    .      Lline one, .
    lline zwei output
    lline 3 .
    variable v1 = valueV1 ${v1}= valueV1; .
    .      $-{""$v1} = valueV1; .
$/tstCompDataVars/ */
    call tstComp1 '= tstCompDataVars',
        , '      Lline one, $** asdf',
        , '   $$ lline zwei output',
        , 'lline 3 $=v1= valueV1 ' ,
        , 'variable v1 = $v1 $"${v1}=" ${  v1  }; ',
        , '      $"$-{""""$v1} =" $-{$""$"v1"}; '
    return
endProcedure tstCompDataVars

tstCompShell: procedure expose m.
/*
$=/tstCompShell3/
    ### start tst tstCompShell3 #######################################
    compile @, 8 lines: call tstOut "T",  "abc" $-¢2*3$! "efg"$-¢2*3$!"+
    hij"
    run without input
    abc 6 efg6hij
    insert into A540769x.tqt002 with n(n) as ( select 4 from sysibm.s
    insert into A540769x.tqt002 with n(n) as ( select 4 from sysibm.s  +
    .   union all .
    abc 6 efg6hij
$/tstCompShell3/ */
    call tstComp1 '@ tstCompShell3',
        , 'call tstOut "T",  "abc" $-¢2*3$! "efg"$-¢2*3$!"hij"',
        , 'ix=3' ,
        , 'call tstOut "T","insert into A540769x.tqt002" ,',
        ,     '"with n(n) as ( select" $-¢ix+1$! "from sysibm.s"',
        , 'call tstOut "T","insert into A540769x.tqt002"  ,  ',
        ,    '"with n(n) as ( select" $-¢ix+1$! "from sysibm.s" , ' ,
        ,    '"    union all "' ,
        , '$$ abc $-¢2*3$! efg$-¢2*3$!hij',
/*
$=/tstCompShell/
    ### start tst tstCompShell ########################################
    compile @, 12 lines:   $$  Lline one, $** asdf
    run without input
    Lline one,
    lline zwei output
    v1 = valueV1 ${v1}= valueV1|
    REXX OUT L5 CONTINUED L6 CONTINUED L7
    L8 ONE
    L9 TWO
    valueV1
    valueV1 valueV2
    out  valueV1 valueV2
    SCHLUSS
$/tstCompShell/ */
    call tstComp1 '@ tstCompShell',
        , '  $$  Lline one, $** asdf',
        , '   $$ lline zwei output',
        , '        $=v1= valueV1 ' ,
        , '$$       v1 = $v1 $"${v1}=" ${  v1  }| ' ,
        , 'call out rexx out l5, ' ,
        , '     continued l6 , ' ,
        , '     continued l7   ' ,
        , 'call out l8 one    ' ,
        , 'call out l9 two$=v2=valueV2  ',
        , '$$- $v1  $$- $v1 $v2   ',
        , 'call out   "out "     $v1 $v2   ',
        , '$$-   schluss    '
/*
$=/tstCompShell2/
    ### start tst tstCompShell2 #######################################
    compile @, 13 lines: $@do j=0 to 1 $@¢ $$ do j=$j
    run without input
    do j=0
    after if 0 $@¢ $!
    after if 0 $=@¢ $!
    do j=1
    if 1 then $@¢ a
    a2
    if 1 then $@=¢ b
    b2
    after if 1 $@¢ $!
    after if 1 $=@¢ $!
    end
$/tstCompShell2/ */
    call tstComp1 '@ tstCompShell2',
        , '$@do j=0 to 1 $@¢ $$ do j=$j' ,
        ,     'if $j then $@¢ ',
        ,          '$$ if $j then $"$@¢" a $$a2' ,
        ,          '$!',
        ,     'if $j then $@=¢ ',
        ,          '$$ if $j then $"$@=¢" b $$b2' ,
        ,          '$!',
        ,     'if $j then $@¢ $!' ,
        ,     '$$ after if $j $"$@¢ $!"' ,
        ,     'if $j then $@=¢ $!' ,
        ,     '$$ after if $j $"$=@¢ $!"' ,
        ,     '$!',
        , '$$ end'
    return
endProcedure tstCompShell

tstCompPrimary: procedure expose m.
    call compIni
/*
$=/tstCompPrimary/
    ### start tst tstCompPrimary ######################################
    compile =, 16 lines: Strings $"$""$""""$""" $'$''$''''$'''
    run without input
    Strings $"$""$" $'$''$'
    rexx $-¢ 3 * 5 $! = 15
    rexx $-¢ 5 * 7 $! = 35
    rexx $-// 7 * 11 $// = 77
    rexx $-/abcEf/ 11 * 13 $/abcEf/ = 143
    data  line three line four  bis hier
    shell line five line six bis hier
    var get   v1 value Eins, v1 value Eins .
    var isDef v1 1, v2 0 .
    #jIn eof 1#
    var read  >1 0 rr undefined
    #jIn eof 2#
    var read  >2 0 rr undefined
    no call abc$-¢4*5$! $-¢efg$-¢6*7$! abc20 EFG42
    brackets $-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$! 1000
    run with 3 inputs
    Strings $"$""$" $'$''$'
    rexx $-¢ 3 * 5 $! = 15
    rexx $-¢ 5 * 7 $! = 35
    rexx $-// 7 * 11 $// = 77
    rexx $-/abcEf/ 11 * 13 $/abcEf/ = 143
    data  line three line four  bis hier
    shell line five line six bis hier
    var get   v1 value Eins, v1 value Eins .
    var isDef v1 1, v2 0 .
    #jIn 1# eins zwei drei
    var read  >1 1 rr eins zwei drei
    #jIn 2# zehn elf zwoelf?
    var read  >2 1 rr zehn elf zwoelf?
    no call abc$-¢4*5$! $-¢efg$-¢6*7$! abc20 EFG42
    brackets $-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$! 1000
$/tstCompPrimary/ */
    call vRemove 'v2'
    call tstComp1 '= tstCompPrimary 3',
        , 'Strings $"$""$""""$"""' "$'$''$''''$'''",
        , 'rexx $"$-¢ 3 * 5 $! =" $-¢ 3 * 5 $!' ,
        , 'rexx $"$-¢ 5 * 7 $! =" $-¢ 5 * 7 $!' ,
        , 'rexx $"$-// 7 * 11 $// =" $-// 7 * 11 $//' ,
        , 'rexx $"$-/abcEf/ 11 * 13 $/abcEf/ ="',
            '$-/abcEf/ 11 * 13 $/abcEf/' ,
        , 'data $-=¢ line three',
        , 'line four $! bis hier'  ,
        , 'shell $-@¢ $$ line five',
        , '$$ line six $! bis hier' ,
        , '$= v1  =   value Eins  $=rr=undefined $= eins = 1 ',
        , 'var get   v1 $v1, v1 ${  v1  } ',
        , 'var isDef v1 ${? v${  eins  }  }, v2 ${?v2 } ',
        , 'var read  >1 ${> rr} rr $rr' ,
        , 'var read  >2 ${> rr} rr $rr',
        , 'no call $"abc$-¢4*5$! $-¢efg$-¢6*7$!"',
            'abc$-¢4*5$! $-¢efg$-¢6*7$!$!',
        , 'brackets $"$-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$!"',
            '$-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$!'
    return
endProcedure tstCompPrimary

tstCompExpr: procedure expose m.
    call compIni
/*
$=/tstCompExprStr/
    ### start tst tstCompExprStr ######################################
    compile -, 3 lines: $=vv=vvStr
    run without input
    vv=vvStr
    o2String($.-vv)=vvStr
$/tstCompExprStr/ */
    call tstComp1 '- tstCompExprStr',
        , '$=vv=vvStr' ,
        , '"vv="$vv' ,
        , '$"o2String($.-vv)="o2String($.-vv)'
/*
$=/tstCompExprObj/
    ### start tst tstCompExprObj ######################################
    compile ., 5 lines: $=vv=vvStr
    run without input
    vv=
    vvStr
    s2o($.vv)=
    vvStr
$/tstCompExprObj/ */
    call tstComp1 '. tstCompExprObj',
        , '$=vv=vvStr' ,
        , '"!vv="', '$.-vv',
        , '$."s2o($.vv)="', 's2o($-vv)'
/*
$=/tstCompExprDat/
    ### start tst tstCompExprDat ######################################
    compile =, 4 lines: $=vv=vvDat
    run without input
    vv=vvDat
    $.-vv= !vvDat
    $.-¢"abc"$!=!abc
$/tstCompExprDat/ */
    call tstComp1 '= tstCompExprDat',
        , '$=vv=vvDat' ,
        , 'vv=$vv',
        , '$"$.-vv=" $.-vv',
        , '$"$.-¢""abc""$!="$.-¢"abc"$!'

/*
$=/tstCompExprRun/
    ### start tst tstCompExprRun ######################################
    compile @, 3 lines: $=vv=vvRun
    run without input
    vv=vvRun
    o2string($.-vv)=vvRun
$/tstCompExprRun/ */
    call tstComp1 '@ tstCompExprRun',
        , '$=vv=vvRun' ,
        , 'call out "vv="$vv',
        , 'call out $"o2string($.-vv)="o2string($.-vv)'
/*
$=/tstCompExprCon/
    ### start tst tstCompExprCon ######################################
    compile #, 2 lines: $$ in # drinnen
    run without input
    $$ in # drinnen
    call out "vv="$vv
$/tstCompExprCon/
$=/tstCompExprCo2/
    ### start tst tstCompExprCo2 ######################################
    compile #, 3 lines: $$ in # drinnen
    run without input
    $$ in # drinnen
    call out "vv="$vv
    nacgh $#@
$/tstCompExprCo2/
*/
    call tstComp1 '# tstCompExprCon',
        , '$$ in # drinnen' ,
        , 'call out "vv="$vv'

    call tstComp1 '# tstCompExprCo2',
        , '$$ in # drinnen' ,
        , 'call out "vv="$vv',
        , '$#@ $$ nacgh $"$#@"'

    return
endProcedure tstCompExpr

tstCompStmt: procedure expose m.
/*
$=/tstCompStmt1/
    ### start tst tstCompStmt1 ########################################
    compile @, 8 lines: $= v1 = value eins  $= v2  =- 3*5*7 .
    run without input
    data v1 value eins v2 105
    eins
    zwei
    drei
    vier
    fuenf
    elf
    . zwoelf  dreiZ  .
    . vierZ .
    fuenfZ
    lang v1 value eins v2 945
    oRun ouput 1
$/tstCompStmt1/ */
    call pipeIni
    call compIni
    call vPut 'oRun', oRunner('call out "oRun ouput" (1*1)')
    call vRemove 'v2'
    call tstComp1 '@ tstCompStmt1',
        , '$= v1 = value eins  $= v2  =- 3*5*7 ',
        , '$$ data v1 $v1 v2 ${   v2  }',
        , '$$eins $@¢$$ zwei $$ drei  ',
        , '   $@¢   $!  $@//   $// $@/q r s /   $/q r s /',
             '       $@/eins/ $@¢ $$vier $! $/eins/ $! $$fuenf',
        , '$$elf $@=¢$@=¢ zwoelf  dreiZ  $!  ',
        , '   $@=¢   $! $@=¢ $@=¢ vierZ $! $! $! $$fuenfZ',
        , '$$- "lang v1" $v1 "v2" ${v2}*9',
        , '$@oRun'
/*
$=/tstCompStmt2/
    ### start tst tstCompStmt2 ########################################
    compile @, 1 lines: $@for qq $$ loop qq $qq
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    loop qq eins zwei drei
    #jIn 2# zehn elf zwoelf?
    loop qq zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    loop qq zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
$/tstCompStmt2/ */
    call tstComp1 '@ tstCompStmt2 3',
        , '$@for qq $$ loop qq $qq'

/*
$=/tstCompStmt3/
    ### start tst tstCompStmt3 ########################################
    compile @, 9 lines: $$ 1 begin run 1
    2 ct zwei
    ct 4 mit assign .
    run without input
    1 begin run 1
    3 run 3 ctV = ct 4 assign ctV|
    run 5 procCall $@prCa
    out in proc at 8
    run 6 vor call $@prCa
    out in proc at 8
    9 run end
    run with 3 inputs
    1 begin run 1
    3 run 3 ctV = ct 4 assign ctV|
    run 5 procCall $@prCa
    out in proc at 8
    run 6 vor call $@prCa
    out in proc at 8
    9 run end
$/tstCompStmt3/ */
    call tstComp1 '@ tstCompStmt3 3',
        , '$$ 1 begin run 1',
        , '$@ct $$ 2 ct zwei',
        , '$$ 3 run 3 ctV = $ctV|',
        , '$@ct $@=¢ct 4 mit assign $=ctV = ct 4 assign ctV $!',
        , '$$ run 5 procCall $"$@prCa" $@prCa',
        , '$$ run 6 vor call $"$@prCa"',
        , '$@prCa',
        , '$@proc prCa $$out in proc at 8',
        , '$$ 9 run end'

/*
$=/tstCompStmt4/
    ### start tst tstCompStmt4 ########################################
    compile @, 4 lines: $=eins=vorher
    run without input
    eins vorher
    eins aus named block eins .
$/tstCompStmt4/ */
    call tstComp1 '@ tstCompStmt4 0',
        , '$=eins=vorher' ,
        , '$$ eins $eins' ,
        , '$=/eins/aus named block eins $/eins/' ,
        , '$$ eins $eins'
/*
$=/tstCompStmtDo/
    ### start tst tstCompStmtDo #######################################
    compile @, 2 lines: ti=0$@do y=3 to 4 $@do 2 $@¢
    run without input
    y=3 ti1 z=7
    y=3 ti1 z=8
    y=3 ti2 z=7
    y=3 ti2 z=8
    y=4 ti3 z=7
    y=4 ti3 z=8
    y=4 ti4 z=7
    y=4 ti4 z=8
$/tstCompStmtDo/ */
    call tstComp1 '@ tstCompStmtDo', 'ti=0$@do y=3 to 4 $@do 2 $@¢',
     ,    'ti = ti + 1',
        '$@do $*(sdf$*) z $*(sdf$*) =7 to 8 $$ y=$y ti$-¢ti$! z=$z $!'

/*
$=/tstCompStmtDo2/
    ### start tst tstCompStmtDo2 ######################################
    compile @, 7 lines: $$ $-=/sqlSel/
    run without input
    select 1 abc select 2 abc after table .
$/tstCompStmtDo2/ */
    call tstComp1 '@ tstCompStmtDo2',
         , '$$ $-=/sqlSel/',
         ,     '$=ty = abc ',
         ,     '$@do tx=1 to 2 $@=/table/',
         ,          'select $tx $ty',
         , '$/table/',
         ,     '$=ty = abc',
         ,     'after table',
         '$/sqlSel/'
/*
$=/tstCompStmtWith/
    ### start tst tstCompStmtWith #####################################
    compile @, 3 lines: $@with $.vA $$ fEins=$FEINS fZwei=$FZWEI va&fEi+
    ns=${vA&FEINS}
    run without input
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
    fEins=2Eins fZwei=2Zwei va&fEins=1Eins
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
$/tstCompStmtWith/
*/
    cl = classNew('n? tstStmtWith u f FEINS v, f FZWEI v')
    v1 = onew(cl)
    m.v1.feins = '1Eins'
    m.v1.fzwei = '1Zwei'
    v2 = oNew(cl)
    m.v2.feins ='2Eins'
    m.v2.fzwei ='2Zwei'
    call vPut 'vA', v1
    call vPut 'vB', v2
    stmt = '$$ fEins=$FEINS fZwei=$FZWEI va&fEins=${vA&FEINS}'
    call tstComp1 '@ tstCompStmtWith',
         , '$@with $.vA' stmt ,
         , '$@with $vA $@¢' stmt ,
         , '$@with $vB ' stmt stmt '$!'
     return
endProcedure tstCompStmt

tstCompSyntax: procedure expose m.
    call tstCompSynPrimary
    call tstCompSynAss
    call tstCompSynRun
    return
endProcedure tstCompSyntax

tstCompSynPrimary: procedure expose m.

/*
$=/tstCompSynPri1/
    ### start tst tstCompSynPri1 ######################################
    compile @, 1 lines: a $ =
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $ =
    .    e 2: pos 3 in line 1: a $ =
    *** err: no method oRun in class String
$/tstCompSynPri1/ */
    call tstComp1 '@ tstCompSynPri1 +', 'a $ ='

/*
$=/tstCompSynPri2/
    ### start tst tstCompSynPri2 ######################################
    compile @, 1 lines: a $. {
    *** err: scanErr primary block or expression expected expected
    .    e 1: last token  scanPosition . {
    .    e 2: pos 4 in line 1: a $. {
    *** err: scanErr primary block or expression expected expected
    .    e 1: last token  scanPosition . {
    .    e 2: pos 4 in line 1: a $. {
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $. {
    .    e 2: pos 3 in line 1: a $. {
    *** err: no method oRun in class String
$/tstCompSynPri2/ */
    call tstComp1 '@ tstCompSynPri2 +', 'a $. {'

/*
$=/tstCompSynPri3/
    ### start tst tstCompSynPri3 ######################################
    compile @, 1 lines: b $-  ¢  .
    *** err: scanErr primary block or expression expected expected
    .    e 1: last token  scanPosition -  ¢
    .    e 2: pos 4 in line 1: b $-  ¢
    *** err: scanErr primary block or expression expected expected
    .    e 1: last token  scanPosition -  ¢
    .    e 2: pos 4 in line 1: b $-  ¢
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $-  ¢
    .    e 2: pos 3 in line 1: b $-  ¢
    *** err: no method oRun in class String
$/tstCompSynPri3/ */
    call tstComp1 '@ tstCompSynPri3 +', 'b $-  ¢  '

/*
$=/tstCompSynPri4/
    ### start tst tstCompSynPri4 ######################################
    compile @, 1 lines: a ${ $*( sdf$*) } =
    *** err: scanErr var name expected
    .    e 1: last token  scanPosition } =
    .    e 2: pos 17 in line 1: a ${ $*( sdf$*) } =
$/tstCompSynPri4/ */
    call tstComp1 '@ tstCompSynPri4 +', 'a ${ $*( sdf$*) } ='

/*
$=/tstCompSynFile/
    ### start tst tstCompSynFile ######################################
    compile @, 1 lines: $@.<$*( co1 $*) $$abc
    *** err: scanErr primary, block or expression expected
    .    e 1: last token  scanPosition .<$*( co1 $*) $$abc
    .    e 2: pos 3 in line 1: $@.<$*( co1 $*) $$abc
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@.<$*( co1 $*) $$abc
    .    e 2: pos 1 in line 1: $@.<$*( co1 $*) $$abc
    *** err: no method oRun in class String
$/tstCompSynFile/ */
    call tstComp1 '@ tstCompSynFile +', '$@.<$*( co1 $*) $$abc'

    return
endProcedure tstCompSynPrimary

tstCompSynAss: procedure expose m.

/*
$=/tstCompSynAss1/
    ### start tst tstCompSynAss1 ######################################
    compile @, 1 lines: $=
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 3 in line 1: $=
$/tstCompSynAss1/ */
    call tstComp1 '@ tstCompSynAss1 +', '$='

/*
$=/tstCompSynAss2/
    ### start tst tstCompSynAss2 ######################################
    compile @, 2 lines: $=   .
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $=
$/tstCompSynAss2/ */
    call tstComp1 '@ tstCompSynAss2 +', '$=   ', 'eins'

/*
$=/tstCompSynAss3/
    ### start tst tstCompSynAss3 ######################################
    compile @, 2 lines: $=   $$
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition $$
    .    e 2: pos 6 in line 1: $=   $$
$/tstCompSynAss3/ */
    call tstComp1 '@ tstCompSynAss3 +', '$=   $$', 'eins'

/*
$=/tstCompSynAss4/
    ### start tst tstCompSynAss4 ######################################
    compile @, 1 lines: $=   eins
    *** err: scanErr = expected in assignment after $= var
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $=   eins
$/tstCompSynAss4/ */
    call tstComp1 '@ tstCompSynAss4 +', '$=   eins'

/*
$=/tstCompSynAss5/
    ### start tst tstCompSynAss5 ######################################
    compile @, 1 lines: $=  abc eins $$ = x
    *** err: scanErr = expected in assignment after $= var
    .    e 1: last token  scanPosition eins $$ = x
    .    e 2: pos 9 in line 1: $=  abc eins $$ = x
$/tstCompSynAss5/ */
    call tstComp1 '@ tstCompSynAss5 +', '$=  abc eins $$ = x'

/*
$=/tstCompSynAss6/
    ### start tst tstCompSynAss6 ######################################
    compile @, 1 lines: $=  abc =
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $=  abc =
$/tstCompSynAss6/ */
    call tstComp1 '@ tstCompSynAss6 +', '$=  abc ='

/*
$=/tstCompSynAss7/
    ### start tst tstCompSynAss7 ######################################
    compile @, 1 lines: $=  abc =..
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 11 in line 1: $=  abc =..
$/tstCompSynAss7/ */
    call tstComp1 '@ tstCompSynAss7 +', '$=  abc =.'
    return
endProcedure tstCompSynAss

tstCompSynRun: procedure expose m.

/*
$=/tstCompSynRun1/
    ### start tst tstCompSynRun1 ######################################
    compile @, 1 lines: $@
    *** err: scanErr primary, block or expression expected
    .    e 1: last token  scanPosition .
    .    e 2: atEnd after line 1: $@
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@
    .    e 2: pos 1 in line 1: $@
    *** err: no method oRun in class String
$/tstCompSynRun1/ */
    call tstComp1 '@ tstCompSynRun1 +', '$@'

/*
$=/tstCompSynRun2/
    ### start tst tstCompSynRun2 ######################################
    compile @, 1 lines: $@=
    *** err: scanErr primary, block or expression expected
    .    e 1: last token  scanPosition =
    .    e 2: pos 3 in line 1: $@=
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@=
    .    e 2: pos 1 in line 1: $@=
$/tstCompSynRun2/ */
    call tstComp1 '@ tstCompSynRun2 +', '$@='

/*
$=/tstCompSynRun3/
    ### start tst tstCompSynRun3 ######################################
    compile @, 1 lines: $@: und
    *** err: scanErr bad kind : in compExpr
    .    e 1: last token  scanPosition und
    .    e 2: pos 5 in line 1: $@: und
    *** err: no method oRun in class Null
$/tstCompSynRun3/ */
    call tstComp1 '@ tstCompSynRun3 +', '$@: und'

/*
$=/tstCompSynFor4/
    ### start tst tstCompSynFor4 ######################################
    compile @, 1 lines: $@for
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $@for
$/tstCompSynFor4/ */
    call tstComp1 '@ tstCompSynFor4 +', '$@for'

/*
$=/tstCompSynFor5/
    ### start tst tstCompSynFor5 ######################################
    compile @, 2 lines: $@for
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $@for
$/tstCompSynFor5/ */
    call tstComp1 '@ tstCompSynFor5 +', '$@for', a

/*
$=/tstCompSynFor6/
    ### start tst tstCompSynFor6 ######################################
    compile @, 2 lines: a
    *** err: scanErr variable or named block after for
    .    e 1: last token  scanPosition .
    .    e 2: pos 15 in line 2:  b $@for   $$q
$/tstCompSynFor6/ */
    call tstComp1 '@ tstCompSynFor6 +', 'a', ' b $@for   $$q'

/*
$=/tstCompSynFor7/
    ### start tst tstCompSynFor7 ######################################
    compile @, 3 lines: a
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 11 in line 2:  b $@for a
$/tstCompSynFor7/ */
    call tstComp1 '@ tstCompSynFor7 +', 'a', ' b $@for a', '  $$q'

/*
$=/tstCompSynCt8/
    ### start tst tstCompSynCt8 #######################################
    compile @, 3 lines: a
    *** err: scanErr ct statement expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 8 in line 2:  b $@ct
$/tstCompSynCt8/ */
    call tstComp1 '@ tstCompSynCt8 +', 'a', ' b $@ct', '  $$q'

/*
$=/tstCompSynProc9/
    ### start tst tstCompSynProc9 #####################################
    compile @, 2 lines: a
    *** err: scanErr var or namedBlock expected after proc
    .    e 1: last token  scanPosition .
    .    e 2: pos 15 in line 2:  b $@proc  $$q
$/tstCompSynProc9/ */
    call tstComp1 '@ tstCompSynProc9 +', 'a', ' b $@proc  $$q'

/*
$=/tstCompSynProcA/
    ### start tst tstCompSynProcA #####################################
    compile @, 2 lines: $@proc p1
    *** err: scanErr proc statement expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $@proc p1
$/tstCompSynProcA/ */
    call tstComp1 '@ tstCompSynProcA +', '$@proc p1', '  $$q'

/*
$=/tstCompSynCallB/
    ### start tst tstCompSynCallB #####################################
    compile @, 1 lines: $@% ¢roc p1$!
    *** err: scanErr primary, block or expression expected
    .    e 1: last token  scanPosition % ¢roc p1$!
    .    e 2: pos 3 in line 1: $@% ¢roc p1$!
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@% ¢roc p1$!
    .    e 2: pos 1 in line 1: $@% ¢roc p1$!
    *** err: no method oRun in class String
$/tstCompSynCallB/ */
    call tstComp1 '@ tstCompSynCallB +', '$@% ¢roc p1$!'

/*
$=/tstCompSynCallC/
    ### start tst tstCompSynCallC #####################################
    compile @, 1 lines: $@%¢call roc p1 !
    *** err: scanErr ending $! expected after ¢
    .    e 1: last token  scanPosition .
    .    e 2: atEnd after line 1: $@%¢call roc p1 !
$/tstCompSynCallC/ */
    call tstComp1 '@ tstCompSynCallC +', '$@%¢call roc p1 !'

/*
$=/tstCompSynCallD/
    ### start tst tstCompSynCallD #####################################
    compile @, 2 lines: $@^¢call( $** roc
    *** err: scanErr ending $! expected after ¢
    .    e 1: last token  scanPosition )
    .    e 2: pos 13 in line 2:  $*( p1 $*) )
$/tstCompSynCallD/ */
    call tstComp1 '@ tstCompSynCallD +',
        ,'$@^¢call( $** roc' , ' $*( p1 $*) )'
    return
endProcedure tstCompSynRun

tstCompObj: procedure expose m.
    call tstReset t
    call oIni
    cl = classNew('n? tstCompCla u v, f FEINS v, f FZWEI v')
    do rx=1 to 10
        o = oNew(cl)
        m.tstComp.rx = o
        m.o = 'o'rx
        if rx // 2 = 0 then do
            m.o.fEins = 'o'rx'.1'
            m.o.fZwei = 'o'rx'.fZwei'rx
            end
        else do
            m.o.fEins = 'o'rx'.fEins'
            m.o.fZwei = 'o'rx'.2'
            end
        call mAdd 'T.TRANS', m.tstComp.rx '<o'rx'>'
        end

/*
$=/tstCompObjRef/
    ### start tst tstCompObjRef #######################################
    compile @, 13 lines: o1=m.tstComp.1
    run without input
    out .$"string" o1
    string
    out . o1
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .{ o2 }
    tstR: @<o2> isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    out .¢ o3 $!
    tstR: @<o3> isA :tstCompCla = o3
    tstR:  .FEINS = o3.fEins
    tstR:  .FZWEI = o3.2
    out .¢ o4 $!
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    out ./-/ o5 $/-/
    tstR: @<o5> isA :tstCompCla = o5
    tstR:  .FEINS = o5.fEins
    tstR:  .FZWEI = o5.2
$/tstCompObjRef/ */
m.ttt=1
    call tstComp1 '@ tstCompObjRef' ,
        , 'o1=m.tstComp.1',
        , 'o2 = m.tstComp.2' ,
        , '$$ out $".$""string""" o1 $$."string"',
        , '$$ out . o1 $$. o1',
        , '$$ out .{ o2 } $$.¢ o2 $!',
        , '$$ out .¢ o3 $"$!" $$.¢ ', ' m.tstComp.3 ', ' $!',
        , '$$ out .¢ o4 $"$!" $$.¢ ', ' m.tstComp.4 ', ' $!',
        , '$$ out ./-/ o5 $"$/-/" $$./-/  m.tstComp.5 ', ' $/-/'

/*
$=/tstCompObjRefPri/
    ### start tst tstCompObjRefPri ####################################
    compile @, 9 lines: $$ out .$"$.{o1}" $$.¢ m.tstComp.1 $!
    run without input
    out .$.{o1}
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .$.-{o2}
    <o2>
    out .$.={o3}
    . m.tstComp.3 .
    out .$.@{out o4}
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    tstWriteO kindOf ORun oRun end   >>>
    out .$.@¢$$abc $$efg$!
    tstWriteO kindOf ORun oRun begin <<<
    abc
    efg
    tstWriteO kindOf ORun oRun end   >>>
    out .$.@¢o5$!
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o5> isA :tstCompCla = o5
    tstR:  .FEINS = o5.fEins
    tstR:  .FZWEI = o5.2
    abc
    tstWriteO kindOf ORun oRun end   >>>
$/tstCompObjRefPri/ */
    call tstComp1 '@ tstCompObjRefPri' ,
        , '$$ out .$"$.{o1}" $$.¢ m.tstComp.1 $!',
        , '$$ out .$"$.-{o2}" $$.-¢ m.tstComp.2 $!',
        , '$$ out .$"$.={o3}" $$.=¢ m.tstComp.3 $!',
        , '$$ out .$"$.@{out o4}" $$.@@¢ call out m.tstComp.4 $!',
    , '$$ out .$"$.@¢$$abc $$efg$!" $$. $.@@¢ $$abc ', ' ', ' $$efg $!',
        , '$$ out .$"$.@¢o5$!" $$. $.@@¢ $$. m.tstComp.5', '$$abc $!'

/*
$=/tstCompObjRefFile/
    ### start tst tstCompObjRefFile ###################################
    compile @, 7 lines: $$ out .$".<.¢o1!" $$.<.¢ m.tstComp.1 $!
    run without input
    out ..<.¢o1!
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .<$.-{o2}
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o2> isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$.<{o3}
    tstWriteO kindOf JRW jWriteNow begin <<<
    . m.tstComp.3 .
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$.<@{out o4}
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$<@¢$$abc $$efg$!
    tstWriteO kindOf JRW jWriteNow begin <<<
    abc
    efg
    tstWriteO kindOf JRW jWriteNow end   >>>
$/tstCompObjRefFile/ */

    call tstComp1 '@ tstCompObjRefFile' ,
        , '$$ out .$".<.¢o1!" $$.<.¢ m.tstComp.1 $!',
        , '$$ out .$"<$.-{o2}" $$<.¢ m.tstComp.2 $!',
        , '$$ out .$"$.<{o3}" $$<=¢ m.tstComp.3 $!',
        , '$$ out .$"$.<@{out o4}" $$<@¢ call out m.tstComp.4 $!',
    , '$$ out .$"$<@¢$$abc $$efg$!" $$<@¢ $$abc ', ' ', ' $$efg $!'

/*
$=/tstCompObjFor/
    ### start tst tstCompObjFor #######################################
    compile @, 2 lines: $@do rx=1 to 3 $$. m.tstComp.rx
    run without input
    FEINS=o1.fEins FZWEI=o1.2
    FEINS=o2.1 FZWEI=o2.fZwei2
    FEINS=o3.fEins FZWEI=o3.2
$/tstCompObjFor/
*/
    call tstComp1 '@ tstCompObjFor' ,
        , '$@do rx=1 to 3 $$. m.tstComp.rx' ,
        , '$| $@forWith witx $$ FEINS=$FEINS FZWEI=$FZWEI'

/*
$=/tstCompObjRun/
    ### start tst tstCompObjRun #######################################
    compile @, 4 lines: $$ out .$"$@¢o1!" $$@¢ $$. m.tstComp.1 $!
    run without input
    out .$@¢o1!
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstWriteO kindOf ORun oRun end   >>>
    out .$<@¢$$abc $$efg$!
    tstWriteO kindOf JRW jWriteNow begin <<<
    abc
    efg
    tstWriteO kindOf JRW jWriteNow end   >>>
$/tstCompObjRun/ */
    call tstComp1 '@ tstCompObjRun' ,
        , '$$ out .$"$@¢o1!" $$@¢ $$. m.tstComp.1 $!',
    , '$$ out .$"$<@¢$$abc $$efg$!" $$<@¢ $$abc ', ' ', ' $$efg $!'

    m.t.trans.0 = 0
/*
$=/tstCompObj/
    ### start tst tstCompObj ##########################################
    compile @, 6 lines: o1=m.tstComp.1
    run without input
    out . o1
    tstR: @tstWriteoV1 isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .{ o2 }
    tstR: @tstWriteoV2 isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    out .¢ o1, o2!
    tstR: @tstWriteoV1 isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstR: @tstWriteoV2 isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
$/tstCompObj/ */
    call tstComp1 '@ tstCompObj' ,
        , 'o1=m.tstComp.1',
        , 'o2 = m.tstComp.2' ,
        , '$$ out . o1 $$. o1',
        , '$$ out .{ o2 } $$.¢ o2 $!',
    , '$$ out .¢ o1, o2!$; $@<.¢  m.tstComp.1  ', '  m.tstComp.2  $!'
    return
    m.t.trans.0 = 0
endProcedure tstCompObj

tstCompORun: procedure expose  m.
/*
$=/tstCompORun/
    ### start tst tstCompORun #########################################
    compile @, 6 lines: $@oRun
    run without input
    oRun arg=1, v2=, v3=, v4=
    oRun arg=1, v2=, v3=, v4=
    oRun arg=2, v2=-{1 arg only} oder?, v3=, v4=
    oRun arg=2, v2=!.{1 obj only} oder?, v3=, v4=
    oRun arg=3, v2={2 args}, v3=und zwei?, v4=
    oRun arg=4, v2={3 args}, v3=zwei, v4=und drei?
$/tstCompORun/ */
    call compIni
    call vPut 'oRun', oRunner('parse arg , v2, v3, v4;',
        'call tstOut t, "oRun arg="arg()", v2="v2", v3="v3", v4="v4' )
    call tstComp1 '@ tstCompORun',
        , '$@oRun', '$@%¢oRun$!' ,
        , '    $@%¢oRun $"-{1 arg only}" oder?$!' ,
        , '    $@%¢oRun - $.".{1 obj only}" ''oder?''$! $=v2=zwei' ,
        , '    $@%¢oRun - $"{2 args}", "und" $v2"?"$!' ,
        , '    $@%¢oRun - $"{3 args}", $v2, "und drei?"$!'
    return
endProcedure tstCompORun

tstCompORu2: procedure expose  m.
/*
$=/tstCompORu2/
    ### start tst tstCompORu2 #########################################
    compile @, 6 lines: $@oRun
    run without input
    oRun arg=1, v2=, v3=, v4=
    oRun arg=1, v2=, v3=, v4=
    oRun arg=2, v2=eins, zwei, drei, v3=, v4=
    oRun arg=2, v2=eins, zwei, drei, v3=, v4=
    oRun arg=4, v2=-eins, v3=zwei, v4=DREI
    oRun arg=4, v2=-eins, v3=zwei, v4=DREI
$/tstCompORu2/ */
    call compIni
    call vPut 'oRun', oRunner('parse arg , v2, v3, v4;',
        'call tstOut t, "oRun arg="arg()", v2="v2", v3="v3", v4="v4' )
    call tstComp1 '@ tstCompORu2',
        , '$@oRun', '$@%oRun',
        , '$@% oRun  eins, zwei, drei' ,
        , '$@%¢ oRun eins, zwei, drei $!',
        , '$@% oRun  - "-eins", "zwei", drei' ,
        , '$@%¢ oRun - "-eins", "zwei", drei $!'
    return
endProcedure tstCompORu2

tstCompORuRe: procedure expose  m.
/*
$=/tstCompORuRe/
    ### start tst tstCompORuRe ########################################
    compile @, 9 lines: $$ primary $-^oRuRe eins, zwei
    run without input
    primary oRuRe(arg=1, v2=, v3=) eins, zwei
    oRuRe(arg=2, v2=expr, zwei, v3=)
    oRuRe(arg=3, v2=-expr, v3=zwei)
    oRuRe(arg=2, v2=block, zwei, v3=)
    oRuRe(arg=3, v2=-block, v3=zwei)
$/tstCompORuRe/ */
    call compIni
    call vPut 'oRuRe', oRunner('parse arg , v2, v3;',
        'return "oRuRe(arg="arg()", v2="v2", v3="v3")"' )
    call tstComp1 '@ tstCompORuRe',
        , '$$ primary $-^oRuRe eins, zwei' ,
        , '$$-^ oRuRe expr, zwei',
        , '$$-^ oRuRe - "-expr", "zwei"',
        , '$$-^¢oRuRe block, zwei$!' ,
        , '$$-^¢',, 'oRuRe - "-block", "zwei"' , , '$!'
    return
endProcedure tstCompORuRe

tstCompDataIO: procedure expose m.
/*
$=/tstCompDataHereData/
    ### start tst tstCompDataHereData #################################
    compile =, 13 lines:  herdata $@#/stop/    .
    run without input
    . herdata .
    heredata 1 $x
    heredata 2 $y
    nach heredata
    . herdata ¢ .
    heredata 1 xValue
    heredata 2 yValueY
    nach heredata ¢
    . herdata { .
    HEREDATA 1 xValue
    heredata 2 yValueY
    nach heredata {
$/tstCompDataHereData/ */
    call tstComp1 '= tstCompDataHereData',
        , ' herdata $@#/stop/    ',
        , 'heredata 1 $x',
        , 'heredata 2 $y',
        , '$/stop/ $$ nach heredata',
        , ' herdata ¢ $@=/stop/    ',
        , '$=x=xValue$=y=yValueY',
        , 'heredata 1 $x',
        , 'heredata 2 $y',
        , '$/stop/ $$ nach heredata ¢',
        , ' herdata { $@/st/',
        , '; call out heredata 1 $x',
        , '$$heredata 2 $y',
        , '$/st/ $$ nach heredata {'
/*
$=/tstCompDataIO/
    ### start tst tstCompDataIO #######################################
    compile =, 5 lines:  input 1 $@.<-=¢$dsn $*+.
    run without input
    . input 1 .
    readInp line 1                       .
    readInp line 2                       .
    . nach dsn input und nochmals mit & .
    readInp line 1                       .
    readInp line 2                       .
    . und schluiss..
$/tstCompDataIO/ */
    dsn = word(tstPdsMbr(tstFilename('lib37', 'r'), 'readInp'), 1)
    dsnFB = strip(dsn tstFB('::F37', 0))
    b = jBuf(tstFB('readInp line 1', 37),
            ,tstFB('readInp line 2', 37))
    extFd = tstFB('&dsn('dsn') dd(xyz)', 0)
    extFd = tstFB(dsn 'dd(xyz) ::f', 0)
    if extFd = '' then
        extFd = dsn
    call jCat file(dsnFB), b
    call vPut 'dsn', dsn
    say 'dsn' dsn 'extFD' extFD'?'
    call tstComp1 '= tstCompDataIO',
        , ' input 1 $@.<-=¢$dsn $*+',
        , tstFB('::f', 0) '$!',
        , ' nach dsn input und nochmals mit & ' ,
        , '         $@.<'extFD,
        , ' und schluiss.'
    return
endProcedure tstCompDataIO

tstObjVF: procedure expose m.
parse arg v, f
    obj  = oNew(classNew('n? TstClassVF u v, f FLD1 v'))
    m.obj = if(f=='','val='v, v)
    m.obj.fld1 = if(f=='','FLD1='v, f)
    return obj
endProcedure tstObjVF

tstCompFile: procedure expose m.
/*
$=/tstCompFileBloSrc/
    $=vv=value-of-vv
    ###file from empty # block
    $@<#¢
        $!
    ###file from 1 line # block
    $@<#¢
    the only $ix+1/0 line $vv
    $!
    ###file from 2 line # block
    $@<#¢
        first line /0 $*+ no comment
        second and last line $$ $wie
    $!
    ===file from empty = block
    $@<=¢     $*+ comment
        $!
    ===file from 1 line = block
    $@<=¢ the only line $!
    ===file from 2 line = block
    $@<=¢ first line$** comment
        second and last line  $!
    ---file from empty - block
    $@<-/s/
        $/s/
    ---file from 1 line - block
    $@<-/s/ the only "line" (1*1) $/s/
    ---file from 2 line = block
    $@<-// first "line" (1+0)
        second   and   "last  line" (1+1)  $//
    ...file from empty . block
    $@<.¢
        $!
    ...file from 1 line . block
    $@<.¢ tstObjVF('v-Eins', '1-Eins') $!
    ...file from 2 line . block
    $@<.¢ tstObjVF('v-Elf', '1-Elf')
        tstObjVF('zwoelf')  $!
    ...file from 3 line . block
    $@<.¢ tstObjVF('einUndDreissig')
            s2o('zweiUndDreissig' o2String($.-vv))
            tstObjVF('dreiUndDreissig')  $!
    @@@file from empty @ block
    $@<@¢
        $!
    $=noOutput=before
    @@@file from nooutput @ block
    $@<@¢ nop
        $=noOutput = run in block $!
    @@@nach noOutput=$noOutput
    @@@file from 1 line @ block
    $@<@¢ $$. tstObjVF('w-Eins', 'w1-Eins') $!
    @@@file from 2 line @ block
    $@<@¢ $$. tstObjVF('w-Elf', 'w1-Elf')
        y='zwoelf' $$- y  $!
    @@@file from 3 line @ block
    $@<@¢ $$. tstObjVF('w einUndDreissig')    $$ +
    zweiUndDreissig $$ 33 $vv$!
    {{{ empty ¢ block
    $@<¢     $!
    {{{ empty ¢ block with comment
    $@<¢    $*+ abc
          $!
    {{{ one line ¢ block
    $@<¢ the only $"¢...$!" line $*+.
        $vv $!
    {{{ one line -¢ block
    $@<-¢ the only $"-¢...$!"  "line" $vv  $!
    {{{ empty #¢ block
    $@<#¢
$!
    {{{ one line #¢ block
    $@<#¢ the only $"-¢...$!"  "line" $vv $¢vv${x}$!
$!
$/tstCompFileBloSrc/ */
/*
$=/tstCompFileBlo/
    ### start tst tstCompFileBlo ######################################
    compile =, 72 lines: $=vv=value-of-vv
    run without input
    ###file from empty # block
    ###file from 1 line # block
    the only $ix+1/0 line $vv
    ###file from 2 line # block
    first line /0 $*+ no comment
    second and last line $$ $wie
    ===file from empty = block
    ===file from 1 line = block
    . the only line .
    ===file from 2 line = block
    . first line
    second and last line  .
    ---file from empty - block
    ---file from 1 line - block
    THE ONLY line 1
    ---file from 2 line = block
    FIRST line 1
    SECOND AND last  line 2
    ...file from empty . block
    ...file from 1 line . block
    tstR: @tstWriteoV1 isA :TstClassVF = v-Eins
    tstR:  .FLD1 = 1-Eins
    ...file from 2 line . block
    tstR: @tstWriteoV2 isA :TstClassVF = v-Elf
    tstR:  .FLD1 = 1-Elf
    tstR: @tstWriteoV3 isA :TstClassVF = val=zwoelf
    tstR:  .FLD1 = FLD1=zwoelf
    ...file from 3 line . block
    tstR: @tstWriteoV4 isA :TstClassVF = val=einUndDreissig
    tstR:  .FLD1 = FLD1=einUndDreissig
    zweiUndDreissig value-of-vv
    tstR: @tstWriteoV5 isA :TstClassVF = val=dreiUndDreissig
    tstR:  .FLD1 = FLD1=dreiUndDreissig
    @@@file from empty @ block
    @@@file from nooutput @ block
    @@@nach noOutput=run in block
    @@@file from 1 line @ block
    tstR: @tstWriteoV6 isA :TstClassVF = w-Eins
    tstR:  .FLD1 = w1-Eins
    @@@file from 2 line @ block
    tstR: @tstWriteoV7 isA :TstClassVF = w-Elf
    tstR:  .FLD1 = w1-Elf
    zwoelf
    @@@file from 3 line @ block
    tstR: @tstWriteoV8 isA :TstClassVF = val=w einUndDreissig
    tstR:  .FLD1 = FLD1=w einUndDreissig
    zweiUndDreissig
    33 value-of-vv
    {{{ empty ¢ block
    {{{ empty ¢ block with comment
    {{{ one line ¢ block
    . the only ¢...$! line value-of-vv .
    {{{ one line -¢ block
    THE ONLY -¢...$! line value-of-vv
    {{{ empty #¢ block
    {{{ one line #¢ block
    . the only $"-¢...$!"  "line" $vv $¢vv${x}$!
$/tstCompFileBlo/ */
    call tstComp2 'tstCompFileBlo', '='
    m.t.trans.0 = 0

/*
$=/tstCompFileObjSrc/
    $=vv=value-vv-1
    $=fE=<¢ $!
    $=f2=. $.<.¢s2o("f2 line 1" o2String($.-vv))
         tstObjVF("f2 line2") $!
    ---empty file $"$@<$fE"
    $@fE
    ---file with 2 lines $"$@<$f2"
    $@.<.f2
    $=vv=value-vv-2
    ---file with 2 lines $"$@<$f2"
    $@.<.f2
    $= dsn =- word(tstPdsMbr(tstFilename('libvb', 'r'), 'fileObj'),1) +
                tstFB('::V', 0)
    $@¢
        fi=jOpen(file($dsn),'>')
        call jWrite fi, 'line one on' $"$dsn"
        call jWrite fi, 'line two on' $"$dsn"
        call jClose fi
    $!
    ---file on disk out
    $@<-dsn
$/tstCompFileObjSrc/ */
/*
$=/tstCompFileObj/
    ### start tst tstCompFileObj ######################################
    compile =, 20 lines: $=vv=value-vv-1
    run without input
    ---empty file $@<$fE
    ---file with 2 lines $@<$f2
    f2 line 1 value-vv-1
    tstR: @tstWriteoV1 isA :TstClassVF = val=f2 line2
    tstR:  .FLD1 = FLD1=f2 line2
    ---file with 2 lines $@<$f2
    f2 line 1 value-vv-1
    tstR: @tstWriteoV1 isA :TstClassVF = val=f2 line2
    tstR:  .FLD1 = FLD1=f2 line2
    ---file on disk out
    line one on $dsn
    line two on $dsn
$/tstCompFileObj/ */
    call tstComp2 'tstCompFileObj', '='

    return
endProcedure tstCompFile

tstCompPipe: procedure expose m.
/*
$=/tstCompPipe1/
    ### start tst tstCompPipe1 ########################################
    compile @, 1 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    (1 eins zwei drei 1)
    #jIn 2# zehn elf zwoelf?
    (1 zehn elf zwoelf? 1)
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    (1 zwanzig 21 22 23 24 ... 29| 1)
    #jIn eof 4#
$/tstCompPipe1/ */
    call tstComp1 '@ tstCompPipe1 3',
        , ' call pipePreSuf "(1 ", " 1)"'
/*
$=/tstCompPipe2/
    ### start tst tstCompPipe2 ########################################
    compile @, 2 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    ¢2 (1 eins zwei drei 1) 2!
    ¢2 (1 zehn elf zwoelf? 1) 2!
    ¢2 (1 zwanzig 21 22 23 24 ... 29| 1) 2!
$/tstCompPipe2/ */
    call tstComp1 '@ tstCompPipe2 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| call pipePreSuf "¢2 ", " 2!"'

/*
$=/tstCompPipe3/
    ### start tst tstCompPipe3 ########################################
    compile @, 3 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <3 ¢2 (1 eins zwei drei 1) 2! 3>
    <3 ¢2 (1 zehn elf zwoelf? 1) 2! 3>
    <3 ¢2 (1 zwanzig 21 22 23 24 ... 29| 1) 2! 3>
$/tstCompPipe3/ */
    call tstComp1 '@ tstCompPipe3 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| call pipePreSuf "¢2 ", " 2!"',
        , ' $| call pipePreSuf "<3 ", " 3>"'

/*
$=/tstCompPipe4/
    ### start tst tstCompPipe4 ########################################
    compile @, 7 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <3 ¢222 ¢221 ¢21 ¢20 (1 eins zwei drei 1) 20! 21! 221! 222! 3>
    <3 ¢222 ¢221 ¢21 ¢20 (1 zehn elf zwoelf? 1) 20! 21! 221! 222! 3>
    <3 ¢222 ¢221 ¢21 ¢20 (1 zwanzig 21 22 23 24 ... 29| 1) 20! 21! 221!+
    . 222! 3>
$/tstCompPipe4/ */
    call tstComp1 '@ tstCompPipe4 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| $@¢    call pipePreSuf "¢20 ", " 20!"',
        ,        ' $| call pipePreSuf "¢21 ", " 21!"',
        ,        ' $| $@¢      call pipePreSuf "¢221 ", " 221!"',
        ,                 ' $| call pipePreSuf "¢222 ", " 222!"',
        ,     '$!     $! ',
        , ' $| call pipePreSuf "<3 ", " 3>"'
    return
endProcedure tstCompPipe


tstCompPip2: procedure expose m.
/*
$=/tstCompPip21/
    ### start tst tstCompPip21 ########################################
    compile @, 3 lines:  $<¢ zeile eins .
    run without input
    (1  zeile eins  1)
    (1    zeile zwei  1)
    run with 3 inputs
    (1  zeile eins  1)
    (1    zeile zwei  1)
$/tstCompPip21/ */
    call tstComp1 '@ tstCompPip21 3',
        , ' $<¢ zeile eins ' ,
        , '   zeile zwei $!' ,
        , ' call pipePreSuf "(1 ", " 1)"'
/*
$=/tstCompPip22/
    ### start tst tstCompPip22 ########################################
    compile @, 3 lines: if ${>i1} then $@¢
    run without input
    #jIn eof 1#
    nachher
    run with 3 inputs
    #jIn 1# eins zwei drei
    <zeile 1: eins zwei drei>
    <zwei>
    nachher
$/tstCompPip22/ */
    call tstComp1 '@ tstCompPip22 3',
        , 'if ${>i1} then $@¢'          ,
        , ' $$ zeile 1: $i1 $$ zwei $| call pipePreSuf "<",">" $!',
        , ' $$ nachher '
    return
endProcedure tstCompPip2

tstCompRedir: procedure expose m.
/*
$=/tstCompRedir/
    ### start tst tstCompRedir ########################################
    compile @, 6 lines:  $=eins=<@¢ $@for vv $$ <$vv> $! .
    run without input
    #jIn eof 1#
    output eins .
    output piped zwei .
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    output eins <eins zwei drei> <zehn elf zwoelf?> +
    <zwanzig 21 22 23 24 ... 29|>
    output piped zwei ab<eins zwei drei>yz ab<zehn elf zwoelf?>yz a+
    b<zwanzig 21 22 23 24 ... 29|>yz
$/tstCompRedir/ */
    call pipeIni
    call vRemove 'eins'  /* alte Variable loswerden */
    dsn = word(tstPdsMbr(tstFilename('libvb', 'r'), 'redir1'), 1)
    call vPut 'dsn', dsn
    say  'dsn' $dsn
    call tstComp1 '@ tstCompRedir 3' ,
        , ' $=eins=<@¢ $@for vv $$ <$vv> $! ',
        , ' $$ output eins $-=¢$.eins$! $; ',
        , ' $@for ww $$b${ww}y ' ,
        , '    $>-= $-¢ $dsn $! 'tstFB('::v', 0),
        ,         '$| call pipePreSuf "a", "z" $<.eins' ,
        , ' $; $$ output piped zwei $-=¢$@<$-dsn$!'
/*
$=/tstCompRedi2/
    ### start tst tstCompRedi2 ########################################
    compile @, 12 lines: call mAdd t.trans, $var "dsnTestRedi"
    run without input
    >1<dsnTestRedi currTimeRedi
    >2<$"dsnTestRedi" currTimeRedi
    >3<$"dsnTestRedi" ::v currTimeRedi
    >4<$-var" currTimeRedi
    >5<$dsnTestRedi" currTimeRedi
$/tstCompRedi2/
*/
    call vPut 'var', tstFileName('compRedi', 'r')
    call vPut 'tst', translate(date()'+'time()'+testRedi2', '_', ' ')
    call tstComp1 '@ tstCompRedi2 ' ,
        , 'call mAdd t.trans, $var "dsnTestRedi"',
        , 'call mAdd t.trans, $tst "currTimeRedi"',
        , '$<> $>'vGet('var') '::v $$ $">1<'vGet('var')'" $tst',
        , '$<> $<'vGet('var') '    $@ call pipeWriteAll' ,
        , '$<> $>$"'vGet('var')' ::v" $$ $">2<$""'vGet('var')'""" $tst',
        , '$<> $<$"'vGet('var') '"   $@ call pipeWriteAll',
   , '$<> $>$"'vGet('var')'" ::v $$ $">3<$""'vGet('var')'"" ::v" $tst',
        , '$<> $<$"'vGet('var') '"   $@ call pipeWriteAll',
        , '$<> $>-var  $$ $">4<$"-var" $tst',
        , '$<> $<-var  $@ call pipeWriteAll',
        , '$<> $>$var ::v $$ $">5<$"$var" $tst',
        , '$<> $<$var  $@ call pipeWriteAll'
    return
endProcedure tstCompRedir

tstCompComp: procedure expose m.
/*
$=/tstCompCompShell/
    ### start tst tstCompCompShell ####################################
    compile @, 5 lines: $$compiling shell $; $= rrr =. $.^compile $<@#/+
    aaa/
    run without input
    compiling shell
    running einmal
    RUN 1 COMPILED einmal
    #jIn eof 1#
    running zweimal
    RUN 1 COMPILED zweimal
    #jIn eof 2#
    run with 3 inputs
    compiling shell
    running einmal
    RUN 1 COMPILED einmal
    #jIn 1# eins zwei drei
    compRun eins zwei dreieinmal
    #jIn 2# zehn elf zwoelf?
    compRun zehn elf zwoelf?einmal
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    compRun zwanzig 21 22 23 24 ... 29|einmal
    #jIn eof 4#
    running zweimal
    RUN 1 COMPILED zweimal
    #jIn eof 5#
$/tstCompCompShell/ */
    call tstComp1 '@ tstCompCompShell 3',
        ,  "$$compiling shell $; $= rrr =. $.^compile $<@#/aaa/",
        ,  "call out run 1*1*1 compiled $cc;" ,
            "$@for v $$ compRun $v$cc" ,
        ,  "$/aaa/ $;",
        ,  "$=cc=einmal $$ running $cc $@rrr",
        ,  "$=cc=zweimal $$ running $cc $@rrr"
/*
$=/tstCompCompData/
    ### start tst tstCompCompData #####################################
    compile @, 5 lines: $$compiling data $; $= rrr =. $.^¢compile = +
    =$!  $<@#/aaa/
    run without input
    compiling data
    running einmal
    call out run 1*1*1 compiled einmal
    running zweimal
    call out run 1*1*1 compiled zweimal
    run with 3 inputs
    compiling data
    running einmal
    call out run 1*1*1 compiled einmal
    running zweimal
    call out run 1*1*1 compiled zweimal
$/tstCompCompData/ */
    call tstComp1 '@ tstCompCompData 3',
        ,  "$$compiling data $; $= rrr =. $.^¢compile = =$!  $<@#/aaa/",
        ,  "call out run 1*1*1 compiled $cc",
        ,  "$/aaa/ $;",
        ,  "$=cc=einmal $$ running $cc $@rrr",
        ,  "$=cc=zweimal $$ running $cc $@rrr"
    return
endProcedure tstCompComp

tstCompDir: procedure expose m.
/*
$=/tstCompDirSrc/
  'in src v1='$v1
  $#@ call out 'src @ out v1='$v1
  $#. $*(komm$*) s2o('src . v1=')
       $.-v1
  $#-
    'src - v1='$v1
  $#=
    src = v1=$v1
$/tstCompDirSrc/

$=/tstCompDir/
    ### start tst tstCompDir ##########################################
    compile @call out 'before v1='$v1 $=v1=eins $#. s2o('. v1='$-v1) $#+
    @ call out '@ v1='$v1 $#= = v1=$v1 $#- '- v1='$v1 $#-, 8 lines: 'in+
    . src v1='$v1
    run without input
    before v1=v1Before
    .. v1=eins
    @ v1=eins
    = v1=eins .
    - v1=eins
    in src v1=eins
    src @ out v1=eins
    src . v1=
    eins
    src - v1=eins
    src = v1=eins
$/tstCompDir/ */
    call compIni
    call vPut 'v1', 'v1Before'
    call tstComp2 'tstCompDir', "@call out 'before v1='$v1 $=v1=eins" ,
        "$#. s2o('. v1='$-v1) $#@ call out '@ v1='$v1" ,
        "$#= = v1=$v1 $#- '- v1='$v1 $#-"
/*
$=/tstCompDirPiSrc/
  zeile 1 v1=$v1
  zweite Zeile vor $"$@$#-"
  $#@  $@proc pi2 $@-¢
  $'zeile drei nach $@$#- v1='v1
  vierte und letzte Zeile $!
$/tstCompDirPiSrc/ */
/*
$=/tstCompDirPi/
    ### start tst tstCompDirPi ########################################
    compile @call pipePreSuf '<','>' $=v1=eiPi $<.pi2 $#=, 5 lines: ze+
    ile 1 v1=$v1
    run without input
    <zeile drei nach $@$#- v1=V1>
    <VIERTE UND LETZTE ZEILE>
    zeile 1 v1=eiPi
    zweite Zeile vor $@$#-
$/tstCompDirPi/ */
    call tstComp2 'tstCompDirPi',
            , "@call pipePreSuf '<','>' $=v1=eiPi $<.pi2 $#="
    return
endProcedure tstCompDir

tstCompColon: procedure expose m.
/*
$=/tstCompColon1/
    ### start tst tstCompColon1 #######################################
    compile :, 12 lines: vA = valueVonA
    run without input
    vA = valueVonA
    vA=valueVonA vB=valueVonB vC=valueVonC
    vC=valueVonC vD=valueVonD vE=valueVonvE
    vF=6
$/tstCompColon1/ */
    call tstComp1 ': tstCompColon1',
        , 'vA = valueVonA' ,
        , ' $$ vA = $vA' ,
        , '        * kommentar ' ,
        , '=vB=- "valueVonB"' ,
        , '=/vC/valueVonC$/vC/' ,
        , ' $$ vA=$vA vB=$vB vC=$vC' ,
        , '$=/vD/valueVonD' ,
        , '$/vD/ vE=valueVonvE' ,
        , '        * kommentar ' ,
        , ' $$ vC=$vC vD=$vD vE=$vE',
        , 'vF=- 2*3 $=vG=@@¢ $$ vF=$vF$!' ,
        , '@vG'

/*
$=/tstCompColon2/
    ### start tst tstCompColon2 #######################################
    compile :, 7 lines: ix=0
    run without input
    #jIn eof 1#
    proc p1 arg(2) total 0 im argumentchen
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <<for 1 -> eins zwei drei>>
    <<for 2 -> zehn elf zwoelf?>>
    <<for 3 -> zwanzig 21 22 23 24 ... 29|>>
    proc p1 arg(2) total 3 im argumentchen
$/tstCompColon2/

*/
    call tstComp1 ': tstCompColon2 3',
        , 'ix=0' ,
        , 'for v @:¢ix=- $ix+1',
        , ' $$ for $ix -> $v' ,
        , '! | @¢call pipePreSuf "<<",">>"',
        , '$! @%¢p1 total $ix im argumentchen$!',
        , 'proc @:/p1/$$- "proc p1 arg(2)" arg(2)' ,
        , '/p1/'
/*
$=/tstCompColon3/
    ### start tst tstCompColon3 #######################################
    compile :, 11 lines: tc3Eins=freeVar1
    run without input
    tc3Eins=freeVar1 o2&tc3Eins= o2&tc3Zwei=
    tc3Eins=freeVar1 o2&tc3Eins=with3Eins o2&tc3Zwei=with3Zwei
    tc3Eins=freeVar1 o2&tc3Eins=ass4Eins o2&tc3Zwei=with5Zwei
    o3&tc3Eins=ass4Eins o3&tc3Zwei=with5 o3 Zwei
    tc3Eins=freeVar1 o2&tc3Eins=ass4Eins o2&tc3Zwei=with5Zwei
$/tstCompColon3/
*/
    call classNew 'n? TstCompColon3 u f tc3Eins v, f tc3Zwei v'
    showO2 = 'tc3Eins=$tc3Eins' ,
            'o2&tc3Eins=${o2&tc3Eins} o2&tc3Zwei=${o2&tc3Zwei}'
    showO3 = 'o3&tc3Eins=${o3&tc3Eins} o3&tc3Zwei=${o3&tc3Zwei}'
    call tstComp1 ': tstCompColon3',
        , 'tc3Eins=freeVar1' ,
     , 'o2 =. oNew("TstCompColon3")' ,
        , '$$' showO2 ,
        , 'with $o2 $@:¢tc3Eins = with3Eins',
        ,     'tc3Zwei = with3Zwei',
        ,    '! $$' showO2 ,
        , '{o2&tc3Eins} = ass4Eins',
        , 'with $o2 $=tc3Zwei = with5Zwei',
        , '$$' showO2 ,
        , 'with o3 =. oCopy($o2) $=tc3Zwei = with5 o3 Zwei',
        , '$$' showO3 '$$' showO2
    return
endProcedure tstCompColon

tstCompTable: procedure expose m.
/*
$=/tstCompTable1/
    ### start tst tstCompTable1 #######################################
    compile :, 6 lines: table $*( sdf $*)   .
    run without input
    tstR: @tstWriteoV2 isA :<TstCT1Class>
    tstR:  .fEins = v1
    tstR:  .fZwei = valueZwei
    tstR:  .fDrei = undDrei
    zweite
    tstR: @tstWriteoV3 isA :<TstCT1Class>
    tstR:  .fEins = w1
    tstR:  .fZwei = wZwei
    tstR:  .fDrei = wwwDrei
$/tstCompTable1/

 */
    call wshIni
    cl = classNew('n* CompTable u f fEins v, f fZwei v, f fDrei v')
    call tstComp1 ': tstCompTable1',
        , 'table $*( sdf $*)   ' ,
        , 'fEins   fZwei $*(....$*) fDrei  ' ,
        , '$@¢call mAdd t.trans, className("'cl'") "<TstCT1Class>"$!',
        , '    v1     valueZwei undDrei     ' ,
        , '$$ zweite',
        , ' w1 wZwei                    wwwDrei     '


/*
$=/tstCompWithNew/
    ### start tst tstCompWithNew ######################################
    compile :, 12 lines: withNew nn $*( sdf $*)  $@:¢  .
    run without input
    tstR: @tstWriteoV2 isA :<TstCT1Class>
    tstR:  .fEins = withNewValue fEins
    tstR:  .fZwei = withNewValue fZwei
    tstR:  .fDrei = withNewValuel drei
    tstR: @tstWriteoV3 isA :<TstCT1Class>
    tstR:  .fEins = withNewValue fEinsB
    tstR:  .fZwei = withNewValue fZweiB
    tstR:  .fDrei = withNewValue fDreiB
    tstR: @tstWriteoV4 isA :<TstCT1Class>
    tstR:  .fEins = withValue fEinsC
    tstR:  .fZwei = .
    tstR:  .fDrei = withValue fDreiC
$/tstCompWithNew/
*/
    call tstComp1 ': tstCompWithNew',
        , 'withNew nn $*( sdf $*)  $@:¢  ' ,
        , 'fEins = withNewValue fEins' ,
        , 'fZwei = withNewValue fZwei' ,
        , '$@¢call mAdd t.trans, className("'cl'") "<TstCT1Class>"',
        , '$@:¢   fDrei = withNewValuel drei $! $!! $$ $nn' ,
        , 'withNew out $*( sdf $*)  $@:¢  ' ,
        , 'fEins = withNewValue fEinsB' ,
        , 'fZwei = withNewValue fZweiB',
        , 'fDrei = withNewValue fDreiB',
        , '! with out =. oNew('cl') $@:¢',
        , 'fEins = withValue fEinsC' ,
        , 'fDrei = withValue fDreiC $!'
    return
endProcedure tstCompTable

tstCompSql: procedure expose m.
/*
$=/tstCompSqlSrc/
$@=¢
   select strip(creator) cr, strip(name) tb,
            (row_number()over())*(row_number()over()) rr
       from sysibm.sysTables
       where creator='SYSIBM' and name like 'SYSTABL%'
       order by 2 fetch first 4 rows only
$!
$| call sqlSel
$| call fTabAuto
$/tstCompSqlSrc/
$=/tstCompSql/
    ### start tst tstCompSql ##########################################
    compile @, 9 lines: $@=¢
    run without input
    CR     TB                RR
    SYSIBM SYSTABLEPART       1
    SYSIBM SYSTABLEPART_HIST  4
    SYSIBM SYSTABLES          9
    SYSIBM SYSTABLESPACE     16
$/tstCompSql/
$=/tstCompSqlFTabSrc/
$$ select 'ahaaaax' ahaCol, 'buuuuh' buhhhh from sysibm.sysDummy1
$| $@. vPut('lc', sqlRdr(scanSqlIn2Stmt()))
$| call fTab  sqlFTabOthers(sqlRdrFTabReset($.lc, tstCompSql1))
$<>
$$ select 'aOh' ahaOhne, 'buuVar' buhVar from sysibm.sysDummy1
$| call sqlSel
$| t2 = fTabReset(sqlRdrFTabReset( , tstCompS2), '2 1', '2 c', '-')
   ox = m.t2.0 + 1
   call sqlFTabOthers t2
   call fTab fTabAddTit(t2, ox, 2, '-----')
$<>
$$ select 'aOh' aDrei, 'buuDre' buhDrei from sysibm.sysDummy1
$| call sql2Tab
$/tstCompSqlFTabSrc/
$=/tstCompSqlFTab/
    ### start tst tstCompSqlFTab ######################################
    compile @, 13 lines: $$ select 'ahaaaax' ahaCol, 'buuuuh' buhhhh fr+
    om sysibm.sysDummy1
    run without input
    AHACOL--BUHHHH---
    ahaaaax buuuuh
    AHACOL--BUHHHH---
    -----
    AHA-BUHVAR---
    aOh buuVar
    -----
    AHAOHNE
    .    BUHVAR
    ADREI
    .    BUHDREI
    ADR-BUHDRE---
    aOh buuDre
    ADR-BUHDRE---
    ADREI
    .    BUHDREI
$/tstCompSqlFTab/
    ### start tst tstCompSql ##########################################
*/
    call sqlConnect
    call tstComp2 'tstCompSql', '@'
    call tstComp2 'tstCompSqlFTab', '@'
    call sqlDisConnect
    return
endProcedure tstCompSql
tstTut0: procedure expose m.
/*
$=/tstTut01Src/
$#=
$*+>.fSub()                               Kommentar
$*+>~tmp.jcl(t)                           Kommentar
$*+@=¢                                    Kommentar
$=subsys=DP4G
$=db=DA540769
$=ts=A977A
$*+@<~wk.jcl(jc)                          Kommentar
??* ?-¢sysvar(sysnode) date() time()?!ts=$ts 10*len=$-¢length($ts)*10$!
//P02     EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769C.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
  $@¢if right($ts, 2) == '7A' then $@=¢
    FULL YES
  $! else
    $$ $''    FULL NO
  $!
    SHRLEVEL CHANGE
$*+!                                      Kommentar
$#out original/src                                 20150212 12:49:04
$/tstTut01Src/
$=/tstTut01/
    ### start tst tstTut01 ############################################
    compile , 28 lines: $#=
    run without input
    ??* ?-¢sysvar(sysnode) date() time()?!ts=A977A 10*len=50
    //P02     EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A540769C.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977A*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    FULL YES
    SHRLEVEL CHANGE
$/tstTut01/
$=/tstTut02Src/
$#@
$**>.fSub()
$**@¢
$=subsys=DP4G
$=db=DA540769
$=jx=0
$@do tx = 976 to 977 $@=¢
    $=ts=A$tx
    $=jx=- $jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$**!
$#out original/src
$/tstTut02Src/
$=/tstTut02/
    ### start tst tstTut02 ############################################
    compile , 28 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut02/
$=/tstTut03Src/
$#@
$=subsys=DP4G
$@:¢table
      db         ts
      DGDB9998   A976
      DA540769   A977
$!
$** $| call fTabAuto
$**    $#end
$|
$=jx=0
$@forWith o $@=¢
    $=jx=- $jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$#out original/src
$/tstTut03Src/
$=/tstTut03/
    ### start tst tstTut03 ############################################
    compile , 31 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut03/
$=/tstTut04Src/
$#@
$=subsys=DP4G
$=db=DA540769
call sqlConnect $subsys
$@=¢  select dbName  db , tsName  ts
          from sysibm.sysTables
          where creator = 'SYSIBM' and name like 'SYSINDEXPAR%'
          order by name desc
$!
$| call sqlSel
$** $| call fTabAuto
$** $#end
$|
$=jx=0
$@forWith o $@=¢
    $=jx=- $jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$TS    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $DB.$TS*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
call sqlDisConnect
$#out original/src
$/tstTut04Src/
$=/tstTut04/
    ### start tst tstTut04 ############################################
    compile , 35 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CSYSHIST     EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DSNDB06 .SYSHIST *   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CSYSTSIPT    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DSNDB06 .SYSTSIPT*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut04/
$=/tstTut05Src/
$#:
subsys = DP4G
lst =<:¢withNew out :¢
    db = DGDB9998
    ts =<:¢table
             ts
             A976
             A977
    $!
    db = DA540769
    <|/ts/
             ts
             A976
             A975
    /ts/
!
$** $$. $lst
$** $@ct $@¢$=tool =. $.compile@ $<~.wk.rexx(wshtut06)$!
$** $@$tool
$@do sx=1 to ${lst.0} $@¢
    $=db = ${lst.$sx.db}
    $** $$. ${lst.$sx}
    $@do tx=1 to ${lst.$sx.ts.0} $@=¢
        $*+ $$. ${lst.$sx.ts.$tx}
        $=ts= ${lst.$sx.ts.$tx.ts}
        $@¢ say $-=¢subsys $subsys db $db ts $ts $! $!
        $@copy()
        $!
    $!
$@ct $@¢
cl = classNew('n? DbTsList u s' ,
     classNew('n? DbTs u f db v, f ts s' ,
     classNew('n? Ts u f ts v')))
$=lst=. oNew(cl)
$!
$@proc copy $@=¢
$@ct $=jx=0
$=jx=- $jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$#out original/src
$/tstTut05Src/
$=/tstTut05/
    ### start tst tstTut05 ############################################
    compile , 56 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407693 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407693.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407694 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA975    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407694.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A975*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut05/
   tstTut06   ==> platz für copy aus tut05
$=/tstTut07Src/
$**$>.fEdit()
call sqlConnect dp4g
$@:¢table
   ts
VTCASHFLOW
VTCASHFLOWAMOUNT
VTINSTRFLATDATA
$!
$| $@=¢
    select name, statstime, strip(dbName) || '.' || strip(tsName) dbts
       from sysibm.sysTables
       where creator = 'VDPS2' and name in
  $=co=(
  $@forWith t $@=¢
                                           $co '$ts'
      $=co=,
  $!
                                           )
$!
$| call sqlSel
$** $| call fTabAuto
$|
$=jx=0
$@forWith t $@=¢
$=jx=- $jx+1
//A540769$jx JOB (CP00,KE50),
//       'CATALOG',MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//STEP$jx EXEC PGM=DSNUTILB,TIME=1440,
//   PARM=(DP4G,'A540769$jx.RUNSTA'),
//   REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//UTPRINT  DD SYSOUT=*
//SYSOUT   DD SYSOUT=*
//RNPRIN01 DD SYSOUT=*
//SYSIN DD *
  LISTDEF LST#STA   INCLUDE TABLESPACE $DBTS
   OPTIONS EVENT (ITEMERROR, SKIP)

   RUNSTATS TABLESPACE LIST LST#STA
         SHRLEVEL CHANGE
           INDEX(ALL KEYCARD)
           REPORT YES UPDATE ALL
$!
call sqlDisconnect
$#out original/src
$/tstTut07Src/
$=/tstTut07/
$=/tstTut07/
    ### start tst tstTut07 ############################################
    compile , 47 lines: $**$>.fEdit()
    run without input
    //A5407691 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP1 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407691.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV27A1T.VDPS329
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
    //A5407692 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP2 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407692.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV28A1T.VDPS390
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
    //A5407693 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP3 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407693.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV21A1T.VDPS004
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
$/tstTut07/
$=/tstTut08Src/
$/tstTut08Src/
$=/tstTut08/
$/tstTut08/
*/
    call sqlIni
    call sqlDisconnect '*'
    call tstComp2 'tstTut01'
    call tstComp2 'tstTut02'
    call tstComp2 'tstTut03'
    if m.err.os == 'TSO' then do
        call tstComp2 'tstTut04'
        /* call tstComp2 'tstTut05' */
     /* call tstComp2 'tstTut07'  ???? anderes Beispiel ???? */
        end
    call tstTotal
    return
endProcedure tstTut0
/****** tstBase ********************************************************
     test the basic classes
***********************************************************************/
tstBase: procedure expose m.
    call tstTstSay
    call tstM
    call tstUtc2d
    call tstMap
    call tstMapVia
    call tstClass
    call tstClass2
    call tstClass3
    call tstClass4
    call scanIni
    call tstO
    call oIni
    call tstF
    call tstFWords
    call tstFtst
    call tstMCat
    call tstOEins
    call jIni
    call tstJSay
    call tstJ
    call tstJ2
    call tstScanSqlStmt
    call catIni
    call tstCat
    call pipeIni
    CALL TstEnv
    CALL TstEnvCat
    call tstPipe
    call tstPipeS
    call tstEnvVars
    call tstvWith
    call tstTotal
    call tstPipeLazy
    call tstEnvClass
    if m.tst_csm then
        call tstDsnEx
    call tstFile
    call tstFileList
    call tstMbrList
    call tstFE
    call tstFTab
    call tstFmt
    call tstfUnits
    call tstCsv
    call tstTotal
    call tstSb
    call tstSb2
    call tstScan
    call ScanReadIni
    call tstScanRead
    call tstScanUtilInto
    call tstScanWin
    call tstScanSQL
    call tstTotal
    return
endProcedure tstBase

/*--- test the tst Module, check the output visually  ----------------*/
tstTstSay: procedure expose m.
    call tstIni
    oldErr = m.tst.err
    oldNames = m.tst.errNames
    say '+++ tstTstSay start with' oldErr 'totErrs and',
            m.tst.tests 'tests'
/*
$=/tstTstSayEins/
    ### start tst tstTstSayEins #######################################
    test eins einzige testZeile
$/tstTstSayEins/
$=/tstTstSayZwei/
    ### start tst tstTstSayZwei #######################################
    zwei 1. testZeile
    zwei 2. und letzte testZeile
$/tstTstSayZwei/
$=/tstTstSayDrei/
    ### start tst tstTstSayDrei #######################################
    drei 1. testZeile vor empty Zeile
    ..
    drei 3. testZeile vor 10 space
    .          .
    drei 5. testZeile ziemlich lang 66                                 +
    .                                77  5   10   15++++++++++++++++++++
    .+++++++++++++++++++++++++++++++++++++++++++.
$/tstTstSayDrei/
*/
    call tst x, 'tstTstSayEins'
    call tstOut x, "test eins einzige testZeile"
    call tstEnd x, 'err 0'


    call tst x, 'tstTstSayZwei'
    call tstOut x, "zwei 1. testZeile"
    call tstOut x, "zwei 2. und letzte testZeile"
    call tstEnd x, 'err 0'

    call tst x, 'tstTstSayZwei'
    call tstOut x, "zwei 1. testZeile "    /* ein space zuviel */
    call tstOut x, "zwei 2. und letzte testZeile"
    call tstOut x, "zwei 3. zuviel"
    call tstEnd x, 'err 3'

    call tst y, 'tstTstSayDrei'
    call tstOut y, 'drei 1. testZeile vor empty Zeile'
    call tstOut y, ''
    call tstOut y, 'drei 3. testZeile vor 10 space'
    call tstOut y, left('', 10)
    call tstOut y, 'drei 5. testZeile ziemlich lang',
                left(66, 66) left('77  5   10   15', 77, '+')
    call tstEnd y, 'err 0'
    if m.y.err <> 0 then
        call err '+++ tstTstSay errs' m.x.err 'expected' 0
    if m.tst.err <> oldErr + 3 then
        call err '+++ tstTstSay totErrs' m.tst.err 'expected' 3
    say '+++ tstTstSay end Ok with' m.tst.err 'totErrs and' ,
        m.tst.tests 'tests'
    m.tst.err = oldErr
    m.tst.errNames = oldNames
    return
endProcedure tstTstSay

tstMark: procedure expose m.
parse arg m, msg
    if symbol('m.m') == 'VAR' then
        m.m = msg';' m.m
    else
        m.m = msg 'new'
    return m
endProcedure tstMark
tstM: procedure expose m.
/*
$=/tstMa/
    ### start tst tstMa ###############################################
    mNew() 1=newM1 2=newM2
    mNew(tst...) 2=2 new 3=4; 3; 1 new 4=5 new
    iter 4; 3; 1 new
    iter 2 new
    iter 5 new
$/tstMa/
*/
    call tst t, 'tstMa'
    m1 = mNew()
    m2 = mNew()
    m.m1 = 'newM1'
    m.m2 = 'newM2'
    call tstOut t, 'mNew() 1='m.m1 '2='m.m2
    call mNewArea 'tst'm1
    t1 = tstMark(mNew('tst'm1), '1')
    t2 = tstMark(mNew('tst'm1), '2')
    call mFree tstMark(t1, '3')
    t3 = tstMark(mNew('tst'm1), '4')
    t4 = tstMark(mNew('tst'm1), '5')
    call tstOut t, 'mNew(tst...) 2='m.t2 '3='m.t3 '4='m.t4
    i = mIterBegin('tst'm1)
    do while assNN('i', mIter(i))
        call tstOut t, 'iter' m.i
        end
    call tstEnd t
/*
$=/tstM/
    ### start tst tstM ################################################
    symbol m.b LIT
    symbol m.a LIT
    mAdd a A.2
    mAdd a A.3
    m.a: 3: 1=eins 2=zwei 3=drei 4=M.A.4
    m.c: 5: 1=c vorAddSt a 2=eins 3=zwei 4=drei 5=c nacAddSt a 6=M.C.6
$/tstM/ */


    drop m.b m.a m.a.0 m.a.1 m.a.2
    call tst t, 'tstM'
    call tstOut t, 'symbol m.b' symbol('m.b')
    m.b = 1
    call tstOut t, 'symbol m.a' symbol('m.a')
    call tstOut t, 'mAdd a' mAdd(mCut(a, 0), 'eins', 'zwei')
    call tstOut t, 'mAdd a' mAdd(a, 'drei')
    call tstOut t, 'm.a:' m.a.0': 1='m.a.1 '2='m.a.2 '3='m.a.3 '4='m.a.4
    call mAdd mCut(c, 0), 'c vorAddSt a'
    call mAddSt c, a
    call mAdd c, 'c nacAddSt a'
    call tstOut t, 'm.c:' m.c.0': 1='m.c.1 '2='m.c.2 '3='m.c.3,
                    '4='m.c.4 '5='m.c.5 '6='m.c.6
    call tstEnd t
    return
endProcedure tstM

tstMCat: procedure expose m.
/*
$=/tstMCat/
    ### start tst tstMCat #############################################
    mCat(0, )                     =;
    mCat(0, 1)                    =;
    mCat(0, 112222)               =;
    mCat(0, 3%#a1%c2)             =;
    mCat(0, 4%#a1%c2@%c333)       =;
    mCat(0, 5%#a1%c2@%c3@%c4)     =;
    mCat(1, )                     =eins;
    mCat(1, 1)                    =eins;
    mCat(1, 112222)               =eins;
    mCat(1, 3%#a1%c2)             =1eins2;
    mCat(1, 4%#a1%c2@%c333)       =1eins2eins333;
    mCat(1, 5%#a1%c2@%c3@%c4)     =1eins2eins3eins4;
    mCat(2, )                     =einszwei;
    mCat(2, 1)                    =eins1zwei;
    mCat(2, 112222)               =eins112222zwei;
    mCat(2, 3%#a1%c2)             =1eins231zwei2;
    mCat(2, 4%#a1%c2@%c333)       =1eins2eins33341zwei2zwei333;
    mCat(2, 5%#a1%c2@%c3@%c4)     =1eins2eins3eins451zwei2zwei3zwei4;
    mCat(3, )                     =einszweidrei;
    mCat(3, 1)                    =eins1zwei1drei;
    mCat(3, 112222)               =eins112222zwei112222drei;
    mCat(3, 3%#a1%c2)             =1eins231zwei231drei2;
    mCat(3, 4%#a1%c2@%c333)       =1eins2eins33341zwei2zwei33341drei2dr+
    ei333;
    mCat(3, 5%#a1%c2@%c3@%c4)     =1eins2eins3eins451zwei2zwei3zwei451d+
    rei2drei3drei4;
$/tstMCat/ */
    call pipeIni
    call scanIni
    call tst t, "tstMCat"
     m.qq.1 = "eins"
     m.qq.2 = "zwei"
     m.qq.3 = "drei"
     do qx = 0 to 3
         m.qq.0 = qx
         call tstMCat1 qx
         call tstMCat1 qx, '1'
         call tstMCat1 qx, '112222'
         call tstMCat1 qx, '3%#a1%c2'
         call tstMCat1 qx, '4%#a1%c2@%c333'
         call tstMCat1 qx, '5%#a1%c2@%c3@%c4'
         end
     call tstEnd t
     return
endProcedure tstMCat

tstMCat1: procedure expose m.
parse arg m.qq.0, fmt
    call out left("mCat("m.qq.0"," fmt")", 30)"="mCat(qq, fmt)";"
return
endProcedure tstMCat1

tstMap: procedure expose m.
/*
$=/tstMap/
    ### start tst tstMap ##############################################
    mapNew m keys m-keys 0
    map m zwei --> 2
    map m Zwei is not defined
    map stem m-keys 4
    map m eins --> 1
    map m zwei --> 2
    map m drei --> 3
    map m vier --> 4
    *** err: duplicate in mapAdd(m, eins, 1)
    map m zwei is not defined
    q 2 zw dr
    map stem Q 2
    map Q zw --> 2Q
    map Q dr --> 3Q
    map stem m 3
    map m eins --> 1
    map m zwei --> 2PUT
    map m vier --> 4PUT
    *** err: duplicate in mapAdd(m, zwei, 2ADDDUP)
    tstMapLong eins keys 3
    tstMapLong zweiMal keys 48
    tstMapLong dreiMal keys 93
    tstMapLong vier    keys 138
    tstMapLong <fuenf> keys 188
    tstMap clear keys 0
    inline1 3
    inline1 1 ==    inline1 eins==
    inline1 2 ====
    inline1 3 ==    inline1 drei==
    inline2 1 1 ==    inline2 eins==
    inline3 ?
$/tstMap/ */
/*
$=/tstMapInline1/
    inline1 eins

    inline1 drei
$/tstMapInline1/ */
/*
$=/tstMapInline2/
    inline2 eins
$/tstMapInline2/ */

    call tst t, 'tstMap'
    m = mapNew('K')
    ky = mapKeys(m)
    call mAdd t'.TRANS', m 'm', ky 'm-keys'
    call tstOut t, 'mapNew' m 'keys' ky m.ky.0
    call mapAdd m, 'eins', 1
    call mapAdd m, 'zwei', 2
    call mapAdd m, 'drei', 3
    call mapAdd m, 'vier', 4
    call tstMapShow m, 'zwei'
    call tstMapShow m, 'Zwei'
    call tstMapShowSt m, mapKeys(m)
    call mapAdd m, 'eins', 1
    call mapReset m, '='
    call tstMapShow m, 'zwei'
    call mapAdd m, 'eins', 1
    call mapAdd m, 'zwei', 2
    call mapPut m, 'zwei', 2Put
    call mapPut m, 'vier', 4Put
    call mapReset q, '='
    call mapAdd q, 'zw', 2q
    call mapAdd q, 'dr', 3q
    call tstOut t, 'q' m.q.0 m.q.1 m.q.2
    call tstMapShowSt q, mapKeys(q)
    call tstMapShowSt m, mapKeys(m)
    call mapAdd m, 'zwei', 2addDup
    call tstMapLong m, 'eins'      ,201, 2000, -2, 2
    call tstMapLong m, 'zweiMal'   ,201, 2000, -2, 2
    call tstMapLong m, 'dreiMal'   ,201, 2000,  2,-2
    call tstMapLong m, 'vier   '   ,2010, 201, -2, 2
    call tstMapLong m, '<fuenf>'   ,2010, 201,  2,-2
    call mapClear m
    keys = mapKeys(m)
    call tstOut t, 'tstMap clear keys' m.keys.0
    i = mapInline('tstMapInline1')
    call tstOut t, 'inline1' m.i.0
    do x=1 to m.i.0
        call tstOut t, 'inline1' x '=='m.i.x'=='
        end
    i = mapInline('tstMapInline2')
    call tstOut t, 'inline2' m.i.0 '1 =='m.i.1'=='
    call tstOut t, 'inline3' mapInline('tstMapInline3', 'r')'?'
    call tstEnd t
    return
endProcedure tstMap

tstMapLong: procedure expose m.
parse arg m, w, f1, t1, f2, t2
    if f1 < t1 then
        b1 = 201
    else
        b1 = -201
    if f2 < t2 then
        b2 = 1
    else
        b2 = -1
    lo = copies(w, 2100 % length(w))
    keys = mapKeys(m)
    keyCn = m.keys.0
    call tstOut t, 'tstMapLong' w 'keys' keyCn
    do x = f1 by b1 to t1
        do y = x+f2 by b2 to x+t2
            k = left(lo, y)
            if mapHasKey(m, k) then
                call err 'mapLong hasKey before' w y
            call mapAdd m, k, w y
            if \ mapHasKey(m, k) then
                call err 'mapLong \ hasKey after' w y
            if mapGet(m, k) \== w y then
                call err 'mapLong \ get <> ' w y
            keys = mapKeys(m)
            if keyCn + 1 \= m.keys.0 then
                call err 'mapLong keys .0 <> ' w y
            keyCn = m.keys.0
            if k \== m.keys.keyCn then
                call err 'mapLong keys . ' keyCn '<>' w y
            end
        end
    return
endProcedure tstMapLong

tstMapVia: procedure expose m.
/*
$=/tstMapVia/
    ### start tst tstMapVia ###########################################
    map M K --> A
    mapVia(m, K)      A
    *** err: missing m.A at 3 in mapVia(M, K|)
    mapVia(m, K|)     M.A
    mapVia(m, K|)     valAt m.a
    mapVia(m, K|)     valAt m.a
    *** err: missing m.A.aB at 5 in mapVia(M, K|aB)
    mapVia(m, K|aB)   M.A.aB
    mapVia(m, K|aB)   valAt m.A.aB
    *** err: missing m.valAt m.a at 4 in mapVia(M, K||)
    mapVia(m, K||)    M.valAt m.a
    mapVia(m, K||)    valAt m.valAt m.a
    mapVia(m, K||F)   valAt m.valAt m.a.F
$/tstMapVia/ */
    call tst t, 'tstMapVia'
    u = 'A.aB'
    v = 'valAt m.a'
    drop m.a m.u m.v m.v.f
    call mapReset m, 'K'
    call mapAdd m, k, a
    call tstMapShow m, k
    call tstOut t, 'mapVia(m, K)     ' mapVia(m, 'K')
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    m.a = v
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    call tstOut t, 'mapVia(m, K|aB)  ' mapVia(m, 'K|aB')
    m.u = 'valAt m.'u
    call tstOut t, 'mapVia(m, K|aB)  ' mapVia(m, 'K|aB')
    call tstOut t, 'mapVia(m, K||)   ' mapVia(m, 'K||')
    v = m.a
    m.v = 'valAt m.'v
    m.v.f = 'valAt m.'v'.F'
    call tstOut t, 'mapVia(m, K||)   ' mapVia(m, 'K||')
    call tstOut t, 'mapVia(m, K||F)  ' mapVia(m, 'K||F')
    call tstEnd t
    return
endProcedure tstMapVia

tstMapShow: procedure expose m.
parse arg a, key
    if mapHasKey(a, key) then
        call tstOut t, 'map' a key '-->' mapGet(a, key)
    else
        call tstOut t, 'map' a key 'is not defined'
    return
endProcedure tstMapShow

tstMapShowSt: procedure expose m.
parse arg a, st
    call tstOut t, 'map stem' st m.st.0
    do wx=1 to m.st.0
        call tstMapShow a, m.st.wx
        end
    return
endProcedure tstMapShow

tstClass2: procedure expose m.
/*
$=/tstClass2/
    ### start tst tstClass2 ###########################################
    @CLASS.10 :class = u
    . choice u union
    .  .NAME = class
    . stem 8
    .  .1 refTo @CLASS.4 :class = u
    .   choice u union
    .    .NAME = v
    .   stem 3
    .    .1 refTo @CLASS.1 :class = m
    .     choice m union
    .      .NAME = oCopy
    .      .MET = return oCopyV(m, t)
    .     stem 0
    .    .2 refTo @CLASS.2 :class = m
    .     choice m union
    .      .NAME = o2String
    .      .MET = return m.m
    .     stem 0
    .    .3 refTo @CLASS.3 :class = m
    .     choice m union
    .      .NAME = o2File
    .      .MET = return file(m.m)
    .     stem 0
    .  .2 refTo @CLASS.13 :class = c
    .   choice c union
    .    .NAME = u
    .   stem 1
    .    .1 refTo @CLASS.12 :class = u
    .     choice u union
    .      .NAME = .
    .     stem 1
    .      .1 refTo @CLASS.11 :class = f
    .       choice f union
    .        .NAME = NAME
    .       stem 1
    .        .1 refTo @CLASS.4 done :class @CLASS.4
    .  .3 refTo @CLASS.14 :class = c
    .   choice c union
    .    .NAME = f
    .   stem 1
    .    .1 refTo @CLASS.12 done :class @CLASS.12
    .  .4 refTo @CLASS.16 :class = c
    .   choice c union
    .    .NAME = s
    .   stem 1
    .    .1 refTo @CLASS.15 :class = u
    .     choice u union
    .      .NAME = .
    .     stem 0
    .  .5 refTo @CLASS.17 :class = c
    .   choice c union
    .    .NAME = c
    .   stem 1
    .    .1 refTo @CLASS.12 done :class @CLASS.12
    .  .6 refTo @CLASS.18 :class = c
    .   choice c union
    .    .NAME = r
    .   stem 1
    .    .1 refTo @CLASS.15 done :class @CLASS.15
    .  .7 refTo @CLASS.21 :class = c
    .   choice c union
    .    .NAME = m
    .   stem 1
    .    .1 refTo @CLASS.20 :class = u
    .     choice u union
    .      .NAME = .
    .     stem 2
    .      .1 refTo @CLASS.11 done :class @CLASS.11
    .      .2 refTo @CLASS.19 :class = f
    .       choice f union
    .        .NAME = MET
    .       stem 1
    .        .1 refTo @CLASS.4 done :class @CLASS.4
    .  .8 refTo @CLASS.23 :class = s
    .   choice s union
    .   stem 1
    .    .1 refTo @CLASS.22 :class = r
    .     choice r union
    .     stem 1
    .      .1 refTo @CLASS.10 done :class @CLASS.10
$/tstClass2/

$=/tstClass2oo/
    ### start tst tstClass2 ###########################################
    @CLASS.4 isA :class = u
    . choice u union
    .  .NAME = class
    .  stem 7
    .   .1 refTo @CLASS.1 :class = u
    .    choice u union
    .     .NAME = v
    .     stem 2
    .      .1 refTo @CLASS.20 :class = m
    .       choice m union
    .        .NAME = o2String
    .        .MET = return m.m
    .      .2 refTo @CLASS.108 :class = m
    .       choice m union
    .        .NAME = o2File
    .        .MET = return file(m.m)
    .   .2 refTo @CLASS.5 :class = c
    .    choice c union
    .     .NAME = u
    .     .CLASS refTo @CLASS.6 :class = u
    .      choice u union
    .       .NAME = .
    .       stem 2
    .        .1 refTo @CLASS.7 :class = f
    .         choice f union
    .          .NAME = NAME
    .          .CLASS refTo @CLASS.1 done :class @CLASS.1
    .        .2 refTo @CLASS.8 :class = s
    .         choice s .CLASS refTo @CLASS.9 :class = r
    .          choice r .CLASS refTo @CLASS.4 done :class @CLASS.4
    .   .3 refTo @CLASS.10 :class = c
    .    choice c union
    .     .NAME = f
    .     .CLASS refTo @CLASS.11 :class = u
    .      choice u union
    .       .NAME = .
    .       stem 2
    .        .1 refTo @CLASS.7 done :class @CLASS.7
    .        .2 refTo @CLASS.12 :class = f
    .         choice f union
    .          .NAME = CLASS
    .          .CLASS refTo @CLASS.9 done :class @CLASS.9
    .   .4 refTo @CLASS.13 :class = c
    .    choice c union
    .     .NAME = s
    .     .CLASS refTo @CLASS.12 done :class @CLASS.12
    .   .5 refTo @CLASS.14 :class = c
    .    choice c union
    .     .NAME = c
    .     .CLASS refTo @CLASS.11 done :class @CLASS.11
    .   .6 refTo @CLASS.15 :class = c
    .    choice c union
    .     .NAME = m
    .     .CLASS refTo @CLASS.16 :class = u
    .      choice u union
    .       .NAME = .
    .       stem 2
    .        .1 refTo @CLASS.7 done :class @CLASS.7
    .        .2 refTo @CLASS.17 :class = f
    .         choice f union
    .          .NAME = MET
    .          .CLASS refTo @CLASS.1 done :class @CLASS.1
    .   .7 refTo @CLASS.18 :class = c
    .    choice c union
    .     .NAME = r
    .     .CLASS refTo @CLASS.12 done :class @CLASS.12
$/tstClass2oo/ */

    call classIni
    call tst t, 'tstClass2'
    call classOut m.class_C, m.class_C
    call tstEnd t
    return
endProcedure tstClass2

tstClass3: procedure expose m.
/*
$=/tstClass3/
    ### start tst tstClass3 ###########################################
    met v#o2String return m.m
    met w#o2String return substr(m, 2)
    met w#o2String return substr(m, 2)
    *** err: no method nonono in class w
    met w#nonono 0
    met class#oCopy return classCopy('<class class>', m, t)
    t1 4 flds FV, FR
    clear q1 FV= FR= FW=! FO=
    orig R1 FV=valFV FR=refFR FW=!valFW FO=obj.FO
    copy <s1> FV=valFV FR=refFR FW=!valFW FO=obj.FO
    t2 2 flds , EINS.ZWEI
    clear q2 EINS.ZWEI= val=
    orig R2 EINS.ZWEI=valR2.eins.zwei val=valR2Self
    copy <s2> EINS.ZWEI=valR2.eins.zwei val=valR2Self
    t3 0 flds M.<class tst...Tf33>.FLDS.1, M.<class tst...Tf33>.FLDS.2
    clear q3 s1.0=0
    orig R3 s1.0=1 s1.1=M.R3.S1.1 s1.1.f1=M.R3.S1.1.F1 s1.1.s2.0=2 s1.1+
    ..s2.1.f2=M.R3.S1.1.S2.1.F2 s1.1.s2.2.f2=M.R3.S1.1.S2.2.F2
    copy <s3> s1.0=1 s1.1=M.R3.S1.1 s1.1.f1=M.R3.S1.1.F1 s1.1.s2.0=2 s1+
    ..1.s2.1.f2=M.R3.S1.1.S2.1.F2 s1.1.s2.2.f2=M.R3.S1.1.S2.2.F2
$/tstClass3/ */

    call oIni
    call tst t, 'tstClass3'
    call mAdd t.trans, m.class_C '<class class>'
    call tstOut t, 'met v#o2String' classMet(m.class_V, 'o2String')
    call tstOut t, 'met w#o2String' classMet(m.class_W, 'o2String')
    call tstOut t, 'met w#o2String' classMet(m.class_W, 'o2String')
    call tstOut t, 'met w#nonono' classMet(m.class_W, 'nonono')
    call tstOut t, 'met class#oCopy' classMet(m.class_C, 'oCopy')
    all =  classNew('n? tstClassTf31 u f FV v, f FR r, f FW w, f FO o'),
           classNew('n? tstClassTf32 u f EINS f ZWEI v, v') ,
           classNew('n? tstClassTf33 u f S1' classNew('s u v, f F1 v,',
                           'f S2 s f F2 v'))
    call mAdd t.trans, word(all, 3) '<class tst...Tf33>'
    m.r1.fv = 'valFV'
    m.r1.fr = 'refFR'
    m.r1.fw = '!valFW'
    m.r1.fo = 'obj.FO'
    m.r2    = 'valR2Self'
    m.r2.eins.zwei  = 'valR2.eins.zwei'
    m.r3.s1.0 = 1
    m.r3.s1.1.s2.0 = 2
    o.1 = "q 'FV='m.q.FV 'FR='m.q.fr 'FW='m.q.fw 'FO='m.q.fo"
    o.2 = "q 'EINS.ZWEI='m.q.EINS.zwei 'val='m.q"
    o.3 = "q 's1.0='m.q.s1.0"
    p.1 = o.1
    p.2 = o.2
    p.3 = "q 's1.0='m.q.s1.0 's1.1='m.q.s1.1 's1.1.f1='m.q.s1.1.f1" ,
            "'s1.1.s2.0='m.q.s1.1.s2.0 's1.1.s2.1.f2='m.q.s1.1.s2.1.f2",
                                      "'s1.1.s2.2.f2='m.q.s1.1.s2.2.f2"
    do tx=1 to words(all)
        t1 = word(all, tx)
        u1 = classFlds(t1)
        q = 'q'tx
        call tstOut t, 't'tx m.u1.0 'flds' m.u1.1',' m.u1.2
        call utInter("m='"q"';" classMet(t1, 'oClear'))
        interpret "call tstOut t, 'clear'" o.tx
        q = 'R'tx
        interpret "call tstOut t, 'orig'" p.tx
        q = utInter("m='"q"';t='';" classMet(t1, 'oCopy'))
        call mAdd t.trans, q '<s'tx'>'
        interpret "call tstOut t, 'copy'" p.tx
        end
    call tstEnd t
    return
endProcedure tstClass3

tstClass: procedure expose m.
/*
$=/tstClass/
    ### start tst tstClass ############################################
    Q u =className= tstClassTf12
    Q.eins.zwei v ==> M.Q.eins.zwei
    *** err: bad type v: classNew(v tstClassTf12)
    *** err: missing key in mapGet(CLASS_N2C, 0)
    R u =className= uststClassTf12
    R u =className= uststClassTf12in
    R u =className= tstClassTf12
    R.eins.zwei v ==> M.R.eins.zwei
    R s =stem.0= 2
    R.1 r ==> M.R.1 :CLASS.8
    R.1 u =className= tstClassTf12
    R.1.eins.zwei v ==> M.R.1.eins.zwei
    R.2 r ==> M.R.2 :CLASS.8
    R.2 u =className= tstClassTf12
    R.2.eins.zwei v ==> M.R.2.eins.zwei
    S u =className= TstClass7
    S s =stem.0= 2
    S.1 u =className= TstClass7s
    S.1.eins v ==> M.S.1.eins
    S.1 m =met-metA--> say "metA"
    S.1 m =met-metB--> say "metB"
    S.2 u =className= TstClass7s
    S.2.zwei v ==> M.S.2.zwei
    S.2 m =met-metA--> say "metA"
    S.2 m =met-metB--> say "metB"
    class of mutate qq tstClassTf12
$/tstClass/ */

    f = 'eins.zwei'
    e = 'eins'
    z = 'zwei'
    drop m.q.f m.r.f m.r.0 m.r.1 m.r.1.f m.r.2 m.r.2.f
    drop m.s.0 m.s.1 m.s.1.e m.s.2 m.s.2.z
    call classIni
    call tst t, 'tstClass'
    t1  =  classNew('n? tstClassTf12 u f eins f zwei v')
    call tstClassOut t, t1, q
    z = m.class.0
    if class4name('tstClassB', '') == '' then do
        t2 = classNew('n tstClassB u n tstClassC u tstClassTf12,',
            's u v tstClassTf12')
        end
    else do /*  the second time we would get a duplicate error */
        call tstOut t, '*** err: bad type v: classNew(v tstClassTf12)'
        call tstOut t, '*** err: missing key in mapGet(CLASS_N2C, 0)'
        end
    t2 = classNew('n? uststClassTf12 u' ,
           'n? uststClassTf12in u tstClassTf12',
        , classNew('s u r, tstClassTf12'))
    m.r.0 = 2
    call tstClassOut t, t2, r
    t3 = classNew('n? TstClass7 u s',
         classNew('n? TstClass7s u c 1 f eins v, c 2 f zwei v',
            ,'m', 'metA say "metA"', 'metB say "metB"'))
    m.s.0 = 2
    m.s.1 = 1
    m.s.2 = 2
    call tstClassOut t, t3, s
    call oMutatName qq, 'tstClassTf12'
    tt = objClass(qq)
    call tstOut t, 'class of mutate qq' className(tt)
    call tstEnd t
    return
endProcedure tstClass

tstClassOut: procedure expose m.
parse arg o, t, a
    if wordPos(t, m.class_V m.class_W m.class_O) > 0 then
        return tstOut(o, a m.t.name '==>' m.a)
    if m.t == 'r' then
        return tstOut(o, a m.t '==>' m.a ':'if(m.t.0==0,'',m.t.1))
    if m.t == 'u' & m.t.name \== '' then
        call tstOut o, a m.t '=className=' m.t.name
    if m.t == 'f' then
        return tstClassOut(o, m.t.1, a'.'m.t.name)
    if m.t = 'u' then do
        do ux=1 to m.t.0
            call tstClassOut o, m.t.ux, a
            end
        return 0
        end
    if m.t = 's' then do
        call tstOut o, a m.t '=stem.0=' m.a.0
        do ux=1 to m.a.0
            call tstClassOut o, m.t.1, a'.'ux
            end
        return 0
        end
    if m.t = 'c' then do
        if m.t.name = m.a then
            call tstClassOut o, m.t.1, a
         return 0
        end
    if m.t = 'm' then
        return tstOut(o, a m.t '=met-'m.t.name'-->' m.t.met)
    call err 'bad class type' m.t
endProcedure tstClassOut

tstClass4: procedure expose m.
parse arg
/*
$=/tstClass4/
    ### start tst tstClass4 ###########################################
    f 1 eins
    f 2 zwei
    f 3 drei
    f 4 vier
    f 5 acht
    s 1 fuenf
    s 2 sechs
    s 3 sie
$/tstClass4/
*/
    call classIni
    call tst t, 'tstClass4'
    x = classNew('n* TstClass4a u f eins v, f%v zwei drei, f vier v',
                             ', f%s-v fuenf sechs sie, f acht v')
    ff = classFlds(x)
    do fx=1 to m.ff.0
        call tstOut t, 'f' fx m.ff.fx
        end
    st = classMet(x, 'stms')
    do sx=1 to m.st.0
        call tstOut t, 's' sx m.st.sx
        end
    call tstEnd t
    return
endProcedure tstClass4

tstO: procedure expose m.
/*
$=/tstO/
    ### start tst tstO ################################################
    o1.class ---
    o1.class <class T..1>
    o1#met1 metEins
    o1#met2 metZwei
    o1#new return classClear('<class T..1>', oMutate(mNew('<class T..1>+
    '), '<class T..1>'))
$/tstO/
*/
    call mIni
    call tst t, 'tstO'
    call oIni
    c1 = classNew('n? TstOCla1 u', 'm', 'met1 metEins', 'met2 metZwei')
    call mAdd t.trans, c1 '<class T..1>'
    o1 = 'tst_o1'
    call tstOut t, 'o1.class' objClass(o1, '---')
    o1 = oMutate('o1', c1)
    call tstOut t, 'o1.class' objClass(o1, '---')
    call tstOut t, 'o1#met1' objMet(o1, 'met1')
    call tstOut t, 'o1#met2' objMet(o1, 'met2')
    call tstOut t, 'o1#new' objMet(o1, 'new')
    call tstEnd t
    return
endProcedure tstO


tstOEins: procedure expose m.
/*
$=/tstOEins/
    ### start tst tstOEins ############################################
    class method calls of TstOEins
    .  met Eins.eins M
     flds of <obj e of TstOEins> FEINS, FZWEI
     methodcalls of object e of TstOEins
    .  met Eins.eins <obj e of TstOEins>
    .  met Eins.zwei <obj e2 of TstOEins>
    *** err: no method nein in class TstOEins
    *** err: no method nein in class String
    class method calls of TstOEins
    .  met Elf.zwei M
    flds of <obj f of TstOElf> FEINS, FZWEI, FELF
    methodcalls of object f of TstOElf
    .  met Eins.eins <obj f of TstOElf>
    .  met Elf.zwei <obj f of TstOElf>
    .  met Elf.drei <obj f of TstOElf>
    oCopy c1 of class TstOEins, c2
    C1 u =className= TstOEins
    C1.FEINS v ==> M.C1.FEINS
    C1.FZWEI v ==> M.C1.FZWEI
    C1 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C1 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    C2 u =className= TstOEins
    C2.FEINS v ==> M.C1.FEINS
    C2.FZWEI v ==> M.C1.FZWEI
    C2 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C2 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    oCopy c3 of class TstOElf, c4
    C4 u =className= TstOElf
    C4 u =className= TstOEins
    C4.FEINS v ==> M.C3.FEINS
    C4.FZWEI v ==> M.C3.FZWEI
    C4 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C4 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    C4.FELF r ==> M.C3.FELF :<class O>
    C4 m =met-zwei--> call tstOut t, "  met Elf.zwei" m
    C4 m =met-drei--> call tstOut t, "  met Elf.drei" m
    tEinsDop <class TstOEins>
    oRun 7*3 21
    oRun 12*12 144
$/tstOEins/ */
    call scanIni
    call tst t, 'tstOEins'
    tEins = classNew('n? TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
    call mAdd t.trans, tEins '<class TstOEins>' ,
                   , m.class_o '<class O>'
    call tstOut t, 'class method calls of TstOEins'
    interpret classMet('TstOEins', 'eins')
    e = oNew('TstOEins')
    e2 = oNew('TstOEins')
    call mAdd t.trans, e '<obj e of TstOEins>'
    call mAdd t.trans, e2 '<obj e2 of TstOEins>'
    call tstOut t, 'flds of' e mCat(oFlds(e), ', ')
    call tstOut t, 'methodcalls of object e of TstOEins'
    call tstOmet e, 'eins'
    call tstOmet e2, 'zwei'
    call tstOmet e, 'nein'
    call tstOmet 'noObj', 'nein'
    tElf = classNew('n? TstOElf u TstOEins, f FELF r', 'm',
        , 'zwei call tstOut t, "  met Elf.zwei" m',
        , 'drei call tstOut t, "  met Elf.drei" m')
    call tstOut t, 'class method calls of TstOEins'
    interpret classMet('TstOElf', 'zwei')
    f = oNew('TstOElf')
    call mAdd t.trans, f '<obj f of TstOElf>'
    call tstOut t, 'flds of' f mCat(oFlds(f), ', ')
    call tstOut t, 'methodcalls of object f of TstOElf'
    call tstOmet f, 'eins'
    call tstOmet f, 'zwei'
    call tstOmet f, 'drei'
    call oMutatName c1, 'TstOEins'
    call tstOut t, 'oCopy c1 of class TstOEins, c2'
    call tstClassOut t, tEins, c1
    call oCopy c1, c2
    call tstClassOut t, tEins, c2
    call tstOut t, 'oCopy c3 of class TstOElf, c4'
    call oMutatName c3, 'TstOElf'
    call oCopy c3, c4
    call tstClassOut t, tElf, c4

/*    tEinsDop = classNew('n TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
*/ tEinsDop = tEins
    call tstOut t, 'tEinsDop' tEinsDop
    e3 = oNew('TstOEins')
    if e3 <<= e | e3 <<= e2 then
        call err 'doppelt reinitialised objects'
    rr = oRunner('return 7 * 3')
    call tstOut t, 'oRun 7*3' oRun(rr)
    r12 = oRunner('return 12 * 12')
    call tstOut t, 'oRun 12*12' oRun(r12)
    call tstEnd t
    return
endProcedure tstOEins

tstOmet: procedure expose m.
parse arg m, met
    interpret objMet(m, met)
    return
endProcedure tstOmet

tstJSay: procedure expose m.
/*
$=/tstJSay/
    ### start tst tstJSay #############################################
    *** err: bad option openArg in jOpen(<obj j of JRW>, openArg)
    *** err: jWrite(<obj j of JRW>, writeArg) but not opened w
    *** err: can only write JSay.jOpen(<obj s of JSay>, <)
    *** err: jWrite(<obj s of JSay>, write s vor open) but not opened+
    . w
    *** err: can only read JRWEof.jOpen(<obj e of JRWEof>, >)
    *** err: jRead(<obj e of JRWEof>) but not opened r
    read e vor open 0 m.xx valueBefore
    read e nach open 0 m.xx valueBefore
    out eins
    #jIn 1# tst in line 1 eins ,
    out zwei in 1 vv=readAdrVV
    #jIn 2# tst in line 2 zwei ;   .
    out drei in 1 vv=readAdrVV Schluss
$/tstJSay/ */

    call jIni
    call tst t, 'tstJSay'
    jrw = oNew('JRW')
    call mAdd t'.TRANS', jrw '<obj j of JRW>'
    call jOpen jrw, 'openArg'
    call jWrite jrw, 'writeArg'
    s = oNew('JSay')
    call mAdd t'.TRANS', s '<obj s of JSay>'
    call jOpen s, m.j.cRead
    s = oNew('JSay')
    call mAdd t'.TRANS', s '<obj s of JSay>'
    call jWrite s, 'write s vor open'
    call jOpen s, '>'
    call jWrite s, 'write s nach open'
    e = oNew('JRWEof')
    call mAdd t'.TRANS', e '<obj e of JRWEof>'
    call jOpen e, '>'
    e = oNew('JRWEof')
    call mAdd t'.TRANS', e '<obj e of JRWEof>'
    m.xx = 'valueBefore'
    call tstOut t, 'read e vor open' jReadVar(e, xx) 'm.xx' m.xx
    call jOpen e, m.j.cRead
    call tstOut t, 'read e nach open' jReadVar(e, xx) 'm.xx' m.xx
    call out 'out eins'
    vv = 'readAdrVV'
    m.vv = 'readAdrVVValueBefore'
    call out 'out zwei in' in() 'vv='vv
    m.vv = 'readAdrVVValueBefore'
    call out 'out drei in' inVar(vv) 'vv='vv 'Schluss'
    call tstEnd t
    return
endProcedure tstJSay

tstJ: procedure expose m.
/*
$=/tstJ/
    ### start tst tstJ ################################################
    out eins
    #jIn 1# tst in line 1 eins ,
    1 in() tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    2 in() tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    3 in() tst in line 3 drei .schluss..
    #jIn eof 4#
    in() 3 reads vv VV
    line buf line one
    line buf line two
    line buf line three
    line buf line four
    *** err: jWrite(<buf b>, buf line five while reading) but not opene+
    d w
$/tstJ/ */

    call jIni
    call tst t, "tstJ"
    b = jOpen(jBuf(), '>')
    call mAdd t'.TRANS', b '<buf b>'
    call out 'out eins'
    do lx=1 by 1 while in()
        call out lx 'in()' m.in
        end
    call out 'in()' (lx-1) 'reads vv' vv
    call jOpen b, '>'
    call jWrite b, 'buf line one'
    call jClose b
    call mAdd m.b.stem, 'buf line two', 'buf line three'
    call jWrite b, 'buf line four'
    call jClose b
    call jOpen b, m.j.cRead
    do while jRead(b)
        call out 'line' m.b
        end
    call jWrite b, 'buf line five while reading'
    call jClose b
    call tstEnd t
    return
endProcedure tstJ

tstJ2: procedure expose m.
/*
$=/tstJ2/
    ### start tst tstJ2 ###############################################
    class1 <Tst?1 class> <Tst?1 name>
    class2 <Tst?1 class> <Tst?1 name>
    class3 <Tst?1 class> <Tst?1 name>
    b read EINS feld eins, ZWEI feld zwei, DREI feld drei
    b read EINS feld eins, ZWEI feld zwei 2, DREI feld drei
    c read EINS feld eins, ZWEI feld zwei, DREI drei cat 1
    tstR: @tstWriteoV3 isA :<Tst?1 name>
    tstR:  .EINS = feld eins
    tstR:  .ZWEI = feld zwei
    tstR:  .DREI = drei cat 1
    c read EINS feld eins, ZWEI feld zwei 2, DREI drei cat 2
    tstR: @tstWriteoV4 isA :<Tst?1 name>
    tstR:  .EINS = feld eins
    tstR:  .ZWEI = feld zwei 2
    tstR:  .DREI = drei cat 2
$/tstJ2/ */

    call jIni
    call tst t, "tstJ2"
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call mAdd t'.TRANS', ty '<Tst?1 class>'
    call mAdd t'.TRANS', m.ty.name '<Tst?1 name>'
    call tstOut t, 'class1' ty m.ty.name
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call tstOut t, 'class2' ty m.ty.name
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call tstOut t, 'class3' ty m.ty.name
    call oMutate qq, ty
    m.qq.eins = 'feld eins'
    m.qq.zwei = 'feld zwei'
    m.qq.drei = 'feld drei'
    b = jBuf()
    call jOpen b, '>'
    call jWrite b, oCopy(qq)
    m.qq.zwei = 'feld zwei 2'
    call jWrite b, qq
    call jOpen jClose(b), m.j.cRead
    c = jOpen(jBuf(), '>')
    do xx=1 while jRead(b)
        res = m.b
        call out 'b read EINS' m.res.eins', ZWEI' m.res.zwei,
                                     || ', DREI' m.res.drei
        m.res.drei = 'drei cat' xx
        call jWrite c, res
        end
    call jOpen jClose(c), m.j.cRead
    do while jRead(c)
        ccc = m.c
        call out 'c read EINS' m.ccc.eins', ZWEI' m.ccc.zwei,
                                     || ', DREI' m.ccc.drei
        call out ccc
        end
    call tstEnd t
    return
endProcedure tstJ2

tstCat: procedure expose m.
/*
$=/tstCat/
    ### start tst tstCat ##############################################
    catRead 1 line 1
    catRead 2 line 2
    catRead 3 line 3
    appRead 1 line 1
    appRead 2 line 2
    appRead 3 line 3
    appRead 4 append 4
    appRead 5 append 5
$/tstCat/ */
    call catIni
    call tst t, "tstCat"
    i = cat(jBuf('line 1', 'line 2'), jBuf('line 3'))
    call jOpen i, m.j.cRead
    do lx=1 by 1 while jRead(i)
        call tstOut t, 'catRead' lx m.i
        end
    call jOpen jClose(i), m.j.cApp
    call jWrite i, 'append 4'
    call jWrite i, 'append 5'
    call jOpen jClose(i), m.j.cRead
    do lx=1 by 1 while jRead(i)
        call tstOut t, 'appRead' lx m.i
        end
    call tstEnd t
    return
endProcedure tstCat

tstEnv: procedure expose m.
    call pipeIni
/*
$=/tstEnv/
    ### start tst tstEnv ##############################################
    before pipeBeLa
    after pipeEnd
    *** err: jWrite(<jBuf c>, write nach pop) but not opened w
    #jIn 1# tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    before writeNow 2 c --> std
    before writeNow 1 b --> c
    b line eins
    b zwei |
    nach writeNow 1 b --> c
    add nach pop
    after push c only
    tst in line 1 eins ,
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    nach writeNow 2 c --> std
    *** err: jWrite(<jBuf c>, ) but not opened w
$/tstEnv/ */

    call tst t, "tstEnv"
    c = jBuf()
    call mAdd t'.TRANS', c '<jBuf c>'
    call out 'before pipeBeLa'
    b = jBuf("b line eins", "b zwei |")
    call pipe '+Ff', c, b
    call out 'before writeNow 1 b --> c'
    call pipeWriteNow
    call out 'nach writeNow 1 b --> c'
    call pipe '-'
    call out 'after pipeEnd'
    call jWrite c, 'write nach pop'
    call mAdd c'.BUF', 'add nach pop'
    call pipe '+A', c
    call out 'after push c only'
    call pipeWriteNow
    call pipe '-'
    call pipe '+f', , c
    call out 'before writeNow 2 c --> std'
    call pipeWriteNow
    call out 'nach writeNow 2 c --> std'
    call pipe '-'
    call jWrite c
    call tstEnd t
    return
endProcedure tstEnv

tstEnvCat: procedure expose m.
    call pipeIni
/*
$=/tstEnvCat/
    ### start tst tstEnvCat ###########################################
    c1 contents
    c1 line eins |
    before writeNow 1 b* --> c*
    b1 line eins|
    b2 line eins
    b2 zwei |
    c2 line eins |
    after writeNow 1 b* --> c*
    c2 contents
    c2 line eins |
$/tstEnvCat/ */

    call tst t, "tstEnvCat"

    b0= jBuf()
    b0= jBuf()
    b1= jBuf("b1 line eins|")
    b2 = jBuf("b2 line eins", "b2 zwei |")
    c1 = jBuf("c1 line eins |")
    c2 = jBuf("c2 line eins |")
    call pipe '+Af', c1, b0, b1, b2, c2
    call out 'before writeNow 1 b* --> c*'
    call pipeWriteNow
    call out 'after writeNow 1 b* --> c*'
    call pipe '-'
    call out 'c1 contents'
    call pipe '+f' , , c1
    call pipeWriteNow
    call pipe '-'
    call pipe '+f' , , c2
    call out 'c2 contents'
    call pipeWriteNow
    call pipe '-'
    call tstEnd t
    return
endProcedure tstEnvCat

tstPipe: procedure expose m.
    call pipeIni
/*
$=/tstPipe/
    ### start tst tstPipe #############################################
    .+0 vor pipeBegin
    #jIn 1# tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    .+7 nach pipeLast
    ¢7 +6 nach pipe 7!
    ¢7 +2 nach pipe 7!
    ¢7 +4 nach nested pipeLast 7!
    ¢7 (4 +3 nach nested pipeBegin 4) 7!
    ¢7 (4 (3 +1 nach pipeBegin 3) 4) 7!
    ¢7 (4 (3 tst in line 1 eins , 3) 4) 7!
    ¢7 (4 (3 tst in line 2 zwei ;    3) 4) 7!
    ¢7 (4 (3 tst in line 3 drei .schluss. 3) 4) 7!
    ¢7 (4 (3 +1 nach writeNow vor pipe 3) 4) 7!
    ¢7 (4 +3 nach preSuf vor nested pipeLast 4) 7!
    ¢7 +4 nach preSuf vor nested pipeEnd 7!
    ¢7 +5 nach nested pipeEnd vor pipe 7!
    ¢7 +6 nach writeNow vor pipeLast 7!
    .+7 nach writeNow vor pipeEnd
    .+8 nach pipeEnd
$/tstPipe/ */

    say 'x0' m.pipe.0
    call tst t, 'tstPipe'
    call out '+0 vor pipeBegin'
    say 'x1' m.pipe.0
    call pipe '+N'
    call out '+1 nach pipeBegin'
    call pipeWriteNow
    call out '+1 nach writeNow vor pipe'
    call pipe 'N|'
    call out '+2 nach pipe'
    call pipe '+N'
    call out '+3 nach nested pipeBegin'
    call pipePreSuf '(3 ', ' 3)'
    call out '+3 nach preSuf vor nested pipeLast'
    call pipe 'P|'
    call out '+4 nach nested pipeLast'
    call pipePreSuf '(4 ', ' 4)'
    call out '+4 nach preSuf vor nested pipeEnd'
    call pipe '-'
    call out '+5 nach nested pipeEnd vor pipe'
    call pipe 'N|'
    call out '+6 nach pipe'
    call pipeWriteNow
    say 'out +6 nach writeNow vor pipeLast'
    call out '+6 nach writeNow vor pipeLast'
    call pipe 'P|'
    call out '+7 nach pipeLast'
    call pipePreSuf '¢7 ', ' 7!'
    call out '+7 nach writeNow vor pipeEnd'
    call pipe '-'
    call out '+8 nach pipeEnd'
    say 'xx' m.pipe.0
    call tstEnd t
    return
endProcedure tstPipe

tstPipeS: procedure expose m.
/*
$=/tstPipeS/
    ### start tst tstPipeS ############################################
    eine einzige zeile
    nach all einzige Zeile
    select strip(creator) cr, strip(name) tb,
    (row_number()over())*(row_number()over()) rr
    from sysibm.sysTables
$/tstPipeS/
*/
    call tst t, "tstPipeS"
    call pipe '+s',, 'eine einzige zeile'
    call pipeWriteAll
    call out 'nach all einzige Zeile'
    call pipe 's',,
              , "select strip(creator) cr, strip(name) tb," ,
              ,      "(row_number()over())*(row_number()over()) rr" ,
              ,      "from sysibm.sysTables"
    call pipeWriteAll
    call pipe '-'
    call tstEnd t
    return
endProcedure tstPipeS

tstEnvVars: procedure expose m.
    call pipeIni
/*
$=/tstEnvVars/
    ### start tst tstEnvVars ##########################################
    put v1 value eins
    v1 hasKey 1 get TST.ADR1
    v2 hasKey 0
    one to theBur
    two to theBuf
    v1=TST.ADR1 o=TST.ADR1
    v3=v3WieGehts? o=v3WieGehts?
    v4=!v4WieGehts? o=!v4WieGehts?
    o o0=<o0>
    s o0=<o0>
    o o0=<o0>
    s o0=<o0>
    o0&fSt0=rexx o0.fSt0 o=rexx o0.fSt0
    o0&fRe0=!rexx o0.fRe0 o=!rexx o0.fRe0
    o0&=rexx o0-value o=rexx o0-value
    o o0=<o0>
    s o0=<o0>
    o0&fSt0=put o0.fSt0 o=put o0.fSt0
    o0&fRe0=!putO o0.fRe0 o=!putO o0.fRe0
    o0&=put o0-value o=put o0-value
$/tstEnvVars/
$=/tstEnvVars1/
    ### start tst tstEnvVars1 #########################################
    m.o1=put-o1-value m.o1.fStr=put-o1.fStr m.o1.fRef=<o0>
    o o1=<o1> s o1=<o1>
    o1&fStr=put-o1.fStr o=put-o1.fStr
    o1&=put-o1-value o=put-o1-value
    o1&fRef=<o0> o=<o0>
    o1&fRef>fSt0=put o0.fSt0 o=put o0.fSt0
    o1&fRef>fRe0=!putO o0.fRe0 o=!putO o0.fRe0
    m.o1&fNest.fSt0= put-o1.fNest.fSt0 m.o1&fNest.fRe0= !put-o1&fNest.f+
    Re0
    o1&fNest.fSt0=put-o1.fNest.fSt0 o=put-o1.fNest.fSt0
    o1&fNest&fRe0=!put-o1&fNest.fRe0 o=!put-o1&fNest.fRe0
$/tstEnvVars1/
$=/tstEnvVars2/
    ### start tst tstEnvVars2 #########################################
    o2=<o2> getO(o2)=<o2> getO(o2&fRef)=<o1>
    o2&fRef>fStr=put-o1.fStr o=put-o1.fStr
    o2&fRef>=put-o1-value o=put-o1-value
    o2&fRef>fRef=<o0> o=<o0>
    o2&fRef>fRef>fSt0=put o0.fSt0 o=put o0.fSt0
    o2&fRef>fRef>fRe0=!putO o0.fRe0 o=!putO o0.fRe0
    o2&fRef>fNest.fSt0=put-o1.fNest.fSt0 o=put-o1.fNest.fSt0
    o2&fRef>fNest&fRe0=!put-o1&fNest.fRe0 o=!put-o1&fNest.fRe0
$/tstEnvVars2/
$=/tstEnvVarsS/
    ### start tst tstEnvVarsS #########################################
    oS=<oS> oS&fStS=<put oS.fStS>
    oS&fStV.0=1 oS&fStV.1=<put oS.fStV.1>
    m.oS.fStR.0=2 .2=!<put oS.fStR.2>
    oS&fStR.0=2 .1=!<put oS.fStR.1> .2=!<put oS.fStR.2>
    m.oS.0=9876 .1234=<put oS.1234>
    *** err: undefined var oS&12
    oS&0=9876 .12=M. .1234=<put oS.1234>
$/tstEnvVarsS/
$=/tstEnvVars3/
    ### start tst tstEnvVars3 #########################################
    m.<o0>=*o0*val vGet(<o0>>)=*o0*val
    m.<o0>.fSt0=*o0.fSt0*val vGet(<o0>>fSt0)=*o0.fSt0*val
    m.<o0>.fRe0=<o1> vGet(<o0>>fRe0)=<o1>
    m.<o1>=*o1*val vGet(<o0>>fRe0>)=*o1*val
    m.<o1>.fStr=*o1.fStr*val vGet(<o0>>fRe0>fStr)=*o1.fStr*val
    m.V.tstEnvVar0=<o0> vGet(tstEnvVar0)=<o0>
    m.V.tstEnvVar0=<o0> vGet(tstEnvVar0&)=<o0>
    m.<o0>=*o0*val vGet(tstEnvVar0&>)=*o0*val
    m.<o0>.fSt0=*o0.fSt0*val vGet(tstEnvVar0&fSt0)=*o0.fSt0*val
    m.<o0>.fRe0=<o1> vGet(tstEnvVar0&fRe0)=<o1>
    m.<o1>=*o1*val vGet(tstEnvVar0&fRe0>)=*o1*val
    m.<o1>.fStr=*o1.fStr*val vGet(tstEnvVar0&fRe0>fStr)=*o1.fStr*val
    m.<o1>.fVar=tstEnvVar2 vGet(tstEnvVar0&fRe0>fVar)=tstEnvVar2
    m.<o2>=*o2*val vGet(tstEnvVar0&fRe0>fVar&)=<o2>
    m.<o2>=*o2*val vGet(tstEnvVar0&fRe0>fVar&>)=*o2*val
    m.<o2>.fStr=*o2.fStr*val vGet(tstEnvVar0&fRe0>fVar&fStr)=*o2.fStr*v+
    al
    m.<o0>=*o0*put2 vGet(<o0>>)=*o0*put2
    m.<o0>.fSt0=*o0.fSt0*put2 vGet(<o0>>fSt0)=*o0.fSt0*put2
    m.<o1>=*o0>fRe0>put2 vGet(<o0>>fRe0>)=*o0>fRe0>put2
    m.<o1>.fStr=*o0>fRe0>fStr*put2 vGet(<o0>>fRe0>fStr)=*o0>fRe0>fStr*p+
    ut2
    m.<o0>=*v0&>*put3 vGet(tstEnvVar0&>)=*v0&>*put3
    m.<o0>.fSt0=*v0&fSt0*put3 vGet(tstEnvVar0&fSt0)=*v0&fSt0*put3
    m.<o1>=*v0&fRe0>*put3 vGet(tstEnvVar0&fRe0>)=*v0&fRe0>*put3
    m.<o1>.fStr=*v0&fRe0>fStr*put3 vGet(tstEnvVar0&fRe0>fStr)=*v0&fRe0>+
    fStr*put3
    m.<o2>=*v0&fRe0>fVar&>*put3 vGet(tstEnvVar0&fRe0>fVar&>)=*v0&fRe0>f+
    Var&>*put3
    m.<o2>.fStr=*v0&fRe0>fVar&fStr*put3 vGet(tstEnvVar0&fRe0>fVar&fStr)+
    =*v0&fRe0>fVar&fStr*put3
$/tstEnvVars3/
 */
    c0 = classNew('n? TstEnvVars0 u f fSt0 v, f = v, f fRe0 r')
    c1 = classNew('n? TstEnvVars1 u f fStr v,f fRef r' ,
              ', f fNest TstEnvVars0, f = v, f fVar v')
    o0 = oNew(c0)
    o1 = oNew(c1)
    o2 = oNew(c1)
    call tst t, "tstEnvVars3"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'
    fSt0 = 'fSt0'
    fRe0 = 'fRe0'
    fStr = 'fStr'
    fRef = 'fRef'
    fVar = 'fVar'
    v0 = 'tstEnvVar0'
    v2 = 'tstEnvVar2'
    m.o0 = '*o0*val'
    m.o0.fSt0 = '*o0.fSt0*val'
    m.o0.fRe0 = o1
    m.o1 = '*o1*val'
    m.o1.fStr = '*o1.fStr*val'
    m.o1.fRef = o2
    m.o1.fVar = v2
    m.o2 = '*o2*val'
    m.o2.fStr = '*o2.fStr*val'
    m.v.v0 = o0
    m.v.v2 = o2
    call tstEnvVarsMG o0, o0'>'
    call tstEnvVarsMG o0'.'fSt0, o0'>'fSt0
    call tstEnvVarsMG o0'.'fRe0, o0'>'fRe0
    call tstEnvVarsMG o1, o0'>'fRe0'>'
    call tstEnvVarsMG o1'.'fStr, o0'>'fRe0'>'fStr
    call tstEnvVarsMG v'.'v0, v0
    call tstEnvVarsMG v'.'v0, v0'&'
    call tstEnvVarsMG o0, v0'&>'
    call tstEnvVarsMG o0'.'fSt0, v0'&'fSt0
    call tstEnvVarsMG o0'.'fRe0, v0'&'fRe0
    call tstEnvVarsMG o1, v0'&'fRe0'>'
    call tstEnvVarsMG o1'.'fStr, v0'&'fRe0'>'fStr
    call tstEnvVarsMG o1'.'fVar, v0'&'fRe0'>'fVar
    call tstEnvVarsMG o2, v0'&'fRe0'>'fVar'&'
    call tstEnvVarsMG o2, v0'&'fRe0'>'fVar'&>'
    call tstEnvVarsMG o2'.'fStr, v0'&'fRe0'>'fVar'&fStr'
    call vPut o0'>', '*o0*put2'
    call tstEnvVarsMG o0, o0'>'
    call vPut o0'>'fSt0, '*o0.fSt0*put2'
    call tstEnvVarsMG o0'.'fSt0, o0'>'fSt0
    call vPut o0'>'fRe0'>', '*o0>fRe0>put2'
    call tstEnvVarsMG o1, o0'>'fRe0'>'
    call vPut o0'>'fRe0'>'fStr, '*o0>fRe0>fStr*put2'
    call tstEnvVarsMG o1'.'fStr, o0'>'fRe0'>'fStr
    call vPut v0'&>', '*v0&>*put3'
    call tstEnvVarsMG o0, v0'&>'
    call vPut v0'&'fSt0, '*v0&fSt0*put3'
    call tstEnvVarsMG o0'.'fSt0, v0'&'fSt0
    call vPut v0'&'fRe0'>', '*v0&fRe0>*put3'
    call tstEnvVarsMG o1, v0'&'fRe0'>'
    call vPut v0'&'fRe0'>'fStr, '*v0&fRe0>fStr*put3'
    call tstEnvVarsMG o1'.'fStr, v0'&'fRe0'>'fStr
    call vPut v0'&'fRe0'>'fVar'&>', '*v0&fRe0>fVar&>*put3'
    call tstEnvVarsMG o2, v0'&'fRe0'>'fVar'&>'
    call vPut v0'&'fRe0'>'fVar'&fStr', '*v0&fRe0>fVar&fStr*put3'
    call tstEnvVarsMG o2'.'fStr, v0'&'fRe0'>'fVar'&fStr'
    call tstEnd t, "tstEnvVars"
    call tst t, "tstEnvVars"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'
    call vRemove 'v2'
    m.tst.adr1 = 'value eins'
    put1 = vPut('v1', oMutate(tst'.'adr1, m.class_V))
    call tstOut t, 'put v1' m.put1
    call tstOut t, 'v1 hasKey' vHasKey('v1') 'get' vGet('v1')
    call tstOut t, 'v2 hasKey' vHasKey('v2')
    if 0 then
        call tstOut t, 'v2 get'    vGet('v2')
    call vPut 'theBuf', jBuf()
    call pipe '+F' , vGet('theBuf')
    call out 'one to theBur'
    call out 'two to theBuf'
    call pipe '-'
    call pipe '+f',, vGet('theBuf')
    call pipeWriteNow
    call pipe '-'
    call tstOut t, 'v1='vGet('v1') 'o='vGet('v1')
    call vPut 'v3', 'v3WieGehts?'
    call tstOut t, 'v3='vGet('v3') 'o='vGet('v3')
    call vPut 'v4', s2o('v4WieGehts?')
    call tstOut t, 'v4='vGet('v4') 'o='vGet('v4')

    call vPut 'o0', o0
    call tstOut t, 'o o0='vGet('o0')
    call tstOut t, 's o0='vGet('o0')
    fSt0 = 'fSt0'
    fRe0 = 'fRe0'
    m.o0 = 'rexx o0-value'
    m.o0.fSt0 = 'rexx o0.fSt0'
    m.o0.fRe0 = s2o('rexx o0.fRe0')
    call tstOut t, 'o o0='vGet('o0')
    call tstOut t, 's o0='vGet('o0')
    call tstOut t, 'o0&fSt0='vGet('o0&fSt0') 'o='vGet('o0&fSt0')
    call tstOut t, 'o0&fRe0='vGet('o0&fRe0') 'o='vGet('o0&fRe0')
    call tstOut t, 'o0&='vGet('o0&>') 'o='vGet('o0&>')
    call vPut 'o0&>', 'put o0-value'
    call vPut 'o0&fSt0', 'put o0.fSt0'
    call vPut 'o0&fRe0', s2o('putO o0.fRe0')
    call tstOut t, 'o o0='vGet('o0')
    call tstOut t, 's o0='vGet('o0')
    call tstOut t, 'o0&fSt0='vGet('o0&fSt0') 'o='vGet('o0&fSt0')
    call tstOut t, 'o0&fRe0='vGet('o0&fRe0') 'o='vGet('o0&fRe0')
    call tstOut t, 'o0&='vGet('o0&>') 'o='vGet('o0&>')

    call tstEnd t
    call tst t, "tstEnvVars1"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'

    call vPut 'o1', o1
    call vPut 'o1&>', 'put-o1-value'
    call vPut 'o1&fStr', 'put-o1.fStr'
    call vPut 'o1&fRef', vGet('o0')
    call tstOut t, 'm.o1='m.o1 'm.o1.fStr='mGet(o1'.fStr'),
         'm.o1.fRef='mGet(o1'.fRef')
    call tstOut t, 'o o1='vGet('o1') 's o1='vGet('o1')
    call tstOut t, 'o1&fStr='vGet('o1&fStr') 'o='vGet('o1&fStr')
    call tstOut t, 'o1&='vGet('o1&>') 'o='vGet('o1&>')
    call tstOut t, 'o1&fRef='vGet('o1&fRef') 'o='vGet('o1&fRef')
    call tstOut t, 'o1&fRef>fSt0='vGet('o1&fRef>fSt0') ,
        'o='vGet('o1&fRef>fSt0')
    call tstOut t, 'o1&fRef>fRe0='vGet('o1&fRef>fRe0'),
         'o='vGet('o1&fRef>fRe0')

    call vPut 'o1&fNest.fSt0', 'put-o1.fNest.fSt0'
    call vPut 'o1&fNest.fRe0', s2o('put-o1&fNest.fRe0')
    call tstOut t, 'm.o1&fNest.fSt0=' mGet(o1'.fNest.fSt0') ,
            'm.o1&fNest.fRe0=' mGet(o1'.fNest.fRe0')
    call tstOut t, 'o1&fNest.fSt0='vGet('o1&fNest.fSt0'),
         'o='vGet('o1&fNest.fSt0')
    call tstOut t, 'o1&fNest&fRe0='vGet('o1&fNest.fRe0'),
         'o='vGet('o1&fNest.fRe0')
    call tstEnd t

    call tst t, "tstEnvVars2"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'
    call vPut 'o2', o2
    call vPut 'o2&fRef', vGet('o1')
    call tstOut t, 'o2='o2 'getO(o2)='vGet('o2'),
        'getO(o2&fRef)='vGet('o2&fRef')

    call tstOut t, 'o2&fRef>fStr='vGet('o2&fRef>fStr'),
         'o='vGet('o2&fRef>fStr')
    call tstOut t, 'o2&fRef>='vGet('o2&fRef>'),
     'o='vGet('o2&fRef>')

    call tstOut t, 'o2&fRef>fRef='vGet('o2&fRef>fRef') ,
        'o='vGet('o2&fRef>fRef')
    call tstOut t, 'o2&fRef>fRef>fSt0='vGet('o2&fRef>fRef>fSt0') ,
        'o='vGet('o2&fRef>fRef>fSt0')
    call tstOut t, 'o2&fRef>fRef>fRe0='vGet('o2&fRef>fRef>fRe0'),
         'o='vGet('o2&fRef>fRef>fRe0')
    call tstOut t, 'o2&fRef>fNest.fSt0='vGet('o2&fRef>fNest.fSt0'),
         'o='vGet('o2&fRef>fNest.fSt0')
    call tstOut t, 'o2&fRef>fNest&fRe0='vGet('o2&fRef>fNest.fRe0'),
         'o='vGet('o1&fNest.fRe0')
    call tstEnd t

    cS = classNew('n? TstEnvVarsS u f fStS v,f fStV s v, f fStR s r',
        ', f fNeS s TstEnvVars0, f = s v')
    oS = oNew(cS)
    call vPut 'oS', oS
    oT = oNew(cS)
    call tst t, "tstEnvVarsS"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>',
        , oS '<oS>', oT '<oT>'
    call mPut oS'.fStS', '<put oS.fStS>'
    call tstOut t, 'oS='vGet('oS') 'oS&fStS='vGet('oS&fStS')
    call mPut oS'.fStV.1', '<put oS.fStV.1>'
    call mPut oS'.fStV.0', 1
    call tstOut t, 'oS&fStV.0='vGet('oS&fStV.0'),
         'oS&fStV.1='vGet('oS&fStV.1')
    call mPut oS'.fStR.1', s2o('<put oS.fStR.1>')
    call mPut oS'.fStR.2', s2o('<put oS.fStR.2>')
    call mPut oS'.fStR.0', 2
    call tstOut t, 'm.oS.fStR.0='mGet(oS'.fStR.0'),
     '.2='mGet(oS'.fStR.2')
    call tstOut t, 'oS&fStR.0='vGet('oS&fStR.0'),
         '.1='vGet('oS&fStR.1') '.2='vGet('oS&fStR.2')
    call mPut oS'.1234', '<put oS.1234>'
    call mPut oS'.0', 9876
    call mPut oS'.fStR.0', 2
    call tstOut t, 'm.oS.0='mGet(oS'.0'),
     '.1234='mGet(oS'.1234')
    call tstOut t, 'oS&0='vGet('oS&0'),
         '.12='vGet('oS&12') '.1234='vGet('oS&1234')
    call tstEnd t
    return
endProcedure tstEnvVars
tstEnvVarsMG: procedure expose m.
parse arg m, g
     call tstOut t, 'm.'m'='m.m 'vGet('g')='vGet(g)
     return

tstvWith: procedure expose m.
/*
$=/tstEW2/
    ### start tst tstEW2 ##############################################
    tstK1             TSTEW1
    tstK1&            !get1 w
    tstK1&f1          get1.f1 v
    tstK1&f2          !get1.f2 w
    tstK1&F3          get1.f3 v
    ttstK1&F3.FEINS   get1.f3.fEins v
    tstK1&F3.FZWEI    !get1.f3.fZwei w
    tstK1&F3.FDREI o  !get1.f3.fDrei w
    tstK1&F3.FDREI    !get1.f3.fDrei w
    tstK1&F3.1        !get1.f3.1 w
    tstK1&F3.2        TSTEW1
    tstK1&F3.2>F1     get1.f1 v
    tstK1&F3.2>F3.2>F2 !get1.f2 w
    *** err: undefined var F1
    F1          M..
    F1          get1.f1 v
    f2          !get1.f2 w
    F3          get1.f3 v
    F3.FEINS    get1.f3.fEins v
    F3.FZWEI    !get1.f3.fZwei w
    F3.FDREI o  !get1.f3.fDrei w
    F3.1        !get1.f3.1 w
    pu1 F1      get1.f1 v
    pu2 F1      get2.f1 v
    po-2 F1     get1.f1 v
    *** err: undefined var F1
    po-1 F1     M..
$/tstEW2/  */
    call pipeIni
    c0 = classNew('n? TstEW0 u f FEINS v,f FZWEI w, f FDREI r,v,s r')
    call classMet c0, 'oFlds' /* new would do it, but we donot use it */
    cl = classNew('n? TstEW u r TstEW0, f F1 v, f F2 r, f F3 TstEW0')
    call classMet cl, 'oFlds' /* new would do it, but we donot use it */
    call oMutate tstEW1, cl
    m.tstEW1    = s2o('get1 w')
    m.tstEW1.f1 = 'get1.f1 v'
    m.tstEW1.f2 = s2o('get1.f2 w')
    m.tstEW1.f3 = 'get1.f3 v'
    m.tstEW1.f3.fEins = 'get1.f3.fEins v'
    m.tstEW1.f3.fZwei = s2o('get1.f3.fZwei w')
    m.tstEW1.f3.fDrei = s2o('get1.f3.fDrei w')
    m.tstEW1.f3.0 = 3
    m.tstEW1.f3.1 = s2o('get1.f3.1 w')
    m.tstEW1.f3.2 = tstEW1
    m.tstEW1.f3.3 = s2o('get1.f3.3 w')
    call oMutate tstEW2, cl
    m.tstEW2    = s2o('get2 w')
    m.tstEW2.f1 = 'get2.f1 v'
    m.tstEW2.f2 = s2o('get2.f2 w')
    call vPut 'tstK1', tstEW1

    call tst t, 'tstEW2'
    call tstOut t, 'tstK1            ' vGet('tstK1')
    call tstOut t, 'tstK1&           ' vGet('tstK1&>')
    call tstOut t, 'tstK1&f1         ' vGet('tstK1&F1')
    call tstOut t, 'tstK1&f2         ' vGet('tstK1&F2')
    call tstOut t, 'tstK1&F3         ' vGet('tstK1&F3')
    call tstOut t, 'ttstK1&F3.FEINS  ' vGet('tstK1&F3.FEINS')
    call tstOut t, 'tstK1&F3.FZWEI   ' vGet('tstK1&F3.FZWEI')
    call tstOut t, 'tstK1&F3.FDREI o ' vGet('tstK1&F3.FDREI')
    call tstOut t, 'tstK1&F3.FDREI   ' vGet('tstK1&F3.FDREI')
    call tstOut t, 'tstK1&F3.1       ' vGet('tstK1&F3.1')
    call tstOut t, 'tstK1&F3.2       ' vGet('tstK1&F3.2')
    call tstOut t, 'tstK1&F3.2>F1    ' vGet('tstK1&F3.2>F1')
    call tstOut t, 'tstK1&F3.2>F3.2>F2' ,
                                vGet('tstK1&F3.2>F3.2>F2')
    call tstOut t, 'F1         ' vGet('F1')
    call vWith '+', tstEW1
    call tstOut t, 'F1         ' vGet('F1')
    call tstOut t, 'f2         ' vGet('F2')
    call tstOut t, 'F3         ' vGet('F3')
    call tstOut t, 'F3.FEINS   ' vGet('F3.FEINS')
    call tstOut t, 'F3.FZWEI   ' vGet('F3.FZWEI')
    call tstOut t, 'F3.FDREI o ' vGet('F3.FDREI')
    call tstOut t, 'F3.1       ' vGet('F3.1')
    call tstOut t, 'pu1 F1     ' vGet('F1')
    call vWith '+', tstEW2
    call tstOut t, 'pu2 F1     ' vGet('F1')
    call vWith '-'
    call tstOut t, 'po-2 F1    ' vGet('F1')

    call vWith '-'
    call tstOut t, 'po-1 F1    ' vGet('F1')
    call tstEnd t
/*
$=/tstEW3/
    ### start tst tstEW3 ##############################################
    .          s c3&F1          = v(c3&f1)
    *** err: null address at &FEINS in c3&F1&FEINS
    *** err: undefined var c3&F1&FEINS
    .          s c3&F1&FEINS    = M..
    *** err: null address at &FEINS in c3&F3&FEINS
    *** err: null address at &FEINS in c3&F3&FEINS
    *** err: undefined var c3&F3&FEINS
    .          s c3&F3&FEINS    = M..
    .          s c3&F3.FEINS    = val(c3&F3.FEINS)
    *** err: undefined var c3&FEINS
    .          s c3&FEINS       = M..
    getO c3&
    aft Put   s c3&>FEINS      = v&&fEins
    Push c3   s F3.FEINS       = val(c3&F3.FEINS)
    aftPut=   s F3.FEINS       = pushPut(F3.FEINS)
    push c4   s F1             = v(c4&f1)
    put f2    s F2             = put(f2)
    put ..    s F3.FEINS       = put(f3.fEins)
    popW c4   s F1             = v(c3&f1)
    *** err: undefined var F1
    popW c3   s F1             = M..
    .          s F222           = f222 pop stop
$/tstEW3/
*/
    call tst t, 'tstEW3'
    c3 = oNew('TstEW')
    call mAdd t.trans, c3 '<c3>'
    m.c3.f1 = 'v(c3&f1)'
    call vPut 'c3', c3
    call tstEnvSG , 'c3&F1'
    call tstEnvSG , 'c3&F1&FEINS'
    call tstEnvSG , 'c3&F3&FEINS'
    call vPut 'c3&F3.FEINS', 'val(c3&F3.FEINS)'
    call tstEnvSG , 'c3&F3.FEINS'
    call tstEnvSG , 'c3&FEINS'
    call tstOut t,  'getO c3&', vGet('c3&')
    call vPut 'c3&>', oNew('TstEW0')
    call vPut 'c3&>FEINS', 'v&&fEins'
    call tstEnvSG 'aft Put', 'c3&>FEINS'
    call vWith '+', c3
    call tstEnvSG 'Push c3', 'F3.FEINS'
    call vPut 'F3.FEINS', 'pushPut(F3.FEINS)'
    call tstEnvSG 'aftPut=', 'F3.FEINS'

    c4 = oNew('TstEW')
    call mAdd t.trans, c4 '<c4>'
    m.c4.f1 = 'v(c4&f1)'
    call vPut f222, 'f222 no stop'
    call vWith '+',  c4
    call tstEnvSG 'push c4', f1
    call vPut f2, 'put(f2)'
    call tstEnvSG 'put f2', f2
    call vPut f222, 'f222 stopped', 1
    call vPut 'F3.FEINS', 'put(f3.fEins)'
    call tstEnvSG 'put .. ', 'F3.FEINS'
    call vWith '-'
    call tstEnvSG 'popW c4', f1
    call vWith '-'
    call vPut f222, 'f222 pop stop'
    call tstEnvSG 'popW c3', f1
    call tstEnvSG          , f222
    call tstEnd t
    return
endProcedure tstvWith

tstEnvSG: procedure expose m. t
parse arg txt, nm
    call tstOut t, left(txt,10)'s' left(nm, 15)'=' vGet(nm)
    return

tstPipeLazy: procedure expose m.
    call pipeIni
/*
$=/tstPipeLazy/
    ### start tst tstPipeLazy #########################################
    a1 vor pipeBegin loop lazy 0 writeNow *** <class TstPipeLazyBuf>
    bufOpen <
    bufClose
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    a5 vor 2 writeNow in inIx 4
    a2 vor writeNow jBuf
    jBuf line 1
    jBuf line 2
    a3 vor writeNow in inIx 1
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    a4 vor barLast inIx 4
    a6 vor barEnd inIx 4
    a7 nach barEnd lazy 0 writeNow ***
    b1 vor barBegin lazy 0 writeNow *** <class TstPipeLazyRdr>
    RdrOpen <
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    rdrClose
    b4 vor writeNow
    b2 vor writeNow rdr inIx 2
    jRead lazyRdr
    tst in line 3 drei .schluss..
    jRead lazyRdr
    b3 vor barLast inIx 4
    b5 vor barEnd inIx 4
    b6 nach barEnd lazy 0 writeNow ***
    a1 vor pipeBegin loop lazy 1 writeAll *** +
        .<class TstPipeLazyBuf>
    a5 vor 2 writeAll in inIx 0
    a2 vor writeAll jBuf
    bufOpen <
    jBuf line 1
    jBuf line 2
    bufClose
    a3 vor writeAll in inIx 0
    #jIn 1# tst in line 1 eins ,
    tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    #jIn eof 4#
    a4 vor barLast inIx 0
    a6 vor barEnd inIx 4
    a7 nach barEnd lazy 1 writeAll ***
    b1 vor barBegin lazy 1 writeAll *** <class TstPipeLazyRdr>
    b4 vor writeAll
    b2 vor writeAll rdr inIx 1
    RdrOpen <
    jRead lazyRdr
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    jRead lazyRdr
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    jRead lazyRdr
    #jIn eof 4#
    rdrClose
    b3 vor barLast inIx 1
    b5 vor barEnd inIx 4
    b6 nach barEnd lazy 1 writeAll ***
$/tstPipeLazy/ */
    call tst t, "tstPipeLazy"
    do lz=0 to 1
        if lz then
            w = 'writeAll'
        else
            w = 'writeNow'
        m.t.inIx = 1-lz
        ty = class4Name('TstPipeLazyBuf', '')
        if ty == '' then do
            ty = classNew('n TstPipeLazyBuf u JRWDeleg', 'm',
            , 'jOpen call tstOut "T", "bufOpen" opt;',
              'call jOpen m.m.deleg, opt',
            , 'jClose call tstOut "T", "bufClose";',
              'call jClose m.m.deleg')
            end
        if \ lz then
             call mAdd t'.TRANS', ty '<class TstPipeLazyBuf>'
        call out 'a1 vor pipeBegin loop lazy' lz w '***' ty
        call pipe '+N'
        call out 'a2 vor' w 'jBuf'
        b = oNew('TstPipeLazyBuf', jBuf('jBuf line 1','jBuf line 2'))
        interpret 'call pipe'w 'b'
        call out 'a3 vor' w 'in inIx' m.t.inIx
        interpret 'call pipe'w
        call out 'a4 vor barLast inIx' m.t.inIx
        call pipe 'P|'
        call out 'a5 vor 2' w 'in inIx' m.t.inIx
        interpret 'call pipe'w
        call out 'a6 vor barEnd inIx' m.t.inIx
        call pipe '-'
        call out 'a7 nach barEnd lazy' lz w '***'

        ty = class4Name('TstPipeLazyRdr', '')
        if ty == '' then
            ty = classNew('n TstPipeLazyRdr u JRW', 'm',
            , 'jOpen call tstOut "T", "RdrOpen" opt',
            , 'jRead call out "jRead lazyRdr";' ,
                  'mr = m.m.rdr; if \ jRead(mr) then return 0;',
                          "m.m = m.mr; return 1",
            , 'jClose call tstOut "T",  "rdrClose";')
        if \ lz then
             call mAdd t'.TRANS', ty '<class TstPipeLazyRdr>'

        r = oNew('TstPipeLazyRdr')
            m.r.rdr = m.j.in
        if lz then
            call mAdd t'.TRANS', r '<lazyRdr>'
     m.t.inIx = 2-lz
     call out 'b1 vor barBegin lazy' lz w '***' ty
     call pipe '+N'
     call out 'b2 vor' w 'rdr inIx' m.t.inIx
     interpret 'call pipe'w 'r'
     call out 'b3 vor barLast inIx' m.t.inIx
     call pipe 'P|'
        call out 'b4 vor' w
        interpret 'call pipe'w
        call out 'b5 vor barEnd inIx' m.t.inIx
        call pipe '-'
     call out 'b6 nach barEnd lazy' lz w '***'
        end
    call tstEnd t
    return
endProcedure tstPipeLazy

tstEnvClass: procedure expose m.
    call pipeIni
/*
$=/tstEnvClass/
    ### start tst tstEnvClass #########################################
    a0 vor pipeBegin loop lazy 0 writeNow *** TY
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    a5 vor writeNow
    a1 vor jBuf()
    a2 vor writeNow b
    tstR: @<o20 of TstEnvClass10> isA :TstEnvClass10
    tstR:  .f11 = .
    tstR:  .F12 = value F12 of o1 <o20 of TstEnvClass10>
    tstR:  .f13 = .
    WriteO o2
    tstR: @<oc0 of TstEnvClass20> isA :TstEnvClass20 = valueO2Lazy0
    tstR:  .f24 = .
    tstR:  .F25 = value F25 of o20 <o20 of TstEnvClass20>
    a3 vor writeNow
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    a4 vor barLast inIx 4
    a6 vor barEnd
    a7 nach barEnd lazy 0 writeNow ***
    a0 vor pipeBegin loop lazy 1 writeAll *** TY
    a5 vor writeAll
    a1 vor jBuf()
    a2 vor writeAll b
    tstR: @<o21 of TstEnvClass10> isA :TstEnvClass10
    tstR:  .f11 = .
    tstR:  .F12 = value F12 of o1 <o21 of TstEnvClass10>
    tstR:  .f13 = .
    WriteO o2
    tstR: @<oc1 of TstEnvClass20> isA :TstEnvClass20 = valueO2Lazy1
    tstR:  .f24 = .
    tstR:  .F25 = value F25 of o21 <o21 of TstEnvClass20>
    a3 vor writeAll
    #jIn 1# tst in line 1 eins ,
    tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    #jIn eof 4#
    a4 vor barLast inIx 0
    a6 vor barEnd
    a7 nach barEnd lazy 1 writeAll ***
$/tstEnvClass/ */

    call tst t, "tstEnvClass"
    t10 = classNew('n? TstEnvClass10 u f f11 v, f F12 v, f f13 v')
    t20 = classNew('n? TstEnvClass20 u v, f f24 v, f F25 v')
    do lz=0 to 1
        if lz then
            w = 'writeAll'
        else
            w = 'writeNow'
        m.t.inIx = 1-lz
        call out 'a0 vor pipeBegin loop lazy' lz w '***' ty
        call pipe '+N'
        call out 'a1 vor jBuf()'
        b = jOpen(jBuf(), m.j.cWri)
        o1 = oNew('TstEnvClass10')
        m.o1.F12 = 'value F12 of o1' o1
        call mAdd t'.TRANS', o1 '<o2'lz 'of TstEnvClass10>'
        call jWrite b, o1
        call jWrite b, 'WriteO o2'
        o2 = oNew('TstEnvClass20')
        m.o2 = 'valueO2Lazy'lz
        m.o2.F25 = 'value F25 of o2'lz o2
        oc = oCopy(oCopy(o2))
        call mAdd t'.TRANS', o2 '<o2'lz 'of TstEnvClass20>'
        call mAdd t'.TRANS', oc '<oc'lz 'of TstEnvClass20>'
        call jWrite b, oc
        call out 'a2 vor' w 'b'
        interpret 'call pipe'w jClose(b)
        call out 'a3 vor' w
        interpret 'call pipe'w
        call out 'a4 vor barLast inIx' m.t.inIx
        call pipe 'P|'
        call out 'a5 vor' w
        interpret 'call pipe'w
        call out 'a6 vor barEnd'
        call pipe '-'
        call out 'a7 nach barEnd lazy' lz w '***'
        end
    call tstEnd t
    m.t.trans.0 = 0
    return
endProcedure tstEnvClass

tstDsnEx: procedure expose m.
/*
$=/tstDsnEx/
    ### start tst tstDsnEx ############################################
    dsnExists(A540769.WK.rexx) 1
    dsnExists(rzZ/A540769.WK.rexx) 1
    dsnExists(A540769.WK.wk.rexxYY) 0
    dsnExists(rzZ/A540769.WK.wk.rexxYY) 0
    dsnExists(A540769.WK.rexx(wsh)) 1
    dsnExists(rzZ/A540769.WK.rexx(wsh)) 1
    dsnExists(A540769.WK.rexx(nonono)) 0
    dsnExists(rzZ/A540769.WK.rexx(nonono)) 0
    dsnExists(A540769.WK.rxxYY(nonon)) 0
    dsnExists(rzZ/A540769.WK.rxxYY(nonon)) 0
    *** err: error in csm mbrList ?ZZ/A540769.WK.RXXYY(NONON) .
    .    e 1: CSMSI77E INVALID SYSTEM NAME (MUST BE * OR A VALID NAME) +
    (COL:8)
    .    e 2: CSMSI77E?????SYSTEM=?ZZ                                  +
    .                                                                  +
    .                                                           ???????+
    ??????????
    dsnExists(qzZ/A540769.WK.rxxYY(nonon)) 0
$/tstDsnEx/
*/
    call tst t, 'tstDsnEx'
    lst = 'rexx wk.rexxYY rexx(wsh) rexx(nonono) rxxYY(nonon)'

    do lx =1 to words(lst)
         d1 = 'A540769.WK.'word(lst,lx)
         call tstOut t, 'dsnExists('d1')' dsnExists(d1)
         call tstOut t, 'dsnExists(rzZ/'d1')' dsnExists('RZZ/'d1)
         end
    call mAdd t'.TRANS', '00'x '?', '0A'x '?'
    call tstOut t, 'dsnExists(qzZ/'d1')' dsnExists('?ZZ/'d1)
    call tstEnd t
    return
endProceudre tstDsnEx

tstFile: procedure expose m.
    call catIni
/*
$=/tstFile/
    ### start tst tstFile #############################################
    write read 0 last 10 vor anfang
    write read 1 last 80  links1 1   und rechts |  ..
    write read 2 last 80 liinks2 2   und rechts |  ..
    write read 5 last 80 links5 5 rechts5
    write read 99 last 80 links99 99 rechts
    write read 100 last 80 links100 100 rechts
    write read 101 last 80 links101 101 rechts
    write read 999 last 80 links999 999 rechts
    write read 1000 last 80 links1000 1000 rechts
    write read 1001 last 80 links1001 1001 rechts
    write read 2109 last 80 links2109 2109 rechts
    out > eins 1                                                       +
    .             .
    out > eins 2 schluss.                                              +
    .             .
    buf eins
    buf zwei
    buf drei
    out > zwei mit einer einzigen Zeile                                +
    .             .
    . links1 1   und rechts |  .                                       +
    .              .
$/tstFile/ */
    call tst t, "tstFile"
    pds = tstFilename('lib', 'r')
    call tstFileWr pds, 0, ' links0', '  und rechts |  .  '
    call tstFileWr pds, 1, ' links1', '  und rechts |  .  '
    call tstFileWr pds, 2, 'liinks2', '  und rechts |  .  '
    call tstFileWr pds, 5, 'links5', 'rechts5'
    call tstFileWr pds, 99, 'links99', 'rechts'
    call tstFileWr pds, 100, 'links100', 'rechts'
    call tstFileWr pds, 101, 'links101', 'rechts'
    call tstFileWr pds, 999, 'links999', 'rechts'
    call tstFileWr pds, 1000, 'links1000', 'rechts'
    call tstFileWr pds, 1001, 'links1001', 'rechts'
    call tstFileWr pds, 2109, 'links2109', 'rechts'
    pd2 = tstFilename('li2', 'r')
    call pipeIni
    call pipe '+F', s2o(tstPdsMbr(pd2, 'eins'))
    call out tstFB('out > eins 1') /* simulate fixBlock on linux */
    call out tstFB('out > eins 2 schluss.')
    call pipe '-'
    call pipe '+F', s2o(tstPdsMbr(pd2, 'zwei'))
    call out tstFB('out > zwei mit einer einzigen Zeile')
    call pipe '-'
    b = jBuf("buf eins", "buf zwei", "buf drei")
    call pipe '+f', , s2o(tstPdsMbr(pd2, 'eins')), b,
                    ,jBuf(),
                    ,s2o(tstPdsMbr(pd2, 'zwei')),
                    ,s2o(tstPdsMbr(pds, 'wr0')),
                    ,s2o(tstPdsMbr(pds, 'wr1'))
    call pipeWriteNow
    call pipe '-'
    call tstEnd t
    return
endProcedure tstFile

/*--- simulate fixBlock 80 on linux ---------------------------------*/
tstFB: procedure expose m.
parse arg line, recL
    if m.err.os \== 'LINUX' then
        return line
    else if recL == '' then
        return left(line, 80)
    else
        return left(line, recL)
endProcedure tstFB

tstPdsMbr: procedure expose m.
parse arg pds, mbr
    if m.err.os = 'TSO' then
        return pds'('mbr') ::F'
    if m.err.os = 'LINUX' then
        return pds'.'mbr
    call err 'tstPdsMbr implement os' m.err.os
endProcedure tstPdsMbr

tstFileWR: procedure expose m.
parse arg dsn, num, le, ri
    io = file(tstPdsMbr(dsn, 'wr'num))
    call jOpen io, m.j.cWri
    do x = 1 to num /* simulate fixBlock 80 on LINUX*/
        call jWrite io, tstFB(le x ri)
        end
    call jClose io
    if num > 100 then
        call jReset io, tstPdsMbr(dsn, 'wr'num)

    call jOpen io, m.j.cRead
    m.io = 'vor anfang'
    do x = 1 to num
        if \ jRead(io) then
            call err x 'not jRead'
        else if m.io <> le x ri then
            call err x 'read mismatch' m.io
        end
    if jRead(io) then
        call err x 'jRead but should be eof 1'
    if jRead(io) then
        call err x'+1 jjRead but should be eof 2'
    call jClose io
    call tstOut t, 'write read' num 'last' length(m.io) strip(m.io,'t')
    return
endProcedure tstFileWr

tstFileList: procedure expose m.
    call catIni
/*
$=/tstFileList/
    ### start tst tstFileList #########################################
    empty dir
    filled dir
    <<pref 2 List>>eins
    <<pref 2 List>>zwei
    <<pref 2 List>>drei
    <<pref 2 List>>vier
    filled dir recursive
    <<pref 2 List>>eins
    <<pref 2 List>>zwei
    <<pref 2 List>>drei
    <<pref 2 List>>vier
    <<pref 1 vier>>eins
    <<pref 1 vier>>zwei
    <<pref 1 vier>>drei
$/tstFileList/ */
/*
$=/tstFileListTSO/
    ### start tst tstFileListTSO ######################################
    empty dir
    filled dir
    <<pref 2 LIST>>DREI
    <<pref 2 LIST>>EINS
    <<pref 2 LIST>>ZWEI
    filled dir recursive
    <<pref 2 LIST>>DREI
    <<pref 2 LIST>>EINS
    <<pref 1 VIER>>DREI
    <<pref 1 VIER>>EINS
    <<pref 1 VIER>>ZWEI
    <<pref 2 LIST>>ZWEI
$/tstFileListTSO/ */
    if m.err.os = 'TSO' then
        call tst t, "tstFileListTSO"
    else
        call tst t, "tstFileList"
    fi = file(tstFileName('FileList', 'r'))
    call fileMkDir fi
    fl = fileList(fi)
    call tstOut t, 'empty dir'
    call jWriteNow t, fl
    call tstFileListMake t, fi, 2
    call tstOut t, 'filled dir'
    call jWriteNow t, fl
    call tstOut t, 'filled dir recursive'
    call jWriteNow t, fileList(fi, 'r')
    call tstEnd t
    return
endProcedure tstFileList

tstFileListMake: procedure expose m.
parse arg t, fi, lev
    if \ fileIsDir(fi) then
        call fileMkDir fi
    call mAdd t'.TRANS', filePath(fi) || m.file.sep ,
                '<<pref' lev right(filePath(fi),4)'>>'
    call jCat fileChild(fi, 'eins','::F'),jBuf('zeile eins','eins' lev)
    call jCat fileChild(fi, 'zwei','::F'),jBuf('zeile zwei','zwei' lev)
    call jCat fileChild(fi, 'drei','::F'),jBuf('zeile drei','drei' lev)
    if lev > 1 then
        call tstFileListMake t, fileChild(fi, 'vier'), lev-1
    return
endProcedure tstFileListMake

tstMail: procedure expose m.
do i=1 to 3
    call mailHead xy, 'mail from walter''s rexx' time() i, A540769
    call mAdd xy, 'und hier kommt der text' ,
                , 'und zeile zwei timestamp' i':' date('s') time() ,
                , left('und eine lange Zeile 159', 156, '+')159 ,
                , left('und eine lange Zeile 160', 157, '+')160 ,
                , left('und eine lange Zeile 161', 158, '+')161 ,
                , left('und eine lange Zeile ', 200, '+')203 'y ',
                , 'und kurz' ,
                , 'und Schluss'
    call mailSend xy
    call sleep 60
    end
    return
endprocedure tstMail

tstF: procedure expose m.
/*
$=/tstF/
    ### start tst tstF ################################################
    f(1 23%c345%c67%%8, eins,  zwei ) =1 23eins345 zwei 67%8;
    f(1\S23%c345%S67%%8, eins,  zwei ) =1\S23eins345zwei67%8;
    f(1 23%C345%C67%%8, eins,  zwei ) =1 23eins345 zwei 67%8;
    f(1 23%c345%S67%%8, eins,  zwei ) =1 23eins345zwei67%8;
    f(1%S2%c3@2%S4@%c5, eins,  zwei ) =1eins2 zwei 3zwei4 zwei 5;
    f(1%-2C2%3C3@2%3.2C4, eins,  zwei ) =1ei2ei 3zwe4;
    f(1@F1%c2@f2%c3@F3%c4, eins,  zwei ) =1fEins2fZwei3fDrei4;
    tstF2 _ %-9C @%5I @%8I @%+8I @%-8I -----
    _ 0             0        0       +0 0       .
    _ -1.2         -1       -1       -1 -1      .
    _ 2.34          2        2       +2 2       .
    _ -34.8765    -35      -35      -35 -35     .
    _ 567.91234   568      568     +568 568     .
    _ -8901     -8901    -8901    -8901 -8901   .
    _ 23456     23456    23456   +23456 23456   .
    _ -789012   *****  -789012  -789012 -789012 .
    _ 34e6      ***** 34000000 ******** 34000000
    _ -56e7     ***** ******** ******** ********
    _ 89e8      ***** ******** ******** ********
    _ txtli     txtli    txtli    txtli txtli   .
    _ undEinLan undEi undEinLa undEinLa undEinLa
    tstF2 _ %-9C @%5.2I @%12.2I @%+12.2I @%-12.2I -----
    _ 0          0.00         0.00        +0.00 0.00        .
    _ -1.2      -1.20        -1.20        -1.20 -1.20       .
    _ 2.34       2.34         2.34        +2.34 2.34        .
    _ -34.8765  *****       -34.88       -34.88 -34.88      .
    _ 567.91234 *****       567.91      +567.91 567.91      .
    _ -8901     *****     -8901.00     -8901.00 -8901.00    .
    _ 23456     *****     23456.00    +23456.00 23456.00    .
    _ -789012   *****   -789012.00   -789012.00 -789012.00  .
    _ 34e6      *****  34000000.00 +34000000.00 34000000.00 .
    _ -56e7     ***** ************ ************ ************
    _ 89e8      ***** ************ ************ ************
    _ txtli     txtli        txtli        txtli txtli       .
    _ undEinLan undEi undEinLanger undEinLanger undEinLanger
    tstF2 _ %-9C @%7e @% 8E @% 9.3e @% 11.4E -----
    _ 0         0.00e00  0.00E00  0.000e00  0.0000E000
    _ -1.2      -1.2e00 -1.20E00 -1.200e00 -1.2000E000
    _ 2.34      2.34e00  2.34E00  2.340e00  2.3400E000
    _ -34.8765  -3.5e01 -3.49E01 -3.488e01 -3.4877E001
    _ 567.91234 5.68e02  5.68E02  5.679e02  5.6791E002
    _ -8901     -8.9e03 -8.90E03 -8.901e03 -8.9010E003
    _ 23456     2.35e04  2.35E04  2.346e04  2.3456E004
    _ -789012   -7.9e05 -7.89E05 -7.890e05 -7.8901E005
    _ 34e6      3.40e07  3.40E07  3.400e07  3.4000E007
    _ -56e7     -5.6e08 -5.60E08 -5.600e08 -5.6000E008
    _ 89e8      8.90e09  8.90E09  8.900e09  8.9000E009
    _ txtli       txtli    txtli     txtli       txtli.
    _ undEinLan undEinL undEinLa undEinLan undEinLange
    _ 8.76e-07  8.76e-7  8.76E-7  8.760e-7  8.7600E-07
    _ 5.43e-11  5.4e-11  5.4E-11  5.43e-11  5.4300E-11
    _ -8.76e-07 -8.8e-7 -8.76E-7 -8.760e-7 -8.7600E-07
    _ -5.43e-11 -5e-011 -5.4E-11 -5.43e-11 -5.4300E-11
    tstF2 _ %-9C @%kt @%kd @%kb -----
    _ 0          0s00    0     0 .
    _ -1.2      -1s20   -1    -1 .
    _ 2.34       2s34 2340m    2 .
    _ -34.8765  -0m35  -35   -35 .
    _ 567.91234  9m28  568   568 .
    _ -8901     -2h28   -9k   -9k
    _ 23456      6h31   23k   23k
    _ -789012   -9d03 -789k -771k
    _ 34e6       394d   34M   32M
    _ -56e7     -++++ -560M -534M
    _ 89e8      +++++ 8900M 8488M
    _ txtli     txtli txtli txtli
    _ undEinLan Text? Text? Text?
    _ 8.76e-07   0s00  876n    0 .
    _ 5.43e-11   0s00   54p    0 .
    _ -8.76e-07 -0s00 -876n   -0 .
    _ -5.43e-11 -0s00  -54p   -0 .
$/tstF/ */
    call scanIni
    call tst t, 'tstF'
    call tstF1 '1 23%c345%c67%%8'
    call tstF1 '1\S23%c345%S67%%8'
    call tstF1 '1 23%C345%C67%%8'
    call tstF1 '1 23%c345%S67%%8'
    call tstF1 '1%S2%c3@2%S4@%c5'
    call tstF1 '1%-2C2%3C3@2%3.2C4'
    call tstF1 '1@F1%c2@f2%c3@F3%c4'
    nums = '0 -1.2 2.34 -34.8765 567.91234 -8901 23456' ,
                '-789012 34e6 -56e7 89e8 txtli undEinLangerText?'
    call tstF2 '_ %-9C @%5I @%8I @%+8I @%-8I', nums
    call tstF2 '_ %-9C @%5.2I @%12.2I @%+12.2I @%-12.2I', nums
    num2 = ' 8.76e-07  5.43e-11 -8.76e-07  -5.43e-11'
    call tstF2 '_ %-9C @%7e @% 8E @% 9.3e @% 11.4E', nums num2
    call tstF2 '_ %-9C @%kt @%kd @%kb', nums num2
    call tstEnd t
    return
endProcedure tstF

tstF1: procedure expose m.
parse arg fmt
    e='eins'
    z=' zwei '
    f2 = 'f2'
    m.e.f1 = 'fEins'
    m.e.f2 = 'fZwei'
    m.e.f3 = 'fDrei'
    call out "f("fmt"," e"," z") ="f(fmt, e, z)";"
    return
endProcedure tstF1

tstF2: procedure expose m.
parse arg fmt, vals
    call out 'tstF2' fmt '-----'
    do vx=1 to words(vals)
        call out f(fmt, word(vals, vx))
        end
    return
endProcedure tstF2

tstFWords: procedure expose m.
/*
$=/tstFWords/
    ### start tst tstFWords ###########################################
    ??empty??  .
    1space     .
    , %#e--    --
    %#a%9c     .
    *%#a%-7c   .
    ??empty??  eins
    1space     eins
    , %#e--    eins
    %#a%9c          eins
    *%#a%-7c   eins   .
    ??empty??  einszwei
    1space     eins zwei
    , %#e--    eins, zwei
    %#a%9c          eins     zwei
    *%#a%-7c   eins   *zwei   .
    ??empty??  einszweidrei
    1space     eins zwei drei
    , %#e--    eins, zwei, drei
    %#a%9c          eins     zwei     drei
    *%#a%-7c   eins   *zwei   *drei   .
$/tstFWords/
*/
    call scanIni
    ws = '  eins zwei   drei '
    call tst t, 'tstFWords'
    do l=0 to 3
      call tstOut t, '??empty?? ' fWords(            ,subword(ws,1,l))
      call tstOut t, '1space    ' fWords(' '         ,subword(ws,1,l))
      call tstOut t, ', %#e--   ' fWords(', %#e--'   ,subword(ws,1,l))
      call tstOut t, '%#a%9c    ' fWords('%#a%9c'    ,subword(ws,1,l))
      call tstOut t, '*%#a%-7c  ' fWords('*%#a%-7c'  ,subword(ws,1,l))
      end
    call tstEnd t
    return
endProcedure tstFWords
tstFe: procedure expose m.
/*
$=/tstFe/
  ### start tst tstFe ###############################################
  .                   1 < 1.00e00> <1.00e00>
  .                   0 < 0.00e00> <0.00e00>
  .                -2.1 <-2.10e00> <-2.1e00>
  .                  .3 < 3.00e-1> <3.00e-1>
  .             -.45678 <-4.57e-1> <-4.6e-1>
  .                 901 < 9.01e02> <9.01e02>
  .               -2345 <-2.35e03> <-2.3e03>
  .              678e90 < 6.78e92> <6.78e92>
  .              123e-4 < 1.23e-2> <1.23e-2>
  .             567e-89 < 5.7e-87> <5.7e-87>
  .              12e456 < 1.2e457> <1.2e457>
  .             78e-901 < 8e-0900> <8e-0900>
  .           2345e5789 < 2e05792> <2e05792>
  .           123e-4567 < 1e-4565> <1e-4565>
  .          8901e23456 < 9e23459> <9e23459>
  .          -123e-4567 <-1e-4565> <-0e-999>
  .          567e890123 <********> <*******>
  .       45678e-901234 < 0e-9999> <0e-9999>
  .                kurz <    kurz> <kurz   >
  .       undLangerText <undLange> <undLang>
$/tstFe/
*/
    call tst t, 'tstFe'
    vAll = '1 0 -2.1 .3 -.45678 901 -2345 678e90 123e-4' ,
            '567e-89 12e456 78e-901 2345e5789  123e-4567 8901e23456' ,
            '-123e-4567 567e890123 45678e-901234' ,
            'kurz undLangerText'
    do vx=1 to words(vAll)
        v = word(vAll, vx)
        call tstOut t, right(v, 20)  '<'fe(v, 8, 2, 'e', ' ')'>' ,
                                     '<'fe(v, 7, 1, 'e', '-')'>'
        end
    call tstEnd t
    return
endProcedure

tstFTst: procedure expose m.
/*
$=/tstFTstS/
    ### start tst tstFTstS ############################################
    1956-01-29-23.34.56.987654     SS => 1956-01-29-23.34.56.987654|
    1956-01-29-23.34.56.987654     Ss => 1956-01-29-23.34.56|
    1956-01-29-23.34.56.987654     S  => 1956-01-29-23.34.56|
    1956-01-29-23.34.56.987654     SD => 19560129|
    1956-01-29-23.34.56.987654     Sd => 560129|
    1956-01-29-23.34.56.987654     SE => 29.01.1956|
    1956-01-29-23.34.56.987654     Se => 29.01.56|
    1956-01-29-23.34.56.987654     St => 23.34.56|
    1956-01-29-23.34.56.987654     ST => 23:34:56.987654|
    1956-01-29-23.34.56.987654     SY => GB29|
    1956-01-29-23.34.56.987654     SM => B2923345|
    1956-01-29-23.34.56.987654     SH => C33456|
$/tstFTstS/
$=/tstFTsts/
    ### start tst tstFTsts ############################################
    2014-12-23-16.57.38            sS => 2014-12-23-16.57.38.000000|
    2014-12-23-16.57.38            ss => 2014-12-23-16.57.38|
    2014-12-23-16.57.38            s  => 2014-12-23-16.57.38|
    2014-12-23-16.57.38            sD => 20141223|
    2014-12-23-16.57.38            sd => 141223|
    2014-12-23-16.57.38            sE => 23.12.2014|
    2014-12-23-16.57.38            se => 23.12.14|
    2014-12-23-16.57.38            st => 16.57.38|
    2014-12-23-16.57.38            sT => 16:57:38.000000|
    2014-12-23-16.57.38            sY => EM23|
    2014-12-23-16.57.38            sM => M2316573|
    2014-12-23-16.57.38            sH => B65738|
$/tstFTsts/
$=/tstFTstD/
    ### start tst tstFTstD ############################################
    23450618                       DS => 2345-06-18-00.00.00.000000|
    23450618                       Ds => 2345-06-18-00.00.00|
    23450618                       D  => 2345-06-18-00.00.00|
    23450618                       DD => 23450618|
    23450618                       Dd => 450618|
    23450618                       DE => 18.06.2345|
    23450618                       De => 18.06.45|
    23450618                       Dt => 00.00.00|
    23450618                       DT => 00:00:00.000000|
    23450618                       DY => PG18|
    23450618                       DM => G1800000|
    23450618                       DH => A00000|
$/tstFTstD/
0----+----1----+----2----+----3
abcdefghijklmnopqrstuvwxyz
$=/tstFTstd/
    ### start tst tstFTstd ############################################
    120724                         dS => 2012-07-24-00.00.00.000000|
    120724                         ds => 2012-07-24-00.00.00|
    120724                         d  => 2012-07-24-00.00.00|
    120724                         dD => 20120724|
    120724                         dd => 120724|
    120724                         dE => 24.07.2012|
    120724                         de => 24.07.12|
    120724                         dt => 00.00.00|
    120724                         dT => 00:00:00.000000|
    120724                         dY => CH24|
    120724                         dM => H2400000|
    120724                         dH => A00000|
$/tstFTstd/
$=/tstFTstE/
    ### start tst tstFTstE ############################################
    09.12.1345                     ES => 1345-12-09-00.00.00.000000|
    09.12.1345                     Es => 1345-12-09-00.00.00|
    09.12.1345                     E  => 1345-12-09-00.00.00|
    09.12.1345                     ED => 13451209|
    09.12.1345                     Ed => 451209|
    09.12.1345                     EE => 09.12.1345|
    09.12.1345                     Ee => 09.12.45|
    09.12.1345                     Et => 00.00.00|
    09.12.1345                     ET => 00:00:00.000000|
    09.12.1345                     EY => PM09|
    09.12.1345                     EM => M0900000|
    09.12.1345                     EH => A00000|
$/tstFTstE/
$=/tstFTste/
    ### start tst tstFTste ############################################
    31.05.2467                     eS => 2024-05-31-00.00.00.000000|
    31.05.2467                     es => 2024-05-31-00.00.00|
    31.05.2467                     e  => 2024-05-31-00.00.00|
    31.05.2467                     eD => 20240531|
    31.05.2467                     ed => 240531|
    31.05.2467                     eE => 31.05.2024|
    31.05.2467                     ee => 31.05.2467|
    31.05.2467                     et => 00.00.00|
    31.05.2467                     eT => 00:00:00.000000|
    31.05.2467                     eY => OF31|
    31.05.2467                     eM => F3100000|
    31.05.2467                     eH => A00000|
$/tstFTste/
$=/tstFTstt/
### start tst tstFTstt ############################################
    12.34.56                       tS => 0000-01-01-12.34.56.000000|
    12.34.56                       ts => 0000-01-01-12.34.56|
    12.34.56                       t  => 0000-01-01-12.34.56|
    12.34.56                       tD => 00000101|
    12.34.56                       td => 000101|
    12.34.56                       tE => 01.01.0000|
    12.34.56                       te => 01.01.00|
    12.34.56                       tt => 12.34.56|
    12.34.56                       tT => 12:34:56.000000|
    12.34.56                       tY => ??01|
    12.34.56                       tM => ?0112345|
    12.34.56                       tH => B23456|
$/tstFTstt/
$=/tstFTstT/
    ### start tst tstFTstT ############################################
    23.45.06.784019                TS => 0000-01-01-23.45.06.784019|
    23.45.06.784019                Ts => 0000-01-01-23.45.06|
    23.45.06.784019                T  => 0000-01-01-23.45.06|
    23.45.06.784019                TD => 00000101|
    23.45.06.784019                Td => 000101|
    23.45.06.784019                TE => 01.01.0000|
    23.45.06.784019                Te => 01.01.00|
    23.45.06.784019                Tt => 23.45.06|
    23.45.06.784019                TT => 23.45.06.784019|
    23.45.06.784019                TY => ??01|
    23.45.06.784019                TM => ?0123450|
    23.45.06.784019                TH => C34506|
$/tstFTstT/
$=/tstFTstY/
    ### start tst tstFTstY ############################################
    FE25                           YS => 2015-04-25-00.00.00.000000|
    FE25                           Ys => 2015-04-25-00.00.00|
    FE25                           Y  => 2015-04-25-00.00.00|
    FE25                           YD => 20150425|
    FE25                           Yd => 150425|
    FE25                           YE => 25.04.2015|
    FE25                           Ye => 25.04.15|
    FE25                           Yt => 00.00.00|
    FE25                           YT => 00:00:00.000000|
    FE25                           YY => FE25|
    FE25                           YM => E2500000|
    FE25                           YH => A00000|
$/tstFTstY/
$=/tstFTstM/
    ### start tst tstFTstM ############################################
    I2317495                       MS => 0000-08-23-17.49.50.000000|
    I2317495                       Ms => 0000-08-23-17.49.50|
    I2317495                       M  => 0000-08-23-17.49.50|
    I2317495                       MD => 00000823|
    I2317495                       Md => 000823|
    I2317495                       ME => 23.08.0000|
    I2317495                       Me => 23.08.00|
    I2317495                       Mt => 17.49.50|
    I2317495                       MT => 17:49:50.000000|
    I2317495                       MY => ?I23|
    I2317495                       MM => I2317495|
    I2317495                       MH => B74950|
$/tstFTstM/
$=/tstFTstH/
    ### start tst tstFTstH ############################################
    B23456                         HS => 0000-01-01-12.34.56.000000|
    B23456                         Hs => 0000-01-01-12.34.56|
    B23456                         H  => 0000-01-01-12.34.56|
    B23456                         HD => 00000101|
    B23456                         Hd => 000101|
    B23456                         HE => 01.01.0000|
    B23456                         He => 01.01.00|
    B23456                         Ht => 12.34.56|
    B23456                         HT => 12:34:56.000000|
    B23456                         HY => ??01|
    B23456                         HM => ?0112345|
    B23456                         HH => B23456|
$/tstFTstH/
$=/tstFTstn/
    ### start tst tstFTstn ############################################
    19560423 17:58:29              nS => 1956-04-23-17.58.29.000000|
    19560423 17:58:29              ns => 1956-04-23-17.58.29|
    19560423 17:58:29              n  => 1956-04-23-17.58.29|
    19560423 17:58:29              nD => 19560423|
    19560423 17:58:29              nd => 560423|
    19560423 17:58:29              nE => 23.04.1956|
    19560423 17:58:29              ne => 23.04.56|
    19560423 17:58:29              nt => 17.58.29|
    19560423 17:58:29              nT => 17:58:29.000000|
    19560423 17:58:29              nY => GE23|
    19560423 17:58:29              nM => E2317582|
    19560423 17:58:29              nH => B75829|
$/tstFTstn/
$=/tstFTstN/
    ### start tst tstFTstN ############################################
    32102130 10:21:32.456789       NS => 3210-21-30-10.21.32.456789|
    32102130 10:21:32.456789       Ns => 3210-21-30-10.21.32|
    32102130 10:21:32.456789       N  => 3210-21-30-10.21.32|
    32102130 10:21:32.456789       ND => 32102130|
    32102130 10:21:32.456789       Nd => 102130|
    32102130 10:21:32.456789       NE => 30.21.3210|
    32102130 10:21:32.456789       Ne => 30.21.10|
    32102130 10:21:32.456789       Nt => 10.21.32|
    32102130 10:21:32.456789       NT => 10:21:32.456789|
    32102130 10:21:32.456789       NY => AV30|
    32102130 10:21:32.456789       NM => V3010213|
    32102130 10:21:32.456789       NH => B02132|
$/tstFTstN/
*/
    call scanIni
    say "f('%t  ')" f('%t  ')
    allOut = 'Ss DdEetTYMH'
    allIn  = 'S1956-01-29-23.34.56.987654' ,
             's2014-12-23-16.57.38' ,
             'D23450618' ,
             'd120724'   ,
             'E09.12.1345' ,
             'e31.05.2467' ,
             't12.34.56'  ,
             'T23.45.06.784019' ,
             'YFE25' ,
             'MI2317495' ,
             'HB23456' ,
             'n19560423*17:58:29' ,
             'N32102130*10:21:32.456789'
    do ix=1 to words(allIn)
        parse value word(allIn, ix) with iF 2 iV
        iv = translate(iv, ' ', '*')
        call tst t, "tstFTst"iF
        do ox=1 to length(allOut)
            ft = iF || substr(allOut, ox, 1)
            call tstOut t, left(iV, 30) ft  '=>' f('%t'ft, iV)'|'
            if 0 & iF = 'Y' then
                say '???' ft '>>>' mGet('F_GEN.%t'ft)
            end
        call tstEnd t
        end
    return
endProcedure tstFTst

tstFmt: procedure expose m.
    call pipeIni
/*
$=/tstFmt/
    ### start tst tstFmt ##############################################
    =   a2i b3b   d4                       fl5          ex6
    -11 -11 b3    d4-11+d4++++    -111.1000000 -1.11000e-12
    -1  -10 b     d4-10+d4+++            null1        null3
    -    -9 b3b-9 d4-9+d4+++       -11.0000000 -1.11900e-10
    -8+  -8 b3b-  d4-8+d4++        -18.0000000 -1.18000e010
    -7   -7 b3b   d4-7+d4+          -7.0000000 -1.70000e-07
    -    -6 b3    d4-6+d4           -0.1111160 -6.00000e006
    -5+  -5 b     d4-5+d                 null2        null2
    -4   -4 b3b-4 d4-4+         -11114.0000000 -1.11140e008
    -    -3 b3b-  d4-3              -0.1130000 -1.13000e-04
    -2+  -2 b3b   d4-               -0.1200000 -1.20000e001
    -1   -1 b3    d4                -0.1000000 -1.00000e-02
    0     0 b     d                      null1        null1
    1+    1 b3    d4                 0.1000000  1.00000e-02
    2++   2 b3b   d42                0.1200000  1.20000e001
    3     3 b3b3  d43+               0.1130000  1.13000e-04
    4+    4 b3b4+ d44+d          11114.0000000  1.11140e008
    5++   5 b     d45+d4                 null2        null2
    6     6 b3    d46+d4+            0.1111160  1.11116e005
    7+    7 b3b   d47+d4++           0.1111117  7.00000e-08
    8++   8 b3b8  d48+d4+++          8.0000000  1.80000e009
    9     9 b3b9+ d49+d4++++         0.9000000  1.19000e-08
    10   10 b     d410+d4++++            null1        null3
    11+  11 b3    d411+d4+++++       0.1110000  1.00000e-12
    1    12 b3b   d412+d4++++++  11112.0000000  2.00000e012
    13   13 b3b1  d               1111.3000000  1.13000e-12
    14+  14 b3b14 d4            111111.0000000  1.40000e013
    1    15 b     d41                    null2        null1
    16   16 b3    d416               6.0000000  1.16000e003
    17+  17 b3b   d417+              0.7000000  1.11170e-03
    1    18 b3b1  d418+d            11.0000000  1.11800e003
    19   19 b3b19 d419+d4            0.1190000  9.00000e-05
    20+  20 b     d420+d4+               null1        null2
    2    21 b3    d421+d4++         11.1210000  1.11000e-05
    22   22 b3b   d422+d4+++     11111.2000000  2.00000e007
    23+  23 b3b2  d423+d4++++        0.1111123  1.11230e-09
    c3L      a2i drei  d4                 fl5          ex6
    -11 -1.10E01 b3    d4-11+d   -111.1000000 -1.11000e-12
    -1  -1.00E01 b     d4-10+d          null1        null3
    -   -9.00E00 b3b-9 d4-9+d4    -11.0000000 -1.11900e-10
    -8+ -8.00E00 b3b-  d4-8+d4    -18.0000000 -1.18000e010
    -7  -7.00E00 b3b   d4-7+d4     -7.0000000 -1.70000e-07
    -   -6.00E00 b3    d4-6+d4     -0.1111160 -6.00000e006
    -5+ -5.00E00 b     d4-5+d           null2        null2
    -4  -4.00E00 b3b-4 d4-4+   -11114.0000000 -1.11140e008
    -   -3.00E00 b3b-  d4-3        -0.1130000 -1.13000e-04
    -2+ -2.00E00 b3b   d4-         -0.1200000 -1.20000e001
    -1  -1.00E00 b3    d4          -0.1000000 -1.00000e-02
    0    0.00E00 b     d                null1        null1
    1+   1.00E00 b3    d4           0.1000000  1.00000e-02
    2++  2.00E00 b3b   d42          0.1200000  1.20000e001
    3    3.00E00 b3b3  d43+         0.1130000  1.13000e-04
    4+   4.00E00 b3b4+ d44+d    11114.0000000  1.11140e008
    5++  5.00E00 b     d45+d4           null2        null2
    6    6.00E00 b3    d46+d4+      0.1111160  1.11116e005
    7+   7.00E00 b3b   d47+d4+      0.1111117  7.00000e-08
    8++  8.00E00 b3b8  d48+d4+      8.0000000  1.80000e009
    9    9.00E00 b3b9+ d49+d4+      0.9000000  1.19000e-08
    10   1.00E01 b     d410+d4          null1        null3
    11+  1.10E01 b3    d411+d4      0.1110000  1.00000e-12
    1    1.20E01 b3b   d412+d4  11112.0000000  2.00000e012
    13   1.30E01 b3b1  d         1111.3000000  1.13000e-12
    14+  1.40E01 b3b14 d4      111111.0000000  1.40000e013
    1    1.50E01 b     d41              null2        null1
    16   1.60E01 b3    d416         6.0000000  1.16000e003
    17+  1.70E01 b3b   d417+        0.7000000  1.11170e-03
    1    1.80E01 b3b1  d418+d      11.0000000  1.11800e003
    19   1.90E01 b3b19 d419+d4      0.1190000  9.00000e-05
    20+  2.00E01 b     d420+d4          null1        null2
    2    2.10E01 b3    d421+d4     11.1210000  1.11000e-05
    22   2.20E01 b3b   d422+d4  11111.2000000  2.00000e007
    23+  2.30E01 b3b2  d423+d4      0.1111123  1.11230e-09
$/tstFmt/ */

    call tst t, "tstFmt"
    b = jBuf()
    st = b'.BUF'
    call pipe '+F', b
    call tstDataClassOut '. c3 a2i i b3b c5 d4 c13 fl5 f8n2 ex6 e9n3',
        , -11, + 23
    call pipe '-'
    call fTabAuto fTabReset(abc, 1), b
    call fTabReset abc, 1
    call fTabAddDetect abc,      , st   , , 'c3L'
    call fTabAdd       abc, 'a2i', '% 8E'
    call fTabAddDetect abc, 'b3b', st   , ,'drei'
    call fTabAdd       abc, 'd4', '%-7C'
    call fTabAddDetect abc, 'fl5', st
    call fTabAddDetect abc, 'ex6', st
    call fTab abc, b
    call tstEnd t
    return
endProcedure tstFmt

tstFTab: procedure expose m.
    call pipeIni
    call scanIni
/*
$=/tstFTab/
    ### start tst tstFTab #############################################
    testData begin
    ..---------a2i-b3b------------------d4------fl5-----ex6---
    -11       -11 b3           -11+d4++++ -111.100 -1e-012
    -1        -10 b            4-10+d4+++    null1   null3
    -          -9 b3b-9        d4-9+d4+++  -11.000 -1e-010
    -8+        -8 b3b-          d4-8+d4++  -18.000 -1.2e10
    -7         -7 b3b            d4-7+d4+   -7.000 -1.7e-7
    -          -6 b3              d4-6+d4   -0.111 -6.0e06
    -5+        -5 b                d4-5+d    null2   null2
    -4         -4 b3b-4             d4-4+ ******** -1.1e08
    -          -3 b3b-               d4-3   -0.113 -1.1e-4
    -2+        -2 b3b                 d4-   -0.120 -1.2e01
    -1         -1 b3                   d4   -0.100 -1.0e-2
    0           0 b                     d    null1   null1
    1+          1 b3                   d4    0.100 1.00e-2
    2++         2 b3b                 d42    0.120 1.20e01
    3           3 b3b3               d43+    0.113 1.13e-4
    4+          4 b3b4+             d44+d ******** 1.11e08
    5++         5 b                d45+d4    null2   null2
    6           6 b3              d46+d4+    0.111 1.11e05
    7+          7 b3b            d47+d4++    0.111 7.00e-8
    8++         8 b3b8          d48+d4+++    8.000 1.80e09
    9           9 b3b9+        d49+d4++++    0.900 1.19e-8
    10         10 b            410+d4++++    null1   null3
    11+        11 b3           11+d4+++++    0.111 1.0e-12
    1          12 b3b          2+d4++++++ ******** 2.00e12
    13         13 b3b1                  d 1111.300 1.1e-12
    14+        14 b3b14                d4 ******** 1.40e13
    1          15 b                   d41    null2   null1
    16         16 b3                 d416    6.000 1.16e03
    17+        17 b3b               d417+    0.700 1.11e-3
    1          18 b3b1             d418+d   11.000 1.12e03
    19         19 b3b19           d419+d4    0.119 9.00e-5
    20+        20 b              d420+d4+    null1   null2
    2          21 b3            d421+d4++   11.121 1.11e-5
    22         22 b3b          d422+d4+++ ******** 2.00e07
    23+        23 b3b2         423+d4++++    0.111 1.11e-9
    ..---------a2i-b3b------------------d4------fl5-----ex6---
    testData end
$/tstFTab/ */

    call tst t, "tstFTab"
    b = jBuf()
    st = b'.BUF'
    call pipe '+F', b
    call tstDataClassOut '. c3 a2i i b3b c5 d4 c13 fl5 f8n2 ex6 e9n3',
        , -11, + 23
    call pipe 'P|'
    call fTabReset ft, 2 1, 1 3, '-'
    call fTabAddRCT   ft, '='   , '%-6C', '.', , 'testData begin',
                                                , 'testData end'
    call fTabAddRCT   ft, 'a2i' , '%6i'
    call fTabAddRCT   ft, 'b3b' , '%-12C'
    call fTabAddRCT   ft, 'd4'  , '%10C'
    call fTabAddRCT   ft, 'fl5' , '%8.3I'
    call fTabAddRCT   ft, 'ex6' , '%7e'
    call fTab ft
    call pipe '-'
    call tstEnd t
    return
endProcedure tstFTab

tstCSV: procedure
/*
$=/tstCSV/
    ### start tst tstCSV ##############################################
    value,value eins,value zwei
    value,"value, , eins",value zwei
    value,"","value ""zwei"" oder?"
    value,,"value ""zwei"" oder?"
$/tstCSV/ */
    m.tstCsv.c.1 = ''
    m.tstCsv.c.2 = eins
    m.tstCsv.c.3 = zwei
    m.tstCsv.c.0 = 3
    call tst t, "tstCSV"
    m.tstCsv.o      = 'value'
    m.tstCsv.o.eins = 'value eins'
    m.tstCsv.o.zwei = 'value zwei'
    call tstOut t, csv4Obj(tstCsv'.'o, tstCsv'.'c, 0)
    m.tstCsv.o.eins = 'value, , eins'
    call tstOut t, csv4Obj(tstCsv'.'o, tstCsv'.'c, 0)
    m.tstCsv.o.eins = ''
    m.tstCsv.o.zwei = 'value "zwei" oder?'
    call tstOut t, csv4Obj(tstCsv'.'o, tstCsv'.'c, 0)
    m.tstCsv.o.eins = '---'
    call tstOut t, csv4Obj(tstCsv'.'o, tstCsv'.'c, 1, '---')
    call tstEnd t
    return
endProcedure tstCSV

tstfUnits: procedure
/*
$=/tstfUnits/
    ### start tst tstfUnits ###########################################
    .             1 ==>    1  =->   -1  =+>    +1  =b>    1 .
    .             5 ==>    5  =->   -5  =+>    +5  =b>    5 .
    .            13 ==>   13  =->  -13  =+>   +13  =b>   13 .
    .           144 ==>  144  =-> -144  =+>  +144  =b>  144 .
    .          1234 ==> 1234  =->   -1k =+> +1234  =b> 1234 .
    .          7890 ==> 7890  =->   -8k =+> +7890  =b> 7890 .
    .             0 ==>    0  =->    0  =+>    +0  =b>    0 .
    .         234E3 ==>  234k =-> -234k =+>  +234k =b>  229k
    .          89E6 ==>   89M =->  -89M =+>   +89M =b>   85M
    .         123E9 ==>  123G =-> -123G =+>  +123G =b>  115G
    .     4567891E9 ==> 4568T =->   -5P =+> +4568T =b> 4154T
    .         0.123 ==>  123m =-> -123m =+>  +123m =b>    0 .
    .  0.0000456789 ==>   46u =->  -46u =+>   +46u =b>    0 .
    .   345.567E-12 ==>  346p =-> -346p =+>  +346p =b>    0 .
    .  123.4567E-15 ==>  123f =-> -123f =+>  +123f =b>    0 .
    .           ABC ==>   ABC =->  -ABC =+>    ABC =b>   ABC
    ABCDEFGHIJKLMN ==> JKLMN =-> JKLMN =+> IJKLMN =b> JKLMN
    .          1E77 ==> +++++ =-> -++++ =+> ++++++ =b> +++++.
    .         1E-77 ==>    0f =->   -0f =+>    +0f =b>    0 .
    .     18.543E18 ==>   19E =->  -19E =+>   +19E =b>   16E
    .     20.987E20 ==> 2099E =-> -++++ =+> +2099E =b> 1820E
    .             1 ==>  1.000  =-> -1.000  =+> +1.000  =b>  1.000 .
    .             5 ==>  5.000  =-> -5.000  =+> +5.000  =b>  5.000 .
    .            13 ==> 13.000  =-> -0.013k =+> +0.013k =b> 13.000 .
    .           144 ==>  0.144k =-> -0.144k =+> +0.144k =b>  0.141k
    .          1234 ==>  1.234k =-> -1.234k =+> +1.234k =b>  1.205k
    .          7890 ==>  7.890k =-> -7.890k =+> +7.890k =b>  7.705k
    .             0 ==>  0.000  =->  0.000  =+> +0.000  =b>  0.000 .
    .         234E3 ==>  0.234M =-> -0.234M =+> +0.234M =b>  0.223M
    .          89E6 ==> 89.000M =-> -0.089G =+> +0.089G =b> 84.877M
    .         123E9 ==>  0.123T =-> -0.123T =+> +0.123T =b>  0.112T
    .     4567891E9 ==>  4.568P =-> -4.568P =+> +4.568P =b>  4.057P
    .         0.123 ==>  0.123  =-> -0.123  =+> +0.123  =b>  0.123 .
    .  0.0000456789 ==> 45.679u =-> -0.046m =+> +0.046m =b>  0.000 .
    .   345.567E-12 ==>  0.346n =-> -0.346n =+> +0.346n =b>  0.000 .
    .  123.4567E-15 ==>  0.123p =-> -0.123p =+> +0.123p =b>  0.000 .
    .           ABC ==>     ABC =->    -ABC =+>     ABC =b>     ABC
    ABCDEFGHIJKLMN ==> HIJKLMN =-> HIJKLMN =+> HIJKLMN =b> HIJKLMN
    .          1E77 ==> +++++++ =-> -++++++ =+> +++++++ =b> +++++++.
    .         1E-77 ==>  0.000f =-> -0.000f =+> +0.000f =b>  0.000 .
    .     18.543E18 ==> 18.543E =->    -19E =+>    +19E =b> 16.083E
    .     20.987E20 ==>   2099E =->  -2099E =+>  +2099E =b>   1820E
$/tstfUnits/
$=/tstfUnitst/
    ### start tst tstfUnitst ##########################################
    .            .3 ==>  0s30 ++>   0s30 -+> -0s30 -->  -0s30
    .            .8 ==>  0s80 ++>   0s80 -+> -0s80 -->  -0s80
    .             1 ==>  1s00 ++>   1s00 -+> -1s00 -->  -1s00
    .           1.2 ==>  1s20 ++>   1s20 -+> -1s20 -->  -1s20
    .            59 ==> 59s00 ++>  59s00 -+> -0m59 --> -59s00
    .         59.07 ==> 59s07 ++>  59s07 -+> -0m59 --> -59s07
    .        59.997 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .            60 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .          60.1 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .           611 ==> 10m11 ++>  10m11 -+> -0h10 --> -10m11
    .        3599.4 ==> 59m59 ++>  59m59 -+> -1h00 --> -59m59
    .        3599.5 ==>  1h00 ++>   1h00 -+> -1h00 -->  -1h00
    .          3661 ==>  1h01 ++>   1h01 -+> -1h01 -->  -1h01
    .         83400 ==> 23h10 ++>  23h10 -+> -0d23 --> -23h10
    .         84700 ==> 23h32 ++>  23h32 -+> -1d00 --> -23h32
    .         86400 ==>  1d00 ++>   1d00 -+> -1d00 -->  -1d00
    .         89900 ==>  1d01 ++>   1d01 -+> -1d01 -->  -1d01
    .       8467200 ==> 98d00 ++>  98d00 -+>  -98d --> -98d00
    .    8595936.00 ==> 99d12 ++>  99d12 -+>  -99d --> -99d12
    .    8638704.00 ==>  100d ++>   100d -+> -100d -->  -100d
    .       8640000 ==>  100d ++>   100d -+> -100d -->  -100d
    .     863913600 ==> 9999d ++>  9999d -+> -++++ --> -9999d
    .     863965440 ==> +++++ ++>  +++++ -+> -++++ --> -+++++.
    .     8.6400E+9 ==> +++++ ++>  +++++ -+> -++++ --> -+++++.
$/tstfUnitst/ */
    call jIni
    call tst t, "tstfUnits"
    d = 86400
    lst = 1 5 13 144 1234 7890 0 234e3  89e6 123e9,
          4567891e9 0.123 0.0000456789 345.567e-12 123.4567e-15 ,
           abc abcdefghijklmn   1e77 1e-77 18.543e18 20.987e20
    do wx=1 to words(lst)
        call tstOut t, right(word(lst, wx), 14) ,
                 '==>' fUnits(    word(lst, wx), 'd') ,
                 '=->' fUnits( '-'word(lst, wx), 'd') ,
                 '=+>' fUnits(    word(lst, wx), 'd',  ,   , '+'),
                 '=b>' fUnits(    word(lst, wx), 'b')
        end
    do wx=1 to words(lst)
        call tstOut t, right(word(lst, wx), 14) ,
                 '==>' fUnits(    word(lst, wx), 'd', 7, 3) ,
                 '=->' fUnits( '-'word(lst, wx), 'd', 7, 3) ,
                 '=+>' fUnits(    word(lst, wx), 'd', 7, 3, '+'),
                 '=b>' fUnits(    word(lst, wx), 'b', 7, 3)
        end
    call tstEnd t
    call tst t, "tstfUnitst"
    d = 86400
    lst = .3 .8 1 1.2 59 59.07 59.997 60 60.1 611 ,
          3599.4 3599.5 3661 d-3000 d-1700 d d+3500 ,
          d * 98 d * 99.49 d * 99.985 d*100 d * 9999 d * 9999.6 ,
          d * 1e5
    do wx=1 to words(lst)
        call tstOut t, right(word(lst, wx), 14) ,
                 '==>' fUnits(    word(lst, wx), 't'   ) ,
                 '++>' fUnits(    word(lst, wx), 't', , , ' '),
                 '-+>' fUnits('-'word(lst, wx),  't' ),
                 '-->' fUnits('-'word(lst, wx), 't', , , ' ')
        end
    call tstEnd t
    return
endProcedure tstfUnits

tstSb: procedure expose m.
/*
$=/tstSb/
    ### start tst tstSb ###############################################
    end        : 0
    char  3    : 1 abc
    lit   d?   : 0 .
    lit   de   : 1 de
    lit   de ? fg fgh: 1 fg
    while HIJ  : 0 .
    end        : 0
    while Jih  : 1 hi
    while ? klj: 1 jklkl ?
    end        : 1
    while ? klj: 0 .
    char  3    : 0 .
    lit        : 0 .
    until cba  : 0 .
    until ?qd  : 1 abc
    until ?qr  : 1 defdef .
    until ?qr  : 0 .
    strEnd ?   : 1 ?
    strEnd ?   : 0 .
    strEnd ?   : 1 ab??cd????gh?
    strEnd ") ": 1 ab) .
    strEnd ") ": 1 cd) ) gh) .
    string     : 1 'eins?''' v=eins?'
    space      : 1  >
    string     : 1 "zwei""" v=zwei"
    string ?   : 1 ?drei??? v=drei?
    *** err: scanErr ending Apostroph(") missing
    .    e 1: last token  scanPosition "noEnd
    .    e 2: pos 27 in string 'eins?''' "zwei"""?drei???"noEnd
    string     : 0  v=drei?
$/tstSb/ */
    call pipeIni
    call scanIni
    call tst t, 'tstSb'
    call scanSrc s, 'abcdefghijklkl ?'
    call out 'end        :' scanSBEnd(s)
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit   d?   :' scanLit(s, 'd?') m.s.tok
    call out 'lit   de   :' scanLit(s, 'de') m.s.tok
    call out 'lit   de ? fg fgh:',
            scanLit(s, 'de', '?', 'fg', 'fgh') m.s.tok
    call out 'while HIJ  :' scanWhile(s, 'HIJ') m.s.tok
    call out 'end        :' scanSBEnd(s)
    call out 'while Jih  :' scanWhile(s, 'Jih') m.s.tok
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'end        :' scanSBEnd(s)
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit        :' scanLit(s) m.s.tok
    call scanSrc s, 'abcdefdef ?'
    call out 'until cba  :' scanUntil(s, 'cba') m.s.tok
    call out 'until ?qd  :' scanUntil(s, '?qd') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab??cd????gh?ijk'
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab) cd) ) gh) jk) )'
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call scanSrc s, "'eins?'''" '"zwei"""?drei???"noEnd'
    call out 'string     :' scanString(s) m.s.tok 'v='m.s.val
    call out 'space      :' scanWhile(s, ' ') m.s.tok'>'
    call out 'string     :' scanString(s) m.s.tok 'v='m.s.val
    call out 'string ?   :' scanString(s, '?') m.s.tok 'v='m.s.val
    call out 'string     :' scanString(s) m.s.tok 'v='m.s.val
    call tstEnd t
    return
endProcedure tstSb

tstSb2: procedure expose m.
/*
$=/tstSb2/
    ### start tst tstSb2 ##############################################
    end        : 0
    char  3    : 1 abc
    lit   d?   : 0 .
    lit   de   : 1 de
    lit   de ? fg fgh: 1 fg
    while HIJ  : 0 .
    end        : 0
    while Jih  : 1 hi
    while ? klj: 1 jklkl ?
    end        : 1
    while ? klj: 0 .
    char  3    : 0 .
    lit        : 0 .
    until cba  : 0 .
    until ?qd  : 1 abc
    until ?qr  : 1 defdef .
    until ?qr  : 0 .
    strEnd ?   : 1 ?
    strEnd ?   : 0 .
    strEnd ?   : 1 ab??cd????gh?
    strEnd ") ": 1 ab) .
    strEnd ") ": 1 cd) ) gh) .
$/tstSb2/ */
    call tst t, 'tstSb2'
    call scanIni
    call scanSrc s, 'abcdefghijklkl ?'
    call out 'end        :' scanEnd(s)
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit   d?   :' scanLit(s, 'd?') m.s.tok
    call out 'lit   de   :' scanLit(s, 'de') m.s.tok
    call out 'lit   de ? fg fgh:',
            scanLit(s, 'de', '?', 'fg', 'fgh') m.s.tok
    call out 'while HIJ  :' scanWhile(s, 'HIJ') m.s.tok
    call out 'end        :' scanEnd(s)
    call out 'while Jih  :' scanWhile(s, 'Jih') m.s.tok
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'end        :' scanEnd(s)
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit        :' scanLit(s) m.s.tok
    call scanSrc s, 'abcdefdef ?'
    call out 'until cba  :' scanUntil(s, 'cba') m.s.tok
    call out 'until ?qd  :' scanUntil(s, '?qd') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab??cd????gh?ijk'
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab) cd) ) gh) jk) )'
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call tstEnd t
    return
endProcedure tstSb2

tstScan: procedure expose m.
/*
$=/tstScan.1/
    ### start tst tstScan.1 ###########################################
    scan src a034,Und hr123sdfER"st1" 'str2''mit''apo''s' .
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    scan n tok 3: Und key  val .
    scan v tok 1:   key  val .
    scan n tok 10: hr123sdfER key  val .
    scan q tok 5: "st1" key  val st1
    scan v tok 1:   key  val st1
    scan a tok 19: 'str2''mit''apo''s' key  val str2'mit'apo's
    scan v tok 1:   key  val str2'mit'apo's
$/tstScan.1/ */
    call scanIni
    call tst t, 'tstScan.1'
    call tstScan1 'l"litEins"l"litZwei"ndv" "aq1' ,
        ,"a034,Und hr123sdfER""st1"" 'str2''mit''apo''s' "
    call tstEnd t

/*
$=/tstScan.2/
    ### start tst tstScan.2 ###########################################
    scan src a034,Und hr123sdfER"st1" 'str2''mit''apo''s' .
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    scan n tok 3: Und key  val .
    scan b tok 1:   key  val .
    scan n tok 10: hr123sdfER key  val .
    scan " tok 5: "st1" key  val st1
    scan b tok 1:   key  val st1
    scan ' tok 19: 'str2''mit''apo''s' key  val str2'mit'apo's
    scan b tok 1:   key  val str2'mit'apo's
$/tstScan.2/ */
    call tst t, 'tstScan.2'
    call tstScan1 'ndsb1' ,
        ,"a034,Und hr123sdfER""st1"" 'str2''mit''apo''s' "
    call tstEnd t

/*
$=/tstScan.3/
    ### start tst tstScan.3 ###########################################
    scan src a034,'wie 789abc
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    *** err: scanErr ending Apostroph(') missing
    .    e 1: last token  scanPosition 'wie 789abc
    .    e 2: pos 6 in string a034,'wie 789abc
    scan ' tok 1: ' key  val .
    scan n tok 3: wie key  val .
    scan s tok 1:   key  val .
    *** err: scanErr illegal number end after 789
    .    e 1: last token 789 scanPosition abc
    .    e 2: pos 14 in string a034,'wie 789abc
    scan d tok 3: 789 key  val .
    scan n tok 3: abc key  val .
$/tstScan.3/ */
    call tst t, 'tstScan.3'
    call tstScan1 'nds1' ,
        ,"a034,'wie 789abc"
    call tstEnd t

/*
$=/tstScan.4/
    ### start tst tstScan.4 ###########################################
    scan src litEinsefr 23 sdfER'str1'litZwei "str2""mit quo"s .
    scan l tok 7: litEins key  val .
    scan n tok 3: efr key  val .
    scan b tok 1:   key  val .
    scan d tok 2: 23 key  val .
    scan b tok 1:   key  val .
    scan n tok 5: sdfER key  val .
    scan a tok 6: 'str1' key  val str1
    scan l tok 7: litZwei key  val str1
    scan b tok 1:   key  val str1
    scan q tok 15: "str2""mit quo" key  val str2"mit quo
    scan n tok 1: s key  val str2"mit quo
    scan b tok 1:   key  val str2"mit quo
$/tstScan.4/ */
    call tst t, 'tstScan.4'
    call tstScan1 'l"litEins"l"litZwei"ndbaq1' ,
           ,"litEinsefr 23 sdfER'str1'litZwei ""str2""""mit quo""s "
    call tstEnd t

/*
$=/tstScan.5/
    ### start tst tstScan.5 ###########################################
    scan src  aha;+-=f ab=cdEf eF='strIng' .
    scan b tok 1:   key  val .
    scan k tok 4:  no= key aha val def
    scan ; tok 1: ; key aha val def
    scan + tok 1: + key aha val def
    scan - tok 1: - key aha val def
    scan = tok 1: = key aha val def
    scan k tok 4:  no= key f val def
    scan k tok 4: cdEf key ab val cdEf
    scan b tok 1:   key ab val cdEf
    scan k tok 8: 'strIng' key eF val strIng
    scan b tok 1:   key eF val strIng
$/tstScan.5/ */
    call tst t, 'tstScan.5'
    call tstScan1 'bk1'," aha;+-=f ab=cdEf eF='strIng' "
    call tstEnd t
    return
endProcedure tstScan

tstScanRead: procedure expose m.
/*
$=/tstScanRead/
    ### start tst tstScanRead #########################################
    name erste
    space
    name Zeile
    space
    nextLine
    nextLine
    space
    name dritte
    space
    name Zeile
    space
    name schluss
    space
$/tstScanRead/ */
    call scanReadIni
    call tst t, 'tstScanRead'
    b = jBuf('erste Zeile  ',,'  dritte Zeile  schluss  ')
    s = jOpen(jReset0(scanRead(b)), m.j.cRead)
    do while \scanEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanVerify(s, ' ') then call tstOut t, 'space'
        else if scanReadNL(s) then      call tstOut t, 'nextLine'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        end
    call jClose s
    call tstEnd t

/*
$=/tstScanReadMitSpaceLn/
    ### start tst tstScanReadMitSpaceLn ###############################
    name erste
    spaceLn
    name Zeile
    spaceLn
    name dritte
    spaceLn
    name Zeile
    spaceLn
    name schluss
    spaceLn
$/tstScanReadMitSpaceLn/ */
    call tst t, 'tstScanReadMitSpaceLn'
    s = scanReadOpen(scanRead(b))
    do forever
        if scanName(s) then         call out 'name' m.s.tok
        else if scanSpace(s) then call out 'spaceLn'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        else                        leave
        end
    call scanReadClose s
    call tstEnd t

/*
$=/tstScanJRead/
    ### start tst tstScanJRead ########################################
    1 jRead n tok erste val .
    2 jRead s tok   val .
    3 jRead n tok Zeile val .
    4 jRead s tok   val .
    5 jRead n tok dritte val .
    6 jRead s tok   val .
    7 jRead n tok Zeile val .
    8 jRead s tok   val .
    9 jRead n tok schluss val .
    10 jRead s tok   val .
    11 jRead 0 tok 1 val 1
    12 jRead s tok   val 1
    13 jRead + tok + val 1
    14 jRead s tok   val 1
    15 jRead 0 tok 2. val 2..
    16 jRead s tok   val 2..
    17 jRead + tok + val 2..
    18 jRead . tok . val 2..
    19 jRead s tok   val 2..
    20 jRead 0 tok +.3 val +.3
    21 jRead 0 tok -45e-3 val -45E-3
    22 jRead s tok   val -45E-3
    23 jRead " tok "a""b" val a"b
    24 jRead s tok   val a"b
    25 jRead ' tok 'c''d' val c'd
    className 1: ScanRes 18: ScanRes
$/tstScanJRead/ */

    call tst t, 'tstScanJRead'
    call jWrite jOpen(b,'>>'), '1  + 2. +. +.3-45e-3 "a""b"' "'c''d'"
    s = jOpen(jReset0(scanRead(jClose(b))), '<')
    do x=1 while jRead(s)
        v = m.s
        call out x 'jRead' m.v.type 'tok' m.v.tok 'val' m.v.val
        v.x = v
        end
    call jClose s
    call out 'className 1:' className(objClass(v.1)),
                    '18:' className(objClass(v.18))
    call tstEnd t
    return
endProcedure tstScanRead

tstScanUtilInto: procedure expose m.
/*
$=/tstScanUtilIntoL/
TEMPLATE P3
     DSN('DBAF.DA540769.A802A.P00003.BV5I3NRN.REC')
     DISP(OLD,KEEP,KEEP)
TEMPLATE P4
     DSN('DBAF.DA540769.A802A.P00004.BV5I3NTK.REC')
     DISP(OLD,KEEP,KEEP)
LOAD DATA        LOG NO RESUME NO REPLACE COPYDDN(TCOPYD)
           STATISTICS INDEX(ALL) REPORT NO UPDATE ALL
 EBCDIC  CCSID(00500,00000,00000)
 SORTKEYS
  -- ENFORCE NO
  SORTDEVT DISK
  SORTNUM 160
  WORKDDN(TSYUTD,TSOUTD)
  INTO TABLE OA1P.TWB981 PART 1 INDDN TREC134
 WORKDDN(TSYUTS,TSOUTS)
 INTO TABLE "A540769"
   ."TWK802A1"
 PART 00001 INDDN P0
 WHEN(00001:00002) = X'0041'
 ( "DE1"
  POSITION(  00003:00010) DECIMAL
 , "CH2"
  POSITION(  00011:00015) CHAR(00005)
 , "TS3"
  POSITION(  00016:00041) TIMESTAMP EXTERNAL
 , "TI4"
  POSITION(  00042:00049) TIME EXTERNAL
 , "DA5"
  POSITION(  00050:00059) DATE EXTERNAL
 , "IN6"
  POSITION(  00060:00063) INTEGER
 , "RE7"
  POSITION(  00064:00067) FLOAT(21)
 )
 INTO TABLE "A540769"."TWK802A1"
 PART 00002 INDDN P0
 WHEN(00001:00002) = X'0041'
 ( "DE1"
  POSITION(  00003:00010) DECIMAL
 , "CH2"
  POSITION(  00011:00015) CHAR(00005)
 )
 dobido
$/tstScanUtilIntoL/
$=/tstScanUtilInto/
  ### start tst tstScanUtilInto #####################################
  -- 1 scanUtilInto
  . ( "DE1"
  .  POSITION(  00003:00010) DECIMAL
  . , "CH2"
  .  POSITION(  00011:00015) CHAR(00005)
  . , "TS3"
  .  POSITION(  00016:00041) TIMESTAMP EXTERNAL
  . , "TI4"
  .  POSITION(  00042:00049) TIME EXTERNAL
  . , "DA5"
  .  POSITION(  00050:00059) DATE EXTERNAL
  . , "IN6"
  .  POSITION(  00060:00063) INTEGER
  . , "RE7"
  .  POSITION(  00064:00067) FLOAT(21)
  . ) .
  .  -- table OA1P.TWB981 part 00001
  -- 2 scanUtilInto
  . ( "DE1"
  .  POSITION(  00003:00010) DECIMAL
  . , "CH2"
  .  POSITION(  00011:00015) CHAR(00005)
  . ) .
  .  -- table A540769.TWK802A1 part 00002
  -- 3 scanUtilInto
$/tstScanUtilInto/ */

    call scanReadIni
    b = jBuf()
    call mAddst b'.BUF', mapInline('tstScanUtilIntoL')
    call tst t, 'tstScanUtilInto'
    s = jOpen(jReset0(scanUtilOpt(ScanRead(b))), '<')
    do ix=1
        call out '--' ix 'scanUtilInto'
        if \ scanUtilInto(s) then
            leave
        call out '  -- table' m.s.tb 'part' m.s.part
        end
    call tstEnd t
    return
endProcedure tstSCanUtilInto

tstScanWin: procedure expose m.
/*
$=/tstScanWin/
    ### start tst tstScanWin ##########################################
    info 0: last token  scanPosition erste     Zeile                 dr+
    itteZe\npos 1 in line 1: erste     Zeile
    name erste
    spaceNL
    name Zeile
    spaceNL
    name dritteZeeeile
    info 5: last token dritteZeeeile scanPosition    zeile4            +
    .    fuenfueberSechs\npos 1 in line 4:    zeile4
    spaceNL
    name zeile4
    spaceNL
    name fuenfueberSechsUnddSiebenUNDundUndUAcht
    spaceNL
    info 10: last token   scanPosition undZehnueberElfundNochWeiterZwoe+
    lfundim1\npos 9 in line 10:         undZehn
    name undZehnueberElfundNochWeiterZwoelfundim13
    spaceNL
    name Punkt
    infoE 14: last token Punkt scanPosition \natEnd after line 13: im13+
    .      Punkt
$/tstScanWin/ */
    call scanWinIni
    call tst t, 'tstScanWin'
    b = jBuf('?erste     Zeile?',,'?  dritteZeeeile?', '?   zeile4 ',
       ,'?          fuenf?', '?ueberSechsUnddS?', '?iebenUNDundUndU?',
       ,'?Acht           ?', '?               ?', '?        undZehn?',
       ,'?ueberElfundNoch?', '?WeiterZwoelfund?', '?im13      Punkt?')
    s = jOpen(jReset0(scanWin(b, '15@2')), m.j.cRead)
    call tstOut t, 'info 0:' scanInfo(s)
    do sx=1 while \scanEnd(s)
        if scanSpace(s) then call tstOut t, 'spaceNL'
        else if scanName(s) then        call tstOut t, 'name' m.s.tok
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        if sx // 5 = 0 then
            call tstOut t, 'info' sx':' scanInfo(s)
        end
    call tstOut t, 'infoE' sx':' scanInfo(s)
    call tstEnd t

/*
$=/tstScanWinRead/
    ### start tst tstScanWinRead ######################################
    info 0: last token  scanPosition erste     Zeile                z3 +
    com Ze\npos 1 in line 1: erste     Zeile
    name erste
    spaceNL
    name Zeile
    spaceNL
    name z3
    info 5: last token z3 scanPosition  com Zeeeile z4 come4          f+
    uenf\npos 4 in line 3:  z3 com Zeeeile
    spaceNL
    name z4
    spaceNL
    name fuenf
    spaceNL
    info 10: last token   scanPosition com    Sechs  com  sieben   comA+
    cht  com\npos 15 in line 5:     fuenf     c
    name com
    spaceNL
    name Sechs
    spaceNL
    name com
    info 15: last token com scanPosition   sieben   comAcht  com com   +
    . com\npos 2 in line 7: m  sieben   com
    spaceNL
    name sieben
    spaceNL
    name Acht
    spaceNL
    info 20: last token   scanPosition ueberElfundNochWeit com elfundim+
    13\npos 1 in line 11: ueberElfundNoch
    name ueberElfundNochWeit
    spaceNL
    name im13
    spaceNL
    name Punkt
    info 25: last token Punkt scanPosition \natEnd after line 13: im13 +
    .     Punkt
    infoE 26: last token Punkt scanPosition \natEnd after line 13: im13+
    .      Punkt
$/tstScanWinRead/ */
    call tst t, 'tstScanWinRead'
    b = jBuf('?erste     Zeile?',,'? z3 com Zeeeile?', '? z4 come4 ',
       ,'?    fuenf     c?', '?om    Sechs  co?', '?m  sieben   com?',
       ,'?Acht  com com  ?', '?  com          ?', '?  com   undZehn?',
       ,'?ueberElfundNoch?', '?Weit com elfund?', '?im13      Punkt?')
    s = jReset0(scanWin(b, '15@2'))
    call scanOpt s, , , 'com'
    call tstOut t, 'info 0:' scanInfo(jOpen(s, m.j.cRead))
    do sx=1 while \scanEnd(s)
        if scanName(s) then        call tstOut t, 'name' m.s.tok
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        if sx // 5 = 0 then
            call tstOut t, 'info' sx':' scanInfo(s)
        end
    call tstOut t, 'infoE' sx':' scanInfo(s)
    call tstEnd t
    return
endProcedure tstScanWin

tstScanSqlStmt: procedure expose m.
/*
$=/tstScanSqlStmt/
    ### start tst tstScanSqlStmt ######################################
    cmd1 select   current time                 stamp from s.1
    cmd2 update ";--""'/*"
    cmd3 delete '*/''"' / 3 - 1
    cmd4 terminator test
    cmd5 und so
    cmd6 term: ist
    cmd7 term>  in com nein >
    cmd8 .
$/tstScanSqlStmt/ */
    call pipeIni
    call scanWinIni
    call tst t, 'tstScanSqlStmt'
    b = jBuf('select -- /* c1', ' /* c1 */ current/* c2 " '' ',
       ,'c3"', '  c4   */time', 'stamp-- c5', 'from s.1; /* c6 */ ;' ,
       ,';update ";--""''/*";;       del123',
       , 'ete ''*/''''"'' / 3 - 1  -- c7', '/*c8 */   ' ,
       , ';terminator test; ','terminator|; und--  ', 'so| | |',
       , 'term: --#SET TERMINATOR : oder', 'ist: ',
       , 'term> /*--#SET TERMINATOR > oder', ' */ in com nein >:')
    call scanWinOpen scanSqlStmtOpt(scanWinReset(tstJcat, b, 30), ';')
    call scanSqlOpt tstJcat
    do sx=1 until nx = ''
        nx = scanSqlStmt(tstJCat)
        call tstOut t, 'cmd'sx nx
        end
    call scanReadCLose tstJCat
    call tstEnd t
/*
$=/tstScanSqlStmtRdr/
    ### start tst tstScanSqlStmtRdr ###################################
    cmd1 select   current time                 stamp from s.1
    cmd2 update ";--""'/*"
    cmd3 delete '*/''"' / 3 - 1
    cmd4 terminator test
    cmd5 und so
    cmd6 term: ist
    cmd7 term>  in com nein >
$/tstScanSqlStmtRdr/ */
    call tst t, 'tstScanSqlStmtRdr'
    r = jOpen(ScanSqlStmtRdr(b, 30), '<')
    do sx=1 while jRead(r)
        call tstOut t, 'cmd'sx m.r
        end
    call jClose r
    call tstEnd t
    return
endProcedure tstScanSqlStmt

tstScanSql: procedure expose m.
    call scanWinIni
/*
$=/tstScanSqlId/
    ### start tst tstScanSqlId ########################################
    sqlId ABC
    spaceNL
    sqlId AB__345EF
    spaceNL
$/tstScanSqlId/ */
    call tst t, 'tstScanSqlId'
    b = jBuf('abc  --  kommentar', right('ab_', 72), '_345ef-- kom')
    s = scanOpen(scanSqlReset(tstScn, b))
    do sx=1 while \scanEnd(s)
        if scanSqlID(s) then       call tstOut t, 'sqlId' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlDelimited/
    ### start tst tstScanSqlDelimited #################################
    sqlDeId ABC
    spaceNL
    sqlDeId AB_3F
    spaceNL
    sqlDeId abc
    spaceNL
    sqlDeId ab_Ef
    spaceNL
$/tstScanSqlDelimited/ */
    call tst t, 'tstScanSqlDelimited'
    b = jBuf('abc  --  kommentar',,'  -- ',,right('ab_', 72),'3F-- kom',
           , '"abc"  --  ko', ' -- ',,right('"ab_', 72),'Ef"-- kom')
    s = scanOpen(scanSqlReset(tstScn, b))
    do sx=1 while \scanEnd(s)
        if scanSqlDeID(s) then       call tstOut t, 'sqlDeId' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlQualified/
    ### start tst tstScanSqlQualified #################################
    sqlQuId ABC 1 ABC
    sqlQuId AB_3F 1 AB_3F
    sqlQuId abc 1 abc
    sqlQuId ab_Ef 1 ab_Ef
    sqlQuId EINS.Zwei.DREI 3 EINS
    sqlQuId vi er.fu  enf 2 vi er
$/tstScanSqlQualified/ */
    call tst t, 'tstScanSqlQualified'
    b = jBuf('abc  --  kommentar',,'  -- ',,right('ab_', 72),'3F-- kom',
           , '"abc"  --  ko', ' -- ',right('"ab_', 72),'Ef"-- kom',
           , 'eins."Zwei', '" -- com', ' . -- com', '  -- com',
           , 'drei -- ko', '"vi er"."fu  enf   " -- co')
    s = scanOpen(scanSqlReset(tstScn, b))
    do sx=1 while \scanEnd(s)
        if scanSqlQuID(s) then
            call tstOut t, 'sqlQuId' m.s.val m.s.val.0 m.s.val.1
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlNum/
    ### start tst tstScanSqlNum #######################################
    sqlNum 1
    spaceNL
    sqlNum 2..
    spaceNL
    sqlNum .3
    spaceNL
    sqlNum 4.5
    spaceNL
    sqlNum +6
    spaceNL
    sqlNum +7.03
    spaceNL
    sqlNum -8
    spaceNL
    sqlNum -.9
    spaceNL
    sqlNum 1E2
    spaceNL
    sqlNum -2.E-2
    spaceNL
    sqlNum +.3E+3
    spaceNL
$/tstScanSqlNum/ */
    call tst t, 'tstScanSqlNum'
    b = jBuf('1 2. .3 4.5 +6 + --kom', , ' -- com ', , ' 7.03 -8 -  .9',
             '1e2 - 2.e-2 + .3e+3')
    s = scanOpen(scanSqlReset(tstScn, b))
    do sx=1 while \scanEnd(s)
        if scanSqlNum(s) then
            call tstOut t, 'sqlNum' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlNumUnit/
    ### start tst tstScanSqlNumUnit ###################################
    sqlNumUnit 1 KB
    spaceNL
    sqlNumUnit .3 MB
    sqlNumUnit .5
    sqlNumUnit +6.E-5 B
    spaceNL
    sqlNumUnit -7
    char *
    spaceNL
    sqlNumUnit -.8
    char T
    char B
    spaceNL
    *** err: scanErr scanSqlNumUnit after +9. bad unit TB
    .    e 1: last token Tb scanPosition .
    .    e 2: pos 41 in line 1: 1 kb .3mB.5 +   6.e-5B -7* -.8 TB + 9.T+
    b
    sqlNumUnit +9..
    spaceNL
$/tstScanSqlNumUnit/ */
    call tst t, 'tstScanSqlNumUnit'
    b = jBuf('1 kb .3mB.5 +   6.e-5B -7* -.8 TB + 9.Tb')
    s = scanOpen(scanSqlReset(tstScn, b))
    do sx=1 while \scanEnd(s)
        if scanSqlNumUnit(s, 0, 'B KB MB') then
            call tstOut t, 'sqlNumUnit' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else if scanChar(s ,1) then call tstOut t, 'char' m.s.tok
        else call scanErr s, 'cannot scan'
        end
    call tstEnd t
    return
endProcedure tstScanSql

/*--- one single test scan with lines to scan in stem ln -------------*/
tstScan1:
parse arg classs, ln
    call tstOut t, 'scan src' ln
    call scanSbReset scanOpt(s), ln
    m.s.key = ''
    m.s.val = ''
    do forever
        x = tstScanType(s, classs)
        if x == '' then
           leave
        call tstOut t, 'scan' x 'tok' length(m.s.tok)':' m.s.tok ,
                  'key' m.s.key 'val' m.s.val
        end
    return
endProcedure tstScan1

tstScanType: procedure expose m.
parse arg s, opt
    cx = 1
    a2 = ''
    res = 0
    do while cx <= length(opt)
        f = substr(opt, cx, 1)
        cx = cx + 1
        if pos(substr(opt, cx, 1), "'""") > 0 then do
            m.tstScanType.src = opt
            m.tstScanType.pos = cx
            call scanString tstScanType
            a2 = m.tstScanType.val
            cx = m.tstScanType.pos
            end
        if      f == 'a' then
            res = scanString(s, "'")
        else if f == 'b' then
            res = scanSpace(s)
        else if f == 'c' then
            res = scanChar(s, a2)
        else if f == 'd' then
            res = scanNat(s, a2)
        else if f == 'k' then
            res = scanKeyValue(s, 'def')
        else if f == 'l' then
            res = scanLit(s, a2)
        else if f == 'q' then
            res = scanString(s, '"')
        else if f == 'v' then
            res = scanVerify(s, a2)
        else if f == 'w' then
            res = scanWord(s)
        else if f == 'y' then
            res = scanVerify(s, a2, 'm')
        if res then
            return f
        end
    return scanType(s)
endProcedure tstScanType


/****** tstData begin *************************************************/
tstData: procedure expose m.
parse arg ty 2 le 'n' nu, l, r, num
    call scanIni
    abs = abs(num)
    if nu \== '' then do
        if abs // 5 = 0 then
            return 'null' || (abs % 5 // nu + 1)
        end
    if ty = 'c' then do
        if le = '' then
            le = 8
        le = abs // le + 1
        if r = '' then
            r = '+'
        return left(l || num || r, le, right(r, 1))
        end
    if pos(ty, 'ief') < 1 then
        call err 'bad type' ty
    nn = abs
    if abbrev(num, '-') | abbrev(num, '+') then
        parse var num si 2 nn
    else
        si = ''
    if ty == 'e' then
        ex = 'e' || left('-', abs // 2) || (abs // 15)
    else
        ex = ''
    if le \== '' then
        nn = right(nn, abs // max(1, le - length(si||ex)) + 1, 1)
    if ty \== 'i' & (abs // 4) \= 0 & length(nn) > 1 then
            nn = overlay('.', nn, length(nn) - abs // length(nn))
    return si || nn || ex
endProcedure tstData

tstDataClassFo: procedure expose m.
parse arg flds
    ty = ''
    do fx=1 by 2 to words(flds)
        if word(flds, fx) = '.' then
            ty = ty', v'
        else
            ty = ty', f' word(flds, fx) 'v'
        end
    t = classNew('n* tstData u' substr(ty, 2))
    fo = oNew(m.t.name)
    ff = oFlds(fo)
    do fx=1 to m.ff.0
        f = fo || left('.', m.ff.fx \== '') || m.ff.fx
        m.f = word(flds, 2*fx)
        end
    return fo
endProcedure tstDataClassFo


tstDataClassOut: procedure expose m.
parse arg flds, f, t
    fo = tstDataClassFo(flds)
    ff = oFlds(fo)
    do x=f to t
        o = oCopy(fo)
        do fx=1 to m.ff.0
            f = o || left('.', m.ff.fx \== '') || m.ff.fx
            m.f = tstData(m.f, m.ff.fx, '+'m.ff.fx'+', x)
            end
        call out o
        end
    return
endProcedure tstDataClassOut
/****** tst **********************************************************
      test infrastructure
***********************************************************************/
/*--- migrate all compares to new ones:
        tstCI input compare
        tstCO ouput migrated compares
        tstCIO input and output -------------------------------------*/
tstUtTime: procedure expose m.
    say 'begin' utTime()  sysvar('sysnode')
    do 3000000
       end
    call sleep 1
    say 'end  ' utTime()
return

tstUtc2d: procedure expose m.
/*
$=/tstUtc2d/
    ### start tst tstUtc2d ############################################
    .             ff            255
    .           ffff          65535
    .          10000          65536          65536 = 1 * 16 ** 4
    .          10001          65537
    .         ffffff       16777215
    .        1000000       16777216       16777216 = 1 * 16 ** 6
    .        1000001       16777217
    .        20000FF       33554687
    .      100000000     4294967296     4294967296 = 1 * 16 ** 8
    .      300000000    12884901888    12884901888 = 3 * 16 ** 8
    .      3020000EF    12918456559
$/tstUtc2d/
*/
    numeric digits 33
    call tst t, 'tstUtc2d'
    all = 'ff ffff 10000 10001 ffffff 1000000 1000001 20000FF' ,
           '100000000 300000000 3020000EF'
    do ax = 1 to words(all)
        a = word(all, ax)
        if substr(a, 2) = 0 then
            b = right(left(a, 1) * 16 ** (length(a)-1), 15) ,
              '=' left(a, 1) '* 16 **' (length(a)-1)
        else
            b = ''
        call tstout t, right(a, 15)right(utc2d(x2c(a)), 15)b
        end
    call tstEnd t
    return
endProcedure tstUtc2d

tstCI: procedure expose m.
parse arg m, nm
    m.m.CIO = 0
    signal tstCIwork
tstCIO: procedure expose m.
parse arg m, nm
    m.m.CIO = 1
tstCIwork:
    m.m.name = nm
    m.m.cmp.1 = left('### start tst' nm '', 67, '#')

    do ix=2 to arg()-1
        m.m.cmp.ix = arg(ix+1)
        end
    m.m.cmp.0 = ix-1
    if m.m.CIO then
        call tstCO m
    return

tstCO: procedure expose m.
parse arg m
    call tst2dpSay m.m.name, m'.CMP', 68
    return
/*--- initialise m as tester with name nm
        use inline input nm as compare lines -----------------------*/
tstReset: procedure expose m.
parse arg m, nm
    call tstIni
    m.m.name = nm
    m.m.inIx  = 0
    m.m.out.0 = 0
    m.m.err   = 0
    m.err.count = 0
    m.tst.act = m
    if \ datatype(m.m.trans.0, 'n') then
        m.m.trans.0 = 0
    m.m.trans.old = m.m.trans.0
    return
endProcedure tstReset

tst: procedure expose m.
parse arg m, nm, cmpSt
    call tstReset m, nm
    m.tst.tests = m.tst.tests+1
    if cmpSt == '' then do
        cmpSt = mCut(t'.CMP', 0)
        call tst4dp cmpSt, mapInline(nm)
        end
    m.m.cmp = cmpSt
    m.m.moreOutOk = 0
    call mAdd mCut(m'.IN', 0), 'tst in line 1 eins ,'    ,
                     , 'tst in line 2 zwei ;   ' ,
                     , 'tst in line 3 drei .schluss.'
    call tstOut m, left('### start tst' nm '', 67, '#')
    call errReset 'h', 'return tstErrHandler(ggTxt)'
    m.m.errCleanup = m.err.cleanup
    if m.tst.ini.j \== 1 then do
        m.tst_m = m
/*      call err implement outDest 'i', 'call tstOut' quote(m)', msg'
*/      end
    else do
        drop m.tst_m
        call oMutatName m, 'Tst'
        call oMutatName m'.IN', 'Tst'
        m.m.jReading = 1
        m.m.jWriting = 1
        m.m.jUsers = 0
        m.m.in.jReading = 1
        m.m.in.jWriting = 1
        m.m.in.jUsers = 0
        if m.tst.ini.e \== 1 then do
            m.m.oldJin = m.j.in
            m.m.oldOut = m.j.out
            m.j.in = m'.IN'
            m.j.out = m
            end
        else do
            if m.pipe.0 <> 2 then
                call tstErr m, 'm.pipe.0' m.pipe.0 '<> 2'
            call pipe '+Ff', m , m'.IN'
            end
        end
    return m
endProcedure tst

tstEnd: procedure expose m.
parse arg m, opt opt2
    cmp = m.m.cmp
    m.m.jReading = 0
    m.m.jWriting = 0
    drop m.tst_m
    if m.tst.ini.j == 1 then do
        m.m.jReading = 0
        m.m.jWriting = 0
           if m.tst.ini.e \== 1 then do
            m.j.in = m.m.oldJin
            m.j.out = m.m.oldOut
            end
        else do
            if m.j.in \== m'.IN' | m.j.out \== m then
                call tstErr m, m.j.in '\==' m '|' m.j.out '\==' m
            call pipe '-'
            if m.pipe.0 <> 2 then
                call tstErr m, 'm.pipe.0' m.pipe.0 '<> 2'
            end
        end
    if m.m.err = 0 then
        if m.m.errCleanup \= m.err.cleanup then
            call tstErr m, 'err.Cleanup' m.err.cleanup '<> old',
                        m.m.errCleanup
    if m.m.out.0 \= m.cmp.0 & \ (m.m.moreOutOk,
            &  m.m.out.0 > m.cmp.0) then do
        call tstErr m, 'old' m.cmp.0 'lines \= new' m.m.out.0
        do nx = m.m.out.0 + 1 to min(m.m.out.0+10, m.cmp.0)
            say 'old -  ' m.cmp.nx
            end
        end
    call errReset 'h'
    m.tst.act = ''
    soll = 0
    if opt = 'err' then do
        soll = opt2
        if m.m.err \= soll then
            call err soll 'errors expected, but got' m.m.err
        end
    if m.m.err \= soll then do
        say 'new lines:' (m.m.out.0 - 1)
           call tst2dpSay m.m.name, m'.OUT', 68
        end
    say left('###' m.m.name 'end with' m.m.err 'errors ', 67, '#')

    if 1 & m.m.err \= soll then
        call err 'dying because of' m.m.err 'errors'
    m.m.trans.0 = m.m.trans.old
    return
endProcedure tstEnd

tst2dp: procedure expose m.
parse arg st, dp, ml
    dx = m.dp.0
    do sx=1 to m.st.0
        li = m.st.sx
        cx = 1
        do until cx > length(li)
            c = substr(li, cx, 1)
            ou = left('.', strip(c) == '' | pos(c, '.+') > 0)
            ex = min(length(li),cx+ml-length(ou)-2)
            ou = ou || substr(li, cx, ex+1-cx)
            dx = dx + 1
            c = right(ou, 1)
            if ex < length(li) then
                m.dp.dx = ou || '+'
            else if strip(c) == '' | pos(c, '.+') > 0 then
                m.dp.dx = ou || '.'
            else
                m.dp.dx = ou
            cx = ex +1
            end
        end
    m.dp.0 = dx
    return
endProcedure tst2dp

tst2dpSay: procedure expose m.
parse arg name, st, ml
    say '$=/'name'/'
    call tst2dp st, mCut('TST.TMP', 0), 68
    do nx=1 to m.tst.tmp.0
           say '   ' m.tst.tmp.nx
           end
     say '$/'name'/'
return tst2dpSay

tst4dp: procedure expose m.
parse arg st, dp
    sx = m.st.0
    inData = 0
    data = ''
    do dx=1 to m.dp.0
        li = strip(m.dp.dx)
        if pos(left(li, 1), '.+') > 0 then
         li = substr(li, 2)
        if right(li, 1) == '+' then do
            inData = 1
            data = data || left(li, length(li)-1)
            iterate
            end
        if right(li, 1) == '.' then
            li = left(li, length(li)-1)
        sx = sx + 1
        m.st.sx = repAll(data || li, '$ä', '/*', '$ö', '*/')
        inData = 0
        data = ''
        end
    m.st.0 = sx
    if inData then
        call err 'end inData'
    return
endProcedure tst4dp

/*--- write to test: say lines and compare them ----------------------*/
tstOut: procedure expose m.
parse arg m, arg
    do tx=m.m.trans.0 by -1 to 1
        arg = repAll(arg, word(m.m.trans.tx, 1),
            , subword(m.m.trans.tx, 2))
        end
    call mAdd m'.OUT', arg
    nx = m.m.out.0
    cmp = m.m.cmp
    c = m.cmp.nx
    if nx > m.cmp.0 then do
        if nx = m.cmp.0+1 & \ m.m.moreOutOK then
            call tstErr m, 'more new Lines' nx
        end
    else if c \== arg then do
        do cx=1 to min(length(c), length(arg)) ,
             while substr(c, cx, 1) == substr(arg, cx, 1)
             end
         msg = 'old line' nx '<> new overnext, firstDiff' cx',',
                 'len old' length(c)', new' length(arg)

        if cx > 10 then
            msg = overlay('|', msg, cx-10)
        call tstErr m, msg
        say c
        end
    say arg
    return 0
endProcedure tstOut

tstWrite: procedure expose m.
parse arg m, var
    cl = objClass(var, '')
    if cl == '' then do
        if var == '' then
            call tstOut t, 'tstR: @ obj null'
        else
            call tstOut m, var
  /*        call tstOut m, 'out:' var   */
  /*        call tstOut t, 'no class for' var 'in tstWriteO|'  */
        end
    else if abbrev(var, m.o_escW) then do
        call tstOut t, o2String(var)
        end
    else if cl == m.class_V then do
        call tstOut t, m.var
        end
    else if oKindOf(var, 'JRW') then do
        call tstOut t, 'tstWriteO kindOf JRW jWriteNow begin <<<'
        call jWriteNow m, var
        call tstOut t, 'tstWriteO kindOf JRW jWriteNow end   >>>'
        end
    else if oKindOf(var, 'ORun') then do
        call tstOut t, 'tstWriteO kindOf ORun oRun begin <<<'
        call oRun var
        call tstOut t, 'tstWriteO kindOf ORun oRun end   >>>'
        end
    else do
        do tx=m.m.trans.0 by -1 to 1 ,
                while word(m.m.trans.tx, 1) \== var
            end
        if tx < 1 then
            call mAdd M'.TRANS', var 'tstWriteoV' || (m.m.trans.0+1)
        call classOut , var, 'tstR: '
        end
    return
endProcedure tstWrite

tstRead: procedure expose m.
parse arg mP
    if right(mP, 3) == '.IN' then
       m = left(mP, length(mP)-3)
    else
        call err 'tstRead bad m' mP
    ix = m.m.inIx + 1
    m.m.inIx = ix
    if ix <= m.m.in.0 then do
        call tstOut m, '#jIn' ix'#' m.m.in.ix
        m.mP = m.m.in.ix
        return 1
        end
    call tstOut m, '#jIn eof' ix'#'
    return 0
endProcedure tstRead

tstFilename: procedure expose m.
parse arg suf, opt
    if m.err.os == 'TSO' then do
        dsn = dsn2jcl('~tmp.tst.'suf)
        if opt = 'r' then do
            if sysDsn("'"dsn"'") \== 'DATASET NOT FOUND' then
                call adrTso "delete '"dsn"'"
            call csiOpen 'TST.CSI', dsn'.**'
            do while csiNext('TST.CSI', 'TST.FINA')
                say 'deleting csiNext' m.tst.fina
                call adrTso "delete '"m.tst.fina"'"
                end
            end
        return dsn
        end
    else if m.err.os == 'LINUX' then do
        if abbrev(suf, '/') then
            fn = suf
        else
            fn = .Stream%%new('~/tmp/tst/'suf)%%qualify /* full path */
        cx = lastPos('/', fn)
        if cx > 0 then do
            dir = left(fn, cx-1)
            if \sysIsFileDirectory(dir) then
                call adrSh "mkdir -p" dir
            if \sysIsFileDirectory(dir) then
                call err 'tstFileName could not create dir' dir
            end
        if opt \= 'r' then
            nop
        else if sysIsFile(fn) then
            call sysFileDelete fn
        else if sysIsFileDirectory(fn) then
            call adrSh 'rm -r' fn
        return fn
        end
    else
        call err 'tstFilename does not implement os' m.err.os
endProcedure tstFilename

/*--- say total errors and fail if not zero --------------------------*/
tstTotal: procedure expose m.
    say '######'
    say '######'
    say '######' m.tst.tests 'tests with' ,
                 m.tst.err 'errors in' m.tst.errNames
    say '######'
    say '######'
    if m.tst.err \== 0 then
        call err m.tst.err 'errors total'
    return
endProcedure tstTotal

/*--- test err: message, count it and continue -----------------------*/
tstErr: procedure expose m.
parse arg m, msg
    say '### error' msg
    m.m.err = m.m.err + 1
    m.tst.err = m.tst.err + 1
    nm = m.m.name
    if wordPos(nm, m.tst.errNames) < 1 then
        m.tst.errNames = m.tst.errNames nm
    return 0
endProcedure tstErr

/*--- tstErrHandler: intercept errors --------------------------------*/
tstErrHandler: procedure expose m.
parse arg ggTxt
    m = m.tst.act
    if m == '' then
        call err ggTxt
    m.err.count = m.err.count + 1
    call splitNl err, errMsg(' }'ggTxt)
    call tstOut m.tst.act, '*** err:' m.err.1
    do x=2 to m.err.0
        call tstOut m, '    e' (x-1)':' m.err.x
        end
    return 0
endSubroutine tstErrHandler

tstTrc: procedure expose m.
parse arg msg
    m.tst.trc = m.tst.trc + 1
    say 'tstTrc' m.tst.trc msg
    return m.tst.trc
endProcedure tstTrc

/*--- tstIni: global initialization ----------------------------------*/
tstIni: procedure expose m.
    if m.tst.ini \== 1 then do
        m.tst.ini = 1
        call mapIni
        m.tst.err = 0
        m.tst.trc = 0
        m.tst.errNames = ''
        m.tst.tests = 0
        m.tst.act = ''
        end
    if m.tst.ini.j \== 1 & m.j.ini == 1 then do
        m.tst.ini.j = 1
          call classNew 'n Tst u JRW', 'm',
             , "jRead return tstRead(m)",
             , "jWrite call tstWrite m, line"
        end
    if m.tst.ini.e \== 1 & m.pipe.ini == 1 then do
        m.tst.ini.e = 1
        end
    return
endProcedure tstIni
/* copy tstAll end   **************************************************/
/* copy wshCopy begin ************************************************/
wshIni: procedure expose m.
    call compIni
    call sqlIni
    call scanWinIni
    return
endProcedure wshIni
/* copy wshCopy end   ************************************************/
/* copy db2Util begin ************************************************/
tstDb2Ut: procedure expose m.
/*
$=/tstDb2Ut/
$/tstDb2Ut/
*/
    call pipeIni
    call scanIni
    call tst t, 'tstDb2Ut'
    call mAdd mCut(t'.IN', 0), '   template old ,'    ,
                     , 'LOAD DATA INDDN oldDD ' ,
                     , '( cols  )'
call db2UtilPunch 'rep iDsn=DSN.INPUT.UNL'
call tstEnd t
return
endProcedure tstDb2Ut
/* ???????????? achtung nicht fertig |
          Idee: allgemein Punch Umformungs Utility
              aber man müsste wohl auf scan Util umstellen
                  und abstürzen wenn man etwas nicht versteht
          GrundGerüst von cadb2 umgebaut
????????????????? */

db2UtilPunch: procedure expose m.
parse upper arg args
    call scanSrc scanOpt(s), args
    a.rep = 1
    a.tb = ''
    a.trunc = 0
    a.iDD = ''
    a.iDSN = ''
    do while scanKeyValue(scanSkip(s), 1)
        ky = m.s.key
        say '????ky' ky m.s.val
        if wordPos(ky, 'REP TB TRUNC IDD IDSN') < 1 then
            call scanErr s, 'bad key' ky
        a.ky = m.s.val
        end
    if a.iDSN \== '' then do
        if a.iDD == '' then
            a.iDD = 'IDSN'
        call out '  TEMPLATE' a.iDD 'DSN('a.iDsn')'
        end
    do while in() & word(m.in, 1) <> 'LOAD'
        call out m.in
        end
    ll = space(m.in, 1)
    if \ abbrev(ll, 'LOAD DATA ') then
        call err 'bad load line:' m.in
    call out subword(m.in, 1, 2) 'LOG NO'
    if abbrev(ll, 'LOAD DATA INDDN ') then
        call db2UtilPunchInDDn word(ll, 4)
    else if \ abbrev(ll, 'LOAD DATA LOG ') then
        call err 'bad load line' ix':' m.i.ix
    if a.rep then
        call out '    STATISTICS INDEX(ALL) UPDATE ALL'
    call out '    DISCARDS 1'
    call out '    ERRDDN   TERRD'
    call out '    MAPDDN   TMAPD '
    call out '    WORKDDN  (TSYUTD,TSOUTD) '
    call mAdd o, '  SORTDEVT DISK '
    do ix=ix+1 to m.i.0
        if pos('CHAR(', m.i.ix) > 0 then
            call mAdd o, strip(m.i.ix, 't') 'TRUNCATE'
        else if word(m.i.ix, 1) word(m.i.ix, 3) == 'PART INDDN' then
            call mAdd o, m.i.ix,
                       , '  RESUME NO REPLACE COPYDDN(TCOPYD)' ,
                       , '  DISCARDDN TDISC '
        else
            call mAdd o, m.i.ix
        end
    call writeDsn oDsn ':~'iDsn, 'M.O.', , 1
    return
endProcedure db2UtilPunch

db2UtilPunchInDDn:
parse arg inDDn
     if a.iDD == '' then
         ll =  '    INDDN' inDDn
     else
         ll =  '    INDDN' a.iDD
     if a.rep then
         call out ll 'RESUME NO REPLACE COPYDDN(TCOPYD)'
     else
         call out ll 'RESUME YES'
     call out  '    DISCARDDN TDISC'
     return
endSubroutine db2UtilPunchInDDn
/* copy db2Util end   ************************************************/
/* copy time begin ****************************************************
 11.12.14 wk: added lrsn2uniq
 11.05.13 wk: numeric digits transparent: in jeder Procedure drin
              time2jul, tst externalisiert
**********************************************************************/
/*--- read timeZoneOffset and leapSeconds registers
        and set variables for uniq ----------------------------------*/
timeReadCvt: procedure expose m.
parse arg debug
    numeric digits 15
    /* offsets documented in z/OS Data Areas  Vol.1 */
    cvtOH      = '00000010'          /* cvt control block Address */
    cvtext2O   = x2d('00000560') /* offset  to extension 2    */
    cvtldtoO   = x2d('00000038') /* offset to timezone    */
    cvtlsoO    = x2d('00000050') /* offset to leapSeconds */

    /* CVT CB        address               + extention2   */
    cvtExt2A       = C2D(STORAGE(cvtOH,4)) + cvtext2O
    /* cvtLdto timeZone              address +offset      */
    m.time_Zone    = c2d(STORAGE(d2x(cvtext2A+cvtldtoO, 8), 8))
    m.time_StckUnit = 1e-6 / 256 / 16
    /* cvtLso LeapSecs               address +offset      */
    m.time_Leap    = c2d(STORAGE(d2x(cvtext2A+cvtlsoO, 8), 8))
    m.time_UQDigits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345678'
                         /* find lrsn of day 0 */
    m.time_UQZero = x2d(timeGmt2Lrsn('2004-12-31-00.00.22.000000')) ,
                   % 64 * 64    /* 0 out last 6 bits  */
    if debug == 1 then do
      say 'stckUnit          =' m.time_StckUnit
      say 'timeLeap          =' d2x(m.time_Leap,16) '=' m.time_Leap ,
                   '=' format(m.time_Leap * m.time_StckUnit,9,3) 'secs'
      say 'timeZone          =' d2x(m.time_Zone,16) '=' m.time_Zone,
                   '=' format(m.time_Zone * m.time_StckUnit,6,3) 'secs'
      say "cvtext2_adr       =" d2x(cvtExt2A, 8)
      say 'timeUQZero        =' m.time_UQZero
      say 'timeUQDigis       =' ,
                    length(m.time_UQDigits) 'digits' m.time_UQDigits
    end
    m.time_ReadCvt = 1
    return
endSubroutine timeReadCvt

timestampParse:
    parse arg yyyy'-'mo'-'dd'-'hh'.'mm'.'ss'.'ffffff
    return

/*--- GMT timestamp yyyy-mm.... -> stck value char(8)
         BLSUXTID is described in z/OS V1R7.0 MVS IPCS Customization
         BLSUXTID format : mo/dd/yyyy hh:mm:ss.ffffff ---------------*/
timeGmt2Stck: procedure expose m.
    parse arg tst
    call timestampParse tst
    tDate = mo'/'dd'/'yyyy' 'hh':'mm':'ss'.'ffffff
    ACC=left('', 8, '00'x)
    ADDRESS LINKPGM "BLSUXTID TDATE ACC"
    RETURN acc
endProcedure timeGmt2Stck

/*--- GMT timestamp yyyy-mm.... -> stck value in hex(16) ------------*/
timeGmt2LRSN: procedure expose m.
    return c2x(left(timeGmt2Stck(arg(1)), 6))
endProcedure timeGmt2LRSN

/*--- LZT (Locale Zurich Tst -> stck value in hex(16) ---------------*/
timeLZT2LRSN: procedure expose m.
parse arg tst
    numeric digits 23
    if m.time_ReadCvt \== 1 then
        call timeReadCvt
    return left(d2x(c2d(timeGmt2Stck(tst)) ,
                     - m.time_Zone + m.time_Leap, 16), 12)
endProcedure timeLZT2LRSN
/*--- conversion from Stck Clock Value to GMT Timestamp
        BLSUXTOD is described in z/OS V1R7.0 MVS IPCS Customization
        input -> + leapseconds -> output ----------------------------*/
timeStck2Gmt: PROCEDURE expose m.
parse arg stck
  stck = left(stck, 8, '00'x)
  TDATE = COPIES('0' , 26)
  ADDRESS LINKPGM "BLSUXTOD stck TDATE"
  /* return format   : mo/dd/yyyy hh:mm:ss.ffffff */
  /* Timestamp format: yyyy-mm-dd-hh.mm.ss.ffffff */
  parse var TDATE mo '/' dd '/' yyyy hh ':' mm ':' ss '.' ffffff
RETURN yyyy'-'mo'-'dd'-'hh'.'mm'.'ss'.'ffffff
endProcedure timeStck2Gmt

/*--- conversion from Lrsn Clock Value to GMT Timestamp -------------*/
timeLrsn2Gmt:
    return timeStck2Gmt(x2c(arg(1)))
endProcedure timeLrsn2Gmt

/*--- conversion from Lrsn Clock Value to LZT Local Zurich Time -----*/
timeLrsn2LZT: procedure expose m.
parse arg lrsn
    numeric digits 23
    if m.time_ReadCvt \== 1 then
        call timeReadCvt
    return timeStck2Gmt(d2c(x2d(left(lrsn, 16, 0)) ,
                           + m.time_Zone-m.time_Leap))
endProcedure timeLrsn2LZT

/*--- timestamp to julian --------------------------------------------*/
time2jul: procedure expose m.
parse arg yyyy '-' mm '-' dd '-'
        /* date function cannot convert to julian, only from julian
            ==> guess a julian <= the correct and
                try the next values
        */
    j = trunc((mm-1) * 29.5) + dd
    yy = right(yyyy, 2)
    do j=j by 1
        j = right(j, 3, 0)

        d = date('s', yy || j, 'j')
        if substr(d, 3) = yy || mm || dd then
            return yy || j
        end
    return
endProcedure time2jul
/* convert a lrsn to the uniq variable ********************************/
timeLrsn2uniq: procedure expose uniqZero uniqDigits debug
parse arg lrsn
    /* unique are bits 0:41 of the TodClock value
              minus 31.12.2004 represented
              by base 35 by 'ABC...YZ01..8'
    */
    if m.time_ReadCvt \== 1 then
        call timeReadCvt
    lrsn = left(lrsn, 12, 0)
    numeric digits 15
    diff = x2d(lrsn) - m.time_UQZero
    if diff < 0 then
        return'< 2005'
    return right(i2q(diff % 64, m.time_UQDigits), 8, 'A')
endProcedure timeLrsn2Uniq

/* convert a uniq variable to lrsn ************************************/
timeUniq2lrsn: procedure expose m.
parse arg uniq
    if m.time_ReadCvt \== 1 then
        call timeReadCvt
    numeric digits 15
    u1 = q2i(left(uniq, 8, 'A'), m.time_UQDigits) * 64
    lrsn = right(d2x(u1 + m.time_UQZero), 12, 0)
    return lrsn
endProcedure uniq2lrsn
/*--- translate a number in q-system to decimal
       arg digits givs the digits corresponding to 012.. in the q sysem
       q = length(digits) --------------------------------------------*/
q2i: procedure expose m.
parse arg v, digits
    b = length(digits)
    i = 0
    do x = 1 to length(v)
        q = substr(v, x, 1)
        r = pos(q, digits)
        if r < 1 then
            call err 'bad digit' q 'in' v 'valid digits' digits
        i = i * b + r - 1
        end
    return i
endProcedure q2i
/*--- translate a decimal number to q-system - inverse of q2i --------*/
i2q: procedure expose m.
parse arg i, digits
    if i = 0 then
        return left(digits, 1)
    b = length(digits)
    v = ''
    do while i > 0
        v = substr(digits, 1 + (i // b), 1) || v
        i = i % b
        end
    return v
endProcedure i2q
/* copy time end -----------------------------------------------------*/
/* copy sort begin ****************************************************/
sortComparator: procedure expose m.
parse arg cmp, le, ri
    if cmp == '' then
        cmp = '<<='
    if length(cmp) < 6 then
        m.sort_comparator = 'cmp =' le cmp ri
    else if pos(';', cmp) < 1 then
        m.sort_comparator = "aLe =" le"; aRi =" ri"; cmp =" cmp
    else
        m.sort_comparator = "aLe =" le "; aRi =" ri";" cmp
    return
endProcedure sort

sortWords: procedure expose m.
parse arg wrds, cmp
    if words(wrds) <= 1 then
        return strip(wrds)
    m.sort_ii.0 = words(wrds)
    do sx=1 to m.sort_ii.0
        m.sort_ii.sx = word(wrds, sx)
        end
    call sort sort_ii, sort_oo, cmp
    r = m.sort_oo.1
    do sx=2 to m.sort_oo.0
        r = r m.sort_oo.sx
        end
    return r
endProcedure sortWords

sortWordsQ: procedure expose m.
parse arg wrds, cmp
    call sortComparator cmp, 'word(le, lx)', 'word(ri, rx)'
    return strip(sortWord1(wrds))
endProcedure sortWordsQ

sortWord1: procedure expose m.
parse arg wrds
    if words(wrds) <= 1 then
        return wrds
    h = words(wrds) % 2
    le = sortWord1(subWord(wrds, 1, h))
    ri = sortWord1(subWord(wrds, h+1))
    lx = 1
    rx = 1
    res = ''
    do forever
        interpret m.sort_comparator
        if cmp then do
            res = res word(le, lx)
            if lx >= words(le) then
                return res subword(ri, rx)
            lx = lx + 1
            end
        else do
            res = res word(ri, rx)
            if rx >= words(ri) then
                return res subword(le, lx)
            rx = rx + 1
            end
        end
endProcedure sortWord1

sort: procedure expose m.
parse arg i, o, cmp
    call sortComparator cmp, 'm.l.l0', 'm.r.r0'
    call sort1 i, 1, m.i.0, o, 1, sort_work, 1
    m.o.0 = m.i.0
    return
endProcedure sort

sort1: procedure expose m.
parse arg i, i0, le, o, o0, w, w0
    if le <= 3 then do
        if le = 3 then do
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, w, w0
            call sortMerge i, i0+2, i0+3, w, w0, w0+2, o, o0
            end
        else if le = 2 then
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, o, o0
        else if le = 1 then
            m.o.o0 = m.i.i0
        return
        end
    h = (le + 1) % 2
    call sort1 i, i0,   h,    o, o0+le-h, w, w0
    call sort1 i, i0+h, le-h, w, w0,      o, o0
    call sortMerge o, o0+le-h, o0+le, w, w0, w0+le-h, o, o0
    return
endProcedure sort1

sortMerge: procedure expose m.
parse arg l, l0, le, r, r0, re, o, o0
    do while l0 < le & r0 < re
        interpret m.sort_comparator
        if cmp then do
            m.o.o0 = m.l.l0
            l0 = l0 + 1
            end
        else do
            m.o.o0 = m.r.r0
            r0 = r0 + 1
            end
        o0 = o0 + 1
        end
    do while l0 < le
        m.o.o0 = m.l.l0
        l0 = l0 + 1
        o0 = o0 + 1
        end
    do while r0 < re
        m.o.o0 = m.r.r0
        r0 = r0 + 1
        o0 = o0 + 1
        end
    return
endProcedure sortMerge
/* copy sort end   ****************************************************/
/* copy match begin ***************************************************/
/*--- 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
    if symbol('m.match_m.mask') == 'VAR' then
        interpret m.match_m.mask
    else
        interpret matchGen('MATCH_M.'mask, mask, 'm')
endProcedure match

matchGG: procedure expose m.
parse arg wert, cd, vars
    interpret cd
endProcedure matchGG

matchVars: procedure expose m.
parse arg wert, mask, vars
    if symbol('m.match_v.mask') == 'VAR' then
        interpret m.match_v.mask
    else
        interpret matchGen('MATCH_V.'mask, mask, 'v')
endProcedure match

matchRep: procedure expose m.
parse arg wert, mask, mOut
    vars = 'MATCH_VV'
    mm = mask'\>'mOut
    if symbol('m.match_r.mm') == 'VAR' then
        interpret m.match_r.mm
    else
        interpret matchGen('MATCH_R.'mm, mask, 'r', mOut)
endProcedure matchRep

matchGen: procedure expose m.
parse arg m, mask, opt, mOut
    a = matchScan(match_sM, mask)
    if symbol('m.match_g') \== 'VAR' then
        m.match_g = 0
    if opt \== 'r' then do
        r = matchgenMat(a, opt, 1, m.a.0, 0)
        end
    else do
        m.match_g = m.match_g + 1
        sub = 'MATCH_G'm.match_g
        m.sub = matchgenMat(a, 'v', 1, m.a.0, 0)
        o = matchScan(match_sO, mOut)
        r = matchGenRep(o, m.a.wildC)
        r = 'if matchGG(wert, m.'sub', vars) then return' r';' ,
            'else return "";'
        end
    m.m = r
    return r
endProcedure matchGen

matchScan: procedure expose m.
parse arg a, mask, opt
    s = match_scan
    call scanSrc s, mask
    ax = 0
    vx = 0
    m.a.wildC = ''
    do forever
        if scanUntil(s, '*?&\') then do
            if m.a.ax == 'c' then do
                m.a.ax.val = m.a.ax.val || m.s.tok
                end
            else do
                ax = ax + 1
                m.a.ax = 'c'
                m.a.ax.val = m.s.tok
                end
            end
        else if scanChar(s, 1) then do
            if pos(m.s.tok, '*?') > 0 then do
                ax = ax + 1
                vx = vx + 1
                m.a.ax = m.s.tok
                m.a.ax.ref = vx
                m.a.wildC = m.a.wildC || m.s.tok
                end
            else if m.s.tok == '\' then do
                call scanChar s, 1
                if pos(m.s.tok, '\*?&') < 1 then
                    return scanErr(s, 'bad char after \')
                if abbrev(m.a.ax, 'c') then
                    m.a.ax.val = m.a.ax.val || m.s.tok
                else do
                    ax = ax + 1
                    m.a.ax = 'c'
                    m.a.ax.val = m.s.tok
                    end
                end
            else if m.s.tok == '&' then do
                if opt \== 'r' then
                    call scanErr s, '& in input'
                if \ scanChar(s,1) | pos(m.s.tok, 'ms123456789')<1 then
                    call scanErr s, 'bad & name' m.s.tok
                ax = ax + 1
                m.a.ax = '&'
                m.a.ax.ref = m.s.tok
                end
            else
                call scanErr s, 'bad char 1 after until'
            end
        else
            leave
        end
    m.a.0 = ax
    if vx \== length(m.a.wildC) then
        call scanErr 'vars' m.a.wildC 'mismatches' vx
    return a
endProcedure matchScan

matchGenMat: procedure expose m.
parse arg a, opt, fx, tx, minLL
    ml = 0
    if fx == 1 then do
        do ax=1 to m.a.0
            if m.a.ax == '?' then
               ml = ml + 1
            else if m.a.ax == 'c' then
               ml = ml + length(m.a.ax.val)
            m.a.minLen.ax = ml
            end
        end
    r = ''
    ret1 = ''
    ret1After = ''
    lO = 0
    do fy=fx to tx
        if m.a.fy == 'c' then do
            r = r 'if substr(wert,' (1+lO)
            if fy < m.a.0 then
                r = r',' length(m.a.fy.val)
            r = r') \==' quote(m.a.fy.val, "'") 'then return 0;'
            lO = lO + length(m.a.fy.val)
            end
        else if m.a.fy == '?' then do
            lO = lO + 1
            if opt == 'v' then
                ret1 = ret1 'm.vars.'m.a.fy.ref ,
                        '= substr(wert,' lO', 1);'
            end
        else if m.a.fy == '*' then
            leave
        else
            call err 'bad match ast' a'.'fy m.a.fy
        end
    rO = 0
    do ty=tx by -1 to fy
        if m.a.ty == 'c' then do
            rO = rO + length(m.a.ty.val)
            r = r 'if substr(wert, length(wert) -' (rO - 1)',' ,
                  length(m.a.ty.val)')' ,
                  '\==' quote(m.a.ty.val, "'") 'then return 0;'
            end
        else if m.a.ty == '?' then do
            rO = rO + 1
            if opt == 'v' then
                ret1 = ret1 'm.vars.'m.a.fy.ref ,
                        '= substr(wert, length(wert) -' (rO-1)', 1);'
            end
        else if m.a.ty ==  '*' then
            leave
        else
            call err 'bad match ast' a'.'fy m.a.fy
        end
    if fy > ty then do /* every thing is handled with fix len */
        if fx = tx & abbrev(m.a.fx, 'c') then
            r = 'if wert \==' quote(m.a.fx.val, "'") ,
                               'then return 0;'
        else
            r = 'if length(wert) \==' (lO + rO) 'then return 0;' r
        end
    else do
        myMiLe = m.a.minLen.ty
        if fy > 1 then do
            fq = fy -1
            myMiLe = myMiLe - m.a.minLen.fq
            end
        if minLL < myMiLe then
            r = 'if length(wert) <' myMiLe 'then return 0;' r
        if fy = ty & m.a.fy == '*' then     /* single * */
            ret1  = ret1 'm.vars.'m.a.fy.ref ,
                 '= substr(wert,' (1+lO)', length(wert) -' (lO+rO)');'
        else if fy < ty & abbrev(m.a.fy, '*') ,
                        & abbrev(m.a.ty, '*') then do
                                /* several variable length parts */
            suMiLe = m.a.minLen.ty - m.a.minLen.fy
            m.match_g = m.match_g + 1
            sub = 'MATCH_G'm.match_g
            m.sub = matchGenMat(a, opt, fy+1, ty, suMiLe)
            if rO = 0 then
                subV = 'substr(wert, lx)'
            else do
                r = r 'wSub = left(wert, length(wert) -' rO');'
                subV = 'substr(wSub, lx)'
                end
            r = r 'do lx = length(wert) -' (suMiLe+rO-1) ,
                       'by -1 to' (lO+1)';' ,
                       'if \ matchGG('subV', m.'sub', vars) then' ,
                            'iterate;'
            ret1  = ret1 'm.vars.'m.a.fy.ref '= substr(wert,' (lO+1) ,
                     ||  ', lx -' (lO+1)');'
            ret1After = 'end; return 0;'
            end
        else
            call err 'matchGenMat bad case' a'.'fy m.a.fy a'.'ty m.a.ty
        end
    if opt == 'v' & fx == 1 then do
        if r <> '' then
           r = 'm.vars.0 = -9;' r
        ret1 = ret1 'm.vars.0 =' length(m.a.wildC)';'
        end
    r = r ret1 'return 1;' ret1After
    return r
endProcedure matchGenMat

matchGenRep: procedure expose m.
parse arg o, wildC
    xQ = 0
    xS = 0
    do ox=1 to m.o.0
        if m.o.ox == '?' then do
             xQ = pos('?', wildC, xQ+1)
             if xQ < 1 then
                 call err 'unmatchted ?' ox
             m.o.ox.re2 = xQ
             end
        else if m.o.ox == '*' then do
             xS = pos('*', wildC, xS+1)
             if xS < 1 then
                 call err 'unmatchted *' ox
             m.o.ox.re2 = xS
             end
        else if m.o.ox == '&' & m.o.ox.ref >> '0' then do
            if m.o.ox.ref > length(wildC) then
                 call err '&'m.o.ox.ref 'but wildcards' wildC
            xQ = m.o.ox.ref
            xS = xQ
            m.o.ox.re2 = xQ
            end
        end
    r = ''
    do ox=1 to m.o.0
        if abbrev(m.o.ox, 'c') then
            r = r '||' quote(m.o.ox.val, "'")
        else if m.o.ox == '&' & m.o.ox.re2 == 's' then
            r = r '|| wert'
        else if m.o.ox == '&' & m.o.ox.re2 == 'm' then
            r = r '||' quote(mask, "'")
        else if pos(m.o.ox, '*?&') > 0 then
            r = r '|| m.vars.'m.o.ox.re2
        end
    if r=='' then
        return "''"
    else
        return substr(r, 5)
endProcedure matchGenRep

/* copy match end *****************************************************/
/* copy comp begin *****************************************************
    the shell compiler
         syntax and semantics of the shell language see javaDoc
***********************************************************************/
/***** initialisation *************************************************/
/*--- module initialisation ------------------------------------------*/
compIni: procedure expose m.
    if m.compIni = 1 then
        return
    m.compIni = 1
    call pipeIni
    call scanReadIni
    cc = classNew('n Compiler u')
    call mNewArea 'COMP.AST', '='

    m.comp_chOp   = '.-<@|?%^'
    m.comp_chKind = '.-=#@:%^'
    m.comp_chKiNO = '=:#'
    m.comp_chKiNBOE = '=<#:' /* nonBLock only expression not Primary*/
    call mPut 'COMP.INFO..', "object"
    call mPut 'COMP.INFO.-', "string"
    call mPut 'COMP.INFO.=', "skeleton"
    call mPut 'COMP.INFO.#', "text"
    call mPut 'COMP.INFO.@', "rexxShell"
    call mPut 'COMP.INFO.:', "pureShell"

    m.comp_chDol = '$'
    m.comp_chSpa = ' 'x2c('09')
    call mPut 'COMP.EXTYPE.b', m.comp_chDol'{}'       /* braces */
    call mPut 'COMP.EXTYPE.d', m.comp_chDol            /* data */
    call mPut 'COMP.EXTYPE.s', m.comp_chDol            /* strip */
    call mPut 'COMP.EXTYPE.w', m.comp_chDol||m.comp_chSpa /* word */

    m.comp.idChars = m.ut_alfNum'@_'
    m.comp.wCatC = 'compile'
    m.comp.wCatO = 'out'
    m.comp.wCatS = 'do withNew with for forWith ct proc table'
    return
endProcedure compIni

compReset: procedure expose m.
parse arg m
    m.m.scan = m'.scan'
    return m
endProcedure compReset

/*--- constructor of Compiler ----------------------------------------*/
comp: procedure expose m.
parse arg src
    nn = oNew('Compiler')
    m.nn.cmpRdr = in2File(src)
    return nn
endProcedure comp

/**** user interface **************************************************/
/*--- compile and run ------------------------------------------------*/
compRun: procedure expose m.
parse arg spec, inO, ouO, infoA
    cmp = comp(inO)
    r = compile(cmp, spec)
    if infoA \== '' then
        m.infoA = 'run'
    if ouO \== '' then
        call pipe '+F', ouO
    call oRun r
    if ouO \== '' then
        call pipe '-'
    return 0
endProcedure compRun

/*--- compile the source and return an oRunner with the code ---------*/
compile: procedure expose m.
parse arg m, spec
    call compReset m
    s = m.m.scan
    if spec = '' then
        spec = '@'
    else
        spec = strip(spec, 'l')
    if m.m.cmpRdr == '' then do
        call scanSbReset scanOpt(scanSbReset(s, spec), , '0123456789')
        end
    else do
        call scanReadOpen scanReadReset(s, m.m.cmpRdr, , '0123456789'),
                          , 1, spec
        end
    m.m.out = ''
    m.m.end = ''
    res = compAst(m, '¢')
    eOld = m.err.count
    do forever
        one = ''
        if pos(scanLook(s, 1), m.comp_chKind'*') > 0 then do
            call scanChar s, 1
            ki = m.s.tok
            one = compileOne(m, ki)
            end
        else if \ scanName(s) then do
            call scanErr s, 'kind or hook expected after $#'
            end
        else if m.s.tok == 'out' then do
            m.m.out = scanPos(s)
            leave
            end
        else if m.s.tok == 'end' then do
            if m.m.end = '' then
                m.m.end = scanPos(s)
            one = compileOne(m)
            end
        else if m.s.tok == 'version' then do
            call scanSpace s
            if \ scanWord(s) | m.s.tok \== '2.0' then
                call scanErr s, 'only version 2.0 is supported'
            call scanNl s, 1
            end
        else do
            one = compileOne(m, , m.s.tok)
            end
        if one \== '' then
            call mAdd res, one
        if scanEnd(s) then
            leave
        if \ scanLit(s, '$#') then
            return scanErr(s, m.comp.info.ki "expected: compile",
                 ki "stopped before end of input")
        end
    if m.m.cmpRdr \== '' then
        call scanReadClose s
    if m.err.count <> eOld then
        return ''
    return oRunner(compAst2Rx(m, ';', res))
endProcedure compile

/*--- compile or use hook for one part from spec or input -----------*/
compileOne: procedure expose m.
parse arg m, ki, hook
    s = m.m.scan
    call compSpComment m
    if ki == '*' | m.m.end \== '' then do
        do until scanLook(s, 2) == '$#' | scanEnd(s)
            call scanNl s, 1
            end
        return ''
        end
    if ki \== '' then do
        call scanNl s
        return compUnit(m, ki, '$#')
        end
    say 'interpreting hook' hook':' strip(scanLook(s))
    interpret return 'wshHook_'hook'(m)'
endProcedure compileOne

/*--- parse the whole syntax of a unit ------------------------------*/
compUnit: procedure expose m.
parse arg m, ki, stopper
    s = m.m.scan
    if pos(ki, m.comp_chKind) < 1 then
        return scanErr(s, 'bad kind' ki 'in compUnit(...'stopper')')
    else if ki <> '#' then do
        a = compAst(m, '¢')
        do forever
            one = compPipe(m, ki)
            if one \== '' then
             call mAdd a, one
            if \ scanLit(m.m.scan, '$;', '$<>') then
                   return a
            end
        end
    else do
        res = compAST(m, '¢')
        call scanChar s
        if verify(m.s.tok, m.comp_chSpa) > 0 then
            call mAdd res, compAstAddOp(m,
                , compAst(m, '=', strip(m.s.tok, 't')), '$')
        do while scanNL(s, 1) & \ abbrev(m.s.src, stopper)
            call mAdd res, compAstAddOp(m,
                , compAst(m, '=', strip(m.s.src, 't')), '$')
            end
        return res
        end
endProcedure compUnit

/*--- compile an expression, type d=data, b=block w=word, s=strip ---*/
compExpr: procedure expose m.
parse arg m, type, ki
    s = m.m.scan
    if symbol('m.comp.exType.type') \== 'VAR' then
        call err s, 'bad type' type 'in compExpr'
    if ki == '#' then do
        call scanChar(s)
        if type == 's' then
            res = compAst(m, '=', strip(m.s.tok))
        else
            res = compAst(m, '=', , m.s.tok)
        res = compAST(m, '-', , res)
        m.res.containsC = 1
        m.res.containsD = 1
        return res
        end
    else if ki == '%' | ki == '^' then do
        call compSpComment m
        vr = compVar(m, left('c', ki == '^'))
        if vr == '' then
            return ''
        if m.vr.var == 'c' then
            res = compAst(m, 'M')
        else
            res = compAst(m, ki, , compASTAddOp(m, vr, '&'))
        call compSpComment m
        ex = compOpBE(m, '=')
        if ex \== '' then
            call mAdd res, ex
        call compSpComment m
        m.res.containsC = 1
        m.res.containsD = 1
        return res
        end

    if length(ki) \== 1 | pos(ki, '.-=@') < 1 then
        return scanErr(s, 'bad kind' ki 'in compExpr')
    res = compAST(m, translate(ki, '-', '='))
    m.res.containsC = 0
    astKi = translate(ki, 'os=c', '.-=@')
    laPrim = 0
    gotTxt = 0
    if pos(type, 'sb') > 0 then
        m.res.containsC = compSpComment(m) >= 2
    do forever
        if scanVerify(s, m.comp.exType.type, 'm') then do
            one = compAST(m, astKi, m.s.tok)
            if verify(m.s.tok, m.comp_chSpa) > 0 then
                gotTxt = 1
            end
        else do
            old = scanPos(s)
            if \ scanLit(s, m.comp_chDol) then
                leave

            if pos(scanLook(s, 1), '.-') > 0 then
                one = compCheckNN(m, compOpBE(m, , 1, 0),
                   , 'primary block or expression expected')
            else
                one = compPrimary(m)
            if one = '' then do
                call scanSetPos s, old
                leave
                end
            laPrim = m.res.0 + 1
            end
        call mAdd res, one
        if compComment(m) then
            m.res.containsC = 1
        end
    if pos(type, 'bs') > 0 then do
        do rx=m.res.0 by -1 to laPrim+1
            one = m.res.rx
            m.one.text = strip(m.one.text, 't')
            if length(m.one.text) <> 0 then
                leave
            call mFree one
            end
        m.res.0 = rx
        end
    m.res.containsD = laPrim > 0 | gotTxt
    return compAstFree0(res, '')
endProcedure compExpr

/*--- compile a primary and return code ------------------------------*/
compPrimary: procedure expose m.
parse arg m, ops
    s = m.m.scan
    if scanString(s) then
        return compASTAddOp(m, compAST(m, '=', m.s.val), ops)
    r = compVar(m, left('c', right(ops, 1) == '^'))
    if r == '' then
        return ''
    if m.r.var \== 'c' then
         return compASTAddOp(m, compAst(m, '&', m.r.var, r), ops)
    else
         return compASTAddOp(m, compAst(m, 'M'),
                              , left(ops, length(ops)-1))
endProcedure compPrimary

/*--- oPBE ops (primary or block or expression)
       oDef = default Kind, oPre = opPrefix,
       uniq=1 extract unique, uniq='<' prefix < ----------------------*/
compOpBE: procedure expose m.
parse arg m, oDef, uniq, withEx
    s = m.m.scan
    old = scanPos(s)
    if \ scanVerify(s, m.comp_chOp || m.comp_chKiNO) then
        op = oDef
    else
        op = left('<', uniq == '<') || m.s.tok
    x = verify(op, '%^', 'm')
    if x > 0 & x < length(op) then
        call scanErr s, 'old syntax? run not at end'
    kx = verify(op, m.comp_chKiNO, 'm')
    if kx = length(op) then
        nop
    else if kx \== 0 then
        call scanErr s, 'kind' substr(op, kx, 1) 'not after ops'
    else if pos(right(op, 1), m.comp_chKind) > 0 then
        nop
    else if right(op, 1) == '<' then
        op = op'='
    else
        call scanErr s, 'no kind after ops' op
    if pos(scanLook(s, 1), '/¢') > 0 then do
        if uniq == 1 & length(op) == 1 then
            if op == '.' then
                op = '|.'
            else if pos(op, '-@<') > 0 then
                op = op || op
        return compBlock(m, op)
        end
    if compSpComment(m) == 0 ,
        & pos(right(op, 1), m.comp_chKiNBOE) <= 0 then
        return compPrimary(m, op)
    if withEx \== 0 then do
        res = compExpr(m, 's', right(op, 1))
        if pos(right(op, 1), m.comp_chKind) < 1 then
            op = left(op, kx-1)
        if res \== '' then
            return compASTAddOp(m, res, op)
        end
    call scanSetPos s, old
    return ''
endProcedure compOPBE

/*--- compile a pipe and return code ---------------------------------*/
compPipe: procedure expose m.
parse arg m, ki
    s = m.m.scan
    res = compAST(m, 'P', ' ', '', '')
    do forever
        one = compExprStmts(m, ki)
        if one \== '' then do
            if m.res.0 > 2 then
                call scanErr s, '$| before statements needed'
            call mAdd res, one
            end
        pre = left(m.comp_chDol, scanLit(s, m.comp_chDol))
        if scanLook(s, 2) == '<>' then
            leave
        if scanLit(s, '<') then do
            if m.res.2 == '' then
                m.res.2 = compAst(m, '.')
            else
                call mAdd m.res.2, compAst(m, 'o', ', ')
            call mAdd m.res.2, compOpBE(m, '<', '<')
            m.res.text = m.res.text'f'
            end
        else if scanLit(s, '>>', '>') then do
            if m.res.1 <> '' then
                call scanErr s, 'duplicate output'
            m.res.text = if(m.s.tok == '>', 'F', 'A') ,
                ||substr(m.res.text, 2)
            m.res.1 = compOpBE(m, '<', '<')
            end
        else if scanLit(s, '|') then do
            if m.res.0 < 3 then
                call scanErr s, 'stmts expected before |'
            call compSpNlComment m
            call mAdd res, compCheckNE(m, compExprStmts(m, ki),
                , 'stmts or expressions after | expected')
            end
        else
            leave
        end
    call scanBack s, pre
    if m.res.0 > 3 | m.res.1 \== '' | m.res.2 \== '' then
        return res
    one = if(m.res.0 = 3, m.res.3)
    call mFree res
    return one
endProcedure compPipe

/*--- compile expressions and stmts ---------------------------------*/
compExprStmts: procedure expose m.
parse arg m, ki
    s = m.m.scan
    res = compAst(m, '¢')
       nlLe = 0 /* sophisticated logic using left and right NLs*/
    tb = ''
    do forever
        if tb \== '' then do
            call compSpComment m
            px = m.s.pos
            e1 = compExpr(m, 'w', '=')
            if e1 \== '' then do
                aa = compAst(m, '¢')
                fx = 0
                do until e1 == ''
                    do fx=fx+1 to m.tb.0 until px < m.ff.end
                        ff = m.tb.fx
                        end
                    if fx > m.tb.0 then
                        call scanErr s, 'right of all table fields'
                    if m.s.pos <= m.ff.pos then
                        call scanErr s, 'before table field' m.ff.name
                    call mAdd aa, compAst(m, 'A', ,
                        , compAst(m, '=', m.ff.name), e1)
                    call compSpComment m
                    px = m.s.pos
                    e1 = compExpr(m, 'w', '=')
                    end
                call mAdd res, compAst(m, 'F', 'with',
                    , compAst(m, 'o', "oNew('"m.tb.class"')"),
                    , aa, compAst(m, '*', '$.'))
                end
            nlRi = scanNL(s)
            end
        else if ki == ':' then do
            call compSpNlComment m, '*'
            nlRi = 0
            end
        else if ki == '@' then do
            call compSpNlComment m
            one = compExpr(m, 's', ki)
            if one == '' then
                nlRi = 0
            else if m.one.0 < 1 then
                call scanErr s, 'assert not empty' m.one.0
            else do
                do forever /* scan all continued rexx lines */
                    nlRi = 1
                    la = m.one.0
                    la = m.one.la
                    if m.la.kind \== 'c' then
                        leave
                    m.la.text = strip(m.la.text, 't')
                    if right(m.la.text, 1) \== ',' then
                        leave
                    m.la.text = strip(left(m.la.text,
                            , length(m.la.text)-1), 't')' '
                    call compSpNlComment m
                    cont = compExpr(m, 's', '@')
                    if cont == '' | m.cont.kind \== m.one.kind then
                        call scanErr s, 'bad rexx continuation'
                    call mAddSt one, cont
                    call mFree cont
                    end
                call mAdd res, one
                end
            end
        else if ki == '%' | ki == '^' then do
            do cc=0 while compSpNlComment(m)
                end
            one = compExpr(m, 's', ki)
            nlRi = one \== ''
            if nlRi then do
                if ki == '^' then
                    one = compAstAddOp(m, one, '$')
                call mAdd res, one
                end
            end
        else do
            do cc=0 while compComment(m)
                end
            one = compExpr(m, 'd', ki)
            nlRi = scanNL(s)
            if one == '' then do
                if nlLe & nlRi & cc < 1 then
                    call mAdd res,compAstAddOp(m,compAst(m,'='), '$')
                end
            else if m.one.containsD | (nlLe & nlRi,
                      & \ (cc > 0 | m.one.containsC)) then do
                call mAdd res, compAstAddOp(m, one, '$')
                end
            else do
                call mFree one
                end
            end
        nlLe = nlRi
        if \ nlRi then do
            one = compStmt(m)
            if one = '' then
                return compAstFree0(res)
            if m.one.kind == 'T' then
                tb = one
            else
                call mAdd res, one
            end
        end
endProcedure compExprStmts

/*--- compile a single statement -------------------------------------*/
compStmt: procedure expose m.
parse arg m
    s = m.m.scan
    res = compAss(m)
    if res \== '' then
        return res
    pre = ''
    old = scanPos(s)
    if scanLit(s,m.comp_chDol'$',m.comp_chDol'@',m.comp_chDol,'@') then
        pre = m.s.tok
    if pre == m.comp_chDol'$' then
        return  compAstAddOp(m, compCheckNN(m, compOpBE(m,'=', 1),
            , 'block or expression expected after $$'), '$')
    wCat = compName(m, 'sv')
    fu = m.s.tok

    if right(pre, 1) == '@' & wCat \== 's' then do
        if wCat == 'v' then
            return compAstAddOp(m, compAst(m, '=', fu), '%.&')
        if compSpNlComment(m) then
                return compAstAddOp(m,
                , compCheckNE(m, compExpr(m, 's','@'),
                , 'block/primary/expr after $@'), ';')

        one = compCheckNN(m, compOpBE(m, '@') ,
                 ,  'primary, block or expression')
        if one \== '' then
            return one
        call scanSetPos s, old
        return ''
        end

    if wordPos(fu, 'for forWith with withNew') > 0 then do
        res = compAst(m, 'F', fu)
        call compSpComment m
        if fu \== 'with' then do
            b = compVar(m, left('o', fu == 'withNew'))
            end
        else do
            b = compAss(m, 'o')
            if b == '' then
                b = compCheckNE(m, compExpr(m, 's', '.'),
                , "assignment or expression after with")
            end
        call compSpComment m
        st = compCheckNN(m, compStmt(m), "var? statement after" fu)
        if b = '' then do
            b = compBlockName(m, st)
            if b == '' then
                call scanErr s, "variable or named block after" fu
            b = compAst(m, '=', b)
            end
        call mAdd res, b, st
        if fu == 'withNew' then do
            vars = substr(compAstAssignedVars(m.res.2), 3)
            m.res.class = classNew('n* CompTable u' vars)
            m.res.1 = compAst(m, 'A', ,m.res.1, compAst(m, 'o',
                , "oNew('"m.res.class"')"))
            end
        if abbrev(fu, 'with') then do
            r1 = m.res.1
            if m.r1.kind == 'A' then do
                a1 = m.r1.1
                if m.a1.var == 'o' then do
                    call mAdd res, compAstAddOp(m, m.r1.2, '$.')

                    m.res.1 = m.r1.2
                    call mFree a1
                    end
                end
            end
        return res
        end

    if fu == 'do' then do
        call compSpComment m
        pre = compExpr(m, 's', '@')
        res = compAst(m, 'D', , pre)
        p1 = m.pre.1
        if pre \== '' then do
            txt = ''
            do px=1 to m.pre.0
                pC = m.pre.px
                if m.pC.kind \== 'c' then
                    leave
                txt = txt m.pC.text
                cx = pos('=', txt)
                if cx > 0 then do
                    m.res.text = strip(left(txt, cx-1))
                    leave
                    end
                end
            end
        call compSpComment m
        call mAdd res, compCheckNN(m, compStmt(m), 'stmt after do')
        return res
        end

    if fu == 'ct' then do
        call compSpComment m
        return compAst(m, 'C', , compCheckNN(m, compStmt(m),
            , 'ct statement'));
        end
    if fu == 'proc' then do
           call compSpComment m
        nm = ''
        if compName(m, 'v') == 'v' then do
            nm = m.s.tok
            call compSpComment m
            end
        st = compCheckNN(m, compStmt(m), 'proc statement')
        if nm == '' then do
            nm = compBlockName(m, st)
            if nm == '' then
                call scanErr s, 'var or namedBlock expected after proc'
            end
        return compAst(m, 'B', '', compAst(m, '=', nm), st)
        end
    if fu == 'table' then do
        call compSpComment m
        if scanNl(s) then
            call compSpComment m
        res = compAst(m, 'T', 'c')
        flds = ''
        do while compName(m, 'v') == 'v'
            f1 = compAst(m, 'T')
            m.f1.end = m.s.pos
            m.f1.pos = m.s.pos - length(m.s.tok)
            m.f1.name = m.s.tok
            m.f1.text = 'f' m.f1.name m.f1.pos m.f1.end
            call mAdd res, f1
            flds = flds', f' m.s.tok 'v'
            call compSpComment m
            end
        if \ scanNl(s) then
            call scanErr s, 'name or nl after table expected'
        if m.res.0 < 1 then
            call scanErr s, 'no names in table'
        m.res.class = classNew('n* CompTable u' substr(flds, 3))
        m.res.text = 'c' cl
        return res
        end
    call scanBack s, pre || fu
    return ''
endProcedure compStmt

compBlockName: procedure expose m.
parse arg m, a
    a1 = m.a.1
    if m.a.kind == '¢' then
         return m.a.text
    else if m.a.kind == '*' & m.a1.kind == '¢' then
        return m.a1.text
    return ''
endProcedure compBlockName

compVar: procedure expose m.
parse arg m, vk
    s = m.m.scan
    ty = compName(m, 'v' || vk)
    if ty \== '' then do
        r = compAst(m, '=', m.s.tok)
        m.r.var = ty
        return r
        end
    if \ scanLit(s, '{') then
        return ''
    call scanLit s, '?', '>'
    f = m.s.tok
    r = compCheckNE(m, compExpr(m, 'b', '='), 'var name')
    if \scanLit(s, '}') then
        call scanErr s, 'closing } missing after {'
    m.r.var = f
    return r
endProcedure compVar

compAss: procedure expose m.
parse arg m, vk
    s = m.m.scan
    call scanLit s, m.comp_chDol'=', '='
    pr = m.s.tok
    if pr \== '' then
        call compSpComment m
    v = compVar(m, vk)
    if v \== '' then do
        call compSpComment m
        if \ scanLit(s, '=') then
            call scanErr s, '= expected in assignment after' pr 'var'
        end
    else if pr == '' then
        return ''
    else
        oldInfo = scanInfo(s)
    eb = compCheckNE(m, compOpBE(m, '=', 1),
        , 'block or expression in assignment after' pr)
    if m.eb.kind == '¢' then
        eb = compAstAddOp(m, eb, '-')
    if v == '' then do
        v = compBlockName(m, eb)
        if v == '' then
            call scanErr s, 'var or namedBlock expected',
                    'in assignment after' pr, oldInfo
        else
            v = compAst(m, '=', v)
        end
    return compAst(m, 'A', , v, eb)
endProcedure compAss

/*--- block deals with the correct kind and operators
      the content is parsed by compUnit ------------------------------*/
compBlock: procedure expose m.
parse arg m, ops
    s = m.m.scan
    if \ scanLit(s, '¢', '/') then
        return ''
    start = m.s.tok
    if ops == '' | pos(right(ops, 1), m.comp_chKind) < 1 then
        return scanErr(s, 'bad kind' ops 'for block')
    ki = right(ops, 1)
    ops = left(ops, length(ops)-1)
    starter = start
    if start == '¢' then
        stopper = m.comp_chDol'!'
    else do
        call scanVerify s, '/', 'm'
        starter = '/'m.s.tok'/'
        stopper = m.comp_chDol || starter
        if \scanLit(s, '/') then
            call scanErr s, 'ending / after stopper' stopper 'expected'
        end
    res = compUnit(m, ki, stopper)
    if \ scanLit(s, stopper, substr(stopper, 2)) then
           call scanErr s, 'ending' stopper 'expected after' starter
    if abbrev(starter, '/') then
        m.res.text = substr(starter, 2, length(starter)-2)
    return compAstAddOp(m, res, ops)
endProcedure compBlock

/**** lexicals ********************************************************/
/*--- skip a comment. return 0 if there is none ----------------------*/
compComment: procedure expose m.
parse arg m
    s = m.m.scan
    got = 0
    do forever
        if scanLit(s, m.comp_chDol'**') then
            m.s.pos = 1 + length(m.s.src) /* before next nl */
        else if scanLit(s, m.comp_chDol'*+') then
            call scanNL s, 1
        else if scanLit(s, m.comp_chDol'*(') then do
            do forever
                if scanVerify(s, m.comp_chDol, 'm') then iterate
                if scanNL(s) then iterate
                if compComment(m) then iterate
                if \ scanLit(s, m.comp_chDol) then
                    call scanErr s, 'source end in comment'
                if scanLit(s, '*)') then
                    return 1
                if scanLit(s, m.comp_chDol) then iterate
                if scanString(s) then iterate
                end
            end
        else
            return got
        got = 1
        end
endProcedure compComment

/*--- skip spaces and comments ---------------------------------------*/
compSpComment: procedure expose m.
parse arg m, xtra
    s = m.m.scan
    got = 0
    do forever
        if scanVerify(s, m.comp_chSpa) then
            got = bitOr(got, 1)
        else if compComment(m) then
            got = bitOr(got, 2)
        else if xtra == '' then
            return got
        else if \ scanLit(s, xtra) then
            return got
        else do
            got = bitOr(got, 4)
            m.s.pos = 1+length(m.s.src)
            end
        end
endProcedure compSpComment

/*--- skip spaces, NLs and comments ----------------------------------*/
compSpNlComment: procedure expose m.
parse arg m, xtra
    found = 0
    do forever
        if compSpComment(m, xtra) < 1 then
            if \ scanNL(m.m.scan) then
             return found
        found = 1
        end
endProcedure compSpNlComment
/*--- scan a name in one of the categories
        v=var, c=compile, s=stmt ----------------------------------*/
compName: procedure expose m.
parse arg m, cats
    s = m.m.scan
    if \ scanName(s) then
        return ''
    if wordPos(m.s.tok, m.comp.wCatS) > 0 then do
        if pos('s', cats) > 0 then
            return 's'
        end
    else if wordPos(m.s.tok, m.comp.wCatC) > 0 then do
        if pos('c', cats) > 0 then
            return 'c'
        end
    else if wordPos(m.s.tok, m.comp.wCatO) > 0 then do
        if pos('o', cats) > 0 then
            return 'o'
        end
    else if pos('v', cats) > 0 then do
        return 'v'
        end
    call scanBack s, m.s.tok
    return ''
endProcedure compName


compSpNlComment: procedure expose m.
/**** small helper routines ******************************************/
/*--- if va == null then issue an error with msg --------------------*/
compCheckNN: procedure expose m.
parse arg m, va, msg
    if va == '' then
        call scanErr m.m.scan, msg 'expected'
    return va
endProcedure compCheckNN

/*--- return true iff expression is empty ---------------------------*/
compIsEmpty: procedure expose m.
parse arg m, a
    if a == '' then
        return 1
    do while a \== '' & m.a.kind == '*'
        a = m.a.1
        end
    return a == '' | (m.a.kind \== '¢' & m.a.0 < 1)
endProcedure compIsEmpty

/*--- if va == null or empty then issue an error with msg -----------*/
compCheckNE: procedure expose m.
parse arg m, ex, msg
    e1 = left(ex, 1)
    if compIsEmpty(m, ex) then
        call scanErr m.m.scan, msg 'expected'
    return ex
endProcedure compCheckNE

/**** AST = Astract Syntax Tree ****************************************

------- atoms, no children
  =  string constant
----------- rexx fragments
  c  rexxStatement
  s  string
  o  object
  r  run
  f  file

------- containers
  ¢  block          ==> * c-.
  @  code = 1 stmt  ==> *
  -  string
  .  object
  *  operand chain  ==> 1

------- molecules
  &  variable access==> 1 =-
  A  assignment     ==> 2
  B  proc           ==> 2
  C  ct             ==> 1
  D  do             ==> 2
  F  for + with     ==> 2
  P  Pipe           ==> * 1=input 2=output
  T  Table
  M  compile
  %  RunOut         ==> 1,2 (Run, arguments)
  ^  RunRet         ==> 1,2 (Run, arguments)
  I??  Input        ==> * .

***********************************************************************/
/*--- create a new AST -----------------------------------------------*/
compAST: procedure expose m.
parse arg m, ki, txt
    n = mNew('COMP.AST')
    if length(ki) <> 1 then
        call err 'compAST bad kind' ki / 0
    m.n.kind = ki
    m.n.text = txt
    if pos(ki, '¢@-.*&ABCDFPTM%^') > 0 then do
        do cx=1 to arg()-3
            m.n.cx = arg(cx+3)
            end
        m.n.0 = cx-1
        if txt \== '' & pos(ki, '&*FPT') < 1 then
            return err('kind' ki 'text='txt'|')/0
        end
    else if pos(ki, '=csorf') > 0  then do
        m.n.0 = 'kind'ki
        end
    else do
        call err "compAst kind '"ki"' not supported"
        1/0
        end
    return n
endProcedure compAST

/*--- free AST if empty ----------------------------------------------*/
compASTFree0: procedure expose m.
parse arg a, ret
    if m.a.0 > 0 then
        return a
    call mFree a
    return ret
endProcedure compAstFree0

/*--- add operandands to an AST -------------------------------------*/
compASTAddOp: procedure expose m.
parse arg m, a, ops
    do while right(ops, 1) == m.a.kind
        ops = left(ops, length(ops)-1)
        end
    if ops == '' then
        return a
    ops = translate(ops, '-', '=')
    if m.a.kind \== '*' then
        return compAst(m, '*', ops, a)
    m.a.text = ops || m.a.text
    return a
endProcedure compAstAddOp

/*--- return the kind of an AST --------------------------------------*/
compASTKind: procedure expose m.
parse arg m, a
    m.m.astOps = ''
    if \ abbrev(a, 'COMP.AST.') then
        return left(a, 1)
    c = a
    do while m.c.kind == 'ops'
        if m.c.text \== '' then
            return left(m.c.text, 1)
        c = m.c.1
        end
    if a == c then
        return '?'
    return compAstKind(m, c)
endProcedure compASTKind

compAstAssignedVars: procedure expose m.
parse arg a
    res = ''
    if m.a.kind == 'F' then
        return ''
    if m.a.kind == 'A' then do
        a1 = m.a.1
        if m.a1.kind == '=' & m.a1.var == 'v' then do
            if words(m.a1.text) \= 1 then
                call astErr a1 'bad var'
            a2 = m.a.2
            say m.a2.kind m.a2.text
            if pos(m.a2.kind, '-=s') > 0 ,
                | (m.a2.kind == '*' & abbrev(m.a2.text, '-')) then
                res = ', f' m.a1.text 'v'
            else if pos(m.a2.kind, '.<@o') > 0 ,
                | (m.a2.kind == '*' & abbrev(m.a2.text, '.<@')) then
                res = ', f' m.a1.text 'r'
            else
                call astErr a2, 'string or object????'
            end
        end
    if datatype(m.a.0, 'n') then
        do ax=1 to m.a.0
            res = res || compAstAssignedVars(m.a.ax)
            end
    return res
endProcedure compAstAssignedVars

compAstSay: procedure expose m.
parse arg a, lv
    if \ abbrev(a, 'COMP.AST.') then do
        if a \== '' then
            return err('bad ast' a)
        say left('', 19)': * empty ast'
        return
        end
    say lefPad(left('', lv) m.a.kind, 10) ,
        || rigPad(if(dataType(m.a.0, 'n'), m.a.0), 3),
        '@'rigPad(substr(a, 10), 4)':' m.a.text'|'
    if dataType(m.a.0, 'n') then do cx=1 to m.a.0
        call compAstSay m.a.cx, lv+1
        end
    return
endProcedure compAstSay

compAstErr: procedure expose m.
parse arg a, txt
    call errSay txt
    call compAstSay a, 0
    return err(txt)
endProcedure compAstErr

compAstOnlyOut: procedure expose m.
parse arg a, rec
    do ax=1 to m.a.0
        a1 = m.a.ax
        if m.a1.kind == '*' then
            if abbrev(m.a1.text, '$') then
                iterate
        if m.a1.kind = '¢' & rec \== 0 then
            if compAstOnlyOut(a1) then
                iterate
        return 0
        end
    return 1
endProcedure compAstOnlyOut

/*--- return the code for an AST with operand chain trg --------------*/
compCode2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if pos(o1, ';') > 0 then
        return compCode2rx(m, oR, strip(f))
    if pos(o1, '-.<|?@') > 0 then
        return compRun2rx(m, ops, quote(oRunner(f)))

    call err 'compCode2rx bad ops' ops
endProcedure compCode2rx

compCon2rx: procedure expose m.
parse arg m, ops, f, a
    do ox=length(ops) by -1 to 1 while pos(substr(ops,ox,1), '.-')>0
        end
    if substr(ops, ox+1, 1) == '.' then
        f = s2o(f)
    if length(f) < 20 then
        v = quote(f, "'")
    else if a \== '' & m.a.text == f then
        v = 'm.'a'.text'
    else
        v =    'm.'compAst(m, '=', f)'.text'
    if substr(ops, ox+1, 1) == '.' then
        return compObj2rx(m, left(ops, ox), v)
    else
        return compString2rx(m, left(ops, ox), v)
endProcedure compCon2rx

compString2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '-' then
        return compString2rx(m, oR, f)
    if o1 == '.' then
        return compObj2rx(m, oR, 's2o('f')')
    if pos(o1, '$') > 0 tOnlyOut then
        return compCode2rx(m, oR, 'call out' f';')
    if o1 == '&' then do
        o2 = substr('1'ops, length(ops), 1)
        if pos(o2,  '.<^%') < 1 then
            return compString2rx(m, oR, 'vGet('f')')
        else
            return compObj2rx(m, oR, 'vGet('f')')
        end
    if o1 == '<' then
        return compFile2rx(m, oR, 'file('f')')
    call err 'compString2rx bad ops' ops
endProcedure compString2rx

compObj2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '.' then
        return compObj2rx(m, oR, f)
    if o1 == '-' then
        return compString2rx(m, oR, 'o2string('f')')
    if o1 == '$' then
        return compCode2rx(m, oR, 'call out' f';')
    if o1 == '<' then
        return compFile2rx(m, oR, 'o2file('f')')
    if o1 == '@' then
        return compRun2Rx(m, oR, f)
    if pos(o1, ';%^') > 0 then
        return compRun2rx(m, ops, f)
    call err 'compObj2rx bad ops' ops
endProcedure compObj2rx

compRun2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '@' then
        return compRun2Rx(m, oR, f)
    if o1 == ';' | o1 == '%' then /*??? immer % verwenden ???*/
        return compCode2Rx(m, oR, 'call oRun' f';')
    if o1 == '^' then
        if pos(right(oR, 1),  '.<^%') < 1 then
            return compString2Rx(m, oR, 'oRun('f')')
        else
            return compObj2Rx(m, oR, 'oRun('f')')
    return compObj2rx(m, ops, f)
endProcedure compRun2rx

compFile2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if pos(o1, '<.@') > 0 then
        return compFile2rx(m, oR, f)
    if o1 == '|' | o1 == '?' then
        return compObj2Rx(m, oR, 'jSingle('f ||if(o1=='?', ", ''")')')
    return compRun2rx(m, ops, f)
endProcedure compFile2rx

compAst2rx: procedure expose m.
parse arg m, ops, a

    ki = m.a.kind

    if ki == 'c' then
        return compCode2Rx(m, ops, m.a.text)
    if ki == 's' then
        return compString2rx(m, ops, m.a.text)
    if ki == 'o' then
        return compObj2Rx(m, ops, m.a.text)
    if ki == '=' then
        return compCon2Rx(m, ops, m.a.text, a)
    if ki == '*' then
        return compAst2Rx(m, ops || m.a.text, m.a.1)
    o1 = right(ops, 1)
    oR = left(ops, max(0, length(ops)-1))
    if ki == '-' then
        if m.a.0 = 1 then
            return compAst2rx(m, ops, m.a.1)
        else
            return compString2rx(m, ops, compCatRexxAll(m, a,,, ' || '))
    if ki == '.' then
        return compObj2Rx(m, ops, compCatRexxAll(m, a,,, ' || '))
    if ki == '@' then
        return compCode2Rx(m, ops, compCatRexxAll(m, a,,,' || ')';')
    if ki == '¢' then do
        a1 = m.a.1
        if m.a.0 == 1 & m.a1.kind == '¢' then
            return compAst2Rx(m, ops, a1)
        if o1 == '-' then do
            if \ compAstOnlyOut(a) then
                return compAst2Rx(m, ops';', a)
            res = ''
            do ax=1 to m.a.0
                a1 = m.a.ax
                if m.a1.kind == '*' & abbrev(m.a1.text, '$') then
                    b = compAst2rx(m, overlay('-',m.a1.text,1), m.a1.1)
                else if m.a1.kind == '¢' then
                    b = compAst2rx(m, '-', a1)
                else
                    call compAstErr a, 'onlyOut but' ax'='a1 ,
                        'kind='m.a1.kind 'text='m.a1.text
                res = compCatRexx(res, b, ' ')
                end
                    /* ???? only necessary if part of expression ????*/
            return compString2rx(m, oR, '('strip(res)')')
            end
        if o1 == '<' then
            return compAst2Rx(m, ops'@', a)
        if o1 == '.' then
            return compAst2Rx(m, oR'|', a)
        if o1 == '|' | o1 == '?' then
            if m.a.0 = 1 & compAstOnlyOut(a, 0) then do
                a1 = m.a.1
                if m.a1.kind \== '*' | \ abbrev(m.a1.text, '$') then
                    call compAstErr a, 'onlyOut but',
                        '1='a1 'kind='m.a1.kind 'text='m.a1.text
                return compAst2Rx(m, oR'.'substr(m.a1.text, 2), m.a1.1)
                end
            else
                return compFile2Rx(m, ops, compAst2rx(m, '<;', a))
        if pos(o1, '@;') > 0 then do
            if m.a.0 = 1 then  /*???pipe und code können multi stmts */
               return compCode2Rx(m, ops, 'do;' ,
                    compAst2rx(m,';',m.a.1) 'end;')
            res = ''
            do ax=1 to m.a.0
                res = res compAst2rx(m, ';', m.a.ax)
                end
            if res = '' then
                return compCode2Rx(m, ops, 'nop;')
            return compCode2Rx(m, ops, 'do;'res 'end;')
            end
        end
    if ki == '&' then do
        nm = compAst2Rx(m, '-', m.a.1)
        if m.a.text=='' | m.a.text=='v' then
            return compString2rx(m, ops'&', nm)
        else if m.a.text == '?' then
            return compString2rx(m, ops, 'vIsDefined('nm')')
        else if m.a.text == '>' then
            return compString2rx(m, ops, 'vRead('nm')')
        else
            call compAstErr a, 'bad text' m.a.text 'in ast &'
        end
    if ki == '%' | ki == '^' then do
        c1 = compAst2Rx(m, '.', m.a.1)
        if m.a.0 > 1 then
            c1 =  c1',' compAst2Rx(m, '', m.a.2)
        return compRun2Rx(m, ops || ki, c1)
        end
    if ki == 'A' then do /* assignment */
        nm = compAst2Rx(m, '-', m.a.1)
        vl = m.a.2
        if m.vl.kind == '=' | m.vl.kind == '-' ,
            | (m.vl.kind == '*' & right(m.vl.text, 1) == '-') then
            return compCode2Rx(m, ops,
                , 'call vPut' nm',' compAst2Rx(m, '-', vl)';')
        else
            return compCode2Rx(m, ops,
                , 'call vPut' nm',' compAst2Rx(m, '.', vl)';')
        end
    if ki == 'B' then do /* proc */
        call vPut utInter('return' compAst2Rx(m, '-', m.a.1)),
            , oRunner(compAst2Rx(m ,';', m.a.2))
        return ''
        end
    if ki == 'C' then do /* ct */
     call utInter compAst2Rx(m, ';', m.a.1)
        return ''
        end
    if ki == 'D' then do /* do */
        res = 'do' compAst2rx(m, '', m.a.1)';'
        if m.a.text \== '' then
            res = res "call vPut '"m.a.text"'," m.a.text";"
        return compCode2Rx(m, ops, res compAst2Rx(m, ';', m.a.2),
             "end;")
        end
    if ki == 'F' then do /* for... */
        a1 = m.a.1
         st = compAst2Rx(m, ';', m.a.2)
        if abbrev(m.a.text, 'with') then do
            if m.a1.kind \== 'A' then do
                v = compAst2Rx(m, '.', a1)
                end
             else do
                v = compAst2Rx(m, ,a1)
                if \ abbrev(v, 'call vPut ') | right(v, 1) \==';' then
                    call scanErr s, 'bad vPut' v
                v = 'vPut('substr(v, 11, length(v)-11)')'
                end
            if m.a.0 <= 2 then
                return 'call vWith "+",' v';' st 'call vWith "-";'
            a3 = m.a.3
            if m.a3.kind == '*' then
                return 'call vWith "+",' v';' st,
                    compObj2Rx(m, m.a3.text, "vWith('-')")
            end
        v = compAst2Rx(m, '-', m.a.1)
        if m.a.text == 'forWith' then
            st = 'call vWith "=", vGet('v');' st
        if abbrev(m.a.text, 'for') then
            st = 'do while vRead('v');' st 'end;'
        if m.a.text == 'forWith' then
            st = 'call vWith "+";' st 'call vWith "-";'
        return compCode2Rx(m, ops, st)
        end
    if ki == 'P' then do /* pipe */
        if ((left(m.a.text, 1) == ' ') \== (m.a.1 == '')) ,
         | ((substr(m.a.text, 2) == '') \== (m.a.2 == '')) ,
         | (m.a.0 <= 3 & m.a.text == '') then
            call compAstErr a, 'bad/trivial astPipe'
        res = ''
        do ax=3 to m.a.0
            a1 = ''
            if ax < m.a.0 then /* handle output */
                t1 = 'N'
            else if m.a.1 == '' then
                t1 = 'P'
            else do
                t1 = left(m.a.text, 1)
                a1 = compAst2Rx(m, '.', m.a.1)
                end
            if ax == 3 then do /* handle input */
                t1 = '+'t1 || substr(m.a.text, 2)
                if m.a.2 \== '' then
                    a1 = a1',' compAst2Rx(m, '.', m.a.2)
                end
            else
                t1 = t1'|'
            res = res "call pipe '"t1"'," a1";" ,
                   compAst2Rx(m, ';', m.a.ax)
            end
        return compCode2Rx(m, ops, res "call pipe '-';")
        end
    if ki == 'R' then do /* Run with args */
        res = 'call oRun' compAst2Rx(m, , m.a.1)
        if m.a.0 > 1 then
            res = res',' compAst2Rx(m, , m.a.2)
        return compRun2rx(m, ops, res';')
        end
    if ki == 'M' then do
        if m.a.0 = 0 then
            args = ''
        else
            args = ',' compAst2Rx(m, , m.a.1)
        return compRun2rx(m, ops, 'compile(comp(in2Buf())' args')')
        end
    call compAstErr a, 'compAst2rx bad ops' ops 'kind' ki
endProcedure compAst2rx

compCatRexxAll: procedure expose m.
parse arg m, a, ops, mi, sep
    res = ''
    do ax=1 to m.a.0
        res = compCatRexx(res, compAst2rx(m, ops, m.a.ax), mi , sep)
        end
    return strip(res)
endProcedure compCatRexxAll

/*--- cat two rexx parts, avoid strange effects--------------------*/
compCatRexx: procedure expose m.
parse arg le, ri, mi, sep
    if mi \== '' then
        return le || mi || ri
    lr = right(le, 1)
    rl = left(ri, 1)
    if (lr == "'" | lr == '"') then do
        if rl == lr then                /* "a","b" -> "ab" */
            return left(le, length(le)-1) || substr(ri, 2)
        else if  rl == '(' then            /* "a",( -> "a" || ( */
            return le||sep||ri            /* avoid function call    */
        end
    else if pos(lr, m.comp.idChars) > 0 then
        if pos(rl, m.comp.idChars'(') > 0 then
            return le || sep || ri        /* a,b -> a || b */
    return le || ri
endProcedure compCatRexx
/* copy comp end ******************************************************/
/* copy scanSB begin ***************************************************
     Achtung: inc generiert SB aus scanSB, Aenderungen nur in scanSB|
ScanSB: basic scan
    scanLook(m,len) : returns next len chars, pos is not moved
    scanChar(m,len) : scans next len chars
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanWord(m,u)  : scan a space delimited word or a string,
                          if u=1 then uppercase non-strings
variable interface
    scanSrc(m, source) starts scanning a single line
    scanEnd(m)     : returns whether we reached end of input
    scanErr(m, txt): error with current scan location

    m is an address, to store our state
    if a scan function succeeds, the scan posititon is moved

returns: true if scanned, false otherwise
         m.m.tok  ==> last token
         m.m.val  ==> last value for scanString/Word
         m.m.pos ==> scan position
         m.m.src ==> scan source
***********************************************************************/
/*--- return the next len characters until end of src ----------------*/
scanLook: procedure expose m.
parse arg m, len
    if len == '' then
        return substr(m.m.src, m.m.pos)
    else
        return substr(m.m.src, m.m.pos,
                     , min(len, 1 + length(m.m.src) - m.m.pos))
endProcedure scanLook

/*--- scan len chararcters, atmost to end of src ---------------------*/
scanChar: procedure expose m.
parse arg m, len
    m.m.tok = scanLook(m, len)
    m.m.pos = m.m.pos + length(m.m.tok)
    return m.m.tok \== ''
endProcedure scanChar

/*--- scan the literal lit ------------------------------------------*/
scanLit: procedure expose m.
parse arg m
    do ax=2 to arg()
        if abbrev(substr(m.m.src, m.m.pos), arg(ax)) then do
            m.m.tok = arg(ax)
            m.m.pos = m.m.pos + length(arg(ax))
            return 1
            end
        end
    m.m.tok = ''
    return 0
endProcedure scanLit

/*--- scan with verify, vOpt is passed to verify ---------------------*/
scanVerify: procedure expose m.
parse arg m, alpha, vOpt, onlyIfMatch
    if arg() > 3 then
        call err 'deimplement onlyIfMatch???'
    if vOpt == '' then   /* empty string does not take default */
        nx = verify(m.m.src, alpha, , m.m.pos)
    else
        nx = verify(m.m.src, alpha, vOpt, m.m.pos)
    if nx = 0 then
        if onlyIfMatch == 1 then
            nx = m.m.pos
        else
            nx = length(m.m.src) + 1
    m.m.tok = substr(m.m.src, m.m.pos, nx - m.m.pos)
    m.m.pos = nx
    return m.m.tok \== ''
endProcedure scanVerify

/*--- scan while in charset ------------------------------------------*/
scanWhile: procedure expose m.
parse arg m, chSet
    return scanVerify(m, chSet, 'n')

/*--- scan until in charset ------------------------------------------*/
scanUntil: procedure expose m.
parse arg m, chSet
    return scanVerify(m, chSet, 'm')

/*--- scan until (and over) string End -------------------------------*/
scanStrEnd: procedure expose m.
parse arg m, sep
    px = m.m.pos
    do forever
        px = pos(sep, m.m.src, px)
        if px = 0 then do
            m.m.tok = ''
            return 0
            end
        px = px + length(sep)
        if \ abbrev(substr(m.m.src, px), sep) then do
            m.m.tok = substr(m.m.src, m.m.pos, px-m.m.pos)
            m.m.pos = px
            return 1
            end
        px = px + length(sep)
        end
endProcedure scanStrEnd

/*--- scan a string with quote char qu -------------------------------*/
scanString: procedure expose m.
parse arg m, prefs
    if prefs = '' then do
        call scanLit m, "'", '"'
        end
    else do
        do px=1 to words(prefs) until scanLit(m, word(prefs, px))
            end
        end
    if m.m.tok == '' then
        return 0
    t1 = m.m.tok
    qu = right(t1, 1)
    if \ scanStrEnd(m, qu) then do
        m.m.pos = m.m.pos - length(t1)
        return scanErr(m, 'ending Apostroph('qu') missing')
        end
    m.m.val = repAll(left(m.m.tok, length(m.m.tok)-1), qu||qu, qu)
    m.m.tok = t1 || m.m.tok
    return 1
endProcedure scanString

/*--- scan a word and put value into *.val
           a word is either delimited by space or stopper
                     or a string (with single or double quotes -------*/
scanWord: procedure expose m.
parse arg m, stopper, ucWord
    if scanString(m) then
        return 1
    if stopper == '' then
        stopper = ' '
    if \scanUntil(m, stopper) then
        return 0
    if ucWord == 1 then
        m.m.val = translate(m.m.val)
    else
        m.m.val = m.m.tok
    return 1
endProcedure scanWord

/*--- skip, scan and return next word --------------------------------*/
scanSkWord: procedure expose m.
parse arg m, stopper, ucWord, eMsg
    if scanWord(scanSkip(m), stopper, ucWord) then
        return m.m.val
    else if eMsg == '' then
        return ''
    else
        call scanErr m, eMsg 'expected'
endProcedure scanSkWord

/*--- go back the current token --------------------------------------*/
scanBack: procedure expose m.
parse arg m, tok
    if m.m.pos <= length(tok) then
        call scanErr sc, 'cannot back "'tok'" length'
    cx = m.m.pos - length(tok)
    if substr(m.m.src, cx, length(tok)) \== tok then
        return scanErr(m, 'cannot back "'tok'" value')
    m.m.pos = cx
    return
endProcedure scanBack

/*--- set new src - allow scanning without open ----------------------*/
scanSrc: procedure expose m.
parse arg m, m.m.src
    return scanSBStart(m)
endProcedure scanSrc

/*--- start scanning -------------------------------------------------*/
scanSBStart: procedure expose m.
parse arg m
    m.m.pos = 1
    m.m.tok = ''
    return m
endProcedure scanSBStart

scanSBSpace: procedure expose m.
parse arg m
    nx = verify(m.m.src, ' ', , m.m.pos)
    if nx = 0 then
        nx = length(m.m.src) + 1
    res = nx <> m.m.pos
    m.m.tok = left(' ', res)
    m.m.pos = nx
    return res
endProcedure scanSBSpace

/*--- skip over space, nl and comments and return m -----------------*/
scanSkip: procedure expose m.
parse arg m
    call scanSpace m
    return m
endProcedure scanSkip

scanErr: procedure expose m.
parse arg m, txt
    if arg() < 3 then
        return err('s}'txt'\n'scanInfo(m))
    else
        return err('scanErr' txt'\n'arg(3))
endProcedure scanErr

scanSBInfo: procedure expose m.
parse arg m
    return 'last token' m.m.tok 'scanPosition' ,
        strip(substr(m.m.src, m.m.pos, 40), 't') ,
        || '\npos' m.m.Pos 'in string' strip(m.m.src, 't')
endProcedure scanSBInfo

/*--- return position in simple format -------------------------------*/
scanSBPos: procedure expose m.
parse arg m
    return 'singleSrc' m.m.pos
    return if(m.m.pos > length(m.m.src), 'E', 'singleSrc' m.m.pos)

/*--- set position to position in arg to------------------------------*/
scanSetPos: procedure expose m.
parse arg m, to
    cur = scanPos(m)
    wc = words(cur)
    if wc <> words(to) ,
        | subWord(cur, 1, wc-1) <> subWord(to, 1, wc-1) then
        call scanErr m 'cannot back from' cur 'to' to
    m.m.pos = word(to, wc)
    return

/*--- return true if at end of src -----------------------------------*/
scanSBEnd: procedure expose m.
parse arg m
    return m.m.pos > length(m.m.src)

/*--- return true if at comment --------------------------------------*/
scanSBCom: procedure expose m.
parse arg m
    m.m.tok = ''
    if m.m.scanComment == '' then
        return 0
    if \ abbrev(substr(m.m.src, m.m.pos), m.m.scanComment) then
        return 0
    m.m.tok = substr(m.m.scr, m.m.pos)
    m.m.pos = 1 + length(m.m.src)
    return 1
endProcedure scanSBCom

/* copy scanSB end ****************************************************/
/* copy scan begin ****************************************************
Scan: scan an input: with multiple lines
    ==> all of scanSB
    scanEnd(m)     : returns whether we reached end of input
    scanLit(m,lit) : scan Literal lit if present or return 0
    scanChar(m,n)  : scan next n characters
    scanName(m)    : scan a name
    ScanNat(m)     : scan a natural number (without sign)
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanKeyValue(m): scan a key = value clause (with spaces)
    scanWord(m,u)  : scan a space delimited word or a string,
                          if u=1 then uppercase non-strings
    scanErr(m, txt): error with current scan location

    m is an address, to store our state
    if a scan function succeeds, the scan posititon is moved

returns: true if scanned, false otherwise
         m.m.tok  ==> last token
         m.m.val  ==> last value for scanString/Word/KeyValue
         m.key    ==> key for scanKeyValue
         m.m.pos ==> scan position
         m.m.src ==> scan source
***********************************************************************/
scanIni: procedure expose m.
    if m.scan.ini == 1 then
        return
    m.scan.ini = 1
    call jIni
    ts = classNew('n ScanRes u f TOK v, f VAL v, f KEY v, f TYPE v')
    call mAdd m.class_s ,
        , classNew('m scanEnd  return 1'),
        , classNew('m scanInfo return scanSBInfo(m)') ,
        , classNew('m scanPos return scanSBPos(m)')
    call classNew 'n ScanSB u', 'm',
        , 'scanEnd  return 1',
        , 'scanNL   m.m.tok = ""; return 0',
        , 'scanCom return scanSBCom(m)',
        , 'scanInfo return scanSBInfo(m)' ,
        , 'scanPos  return scanSBPos(m)',
        , 'jOpen    return scanSBStart(m)',
        , 'jClose   m.m.pos = "-- closed --"; return m',
        , 'isWindow 0',
        , "jRead if scanType(m) == '' then return 0;" ,
                    " m.m = oClaCopy('"ts"', m, ''); return 1"
    call classNew 'n ScanSqlStmtRdr u JRW', 'm',
        , "jReset   call scanSqlStmtRdrReset m, arg, arg2, arg3",
        , "jOpen    call scanOpen  m'.SCAN'" ,
        , "jClose   call scanClose m'.SCAN'" ,
        , "jRead    r = scanSqlStmt(m'.SCAN');if r==''then return 0" ,
                         "; m.m = r; return 1"
    return
endProcedure scanIni

scanOpen: procedure expose m.
parse arg m
    interpret objMet(m, 'jOpen')
    return m
endProcedure scanOpen

scanClose: procedure expose m.
parse arg m
    interpret objMet(m, 'jClose')
    return m
endProcedure scanOpen

scanSB: procedure expose m.
parse arg n1, np, co
     return oNew('ScanSB')
endProcedure scanSB

/*--- start scanning with a new single src ---------------------------*/
scanSBReset: procedure expose m.
parse arg m, m.m.src
    return scanSbStart(oMutatName(m, 'ScanSB'))
endProcedure scanSBReset

scanInfo: procedure expose m.
parse arg m
    interpret objMet(m, 'scanInfo')

/*--- return true if at end of src -----------------------------------*/
scanEnd: procedure expose m.
parse arg m
    if m.m.pos <= length(m.m.src) then
        return 0
    interpret objMet(m, 'scanEnd')

/*--- scan over white space, nl, comments ...-------------------------*/
scanSpace: procedure expose m.
parse arg m
    fnd = 0
    do while scanSBSpace(m) | scanCom(m) | scanNl(m)
        fnd = 1
        end
    m.m.tok = left(' ', fnd)
    return fnd
endProcedure scanSpace

/*--- scan next line -------------------------------------------------*/
scanNL: procedure expose m.
parse arg m, unCond
    interpret objMet(m, 'scanNL')

/*--- scan one comment -----------------------------------------------*/
scanCom: procedure expose m.
parse arg m
    interpret objMet(m, 'scanCom')

scanPos: procedure expose m.
parse arg m
    interpret objMet(m, 'scanPos')
endProcedure scanPos

scanOpt: procedure expose m.
parse arg m, m.m.scanName1, namePlus, m.m.scanComment
    if m.m.scanName1 == '' then
        m.m.scanName1 = m.ut_alfa
    if namePlus == '' then
        m.m.scanNameR = m.m.scanName1 || '0123456789'
    else
        m.m.scanNameR = m.m.scanName1 || namePlus
    m.m.scanNestCom = 0
    return m
endProcedure scanOpt

/*--- scan a Name, first char in *.scanName1, rest in *.scanNameR ----*/
scanName: procedure expose m.
parse arg m
    if pos(substr(m.m.src, m.m.pos, 1),
                 , m.m.scanName1) <= 0 then do
        m.m.tok = ''
        return 0
        end
    return scanVerify(m, m.m.scanNameR)
endProcedure scanName

/*--- scan a natural number (no sign, decpoint ...) ------------------*/
scanNat: procedure expose m.
parse arg m, chEn
    if \ scanVerify(m, '0123456789') then
        return 0
    if chEn \== 0 then
        if pos(scanLook(m , 1), m.m.scanNameR) > 0 then
            call scanErr m, 'illegal number end after' m.m.tok
    return 1
endProcedure ScanNat

/*--- scan an integer (optional sign, no decpoint ...) ---------------*/
scanInt: procedure expose m.
parse arg m, chEn
    call scanLit m, '+', '-'
    si = m.m.tok
    if \ scanNat(m, chEn) then do
        m.m.pos = m.m.pos - si
        return 0
        end
    m.m.tok = si || m.m.tok
    return 1
endProcedure scanInt

/*--- scan a number (optional sign, decpoint, exponent) ------------*/
scanNum: procedure expose m.
parse arg m, chEn
    sx = m.m.pos
    call scanLit m, '+', '-'
    po = scanLit(m, '.')
    if \ scanNat(m, 0) then do
        m.m.pos = sx
        return 0
        end
    if \ po then
        if scanLit(m, '.') then
            call scanNat m, 0
        if scanLit(m, 'e', 'E') then
            if \ scanInt(m, 0) then
                call scanErr m, 'exponent expected after' ,
                             substr(m.m.src, sx, m.m.pos-sx)
    m.m.tok = substr(m.m.src, sx, m.m.pos-sx)
    m.m.val = translate(m.m.tok)
    if chEn \== 0 then
        if pos(scanLook(m , 1), m.m.scanNameR) > 0 then
            call scanErr m, 'illegal number end after' m.m.tok
    return 1
endProcedure scanNum

scanType: procedure expose m.
parse arg m, opt
    m.m.tok = ''
    if scanName(m) then
        m.m.type = 'n'
    else if scanNum(m) then
        m.m.type = 0
    else if scanString(m) then
        m.m.type = left(m.m.tok, 1)
    else if scanSpace(m) then
        m.m.type = 's'
    else do
        call scanChar m, 1
        m.m.type = m.m.tok
        end
    return m.m.type
endProcedure scanType

/*--- scan a key = word phrase
          put key into m.key and word into m.m.val -------*/
scanKeyValue: procedure expose m.
parse arg m, def
    if \ scanName(m) then
        return 0
    m.m.key = m.m.tok
    if \ scanLit(scanSkip(m), '=') then do
        m.m.val = def
        m.m.tok = ' no='
        end
    else if \scanWord(scanSkip(m)) then
        return scanErr(m, 'word expected after' m.m.key '=')
    return 1
endProcedure scanKeyValue

/*--- emit an error with current scan pos ----------------------------*/
/* copy scan end   ****************************************************/
/* copy scanRead begin ************************************************/
scanReadIni: procedure expose m.
    if m.scanRead.ini = 1 then
        return
    m.scanRead.ini = 1
    call scanIni
 /* ts = classNew('n Scan u f TOK v, f VAL v, f KEY v, f TYPE v') */
    call classNew 'n ScanRead u ScanSB', 'm',
        , 'scanEnd return m.m.atEnd' ,
        , 'scanNL return scanReadNL(m, unCond)',
        , 'scanInfo return scanReadInfo(m)',
        , 'scanPos return scanReadPos(m)',
        , "jOpen   call scanReadOpen m, arg()>=3, arg(3)" ,
        , "jClose  call scanReadClose m"
    call classNew "n EditRead u JRW", "m" ,
        , "jRead  return editRead(m)",
        , "jOpen" ,
        , "jReset m.m.linex = arg - 1"
    return
endProcedure scanReadIni

/*--- begin scanning the lines of a reader ---------------------------*/
scanRead: procedure expose m.
parse arg rdr, n1, np, co
    return scanReadReset(oNew('ScanRead'), rdr, n1, np, co)

scanReadReset: procedure expose m.
parse arg m, m.m.rdr, n1, np, co
    return oMutatName(scanOpt(m, n1, np, co), 'ScanRead')
endProcedure scanReadReset

scanReadOpen: procedure expose m.
parse arg m, hasLine0, line0
    call scanReadO0 m
    if hasLine0 == 1 then
        m.m.src = line0
    else
        call scanNL m, 1
    return m
endProcedure scanReadOpen

scanReadO0: procedure expose m.
parse arg m
    call scanSrc m
    m.m.atEnd = 0
    m.m.lineX = 0
    m.m.val = ''
    m.m.key = ''
    call jOpen m.m.rdr, '<'
    return m
endProcedure scanReadO0

scanReadClose: procedure expose m.
parse arg m
    call jClose m.m.rdr
    m.m.atEnd = 'closed'
    return m
endProcedure scanReadClose

/*--- scan over next newLine
        if unCond \== 1 only if we are already at endOfLine
         return true if we scanned a NL ------------------------------*/
scanReadNL: procedure expose m.
parse arg m, unCond
    m.m.tok = ''
    if unCond \== 1 then
        if m.m.pos <= length(m.m.src) then
            return 0
    if m.m.atEnd then
        return 0
    m.m.tok = substr(m.m.src, m.m.pos)
    r = m.m.rdr
    if \ jRead(r) then do
        m.m.atEnd = 1
        m.m.pos = 1 + length(m.m.src)
        return 0
        end
    m.m.src = m.r
    m.m.pos = 1
    m.m.lineX = m.m.lineX + 1
    return 1
endProcedure scanReadNl

scanTextCom: procedure expose m.
parse arg m, untC, untWrds
    if m.m.scanComment \= '' then
        untC = untC || left(m.m.scanComment, 1)
    if \ m.m.scanNestCom then
        return scanText(m, untC, untWrds)
    else if wordPos('*/', untWrds) > 0 then
        return scanText(m, untC'*/', untWrds)
    res = scanText(m, untC'*/', untWrds '*/')
    if res then
        if scanLook(m, 2) == '*/' then
            call scanErr m, '*/ without preceeding comment start /*'
    return res
endProcedure scanTextCom

scanText: procedure expose m.
parse arg m, untC, untWrds
    res = ''
    do forever
        if scanUntil(m, untC) then do
            res = res || m.m.tok
            if m.m.pos > length(m.m.src) then do
                /* if windowing we need to move the window| */
                if scanNl(m, 0) then
                    if right(res, 1) \==' ' & scanLook(m, 1)\==' ' then
                        res = res' '
                iterate
                end
            end
        m.m.tok = res
        c9 = scanLook(m, 9)
        do sx=1 to words(untWrds)
            if abbrev(c9, word(untWrds, sx)) then
                return 1
            end
        if scanCom(m) | scanNl(m, 0) then do
            if right(res, 1) \== ' ' & scanLook(m, 1) \== ' ' then
                res = res' '
            end
        else if scanString(m) then
            res = res || m.m.tok
        else if scanChar(m, 1) then
            res = res || m.m.tok
        else if scanEnd(m) then do
            m.m.tok = res
            return res \== ''  /* erst hier NACH scanCom,  scanNl */
            end
        else
            call scanErr m, 'bad pos'
        end
endProcedure scanText

scanReadPos: procedure expose m.
parse arg m, msg
    return m.m.lineX m.m.pos
endProcedure scanReadPos

scanReadInfo: procedure expose m.
parse arg m
    msg = 'last token' m.m.tok 'scanPosition' ,
        strip(substr(m.m.src, m.m.pos, 40), 't')
    if scanEnd(m) then
        msg = msg'\natEnd after'
    else
        msg = msg'\npos' m.m.pos 'in'
    return msg 'line' m.m.lineX':' strip(m.m.src, 't')
endProcedure scanReadInfo

/*--- use scan sqlEdit macro --> temporarily here --------------------*/
/*--- read next line from edit data ----------------------------------*/
editRead: procedure expose m.
parse arg m
    m.m.lineX = m.m.lineX + 1
    if adrEdit('(ll) = line' m.m.lineX, 12) \= 0 then
        return 0
    m.m = ll
    return 1
endProcedure editRead
/*--- search loop in edit macro --------------------------------------*/
scanSqlSeekId: procedure expose m.
parse arg m, lx, cmd, opts
    if opts = '' then
        opts = word
                     /* line 1 col 0, otherwise first word is skipped*/
    if adrEdit("cursor =" max(trunc(lx), 1) 0, 12) = 12 then
        return -1
    do while adrEdit("seek" cmd opts, 4) = 0 /* find each command*/
        call adrEdit "(fx) = cursor"
        if m.debug then do
            call adrEdit "(LI) = LINE" fx
            call debug 'scanSqlSeekId after' lx 'found' cmd 'at' fx li
            end
        call jReset m.m.rdr, fx
        call jOpen m, '<'
        m.m.lineX = fx
        do while word(scanPos(m), 1) <= fx & scanSqlClass(m)
            if m.m.sqlClass = 'i' & m.m.val == cmd then
                return fx
            end
        call jClose m
        end
    return -1
endProcedure scanSqlSeekId
/* copy scanRead end **************************************************/
/* copy scanWin begin *************************************************
     scan the the concatenation of the lines of a reader
         any token my be split over several line
         except the end-of-line-comment-token
***********************************************************************/
scanWinIni: procedure expose m.
    if m.scanWin.ini = 1 then
        return
    m.scanWin.ini = 1
    call scanReadIni
    call classNew 'n ScanWin u ScanSB', 'm',
        , "jOpen call scanWinOpen m",
        , "jClose call scanReadClose m",
        , 'scanNL return scanWinNl(m, unCond)',
        , 'scanCom return scanWinCom(m)',
        , 'scanInfo return scanWinInfo(m)',
        , 'scanPos  return scanWinPos(m)',
        , 'isWindow 1',
        , 'jRead' classMet(class4Name('ScanSB'), 'jRead')
    return
endProcedure scanWinIni

/*--- instanciate a new window scanner, open rdr ---------------------*/
scanWin: procedure expose m.
parse arg rdr, wOpts
    return scanWinReset(scanSqlOpt(oNew('ScanWin')), rdr, wOpts)

/*--- set the reader and attributes of window scanner m -------------*/
scanWinReset: procedure expose m.
parse arg m, m.m.rdr, cuLe wiFo wiBa
    call oMutatName m, 'ScanWin'
    if pos('@', cuLe) > 0 then
        parse var cuLe cuLe '@' m.m.cutPos
    else
        m.m.cutPos = 1
    cuLe = word(cuLe 72, 1)
    m.m.cutLen = cuLe
    m.m.posMin = word(wiba 3, 1) * cuLe    /* room to go back */
    m.m.posLim = m.m.posMin + 3 * cuLe
    m.m.winTot = m.m.posLim + cuLe * (1 + word(wiFo 5, 1))
    m.m.cutLen = cuLe                      /* fix recLen */
    return m
endProcedure scanWinReset

/*--- open reader and start scanning --------------------------------*/
scanWinOpen: procedure expose m.
parse arg m, lx
    call scanReadO0 oMutatName(m, 'ScanWin')
    call scanWinRead m
    m.m.lineX = word(lx 1, 1)
    return m
endProcedure scanWinOpen

/*--- move the source window: cut left side and append at right side
      return number of characters cut at left ------------------------*/
scanWinRead: procedure expose m.
parse arg m
    dlt = 0
    if m.m.atEnd then
        return 0
    if m.m.pos >= m.m.posLim then do     /*  cut left side */
        dlt = m.m.pos - ((m.m.pos-1) // m.m.cutLen + 1 + m.m.posMin)
        call assert 'dlt >= 0 & dlt // m.m.cutLen = 0', 'dlt m.m.cutLen'
        m.m.src = substr(m.m.src, dlt+1)
        m.m.pos = m.m.pos - dlt
        m.m.lineX = m.m.lineX + dlt % m.m.cutLen
        end
    r = m.m.rdr
    do while length(m.m.src) < m.m.winTot /* read and fill to len */
        if \ jRead(r) then do
            m.m.atEnd = 1
            return dlt
            end
        m.m.src = m.m.src || substr(m.r, m.m.cutPos, m.m.cutLen)
        end
    call assert 'length(m.m.src) = m.m.winTot',
              , 'm.m.winTot length(m.m.src) m.m.src'
    return dlt
endProcedure scanWinRead

/*--- return position of next line start -----------------------------*/
scanWinNLPos: procedure expose m.
parse arg m
    return m.m.pos + m.m.cutLen - ((m.m.pos - 1) // m.m.cutLen)

/*--- scan comment ---------------------------------------------------*/
scanWinCom: procedure expose m.
parse arg m
    call scanWinRead m
    if m.m.scanComment \== '' then do
        cl = length(m.m.scanComment)
        if scanLook(m, cl) == m.m.scanComment then do
            np = scanWinNlPos(m)
            if np = m.m.pos then do
                m.m.pos = np +  m.m.cutLen
                return 1
                end
            if np >= m.m.pos + cl then do
                m.m.pos = np
                return 1
                end
            end
        end
    if m.m.scanNestCom then
        if scanLit(m, '/*') then do
            call scanTextCom m, , '*/'
            if \ scanLit(m, '*/') then
                 call scanErr m, 'nested comment after /* not finished'
            return 1
            end
    m.m.tok = ''
    return 0
endProcedure scanWinCom


/*--- scan nl --------------------------------------------------------*/
scanWinNL: procedure expose m.
parse arg m, unCond
    call scanWinRead m
    m.m.tok = ''
    if unCond \== 1 then
       return 0
    np = scanWinNLPos(m)
    if np = m.m.pos then
        return 0
    m.m.tok = substr(m.m.pos, np-m.m.pos)
    m.m.pos = np
    return 1
endProcedure scanWinNl

/*--- return current position in input ------------------------------*/
scanWinPos: procedure expose m.
parse arg m
    if scanEnd(m) then
        return 'E'
    ps = m.m.pos - 1
    return (m.m.lineX + (ps % m.m.cutLen)) (ps // m.m.cutLen + 1)
endProcedure scanWinPos

/*--- return a description of the current scan position --------------*/
scanWinInfo: procedure expose m.
parse arg m
    p = scanWinPos(m)
    if p == 'E' then do
        res = 'atEnd after'
        p = m.m.lineX - 1 + length(m.m.src) % m.m.cutLen
        end
    else do
        res = 'pos' word(p, 2) 'in'
        p = word(p, 1)
        end
    return 'last token' m.m.tok 'scanPosition' ,
          strip(left(substr(m.m.src, m.m.pos), 40), 't') ,
        || '\n'res 'line' p':' strip(substr(m.m.src,
          , 1 + (p - m.m.lineX) * m.m.cutLen, m.m.cutLen), 't')
endProcedure scanWinInfo
/* copy scanWin end   *************************************************/
/* copy scanSql begin *************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanSqlReset: procedure expose m.
parse arg m, r, scanWin, stmtOpt
    call scanSqlStmtOpt scanSqlOpt(m), strip(stmtOpt)
    if scanWin \== 0 then
        return scanWinReset(m, r, scanWin)
    else if r \== '' then
        return scanReadReset(m, r)
    else
        return scanSbReset(m, m.m.src)
endProcedure scanSqlReset

scanSqlOpt: procedure expose m.
parse arg m
    call scanOpt m, , '0123456789_' , '--'
    m.m.scanNestCom = 1
    return m
endProcedure scanSqlOpt

/*--- scan a sql token put class in m.sqlclass:
      'i': ordinary identifier   e.g. Name
      'd': delimited identifier  e.g. "Delimited"
      'q': qualified identifier  e.g. abc."efg"
      'u': integer units         e.g. 8G
      'n': number                e.g. -234 or .2e3
      's': string                e.g. 'abc''ef'
      '' : at end
         : any other character   e.g. ;
      ----------------------------------------------------------------*/
scanSqlClass: procedure expose m.
parse arg m, retSpace
    m.m.val = ''
    if scanSpace(m) & retSpace = 1 then do
        m.m.sqlClass = 'b'
        return 1
        end
    c2 = scanLook(m ,2)
    if scanString(m, "' x' X'") then do
        m.m.sqlClass = 's'
        if \abbrev(m.m.tok, "'") then
            m.m.val = x2c(m.m.val)
        end
    else if scanSqlQuId(m) then do
        if m.m.val.0 > 1 then
            m.m.sqlClass = 'q'
        else if abbrev(m.m.tok, '"') then
            m.m.sqlClass = 'd'
        else
            m.m.sqlClass = 'i'
        end
    else if scanSqlNum(m, 0)  then
        m.m.sqlClass = 'n'
    else if scanChar(m, 1) then
        m.m.sqlClass = m.m.tok
    else if scanEnd(m) then do
        m.m.sqlClass = ''
        return 0
        end
    else
        call scanErr m, 'cannot scan sql'
    return 1
endProcedure scanSqlClass

scanSqlSkipBrackets: procedure expose m.
parse arg m, br
    if br \== '' then
        nop
    else if scanLit(m, '(') then
        br = 1
    else
        return 0
    do while scanSqlClass(m) & m.m.sqlClass \== ';'
        if m.m.sqlClass = '('        then br = br + 1
        else if m.m.sqlClass \== ')' then iterate
        else if br > 1              then br = br - 1
        else                             return 1
        end
    call scanErr m, '; or eof, but' br 'closing ) expected'
endProcedure skipBrackets
/*--- scan an ordinary sql identifier e.g. abc, ef_12 ----------------*/
scanSqlId: procedure expose m.
parse arg m
    if \ scanName(m) then
        return 0
    m.m.val = translate(m.m.tok)
    return 1
endProcedure scanSqlId

/*--- scan a delimited or ordinay sql identifier ---------------------*/
scanSqlDeId: procedure expose m.
parse arg m
    if scanSqlId(m) then
        return 1
    if \ scanString(m, '"') then
        return 0
    m.m.val = strip(m.m.val, 't')
    return 1
endProcedure scanSqlDeId

/*--- scan a qualified sql identifier --------------------------------*/
scanSqlQuId: procedure expose m.
parse arg m
    res = ''
    rto = ''
    do qx=1
        if \ scanSqlDeId(m) then do
            if qx <> 1 then
                call scanErr m, 'id expected after .'
            return 0
            end
        m.m.val.qx = m.m.val
        res = res'.'m.m.val
        rto = rto'.'m.m.tok
        if \ scanLit(scanSkip(m), '.') then
            leave
        call scanSpace m
        end
    m.m.val.0 = qx
    m.m.val = substr(res, 2)
    m.m.tok = substr(rto, 2)
    return 1
endProcedure scanSqlQuId

/*--- scan a sql number ----------------------------------------------*/
scanSqlNum: procedure expose m.
parse arg m, checkEnd, noSp
    si = ''
    if noSp == 1 then
        call err 'deimplement noSp, use scanNum instead'
    if scanLit(m, '+', '-') then do
        si = m.m.tok
        call scanSpace m
        ch = scanLook(m, 2)
        if left(ch, 1) == '.' then
            ch = substr(ch, 2)
        if pos(left(ch, 1), '0123456789') < 1 then do
            call scanBack m, si
            m.m.val = ''
            return 0
            end
        end
    res = scanNum(m, checkEnd)
    m.m.val = si || m.m.val
    return res

endProcedure scanSqlNum

/*--- scan a sql number with a unit which may follow without space ---*/
scanSqlNumUnit: procedure expose m.
parse arg m, both, units
    if \ scanSqlNum(m, 0) then
        return 0
    nu = m.m.val
    sp = scanSpace(m)
    if scanSqlId(m) then do
        if units == '' | wordpos(m.m.val, units) > 0 then
            nu = nu m.m.val
        else if both | \ sp then
            call scanErr m, 'scanSqlNumUnit after' nu 'bad unit' m.m.val
        else
            call scanBack m, m.m.tok
        end
    else if both then
        call scanErr m, 'scanSqlNumUnit no unit after' nu
    else if \sp & pos(scanLook(m, 1), m.m.scanNameR) > 0 then
        call scanErr m, 'scanSqlNumUnit bad number end after' nu
    m.m.val = nu
    return 1
endProcedure scanSqlNumUnit

/*--- find next statement, after scanSqlStmtOpt -----------------------
       m.m.stop contains delimiter, will be changed by
          terminator?; or --#terminator               */
scanSqlStmtOpt: procedure expose m.
parse arg m, m.m.stop
    if m.m.stop == '' then
        m.m.stop = ';'
    return m
endProcedure scanSqlStmtOpt

scanSqlStmt: procedure expose m.
parse arg m
    loop = 0
    res = ''
    fuCo = copies(m.m.scanComment'#SET', m.m.scanComment \== '')
    do lx=1
        if lx > 100 then
            say '????iterating' scanLook(m)
        if m.m.stop == '' then
            scTx = scanTextCom(m, '''"' ,fuCo)
        else
            scTx = scanTextCom(m, '''"'left(m.m.stop,1), m.m.stop fuCo)
        if scTx then do
            res = res || m.m.tok
            if fuCo \== '' then
                if scanLook(m, length(fuCo)) == fuCo then do
                    tx = scanLook(m)
                    ok = word(tx, 2) == 'TERMINATOR' ,
                         & length(word(tx, 3)) == 1
                    if ok then
                       ok = scanCom(m)
                    if ok then do
                        m.m.stop = word(tx, 3)
                        if \ (right(res, 1) == ' ' ,
                                 | scanLook(m, 1) == ' ') then
                            res = res' '
                        end
                    else if scanChar(m, 1) then
                        res = res || m.m.tok
                    else
                        call scanErr m, 'no char, now what?'
                    iterate
                    end
            if m.m.stop \== '' then
                if \ scanLit(m, m.m.stop) then
                    iterate
            end
        res = strip(res)
        if length(res)=11 ,
            & abbrev(translate(res), 'TERMINATOR') then do
            m.m.stop = substr(res, 11, 1)
            res = ''
            end
        else if res \== '' | \ scTx then
            return res
        end
     call scanErr m, 'loop in scanSqlStmt'
endProcedure scanSqlStmt

/*-- return next sqlStmt from rdr ( or string or '' = j.in) ---------*/
scanSqlIn2Stmt: procedure expose m.
parse arg rdr, wOpt
    s = scanSqlIn2Scan(rdr, m'.SCAN_SqlIn2Stmt', wOpt)
    res = scanSqlStmt(scanOpen(s))
    call scanReadClose s
    return res
endProcedure scanSqlIn2Stmt

/*-- reset s as scanSql from rdr ( or string or '' = j.in) ----------*/
scanSqlIn2Scan: procedure expose m.
parse arg rdr, s, wOpt, sOpt
    interpret objMet(rdr, 'scanSqlIn2Scan')
endProcedure scanSqlIn2Scan

/*-- create a new scanSqlStmtRdr ------------------------------------*/
scanSqlStmtRdr: procedure expose m.
parse arg rdr, wOpt, sOpt
    return oNew('ScanSqlStmtRdr', rdr, wOpt, sOpt)

/*-- reset a new scanSqlStmtRdr
         must be called from jReset to allow jRead ------------------*/
scanSqlStmtRdrReset: procedure expose m.
parse arg m, rdr, wOpt, sOpt
    call scanSqlIn2Scan rdr, m'.SCAN', wOpt, sOpt
    return oMutatName(m, 'ScanSqlStmtRdr')
endProcedure scanSqlStmtRdrReset
/* copy scanSql end   *************************************************/
/* copy scanUtil begin *************************************************
    scan db2 utility input statements using scan and a reader
**********************************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanUtilOpt: procedure expose m.
parse arg m
    call scanSqlOpt m
    m.m.scanNestCom = 0
    m.m.utilBrackets = 0
    m.scanUtil =  'BACKUP CATENFM CATMAINT CHECK' ,
                  'COPY COPYTOCOPY DIAGNOSE EXEC LISTDEF LOAD' ,
                  'MERGECOPY MODIFY OPTIONS QUIESCE REBUILD' ,
                  'RECOVER REORG REPAIR REPORT RESTORE' ,
                  'RUNSTATS STOSPACE TEMPLATE UNLOAD'
    return m
endProcedure scanUtilOpt
/*--- scan next token and put its class in m.sc.utilClass:
      'u' a utility name
      'n' a name
      '"' a quoted name
      "'" an apostroph'd string
      '.' a .
      ',' a ,
      'v' a value
      ''  at end
      ---------------------------------------------------------------*/
scanUtil: procedure expose m.
parse arg sc
    m.sc.utilSpace = scanSpace(sc)
    ty = '?'
    if scanLit(sc, '(') then do
        m.sc.utilBrackets = m.sc.utilBrackets + 1
        end
    else if scanLIT(sc, ')') then do
        m.sc.utilBrackets = m.sc.utilBrackets - 1
        if m.sc.utilBrackets < 0 then
           call scanErr sc, 'unmatched closing bracket )'
        end
    else if scanLit(sc, ',') then do
        end
    else if scanLit(sc, '.') then do
        end
    else if scanString(sc, "'") then do
        end
    else if scanString(sc, '"') then do
        end
    else if scanName(sc) then do
        m.sc.val = translate(m.sc.tok)
        if m.sc.utilBrackets > 0 then
            ty = 'n'
        else if 0 < wordPos(m.sc.val, m.scanUtil) then
            ty = 'u'
        else
            ty = 'n'
        end
    else if scanVerify(sc, ' (),''"', 'm') then do
        ty = 'v'
        m.sc.val = translate(m.sc.tok)
        end
    else if \scanEnd(sc) then do
            call scanErr sc, 'scanUtil stopped before end'
        end
    else do
        ty = ''
        m.sc.val = ''
        end
    if ty == '?' then
        m.sc.utilClass = left(m.sc.tok, 1)
    else
        m.sc.utilClass = ty
    return m.sc.utilClass
endProcedure scanUtil

/*--- scan a value or a bracketed list of values ---------------------*/
scanUtilValue: procedure expose m.
parse arg sc, remApo, nl
    if remApo = '' | rempApo = 0 then
        remApo = "nv"
    else if rempApo = 1 then
        remApo = "nv'"
    if '(' \== scanUtil(sc) then
         return scanUtilValueOne(sc, remApo)
    v = ''
    brx = m.sc.utilBrackets
    oLine = word(scanPos(sc), 1)
    do forever
        call scanUtil sc
        one = scanUtilValueOne(sc, remApo)
        if one == '' then
           call scanErr sc, 'eof in brackets'
        else if brx > m.sc.utilBrackets then
           return v
        nLine = word(scanPos(sc), 1)
        if \ m.sc.utilSpace then
            v = v || one
        else if nl \== '' & oLine <> nLine then
            v = v || nl || one
        else
            v = v' 'one
        oLine = nLine
        end
endProcedure scanUtilValue

scanUtilValueOne: procedure expose m.
parse arg sc, valTy
    if m.sc.utilClass == '' then
        return ''
    else if m.sc.utilClass == 'u' then
        call scanErr sc, 'util in scanUtilValueOne'
    if pos(m.sc.utilClass, valTy) > 0 then
        return m.sc.val
    else
        return m.sc.tok
endProcedure scanUtilValueOne

/*--- skip over nested brackets --------------------------------------*/
scanUtilSkipBrackets: procedure expose m.
parse arg m, br, doCat
    if br \== '' then
        lim = m.m.utilBrackets - br
    else if scanLit(m, '(') then do
        lim = m.m.utilBrackets
        m.m.utilBrackets = lim + 1
        end
    else
        return 0
    doCat = doCat == 1
    res = ''
    do while scanUtil(m) \== ''
        if m.m.utilBrackets <= lim then do
            if doCat then
                m.m.val = res
            return 1
            end
        if doCat then
            res = res m.m.tok
        end
    return scanErr(m, 'eof with' m.m.utilBrackets 'open (')
endProcedure skipBrackets

/*--- analyze a punch file write intoField to stdOut -----------------*/
scanUtilInto: procedure expose m.
parse arg m
    if m.m.utilBrackets \== 0 then
        call scanErr m, 'scanUtilInto with brackets' m.m.utilBrackets
  /*sc = scanUtilReader(m.j.in)
    call jOpen sc, 'r'
 */ do forever
        cl = scanUtil(m)
        if cl == '' then
            return 0
        if cl = 'n' & m.m.tok == 'INTO' then
            leave
        end
    if scanUtil(m) \== 'n' | m.m.tok \== 'TABLE' then
        call scanErr m, 'bad into table '
    if \ scanSqlQuId(scanSkip(m)) then
        call scanErr m, 'table name expected'
    if m.m.utilBrackets \== 0 then
        call scanErr m, 'into table in brackets' m.m.utilBrackets
    m.m.tb = m.m.val
    m.m.part = ''
    m.m.when = ''
    do forever
        cl = scanUtil(m)
        if cl == '' then
            call scanErr m, 'eof after into'
        if cl == 'n' & m.m.tok == 'PART' then do
            if scanUtil(m) == 'v' then
                m.m.part = m.m.val
            else
                call scanErr m, 'bad part'
            end
        else if cl == 'n' & wordPos(m.m.val, 'WHEN WORKDDN') > 0 then do
            call scanUtilSkipBrackets m
            end
        else if cl == '(' then do
           leave
           end
        end
    oX =  m.m.lineX
    oL =  overlay('', m.m.src, 1, m.m.pos-2)
    do while m.m.utilBrackets > 0
        call scanUtil m
        if oX \== m.m.lineX then do
            call out strip(oL, 't')
            oX =  m.m.lineX
            oL =  m.m.src
            end
        end
    call out left(oL, m.m.pos)
 /* call jClose sc
 */ return 1
endProcedure scanUtilInto
/* copy scanUtil end **************************************************/
/* copy pipe begin *****************************************************
***********************************************************************/
pipeIni: procedure expose m.
    if m.pipe.ini == 1 then
        return
    m.pipe.ini = 1
    call catIni
    call mapReset v
    m.v_with.0 = 0
    m.v_withMap = ''
    m.v_with.0.map = ''
    m.pipe.0 = 1
    m.pipe.1.in  = m.j.in
    m.pipe.1.out = m.j.out
    call pipe '+'
    return
endProcedure pipeIni

/*-------------------------------
  +-       push pop frame
  PYNFA    ouput: Parent saY Newcat File, Appendtofile
  psf|     input: parent string file oldOut
  old          --> new
  pipeBegin    --> pipe '+N'
  pipeBeLa f   --> pipe '+F'
  pipeLast     --> pipe 'P|'
  pipeLast f   --> pipe 'F|', f
  pipeEnd      --> pipe '-'
--------------------------------*/
pipe: procedure expose m.
parse arg opts, aO, aI
    ox = 1; oc = substr(opts, ox, 1)
    ax = m.pipe.0
    px = ax -1
    if oc == '-' then do
        if px < 2 then
            call err 'pipe pop empty'
        call jClose m.pipe.ax.out
        call jClose m.pipe.ax.in
        ax = px
        m.pipe.0 = ax
        px = ax-1
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if oc == '+' then do
        px = ax
        ax = ax+ 1
        m.pipe.0 = ax
        m.pipe.ax.in  = jOpen(m.pipe.px.in, '<')
        m.pipe.ax.out = jOpen(m.pipe.px.out, '>')
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    oOut = m.pipe.ax.out
    if pos(oc, 'NYPFA') > 0 then do
        call jClose oOut
        if oc == 'Y' then
            m.pipe.ax.out = jOpen(m.pipe.1.out, '>')
        else if oc == 'P' then
            m.pipe.ax.out = jOpen(m.pipe.px.out, '>')
        else if oc == 'N' then
            m.pipe.ax.out = jOpen(Cat(), '>')
        else if oc == 'F' then
            m.pipe.ax.out = jOpen(o2file(aO), '>')
        else if oc == 'A' then
            m.pipe.ax.out = jOpen(o2file(aO), '>>')
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if pos(oc, 's|fp') > 0 then do
        call jClose m.pipe.ax.in
        if oc == 'p' then
            m.pipe.ax.in = jOpen(m.pipe.px.in, '<')
        else if oc == '|' then
            m.pipe.ax.in = jOpen(oOut, '<')
        else if oc == 'f' then do
            if arg() <= 3 then
                m.pipe.ax.in = jOpen(o2file(aI), '<')
            else do
                ct = jOpen(Cat(), '>')
                do lx = 3 to arg()
                    call jWriteAll ct, arg(lx)
                    end
                m.pipe.ax.in = jOpen(jclose(ct), '<')
                end
            end
        else if arg() <= 3 then
            m.pipe.ax.in = jOpen(jBuf(aI), '<')
        else do
            bu = jOpen(jBuf(), '>')
            do lx = 3 to arg()
                call jWrite bu, arg(lx)
                end
            m.pipe.ax.in = jOpen(jclose(bu), '<')
            end
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if oc \== ' ' then
        call err 'implement' substr(opts, ox) 'in pipe' opts
    m.j.in  = m.pipe.ax.in
    m.j.out = m.pipe.ax.out
    return
endProcedure pipe

/*--- write all from rdr (rsp in) to out, not lazy ----------------*/
pipeWriteNow: procedure expose m.
    parse arg rdr
    call jWriteNow m.j.out, if(rdr == '', m.j.in, rdr)
    return
endProcedure pipeWriteNow

/*--- write all from rdr (rsp in) to out, possibly lazy -----------*/
pipeWriteAll: procedure expose m.
parse arg rdr
    call jWriteAll m.j.out, if(rdr == '', m.j.in, rdr)
    return
endProcedure pipeWriteAll

pipePreSuf: procedure expose m.
parse arg le, ri
    do while in()
        call out le || m.in || ri
        end
    return
endProcedure pipePreSuf

vIsDefined: procedure expose m.
parse arg na
    return   '' \== vAdr(na, 'g')
endProcedure vIsDefined

vWith: procedure expose m.
parse arg fun, o
    if fun == '-' then do
        tBe = m.v_with.0
        tos = tBe - 1
        if tos < 0 then
            call err 'pop empty withStack'
        m.v_with.0 = tos
        m.v_withMap = m.v_with.tos.map
        return m.v_with.tBe.obj
        end
    tos = m.v_with.0
    if fun == '=' then do
        if o == '' & m.v_with.tos.obj == '' then
            return
        if o \== '' & m.v_with.tos.obj \== '' then
            if objClass(o) == objClass(m.v_with.tos.obj) then do
                m.v_with.tos.obj = o
                return
                end
        par = tos - 1
        end
    else if fun == '+' then do
        par = tos
        tos = tos + 1
        m.v_with.0 = tos
        end
    m.v_with.tos.obj = o
    if par > 0 then
        key = m.v_with.par.classes
    else
        key = ''
    if o \== '' then
        key = strip(key objClass(o))
    m.v_with.tos.classes = key
    if symbol('m.v_withManager.key') == 'VAR' then do
        m.v_with.tos.map = m.v_withManager.key
        m.v_withMap = m.v_withManager.key
        return
        end
    m = mapNew()
    m.v_with.tos.map = m
    m.v_withMap = m
    m.v_withManager.key = m
    do kx=1 to words(key)
        c1 = word(key, kx)
        call vWithAdd m, kx, classMet(c1, 'oFlds')
        call vWithAdd m, kx, classMet(c1, 'stms')
        end
    return
endProcedure vWith

vWithAdd: procedure expose m.
parse arg m, kx, ff
    do fx=1 to m.ff.0
        n1 = m.ff.fx
        dx = pos('.', n1)
        if dx > 1 then
            n1 = left(n1, dx-1)
        else if dx = 1 | n1 = '' then
            iterate
        call mPut m'.'n1, kx
        end
    return
endProcedure vWithAdd

vGet: procedure expose m.
parse arg na
    a = vAdr(na, 'g')
    if a = '' then
        call err 'undefined var' na
    return m.a
endProcedure vGet


vPut: procedure expose m.
parse arg na, val
    a = vAdr(na, 'p')
    m.a = val
    return val
endProcedure vPut

/*--- find the final address
      return f || a with address a and
             f = m -> mapGet(a), o -> obect m.a, s -> string m.a  ---*/
vAdr: procedure expose m.
parse arg na, f
    cx = 0
    cx = verify(na, '&>', 'm')
    if cx > 0 then
        a = left(na, cx-1)
    else do
        a = na
        cx = length(na)+1
        end
    nxt = 0
    do forever
        cy = verify(na, '&>', 'm', cx+1)
        if cy > 0 then
            fld = substr(na, cx+1, cy-cx-1)
        else
            fld = substr(na, cx+1)
        if substr(na, cx, 1) == '>' then do
            if nxt then
                a = vAdrByM(a)
            if fld \== '' then
               a = a'.'fld
            end
        else do
            if nxt then
                a = vAdrByM(a)
            mp = m.v_withMap
            aL = a
            if pos('.', a) > 0 then
                aL = left(a, pos('.', a)-1)
            if mp \== '' & symbol('m.mp.aL') == 'VAR' then do
                wx = m.mp.aL
                a = m.v_with.wx.obj'.'a
                end
            else if cx >= length(na) then
                return mapAdr(v, a, f)
            else
                a = mapAdr(v, a, 'g')
            if fld \== '' then
                a = vAdrByM(a)'.'fld
            end
        if cy < 1 then do
            if f == 'g' then
                if symbol('m.a') \== 'VAR' then
                    return ''
            return a
            end
        cx = cy
        nxt = 1
        end
endProcedure vAdr
vAdrByM:
parse arg axx
    if axx = '' then
        return err('null address at' substr(na, cx) 'in' na)
    if symbol('m.axx') \== 'VAR' then
        return err('undef address' axx 'at' substr(na, cx) 'in' na)
    ayy = m.axx
    if ayy == '' then
          return err('null address at' substr(na, cx) 'in' na)
    return ayy
endProcedure vAdrByM

vRead: procedure expose m.
parse arg na
    return inVar("V."na)

vReadO: procedure expose m.
parse arg na
    return vRead()
endProcedure vReadO

vHasKey: procedure expose m.
parse arg na
    return mapHasKey(v, na)

vRemove: procedure expose m.
parse arg na
    return mapRemove(v, na)
/* copy pipe end ******************************************************/
/* copy cat  begin ****************************************************
***********************************************************************/
/*--- create a new cat -----------------------------------------------*/
cat: procedure expose m.
    m = oNew('Cat') /* calls catReset */
    do ax=1 to arg()
        call catWriteAll m, arg(ax)
        end
    return m
endProcedure cat

catReset: procedure expose m.
parse arg m
    m.m.RWs.0 = 0
    m.m.catWr = ''
    m.m.catRd = ''
    m.m.catIx = -9e9
    m.m.catKeepOpen = ''
    return m
endProcedure catReset

catClose: procedure expose m.
parse arg m
    if m.m.catWr \== '' then do
        call jClose m.m.catWr
        call mAdd m'.RWS', m.m.catWr
        m.m.catWr = ''
        end
    if m.m.catRd \== '' then do
        call jClose m.m.catRd
        m.m.catRd = ''
        end
    m.m.catIx = -9e9
    return m
endProcedure catClose

catOpen: procedure expose m.
parse arg m, oo
    if oo == m.j.cRead then do
        m.m.catIx = 0
        call catNextRdr m
        m.m.jReading = 1
        end
    else if oo == m.j.cWri | oo == m.j.cApp then do
        if oo == m.j.cWri then
            m.m.RWs.0 = 0
        m.m.catIx = -9e9
        m.m.jWriting = 1
        end
    else do
        call err 'catOpen('m',' oo') bad opt'
        end
    return m
endProcedure catOpen

/*--- return and open next reader ------------------------------------*/
catNextRdr: procedure expose m.
parse arg m
    if m.m.catRd \== '' then
        call jClose m.m.catRd
    cx = m.m.catIx + 1
    m.m.catIx = cx
    if cx > m.m.RWs.0 then do
        m.m.catRd = ''
        return 0
        end
    m.m.catRd = m.m.RWs.cx
    if cx = word(m.m.catKeepOpen, 1) then
        m.m.catKeepOpen = subWord(m.catKeepOpen, 2)
    else
        call jOpen m.m.catRd , m.j.cRead
    return 1
endProcedure catNextRdr

catRead: procedure expose m.
parse arg m
    do while m.m.catRd \== ''
        cr = m.m.catRd
        if jRead(cr) then do
            m.m = m.cr
            return 1
            end
        call catNextRdr m
        end
    return 0
endProcedure catRead

catWrite: procedure expose m.
parse arg m, line
    if m.m.catWr == '' then
        m.m.catWr = jOpen(jBuf(), m.j.cWri)
    call jWrite m.m.catWr, line
    return
endProcedure catWrite

/*--- write contents of a reader to cat
          or keep it for later reading -------------------------------*/
catWriteAll: procedure expose m.
parse arg m
    if m.m.catWr \== '' then do
        call mAdd m'.RWS', jClose(m.m.catWr)
        m.m.catWr = ''
        end
    do ax=2 by 1 to arg()
        r = o2File(arg(ax))
        call mAdd m'.RWS', r
        if m.r.jReading then do
            m.m.catKeepOpen = m.m.rws.0 m.m.catKeepOpen
            call jOpen r, m.j.cRead
            end
        end
    return
endProcedure catWriteAll

/*--- create a reader/WriteO for an external file --------------------*/
file: procedure expose m.
parse arg str
    return oNew('File', str)
endProcedure file

fileChild: procedure expose m.
parse arg m, name, opt
    interpret objMet(m, 'fileChild')
endProcedure fileChild

fileRm: procedure expose m.
parse arg m
    interpret objMet(m, 'fileRm')
    return
endProcedure fileRm

filePath: procedure expose m.
parse arg m
    interpret objMet(m, 'filePath')
endProcedure filePath

fileIsFile: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsFile')
endProcedure fileIsFile

fileIsDir: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsDir')
endProcedure fileIsDir

fileMkDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileMkDir')
    return
endProcedure fileRm

fileRmDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileRmDir')
    return
endProcedure fileRm

/*--- create a reader/WriteO for the filelist of a directory----------*/
fileList: procedure expose m.
parse arg m, opt
    str = oIfStr(m, '')
    if str == '' then
        return oNew('FileList', filePath(m),  opt)
    else
        return oNew('FileList', dsn2Jcl(str),  opt)
endProcedure fileList

catIni: procedure expose m.
    if m.cat.ini == 1 then
        return
    m.cat.ini = 1
    call errIni
    call jIni
    call classNew "n Cat u JRW", "m",
        , "jOpen  call catOpen m, opt",
        , "jReset call catReset m, arg",
        , "jClose call catClose m",
        , "jRead return catRead(m)",
        , "jWrite call catWrite m, line; return",
        , "jWriteAll call catWriteAll m, rdr; return"

    if m.err.os == 'TSO' then
        call fileTsoIni
    else if m.err.os == 'LINUX' then
        call fileLinuxIni
    else
        call err 'file not implemented for os' m.err.os
    return
endProcedure catIni
/* copy cat  end   ****************************************************/
/* copy mail begin ***************************************************/
mailHead: procedure expose m.
parse arg m, subj, rec, snd
    m.m.1 = 'SUBJECT   ' subj
    m.m.2 = 'RECPLIST  ' rec
    m.m.3 = 'SENDERID  ' if(snd=='', userid(), snd)
    m.m.4 = 'SENDERIDCC N'
    m.m.5 = 'DOCTEXT'
    m.m.0 = 5
    return m
endProcedure mailHead

mailSend: procedure expose m.
parse arg m, dsn
    call mAdd m, 'DOCTEXTEND'
    if dsn == '' then
        call dsnAlloc 'dd(iemapi) new  ::v255'
    else
        call dsnAlloc 'dd(iemapi) shr' dsn
    call writeDD iemapi, 'M.'m'.'
    call tsoClose iemapi
    call dsnAlloc 'dd(iemlog) sysout(*)'
    call adrTso "call *(os3550)", '*'
    if rc \== 0 then
        say 'error os3550 rc='rc 'mail not sent|||||'
    call tsoFree iemlog iemapi
    return 0
endProcedure mailSend
/* copy mail end *****************************************************/
/* copy fiLinux begin *************************************************/
/*--- send ggShCmd to shell bash,
        fail if rc <> 0 or not listed in ggRet -----------------------*/
adrSh: procedure expose m.  /* really no need for variables???? */
    parse arg ggShCmd, ggRet
    address 'bash' ggShCmd
    if rc == 0                     then return 0
    else if ggRet == '*'           then return rc
    else if wordPos(rc, ggRet) > 0 then return rc
    else
        call err 'adrSh rc' rc 'for' ggShCmd
endProcedure adrSh

fileLinuxReset: procedure expose m.
parse arg m, nm
    m.m.spec = nm
    if abbrev(nm, '&') then do
        if nm == '&in' then do
            m.m.stream = .input
            m.m.jReading = 1
            end
        else if nm == '&out' then do
            m.m.stream = .output
            m.m.jWriting = 1
            end
        else do
            call err 'bad spec' nm
            end
        end
    else do
        m.m.stream = .Stream%%new(nm)
        m.m.stream%%init(m.m.stream%%qualify)
        end
    return m
endProcedure fileLinuxReset

fileLinuxOpen: procedure expose m.
parse arg m, opt
    if opt == m.j.cRead then do
        res = m.m.stream%%open(read shareread)
        m.m.jReading = 1
        end
    else do
        if opt == m.j.cApp then
            res = m.m.stream%%open(write append)
        else if opt == m.j.cWri then
            res = m.m.stream%%open(write replace)
        else
            call err 'fileLinuxOpen('m',' opt') with bad opt'
        m.m.jWriting = 1
        end
    if res \== 'READY:' then
        call err 'fileLinuxOpen fails' res':' opt ,
        "'"m.m.stream%%qualify"'"
    return m
endProcedure fileLinuxOpen

fileLinuxClose:
parse arg m
    res = m.m.stream%%close
    if res \== 'READY:' then
        call err 'fileLinuxClose' res':' m.m.stream%%qualify
    return m
endProcedure fileLinuxClose

fileLinuxRead: procedure expose m.
parse arg m, var
    res = m.m.stream%%lineIn
    if res == '' then
        if m.m.stream%%state \== 'READY' then
            return 0
    m.var = res
    m.o.o2c.var = m.class_V
    return 1
endProcedure fileLinuxRead

fileLinuxWrite: procedure expose m.
parse arg m, line
    if m.m.stream%%lineOut(line) then
        call err 'fileLinuxWrite'
    return
endProcedure fileLinuxWrite

fileLinuxRmDir: procedure expose m.
parse arg m, opt
    if opt == '' then
        return adrSh('rmdir' m.m.spec)
    else if opt == '-r' then
        return adrSh('rm -r' m.m.spec)
    else
        call err 'bad opt' opt 'in fileLinuxRmDir'
endProcedure fileLInuxRmDir

fileLinuxListReset: procedure expose m.
parse arg m, m.m.spec, o
    if o == 'r' then
        m.m.opt = 'S'
    else if o == '' then
        m.m.opt = ''
    else
        call err 'bad opt' o 'in fileLinuxListReset'
    m.m.rx = 'closed'
    return m
endProcedure fileLinuxListReset

fileLinuxListOpen: procedure expose m.
parse arg m
    if m \== translate(m) then
        call err 'bad m for fileLinuxList:' m
    if m.m.opt == '' then
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST')
    else
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST', m.m.opt)
    if rc \== 0 then
        call err 'rc' rc 'in sysFileTree('m.m.spec', m.'m'.LIST)'
    m.m.rx = 0
    m.m.jReading = 1
    return m
endProcedure fileLinuxListOpen

fileLinuxListRead: procedure expose m.
parse arg m, var
    x = m.m.rx + 1
    if x > m.m.list.0 then
        return 0
    m.var = substr(m.m.list.x, 43)
    m.m.rx = x
    call oMutate var, m.class_V
    return 1
endProcedure fileLinuxListRead

fileLinuxIni: procedure expose m.
    if m.fileLinux.ini == 1 then
        return
    m.fileLinux.ini = 1
    m.file.sep = '/'
    call jIni
    call classNew "n File u JRW", "m",
        , "jReset call fileLinuxReset m, arg",
        , "jOpen  call fileLinuxOpen m, opt",
        , "jClose call fileLinuxClose m",
        , "jRead return fileLinuxRead(m, var)",
        , "jWrite call fileLinuxWrite m, line",
        , "filePath return m.m.stream%%qualify",
        , "fileIsFile return sysIsFile(m.m.stream%%qualify)" ,
        , "fileIsDir return sysIsFileDirectory(m.m.stream%%qualify)" ,
        , "fileChild return file(m.m.stream%%qualify'/'name)",
        , "fileRm return adrSh(m.m.spec)",
        , "fileMkDir return adrSh('mkdir' m.m.stream%%qualify)" ,
        , "fileRmDir return fileLinuxRmDir(m, opt)"
    call classNew "n FileList u JRW", "m",
        , "jReset call fileLinuxListReset m, arg, arg2",
        , "jOpen  call fileLinuxListOpen m, opt",
        , "jClose m.m.rx = 'closed'",
        , "jRead return fileLinuxListRead(m, var)"
    return
endProcedure fileLinuxIni
/* copy fiLinux end   *************************************************/
/* copy fileTso begin *************************************************/
fileTsoReset: procedure expose m.
parse arg m, sp
    m.m.readIx = 'c'
    if symbol('m.m.defDD') \== 'VAR' then do
        m.m.defDD = 'CAT*'
        m.fileTso.buf = m.fileTso.buf + 1
        m.m.buf = 'FILETSO.BUF'm.fileTso.buf
        m.m.spec = sp
        end
    if sp \== '' then do
        m.m.spec = dsnSpec(sp)
        rr = translate(subword(m.m.spec, 4))
        m.m.stripT = \ (pos(':V', rr) > 0 | pos('RECFM(V', rr) > 0)
        end
    return m
endProcedure fileTsoReset

fileTsoOpen: procedure expose m.
parse arg m, opt
    buf = m.m.buf
    if opt == m.j.cRead then do
        aa = dsnAlloc(m.m.spec, 'SHR', m.m.defDD)
        aDD = word(aa, 1)
        aDsn = m.tso_dsn.aDD
        if aDsn <> '' then
            if pos('(', aDsn) > 0 & pos('/', aDsn) < 1 then
                if sysDsn("'"m.tso_dsn.aDD"'") <> 'OK' then
                    call err 'cannot read' m.tso_dsn.aDD':',
                               sysDsn("'"m.tso_dsn.aDD"'")
        call tsoOpen word(aa, 1), 'R'
        m.m.jReading = 1
        m.buf.0 = -1
        m.m.readIx = 0
        end
    else do
        if opt == m.j.cApp then
            aa = dsnAlloc(m.m.spec, 'MOD', m.m.defDD)
        else if opt == m.j.cWri then
            aa = dsnAlloc(m.m.spec, 'OLD', m.m.defDD)
        else
            call err 'fileTsoOpen('m',' opt') with bad opt'
        call tsoOpen word(aa, 1), 'W'
        m.m.jWriting = 1
        m.buf.0 = 0
        m.m.readIx = 'w'
        end
    parse var aa m.m.dd m.m.free
    m.m.dsn = mGet('TSO_DSN.'m.m.dd)
    return m
endProcedure fileTsoOpen

fileTsoClose: procedure expose m.
parse arg m
    buf = m.m.buf
    if m.m.readIx \== 'c' then do
        if m.m.readIx == 'w' & m.buf.0 > 0 then
            call writeDD m.m.dd, 'M.'BUF'.'
        call tsoClose m.m.dd
        call tsoFree  m.m.free
        end
    m.buf.0 = 'closed'
    m.m.readIx = 'c'
    m.m.free  = ''
    m.m.dd    = ''
    return m
endProcedure fileTsoClose

fileTsoRead: procedure expose m.
parse arg m
    ix = m.m.readIx + 1
    buf = m.m.buf
    if ix > m.buf.0 then do
        res = readDD(m.m.dd, 'M.'buf'.')
        if \ res then
            return 0
        ix = 1
        end
    m.m.readIx = ix
    m.m = m.buf.ix
 /* call oMutate var, m.class_V  ?????????? */
    return 1
endProcedure fileTsoRead

fileTsoWrite: procedure expose m.
parse arg m, var
    buf = m.m.buf
    ix = m.buf.0 + 1
    m.buf.0 = ix
    if m.m.stripT then
        m.buf.ix = strip(var, 't')
    else
        m.buf.ix = var
    if ix > 99 then do
        call writeDD m.m.dd, 'M.'buf'.'
        m.buf.0 = 0
        end
    return
endProcedure fileTsoWrite

fileTsoWriteO: procedure expose m.
parse arg m, var
    if objClass(var, m.class_V) == m.class_V then do
        call fileTsoWrite m, m.var
        return
        end
    call err 'fileTsoWriteO('m',' var') cannot write objects of class',
                              objClass(var)
endProcedure fileTsoWriteO

fSub: procedure expose m.
    return file('.sysout(T) writer(intRdr)')
endProcedure fSub

fEdit: procedure expose m.
parse arg spec, vw
    if spec == '' then
        spec = 'new ::f'
    else if abbrev(spec, '::') then
        spec = 'new' spec
    else if abbrev(spec, ':') then
        spec = 'new' ':'spec
    f  = oNew('FileEdit', spec)
    m.f.editType = if(abbrev(translate(vw), 'V'), 'view', 'edit')
    return f
endProcedure fEdit

fileTsoEditClose: procedure expose m.
parse arg m
    dsn = m.m.dsn
    if dsn \== '' then do
        call fileTsoClose m
        call adrIsp m.m.editType "dataset('"dsn"')", 4
        return
        end
    fr = m.m.free
    dd = m.m.dd
    m.m.free = ''
    call fileTsoClose m
    call adrIsp "LMINIT DATAID(lmmId) ddName("dd") ENQ(SHRW)"
    eRc = adrIsp(m.m.editType "dataid("lmmId")", '*')
    lRc = adrIsp("LMFree DATAID("lmmId")", '*')
    call tsoFree fr
    if (eRc \== 0 & eRc \== 4) | lRc \== 0 then
        call err m.m.editType 'rc' eRc', lmFree rc' lRc
    return
endProcedure fileTsoEditClose

fileTsoIni: procedure expose m.
    if m.fileTso.ini == 1 then
        return
    m.fileTso.ini = 1
    m.file.sep = '.'
    m.fileTso.buf = 0
    call jIni
    um = "call err 'for tso undefined method'"
    call classNew "n File u JRW", "m",
        , "jOpen  call fileTsoOpen m, opt",
        , "jReset call fileTsoReset m, arg",
        , "jClose call fileTsoClose m",
        , "jRead return fileTsoRead(m, var)",
        , "jWrite call fileTsoWrite m, line",
        , "filePath return word(m.m.spec, 1)"           ,
        , "fileIsFile" um "'fileIsFile'"      ,
        , "fileIsDir   return 1"              ,
        , "fileChild   return file(word(m.m.spec, 1)'.'name opt)",
        , "fileRm"     um "'fileRm'"          ,
        , "fileMkDir"  ,
        , "fileRmDir"  um "'fileRmDir'"
 /*     , "filePath return m.m.stream%%qualify",
        , "fileIsFile return sysIsFile(m.m.stream%%qualify)" ,
        , "fileIsDir return sysIsFileDirectory(m.m.stream%%qualify)" ,
        , "fileChild return file(m.m.stream%%qualify'/'name)",
        , "fileRm return adrSh(m.m.spec)",
        , "fileMkDir return adrSh('mkdir' m.m.stream%%qualify)" ,
        , "fileRmDir return fileLinuxRmDir(m, opt)" */
    call classNew "n FileList u JRW", "m",
        , "jReset if arg2 == 'r' then m.m.dsnMask=arg'.**';",
                                "else m.m.dsnMask=arg'.*';",
        , "jOpen  call csiOpen m, m.m.dsnMask",
        , "jClose" ,
        , "jRead return csiNext(m, m)"
    call classNew "n FileEdit u File", "m",
        , "jClose call fileTsoEditClose m"
    return
endProcedure fileTsoIni
/* copy fileTso end   *************************************************/
/* copy mat begin *****************************************************/
sqrt: procedure expose m.
parse arg n
    if n < 2 then
        return n
     k = 1
     g = n
     do while k+1 < g
         m = (g + k) % 2
         if m * m <= n then
             k = m
         else
             g = m
         end
     return k
endProcedure sqrt

isPrime: procedure expose m.
parse arg n
    if n < 2 then
        return 0
    if n // 2 = 0 then
        return n = 2
    do q=3 by 2 to sqrt(n)
        if n // q = 0 then
            return 0
        end
    return 1
endProcedure isPrime

nxPrime: procedure expose m.
parse arg n
    do i = n + (\ (n // 2)) by 2
        if isPrime(i) then
            return i
        end
endProcedure nxPrime

permut: procedure expose m.
parse arg m, p
    m.m.1 = 1
    do i=2 while p > 0
        j = i - (p // i)
        m.m.i = m.m.j
        m.m.j = i
        p = p % i
        end
    m.m.0 = i-1
    return i-1
endProcedure permut
/* copy mat end   *****************************************************/
/* copy sqlDiv begin **************************************************/
/*--- generate the format m for a sql cx as specified in sp
          use the information from the sqlDa -------------------------*/
sqlFTabReset: procedure expose m.
parse arg ff, c, maxCh, maxBlo, maxDe
    return sqlFTabOpts(fTabReset(ff, , , '-'), c, maxCh, maxBlo, maxDe)

sqlFTabOpts: procedure expose m.
parse arg ff, cx, m.ff.maxChar, m.ff.blobMax, m.ff.maxDec
    if m.ff.maxChar == '' then
        m.ff.maxChar = 32
    if m.ff.blobMax == '' then
        m.ff.blobMax = 200
    bf = '%-'max(m.ff.blobMax, 4)'C'
    m.ff.flds = ''
    m.ff.sqlX = cx
    m.ff.sqlOthers = 0
    m.ff.sql2fmt.384 = '%-10C' /* date    */
    m.ff.sql2fmt.388 = '%-8C'  /* time    */
    m.ff.sql2fmt.392 = '%-26C' /* timestamp */
    m.ff.sql2fmt.400 = 'c'     /* graphic string */
    m.ff.sql2fmt.404 = bf      /* BLOB           */
    m.ff.sql2fmt.408 = bf      /* CLOB           */
    m.ff.sql2fmt.412 = bf      /* DBCLOB         */
    m.ff.sql2fmt.448 = 'c'     /* varchar        */
    m.ff.sql2fmt.452 = 'c'     /* char           */
    m.ff.sql2fmt.452 = 'c'     /* long varchar   */
    m.ff.sql2fmt.460 = 'c'     /* null term. string */
    m.ff.sql2fmt.464 = 'c'     /* graphic varchar   */
    m.ff.sql2fmt.468 = 'c'     /* graphic char      */
    m.ff.sql2fmt.472 = 'c'     /* long graphic varchar   */
    m.ff.sql2fmt.480 = '%-7e'  /* float                  */
    m.ff.sql2fmt.484 = 'd'     /* packed decimal         */
    m.ff.sql2fmt.492 = '%20i'  /* bigInt                 */
    m.ff.sql2fmt.496 = '%11i'  /* int                    */
    m.ff.sql2fmt.500 = '%6i'   /* smallInt               */
    m.ff.sql2fmt.904 = '%-34H' /* rowID 17 Byte Binary   */
    return ff
endProcedure sqlFTabOpts
/*--- set a defaultFormat for type tx in fTab ff ---------------------*/
sqlFTabDef: procedure expose m.
parse arg ff, tx, m.ff.sql2fmt.tx
return ff

sqlFTabAdd: procedure expose m.
parse arg m, c1 aDone, f1, l1
    if symbol('m.m.set.c1') == 'VAR' then do
        sx = m.m.set.c1
        if word(m.m.set.sx, 1) == c1 & sx <= m.m.set.0 then do
            parse var m.m.set.sx c1 aDone
            f1 = m.m.set.sx.fmt
            l1 = m.m.set.sx.labelTi
            end
        end
    cx = m.m.sqlX
    f2x = classMet(sqlFetchClass(cx), 'f2x')
    if symbol('m.f2x.c1') \== 'VAR' then
        call err 'colName not found' c1
    kx = m.f2x.c1
    t1 = m.sql.cx.d.kx.sqlName
    if l1 == '' then
        l1 = t1
    if f1 == '' then do
        ty = m.sql.cx.d.kx.sqlType
        le = m.sql.cx.d.kx.sqlLen
        withNulls = ty // 2
        ty = ty - withNulls
        if symbol('m.m.sql2fmt.ty') <> 'VAR' then
            call err 'sqlType' ty 'col' c1 'not supported'
        f1 = m.m.sql2fmt.ty
        if f1 == 'c' then
            f1 = '%-'min(le, m.m.maxChar)'C'
        else if f1 == 'd' then do
            trace ?r
            pr =  le % 256
            de =  le // 256
            f1 = '%'pr'.'de'i'
            end
        if \ abbrev(f1, '%') then
            call err 'sqlType' ty 'col' c1 'bad format' f1
        end
    call fTabAddRCT m, c1 aDone, f1, t1, l1
    ox = m.m.0
    m.m.ox.tit.0 = max(arg()-3, 1)
    do tx=2 to m.m.ox.tit.0
        m.m.ox.tit.tx = arg(tx+3)
        end
    return m
endProcedure sqlFTabAdd

sqlFTabOthers: procedure expose m.
parse arg m, doNot
    cx = m.m.sqlX
    ff = m.sql.cx.fetchFlds
    m.m.sqlOthers = 1
    do kx=1 to m.sql.cx.d.sqlD
        c1 = word(ff, kx)
        wx = wordPos(c1, m.m.cols)
        if (wx < 1 | m.m.wx.done \== 1) & wordPos(c1, doNot) < 1 then
            call sqlFTabAdd m, c1
        end
    return m
endProcedure sqlFTabOthers

sqlFTab: procedure expose m.
parse arg m
    call fTabBegin m
    do while sqlRxFetch(m.m.sqlX, 'sqlFTab')
        call out f(m.m.fmt, 'sqlFTab')
        end
    return fTabEnd(m)
endProcedure sqlFTab

sqlFTabCol: procedure expose m.
parse arg m
    if pos('c', m.m.generated) < 1 then
        call fTabColGen m
    do rx=1 while sqlRxFetch(m.m.sqlX, 'sqlFTab')
        call out left('--- row' rx '',  80, '-')
        call fTabCol m, 'sqlFTab'
        end
    call out left('--- end of' (rx-1) 'rows ', 80, '-')
    return
endProcedure sqlFTabCol

sqlCatTb: procedure expose m.
parse arg ty gOnly, wh, ord, fTab, paPlus
    tb = tkrTable(, ty)
    if gOnly == 1 then
        edFun = ''
    else
        edFun = tkrTable(, ty, 'e')
    cx = 1
    ft = 'ft'm.tb.alias
    call sqlFTabOpts FTabReset(ft, 'c 1', '1 c', '-'),
                     ,cx , 12, if(fTab, , 2000)
    call sqlFTabDef      ft, 492, '%7e'
    call FTabSet         ft, 'CONTOKEN'  , '%-16H'
    call FTabSet         ft, 'DCONTOKEN'  , '%-16H'
    call FTabSet         ft, 'DBNAME'    , '%-8C', 'db'
    call FTabSet         ft, 'DSNAME'    , '%-44C'
    call FTabSet         ft, 'DSNUM'     , '%5i'
    call FTabSet         ft, 'PARTITION' ,'%5i' , 'part'
    call FTabSet         ft, 'PIT_RBA'   , '%-12H'
    call FTabSet         ft, 'RBA1'      , '%-12H'
    call FTabSet         ft, 'RBA2'      , '%-12H'
    call FTabSet         ft, 'START_RBA' ,'%-12H'
    call FTabSet         ft, 'TSNAME'    , '%-8C', 'ts'
    call FTabSet         ft, 'VERSION'   , '%-28C'
    if edFun \== '' then do
        interpret 'sq =' edFun'(ft, tb, wh, ord)'
        end
    else do
        cl = sqlColList(m.tb.table, m.ft.blobMax)
        sq = 'select' cl tkrTable( , tb, 'f') wh ,
             'order by' if(ord=='', m.tb.order, ord)
        call sqlQuery cx, sq
        call sqlFTabOthers ft
        call sqlCatTbVl ft, tb
        end
    if fTab then
        call sqlFTab ft
    else
        call sqlFTabCol ft
    call sqlClose cx
    call sqlCatTbTrailer space(m.TKR.path paPlus, 1), sq
    return 0
endProcedure sqlCatTb

sqlCatTbVlsep:
    return '+++'

sqlCatTbVl: procedure expose m.
parse arg ft, tb, sep
    if sep == '' then
        sep = sqlCatTbVLsep()
    if m.tb.vlKey == '' then
        return
    ky = m.tb.vlKey
    ff = ''
    tt = ''
    do kx=1 to m.ky.0
        tt = tt || sep || m.ky.kx.col
        ff = ff || sep'@'m.ky.kx.col'%S'
        end
    call fTabAddRCT ft, substr(tt,length(sep)+1) ,
          , substr(ff,length(sep)+1)
    return
endProcedure sqlCatTbVl

sqlCatTbTrailer: procedure expose m.
parse arg pa, sq
    ox = lastPos(' order by ', sq)
    if ox < 1 then
        call err 'order by not found in' sq
    ord = substr(sq, ox+10)
    sq = left(sq, ox-1)
    sqUp = translate(sq)
    call out ''
    call out 'dbSys:' m.sql_dbSys
    call out 'path:' pa
    int = ''
    iNx = '  '
    br = ''
    cx = 1
    stops = '(select from where'
    do while cx < length(sq)
        nx = -1
        do sx=1 to words(stops)
            n2 = pos(word(stops, sx), sq, cx+1)
            if n2 > cx & (nx < 1 | n2 < nx) then
                nx = n2
            end
        if nx < 0 then
            leave
        call out int || substr(sq, cx, nx-cx)
        int = iNx
        if substr(sq, nx, 3) = '(se' then do
            iNx = iNx'  '
            br = left(br, length(int))')'
            end
        cx = nx
        end
    ll =  strip(substr(sq, cx))
    bq = strip(br)
    do while bq <> ''
        if right(bq, 1) \== ')' | right(ll, 1) \== ')' then
           call err 'missing ) bq:' bq', ll:' ll
        ll = strip(left(ll, length(ll) - 1))
        bq = strip(left(bq, length(bq) - 1))
        end
    call out int || ll
    if br <> '' then
        call out br
    if ord <> '' then
        call out '  order by' ord
    return
endProcedure sqlCatTbTrailer

sqlCatIxKeys: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select ikK.colSeq, ikK.colName, ikK.ordering, ikK.period' ,
             ', ik.creator, ik.name, ik.tbCreator, ik.tbName, ikC.*'  ,
          tkrTable(, tb ,'f') wh,
          'order by' if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, CREATOR, '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME   , '%-16C','index'
    call sqlFTabAdd      ft, colSeq  , '%5i',  'coSeq'
    call sqlFTabAdd      ft, colName, '%-16C', 'column'
    call sqlFTabAdd      ft, ordering
    call sqlFTabAdd      ft, period
    call sqlFTabAdd      ft, COLNO
    call sqlFTabAdd      ft, COLTYPE
    call sqlFTabAdd      ft, LENGTH
    call sqlFTabAdd      ft, SCALE
    call sqlFTabAdd      ft, NULLS
    call sqlFTabOthers ft, 'COL9 COL10 COL11 COL47'
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatIxKeys

sqlCatIXStats: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select *' tkrTable( , tb, 'f') wh ,
         'order by' if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, CREATOR, '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME   ,       , 'index'
    call sqlFTabAdd      ft, INSTANCE   , '%1i' , 'i'
    call sqlFTabAdd      ft, PARTITION , , 'part'
    call sqlFTabOthers ft
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatIXStats

sqlCatTables: procedure expose m.
parse arg ft, tb, wh, ord
    al = m.tb.alias
    sq = 'select' al'.*, tsX.type tsType, tsX.partitions',
            ', tsX.pgSize, tsX.dsSize' ,
            ', timestamp(rba1 || x''0000'') rba1Tst' ,
            ', timestamp(rba2 || x''0000'') rba2Tst' ,
          'from' m.tb.table 'left join sysibm.sysTablespace tsX',
            'on' al'.dbName = tsx.dbName and' al'.tsName = tsX.name',
            'where' m.tb.cond wh ,
            'order by'  if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, creator   , '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME      , '%-24C', 'table'
    call sqlFTabAdd      ft, type
    call sqlFTabAdd      ft, dbNAME    , '%-8C', 'db'
    call sqlFTabAdd      ft, tsNAME    , '%-8C', 'ts'
    call sqlFTabAdd      ft, tsType
    call sqlFTabAdd      ft, partitions,       , 'parts'
    call sqlFTabAdd      ft, pgSize
    call sqlFTabAdd      ft, dsSize
    call sqlFTabOthers ft, 'RBA1 RBA1TST RBA2 RBA2TST'
    call sqlFTabAdd      ft, rba1      , '%-12H'
    call sqlFTabAdd      ft, rba1Tst   ,       , 'rba1Timestamp:GMT'
    call sqlFTabAdd      ft, rba2      , '%-12H'
    call sqlFTabAdd      ft, rba2Tst   ,       , 'rba2Timestamp:GMT'
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatTables

sqlCatTSStats: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select' m.tb.alias'.*' ,
           tkrTable( , tb, 'f') wh ,
           'order by' if(ord == '', m.tb.order , ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, DBNAME, '%-8C', 'db'
    call sqlFTabAdd      ft, NAME   , '%-8C', 'ts'
    call sqlFTabAdd      ft, INSTANCE   , '%1i' , 'i'
    call sqlFTabAdd      ft, PARTITION , , 'part'
    call sqlFTabAdd      ft, NACTIVE   , , 'nActive'
    call sqlFTabAdd      ft, NPAGES    , , 'nPages'
    call sqlFTabAdd      ft, SPACE       , , 'spaceKB'
    call sqlFTabAdd      ft, TOTALROWS   , , 'totRows'
    call sqlFTabAdd      ft, DATASIZE         , , 'dataSz'
    call sqlFTabAdd      ft, LOADRLASTTIME    , , 'loadRLasttime'
    call sqlFTabAdd      ft, REORGLASTTIME    , , 'reorgLasttime'
    call sqlFTabAdd      ft, REORGINSERTS     , , 'inserts'
    call sqlFTabAdd      ft, REORGDELETES     , , 'deletes'
    call sqlFTabAdd      ft, REORGUPDATES     , , 'updates'
    call sqlFTabAdd      ft, REORGUNCLUSTINS  , , 'unClIns'
    call sqlFTabAdd      ft, REORGDISORGLOB   , , 'disorgL'
    call sqlFTabAdd      ft, REORGMASSDELETE  , , 'massDel'
    call sqlFTabAdd      ft, REORGNEARINDREF  , , 'nearInd'
    call sqlFTabAdd      ft, REORGFARINDREF   , , 'farInd'
    call sqlFTabAdd      ft, REORGCLUSTERSENS , , 'cluSens'
    call sqlFTabAdd      ft, REORGSCANACCESS  , , 'scanAcc'
    call sqlFTabAdd      ft, REORGHASHACCESS  , , 'hashAcc'
    call sqlFTabAdd      ft, STATSLASTTIME    , , 'statsLasttime'
    call sqlFTabAdd      ft, STATSINSERTS     , , 'inserts'
    call sqlFTabAdd      ft, STATSDELETES     , , 'deletes'
    call sqlFTabAdd      ft, STATSUPDATES     , , 'updates'
    call sqlFTabAdd      ft, STATSMASSDELETE  , , 'massDel'
    call sqlFTabAdd      ft, COPYLASTTIME     , , 'copyLasttime'
    call sqlFTabAdd      ft, COPYUPDATETIME   , , 'copyUpdatetime'
    call sqlFTabAdd      ft, COPYUPDATELRSN   , '%-12H', 'updateLRSN'
    call sqlFTabAdd      ft, COPYUPDATEDPAGES , , 'updaPgs'
    call sqlFTabAdd      ft, COPYCHANGES      , , 'changes'
    call sqlFTabOthers ft
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatTSStats

sql4obj: procedure expose m.
parse arg m, tb
    call out 'insert into' tb '--' className(objClass(m))
    line = ''
    ff = oFlds(m)
    pr = '   ('
    do fx=1 to m.ff.0
        call sql4ObjOut m.ff.fx
        end
    call sql4ObjOut , 1
    call out '   ) values '
    pr = '   ('
    do fx=1 to m.ff.0
        f1 = m || left('.', m.ff.fx  \== '')m.ff.fx
        v = m.f1   /* no strip T, gives errors in RCM profile | */
        if dataType(v, n) then
            call sql4ObjOut v
        else do qx=1 until v == ''
            vx = verify(v, m.ut_alfPrint)
            if vx = 0 then do
                l1 = min(60, length(v))
                w = quote(left(v, l1), "'")
                end
            else if vx > 29 then do
                l1 = min(60, vx-1)
                w = quote(left(v, l1), "'")
                end
            else do
                l1 = min(29, length(v))
                w = 'x'quote(c2x(left(v, l1)), "'")
                end
            if qx == 1 then
                call sql4ObjOut w
            else do
                if qx = 2 then
                    call sql4ObjOut , 1
                call out '   ||' w
                end
            v = substr(v, l1+1)
            end
        end
    call sql4ObjOut , 1
    call out '   ) ; '
    return
endProcedure
sql4objOut:
parse arg t1, force
    if (force == 1 & line \== '') | length(line t1) > 65 then do
        call out pr  substr(line, 3)
        pr = '   ,'
        line = ''
        end
    if force \== 1 then
        line = line',' t1
    return
endProcedure sql4objOut
/*--- -dis db interface ---------------------------------------------*/
/*--- do one -dis db... and insert it into stem  --------------------*/
sqlDisDb: procedure expose m.
parse upper arg o, cc
    do cx=1
        mid = strip(left(m.cc.cx, 10))
        if words(mid) > 1 then
            call err 'bad msgId' mid 'line:' m.cc.cx
        if mid == '' | wordPos(mid, 'DSNT360I DSNT361I DSNT365I') ,
                > 0 then
            iterate
        if mid == 'DSN9022I' then
            if cx = m.cc.0 then
                return m.o.0
            else
                call err 'not at end' cx':' m.cc.cx
        if mid \== 'DSNT362I' then
            call err 'DSNT362I expected not line:' m.cc.cx
        dx = pos('DATABASE =', m.cc.cx)
        sx = pos('STATUS ='  , m.cc.cx)
        if dx < 1 | sx <= dx then
            call err 'bad DSNT362I line' cx':' m.cc.cx
        db = word(substr(m.cc.cx, dx+10), 1)
        sta = strip(substr(m.cc.cx, sx+8))
        call sqlDisDbAdd o, db, ,0, 0, 'DB', sta
        do cx=cx+1 while abbrev(m.cc.cx, '   ')
            end
        if abbrev(m.cc.cx, 'DSNT397I ') then do
            cx = cx + 1
            if \ abbrev(space(m.cc.cx, 1),
                 , 'NAME TYPE PART STATUS ') then
                call err 'NAME TYPE PART STATUS mismatch' cx m.cc.cx
            txNa = pos('NAME', m.cc.cx)
            txTy = pos('TYPE', m.cc.cx)
            txPa = pos('PART', m.cc.cx)
            txSt = pos('STAT', m.cc.cx)
            txEn = verify(m.cc.cx, ' ', 'n', txSt+6)
            if 0 then say 'title' txNa txTy txPa txSt txEn cx m.cc.cx
            cx=cx+1
            do forever
                do while abbrev(m.cc.cx, '----')
                    cx = cx + 1
                    end
                if abbrev(m.cc.cx, '*') then
                    leave
                parse var m.cc.cx sp =(txTy)  ty . =(txPa)  paFr . ,
                                       =(txSt) sta   =(txEn)
                sp = strip(sp)
                if words(sp) \= 1 | wordPos(ty, 'TS IX') < 0 then
                    call err 'bad name or type' cx':'m.cc.cx
                if paFr == '' | paFr == 'L*' then
                    paFr = 0
                else if abbrev(paFr, 'D') | abbrev(paFr, 'L') then
                    paFr = substr(paFr, 2)
                if \ datatype(paFr, 'n') then
                    call err 'part not numeric' cx':'m.cc.cx
                paTo = paFr
                cw = cx
                cx = cx + 1
                if abbrev(m.cc.cx, '    -THRU ') then do
                    parse var m.cc.cx =(txPa)  paTo . =(txSt)
                    if \ datatype(paTo, 'n') then
                        call err '-thru part not numeric' cx':'m.cc.cx
                    cx = cx + 1
                    end
                call sqlDisDbAdd o, db, sp, paFr, paTo, ty, sta
                end
            end
        if m.cc.cx = '******** NO SPACES FOUND' then
            cx = cx + 1
        if abbrev(m.cc.cx, '******* DISPLAY OF DATABASE ') ,
          & word(m.cc.cx,5) == db then
            if word(m.cc.cx,6) == 'ENDED' then
                iterate
            else if word(m.cc.cx,6) == 'TERMINATED' then
                call err 'db display overflow' cx':' m.cc.cx
        call err 'database' db 'ENDED mismatch' cx':' m.cc.cx
        end
endProcedure sqlDbDis

/*--- insert one tuple into tDbState ---------------------------------*/
sqlDisDbAdd: procedure expose m.
    if arg(7) == '' | arg(7) == 'RW' then
         return
parse arg o
    m.o.0 = m.o.0 + 1
    q = o'.'m.o.0
parse arg , m.q.db, m.q.sp, m.q.paFr, m.q.paTo, m.q.ty, m.q.sta
 /* say added q m.q.db'.'m.q.sp':'m.q.paFr'-'m.q.paTo m.q.ty':'m.q.sta*/
    ky = m.q.db'.'m.q.sp
    if symbol('m.o.ky') \== 'VAR' then
        m.o.ky = m.o.0
    return
endProceedure sqlDisDbAdd

/*--- get index in o for db sp part ----------------------------------*/
sqlDisDbIndex: procedure expose m.
parse arg st, d, s, pa
     if symbol('m.st.d.s') \== 'VAR' then
         return 0
     ix = m.st.d.s
     if ix > m.st.0 | d \== m.st.ix.db | s \== m.st.ix.sp then
         return 0
     if pa == '' then
         return ix
     do ix=ix to m.st.0 while d == m.st.ix.db & s == m.st.ix.sp
         if pa < m.st.ix.paFr then
             return 0
         else if pa <= m.st.ix.paTo then
             return ix
         end
     return 0
endProcedure sqlDisDbIndex

/*--- dsn Command, return true if continuation needed ----------------*/
sqlDsnCont: procedure expose m.
parse arg cc, ssid, cmd
    say '???dsnCont' cmd
    cont = sqlDsn(cc, ssid, cmd, 12) <> 0
    if cont then do
        cz = m.cc.0
        cy = cz - 1
        if \ abbrev(m.cc.cy, DSNT311I) ,
                | \ abbrev(m.cc.cz, 'DSN9023I') then
            call err 'sqlDsn rc=12 for' cmd 'out='cz ,
                     '\n'cy'='m.cc.cy'\n'cz'='m.cc.cz
        m.cc.0 = cz-2
        end
    return cont
endProcedure sqlDsnCont
/* copy sqlDiv end   **************************************************/
/* copy db2Cat begin **************************************************/
catTbLastCol: procedure expose m.
parse upper arg cr, tb
    return sql2one( ,
          "select strip(char(colcount)) || ' ' || strip(c.name) one"  ,
              "from sysibm.sysTables t left join sysibm.sysColumns c" ,
                  "on c.tbCreator = t.creator and c.tbName = t.name"  ,
                       "and c.colNo = t.colCount"                     ,
               "where t.creator = '"cr"' and t.name = '"tb"'",,,, 'r')
endProcedure catTbLastCol

catTbCols: procedure expose m.
parse upper arg cr, tb
    if sql2St("select strip(name) name "     ,
          "from sysibm.sysColumns " ,
          "where tbcreator = '"cr"' and tbname='"tb"'",
          "order by colNo", ggSt) < 1 then
        return ''
    res = m.ggst.1.name
    do cx=2 to m.ggst.0
        res = res m.ggst.cx.name
        end
    return res
endProcedure catTbCols

catTbColsTrunc: procedure expose m.
parse upper arg cr, tb, maxL
    if sql2St("select strip(name) name, colType, length, length2"     ,
          "from sysibm.sysColumns " ,
          "where tbcreator = '"cr"' and tbname='"tb"'",
          "order by colNo", ggSt) < 1 then
        return ''
    res = ''
    do cx=1 to m.ggst.0
        ty = m.ggSt.cx.colType
        if pos('LOB', ty) > 0 then
            res = res', substr('m.ggSt.cx.name', 1,' ,
                 min(maxL, m.ggSt.cx.length2)') 'm.ggSt.cx.name
        else if pos('CHAR', ty) > 0 & m.ggSt.cx.length > maxL then
            res = res', substr('m.ggSt.cx.name', 1,' maxL')',
                 m.ggSt.cx.name
        else
            res = res',' m.ggSt.cx.name
        end
    return substr(res, 3)
endProcedure catTbColsTrunc

catIxKeys: procedure expose m.
parse upper arg cr, ix
    sql = "select colSeq sq, colName col, ordering ord"       ,
              "from sysibm.sysKeys"                                   ,
               "where ixCreator = '"cr"' and ixName = '"ix"'" ,
               "order by colSeq"
    call sqlQuery 1, sql
    res = ''
    drop d
    do kx=1 while sqlFetch(1, d)
        if m.d.sq \= kx then
            call err 'expected' kx 'but got colSeq' m.d.sq ,
                     'in index' cr'.'ix'.'m.d.col
        res = res || strip(m.d.col) || translate(m.d.ord, '<>?', 'ADR')
        end
    call sqlClose 1
    return res
endProcedure catIxKeys

catColCom: procedure expose m.
parse upper arg fCr, fTb, tCr, tTb
    sql = "select t.name, t.colType, t.nulls, t.""DEFAULT"""        ,
                    ", coalesce(f.nulls, 'new')"                    ,
              "from sysibm.sysColumns t"                            ,
                "left join sysibm.sysColumns f"                     ,
                  "on f.tbCreator = '"fCr"' and f.tbName = '"fTb"'" ,
                    "and f.name = t.name"                           ,
              "where t.tbCreator = '"tCr"' and t.tbName = '"tTb"'"  ,
              "order by t.colNo"
    call sqlQuery 1, sql, 'na ty nu de nn'
    pr = ' '
    do kx=1 while sqlFetch(1)
        /* say kx m..na m..ty m..nu m..de 'nn' m..nn */
        if pos('CHAR', m..ty) > 0 then
            dv = "''"
        else if pos('INT' ,m..ty) > 0 ,
                | wordPos(m..ty, 'REAL FLOAT') > 0 then
            dv = 0
        else if m..ty == 'TIMESTMP' then
            dv = '0001-01-01-00.00.00'
        else if pos('LOB', m..ty) > 0 then
            dv = m..ty"('')"
        else
            dv = '???'
        if m..nu = 'Y' then
            dv = 'case when 1=0 then' dv 'else null end'
        r = '???'
        if m..ty = 'ROWID' then do
            r = '--'
            end
        else if m..nn == 'new' then do
            if m..de = 'Y' then
                r = '--'
            else if m..nu == 'N' then
                r = dv
            else
                r = 'case when 1=0 then' dv 'else null end'
            end
        else do
            if m..nu = 'Y' | (m..nu = m..nn) then
                r = ''
            else
                r = 'coalesce('m..na',' dv')'
            end
        if abbrev(r, '--') then do
            r = ' ' r
            end
        else do
            r = pr r
            pr = ','
            end
        if pos('???', r) > 0 then
            call err 'no default for type' m..ty 'in' tCr'.'tTb'.'m..na
        call out r m..na
        end
    call sqlClose 1
    return
endProcedure catColCom
/* copy db2Cat end   **************************************************/
/* copy sqlO   begin ***************************************************
    sql interface  mit  o und j Anbindung
***********************************************************************/
sqlIni: procedure expose m.
    if m.sql_ini == 1 then
        return
    m.sql_ini = 1
    call sqlRxIni
    call jIni
    call fTabIni
    call scanWinIni
    m.sqlO.cursors  = left('', 200)
    m.sql_rdrClass = classNew('n SqlRdr u JRW', 'm',
        , "jReset m.m.src = arg; m.m.type = arg2;",
        , "jOpen  call sqlRdrOpen m, opt",
        , "jClose call sqlRdrClose m",
        , "jRead return sqlRdrRead(m)")
    call classNew 'n SqlResRdr u JRW', 'm',
        , "jReset m.m.cursor = arg; m.m.type = arg2;",
        , "jOpen  call sqlRdrO2 m" ,
        , "jClose call sqlClose m.m.cursor" ,
        , "jRead return sqlRdrRead(m)"
    call classNew 'n SqlRxConnection u', 'm',
        , "sqlQuery  return sqlRxQuery(cx, src, feVa, 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, feVa, 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, feVa, retOK)",
        , "sqlFetch  return sqlCsmFetch(cx, dst, retOk)",
        , "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, feVa, retOk)",
        , "sqlFetch  return sqlCsmFetch(m.cx.cursor, dst, retOk)",
        , "sqlClose  return 0",
        , "sqlUpdate call err 'implement sqlCsmUpdate'"   ,
        , "sqlCall   call err 'implement sqlCsmCall'"
/*  call classNew 'n SqlExecuteRdr u JRW', 'm',
        , "jReset    call sqlExecuteRdrReset(m, arg, arg2)" ,
        , "jOpen     call sqlExecuteRdrOpen(m)" ,
        , "jClose    call sqlExecuteRdrClose(m)" ,
        , "jRead     call sqlExecuteRdrRead(m)"  ???????? */
    return 0
endProcedure sqlIni

/*--- connect to DB2 dsnRexx or csm ----------------------------------*/
sqlConnect: procedure expose m.
parse upper arg sys, retOk
    call sqlIni
    if sys == '' then
        sys = sqlDefaultSys()
    if pos('/', sys) <= 0 then do
        call  sqlRxConnect sys
        m.sql_connClass = class4Name('SqlRxConnection')
        end
    else do
        parse var sys m.sql_csmHost '/' m.sql_dbSys
        m.sql_connClass = class4Name('SqlCsmConnection')
        end
    return 0
endProcedure sqlConnect

/*--- disconnect from db2 --------------------------------------------*/
sqlDisconnect: procedure expose m.
parse arg retOk
    if m.sql_csmHost == '' then
        call sqlRxDisconnect
    else
        m.sql_csmHost = ''
    m.sql_dbSys = ''
    m.sql_connClass = 'sql not connected'
    return 0
endProcedure sqlDisonnect

/*--- execute a query from sql, with one resultset -------------------*/
sqlQuery: procedure expose m.
parse arg cx, src, feVa, retOk
    interpret classMet(m.sql_connClass, 'sqlQuery')
endProcedue sqlQuery
/*--- fetch next row to m.dst.* at end return false ------------------*/
sqlFetch: procedure expose m.
parse arg cx, dst, retOk
    interpret classMet(m.sql_connClass, 'sqlFetch')
endProcedue sqlFetch
/*--- close cursor 'c'cx ---------------------------------------------*/
sqlClose: procedure expose m.
parse arg cx, retOk
    interpret classMet(m.sql_connClass, 'sqlClose')
endProcedue sqlClose
/*-- execute an sql with no resultset, but possibly outParms ---------*/
sqlUpdate: procedure expose m.
parse arg cx, src, retOk
    interpret classMet(m.sql_connClass, 'sqlUpdate')
endProcedue sqlUpdate

/*-- execute an sql call with outParms and multiple resultSets -------*/
sqlCall: procedure expose m.
parse arg cx, src, retOk
    interpret classMet(m.sql_connClass, 'sqlCall')
endProcedure sqlCall

/*--- return a free cursor -------------------------------------------*/
sqlGetCursor: procedure expose m.
parse arg rng
    if rng == '' then
        return sqlGetCursorRng(rng, 10, 48)
    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

/*** execute sql's in a stream (separated by ;)
       opt: 'o' ==> write objects, otherwise fTabAuto
            'sql72' ==> spufi formatting (window 72) else linebreaks */
sqlStmts: procedure expose m.
parse arg sqlSrc, retOk, opt
   return sqlsOut(scanSqlStmtRdr(sqlSrc, opt), retOk, 'a')
   return
endProcedure sqlStmts

/*--- execute the given sql plus a commit
         until no more rows are updated -----------------------------*/
sqlUpdComLoop: procedure expose m.
parse arg src, retOk
    crs = sqlGetCursor()
    upds = 0
    if retOk == '' then
        retOk = 100
    do coms=0
        cd = sqlExecute(crs, src, retOk)
        if m.sql.crs.updateCount < 1 then do
            call sqlFreeCursor(crs)
            return sqlMsgLine( , upds, src, coms 'commits')
            end
        upds = upds + m.sql.crs.updateCount
        call sqlCommit
        if coms // 20 = 19 then
            say sqlMsgLine(time(), upds, src, (coms+1) 'commits')
        end
endProcedure sqlUpdComLoop

/*--- sql call statement ---------------------------------------------*/
sqlStmtCall: procedure expose m.
parse arg src, retOk, opt
    s = scanSqlReset(scanSrc(sqlstmtcall, 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.ut_alfDot) 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 fTabAuto 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 jRead(rdr)
         a = m.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

/*--- prepare and open cursor
      generate type and fetchList ------------------------------------*/
sqlRdr: procedure expose m.
parse arg src, type
     return oNew('SqlRdr', scanSqlIn2Stmt(src), type)
endProcedure sqlRdr

sqlRdrOpen: procedure expose m.
parse arg m, opt
    if opt\== m.j.cRead then
        call err 'opt not' m.j.cRead 'sqlRdrOpen('m',' opt')'
    cx = sqlGetCursor()
    m.m.cursor = cx
    if words(m.m.type) <> 1 | abbrev(m.m.type, ' ') then do
        m.sql.cx.fetchClass = ''
        call sqlQuery m.m.cursor, m.m.src, m.m.type
        m.m.type = sqlFetchClass(cx)
        end
    else do
        m.m.type = class4name(m.m.type)
        call sqlQuery m.m.cursor, m.m.src, mCat(classFlds(m.m.type),' ')
        m.sql.cx.fetchClass = m.m.type
        end
    call sqlRdrO2 m
    return
endProcedure sqlRdrOpen

sqlQuery2Rdr: procedure expose m.
parse arg cx
    r = jReset(oMutate('SQL_RDR.'cx, 'SqlResRdr'), cx)
    m.r.type = sqlFetchClass(cx)
    return r
endProcedure

sqlFetchClass: procedure expose m.
parse arg cx, force
     if m.sql.cx.fetchClass == '' | force == 1 then
          m.sql.cx.fetchClass = classNew('n* Sql u f%v' ,
              m.sql.cx.fetchFlds)
     return m.sql.cx.fetchClass
endProcedure sqlFetchClass

sqlRdrO2: procedure expose m.
parse arg m
    cx  = m.m.cursor
    if m.m.type \== m.sql.cx.fetchClass | m.m.Type == '' then
        call err 'type' m.m.type '\==' m.sql.cx.fetchClass 'fetchClass'
    m.m.rowCount = 0
    m.sql_lastRdr = m
    return
endProcedure sqlRdrO2

/*--- close sql Cursor -----------------------------------------------*/
sqlRdrClose: procedure expose m.
parse arg m, v
    call sqlClose m.m.cursor
    call sqlFreeCursor m.m.cursor
    m.m.cursor = ''
    return m
endProcedure sqlRdrClose

/*--- read next from cursor, return as object ------------------------*/
sqlRdrRead: procedure expose m.
parse arg m
    v = oNew(m.m.type)
    if \ sqlFetch(m.m.cursor, v) then do
        call mFree v
        return 0
        end
    m.m.rowCount = m.m.rowCount + 1
    m.m = v
    return 1
endProcedure sqlRdrRead

/*--- return sqlFTab for this (open) rdr -----------------------------*/
sqlRdrFtabReset: procedure expose m.
parse arg m, q, maxChar, blobMax, maxDec
    if m == '' then
        m = m.sql_lastRdr
    if \ dataType(m.m.cursor, 'n') then
        call err 'sqlRdrFTabReset('m') but cursor empty'
    return sqlFTabReset(q, m.m.cursor, maxChar, blobMax, maxDec)
endProcedure sqlRdrFTabReset

/*--- output sql as table --------------------------------------------*/
sql2tab: procedure expose m.
parse arg tBef, tAft, maxChar, blobMax, maxDec
    cx = sqlGetCursor()
    call sqlQuery cx, in2str(,' ')
    t = sqlFTabReset('SQL.'cx'.fTab', cx,
            , tBef, tAft, maxChar, blobMax, maxDec)
    call sqlFTab sqlFTabOthers(t)
    call sqlClose cx
    call sqlFreeCursor cx
    return
endProcedure sql2tab

/*--- select and write all to stdOut ---------------------------------*/
sqlSel: procedure expose m.
parse arg src, type
     s = sqlRdr(src, type)
     call pipeWriteAll s
     trace ?r
     say    m.s.rowCount
     return m.s.rowCount
endProcedure sqlSel

/*--- result of each sql read from rdr to out
           oo='a' autoTab, 't'=sqlTab, 'o'=object, 'c'=csv ----------*/
sqlsOut: procedure expose m.
parse arg rdr, retOk, oo
    m.sql_errRet = 0
    if oo == '' then
        oo = 'a'
    cx = sqlGetCursor()
    r = jOpen(in2file(rdr), '<')

    do while jRead(r)
        sqlC = sqlExecute(cx, m.r, retOk)
        if m.sql_errRet then
            leave
        if m.sql.cx.resultSet == '' then do
             call outNl(m.sql_HaHi ,
                     || sqlMsgLine(sqlC, m.sql.cx.updateCount, m.r))
           end
        else if oo == 'o' then do
            call pipeWriteAll sqlQuery2Rdr(cx)
            end
        else if oo == 'a' | oo == 't' then do
            sqR = sqlQuery2Rdr(cx)
            ft = sqlfTabOpts(fTabReset('sqls2AutoFT', 'c 1'), cx)
            if oo == 't' then do
                call sqlFTabOthers(ft)
                end
            else do
                bf = in2Buf(sqR)
                if m.sql_errRet then
                    leave
                call sqlFTabDetect ft, bf'.BUF'
                call fTab ft, bf
                call out sqlMsgLine(m.sqR.rowCount 'rows fetched',
                                   , , m.r)
                end
            end
        else
            call err 'bad outputOption' oo
        end
    call jClose r
    if m.sql_errRet then do
   /*   call out 'sqlsOut terminating because of sql error' */
        call sqlClose cx, '*'
        say 'sqlsOut terminating because of sql error'
        end
    call sqlFreeCursor cx
    return \ m.sql_errRet
endProcedure sqlsOut

/*-- execute and put result to m -------------------------------------*/
sqlExecuteRes: procedure expose m.
parse arg m, cx, m.m.sql, retOk  ?????
    m.m.sqlCode = sqlExecute(cx, m.m.sql, retOk) + deimplement
    m.m.sqlMsg = m.sql_HaHi || sqlMsgLine(m.m.sqlCode,
                             , m.sql.cx.updateCount, m.m.sql)
endProcedure sqlExecuteRes
/*--- execute stmts with options -------------------------------------*/
sqlExecuteRdrReset: procedure expose m.
parse arg rdr, wOpt, m.m.retOk
    if abbrev(wOpt, '-sql') then  + deimplement  ??????????????????
        wOpt = substr(wOpt, 5)
    call scanSqlReset m'.SCAN', rdr, wOpt, ';'
    return m
endProcedure sqlExecuteRdrReset

sqlExecuteRdrOpen: procedure expose m.
parse arg m
    call scanOpt m'.SCAN'  + deimplement  ??????????????????
    m.m.cursor = sqlGetCursor()
    return m
endProcedure sqlExecuteRdrOpen

sqlExecuteRdrClose: procedure expose m.
parse arg m
    call scanOpt m'.SCAN'    + deimplement  ??????????????????
    call sqlFreeCursor m.m.cursor
    drop m.m.cursor
    return m
endProcedure sqlExecuteRdrClose

sqlExecuteRdrRead: procedure expose m.
parse arg m, var
    src = scanSqlStmt(m'.SCAN') + deimplement  ??????????????????
    if src == '' then
        return 0
    call sqlExecuteRes m, m.m.cursor, src, m.m.retOk
    m.var = m.m.cursor
    return 1
endProcedure sqlExecuteRdrRead

/* copy sqlO   end   **************************************************/
/* copy sqlCsm begin **************************************************/
/*--- send an sql to csm and handle sqlCode --------------------------*/
sqlCsmExe:
parse arg ggSqlStmt, ggRetOk
    sql_HOST =  m.sql_csmhost
    SQL_DB2SSID = m.sql_dbSys
    sql_query = ggSqlStmt
    address tso "CSMAPPC START PGM(CSMASQL)"
    if \ (rc = 0 |  rc = 4) then
        return 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\nsql =' ggSqlStmt
        else
            call errSay 'w}'sqlMsg(sqlCA2rx(sqlCa))
    return sqlCode
endProcedure sqlCsmExe

/*--- execute a query from sql, with one resultset -------------------*/
sqlCsmQuery: procedure expose m.
parse arg cx, sqlSrc, feVa, retOk, dst
    res = sqlCsmExe(sqlSrc, 100 retOk)
    if res < 0 then
        return res
    if dst == '' then
        dst = 'SQL.'cx'.CSMDATA'
    m.dst.0 = 0
    m.dst.laIx = 0
    st = 'SQL.'cx'.COL'
    if abbrev(feVa, '?') | abbrev(feVa, ':') then do
        return err('implement sqlCmsQuery fetchVars ? or :' feVa)
        end
    else if feVa <> '' then do
        vv = feVa
        end
    else do
        vv = ''
        do kx=1 to sqlD
            vv = sqlNiceVarsAdd(vv, SQLDA_REXXNAME.kx)
            end
        end
    m.sql.cx.fetchFlds = vv
    if sqlD <> words(vv) then
        return err('sqlCsmQuery sqlD' sqlD '<>' words(vv) 'for' vv)
    do kx=1 to sqlD
        rxNa = SQLDA_REXXNAME.kx
        cn = word(vv, kx)
        do rx=1 to sqlRow#
            if substr(sqlIndicator.rx, kx ,1) == 'ff'x then
                m.dst.rx.cn = m.sqlNull
            else
                m.dst.rx.cn = value(rxNa'.'rx)
            end
        end
    m.dst.0 = sqlRow#
    m.sql_lastRdr  = 'cms' cx
    return 0
endProcedure sqlCsmQuery

sqlCsmFetch: procedure expose m.
parse arg cx, dst
    src = 'SQL.'cx'.CSMDATA'
    rx = m.src.laIx + 1
    if rx > m.src.0 then
        return 0
    m.src.laIx = rx
    ff = m.sql.cx.fetchFlds
    do kx = 1 to words(ff)
        c = word(ff, kx)
        m.dst.c = m.src.rx.c
        end
    return 1
endProcedure sqlCsmFetch
/* copy sqlCsm end   **************************************************/
/* copy sqlRx  begin ***************************************************
       Achtung: inc generiert sql aus sqlRx, Aenderungen nur in sqlRx|
    sql interface
***********************************************************************/
/*--- initialize sqlRx -----------------------------------------------*/
sqlRxIni: procedure expose m.
    if m.sqlRx_ini == 1 then
        return
    m.sqlRx_ini = 1
    call utIni
    m.sqlNull = '---'
    m.sqlInd = 'sqlInd'
    m.sql_defCurs= 49
    m.sqlCAMsg = 0
    m.sqlSuMsg = 2
    m.sql_dbSys = ''
    m.sql_csmhost = ''
    isInProd = wordPos(sysvar(sysNode), 'RZ2') > 0
    m.sql_retOk   = 'dne' copies('rod', \ isInProd)
    return 0
endProcedure sqlRxIni

/*--- get default db2System ------------------------------------------*/
sqlDefaultSys: procedure expose m.
parse arg
    if sysvar(sysnode) == 'RZ1' then
        return 'DBAF'
    else if sysvar(sysnode) == 'RZ4' then
        return 'DP4G'
    else
        call err 'no default subsys for' sysvar(sysnode)
endProcedure sqlDefaultSys

/*--- 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
    if sys == '' then
        sys = sqlDefaultSys()
    m.sql_dbSys = sys
    ggSqlStmt =  'connect' sys
    address dsnRexx ggSqlStmt
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, ggRetOk, ggSqlStmt)
    return sqlCode
endProcedure sqlRxConnect

/*--- diconnect from db2 ---------------------------------------------*/
sqlRxDisconnect: procedure expose m.
parse arg retOk
    if m.sql_dbSys == '' then
        return 0
    ggSqlStmt =  'disconnect'
    m.sql_dbSys = ''
    m.sql_csmHost = ''
    address dsnRexx ggSqlStmt
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, retOk, ggSqlStmt)
    return sqlCode
endProcedure sqlRxDisconnect

/*--- reset sql cursor 'c'cx fields ----------------------------------*/
sqlReset: procedure expose m.
parse arg cx
     m.sql.cx.updateCount = ''
     m.sql.cx.resultSet   = ''
     m.sql.cx.d.sqlD = 'noSqlDA'
     m.sql.cx.i.sqlD = 'noDescInp'
     m.sql.cx.fetchVars = ''
     m.sql.cx.fetchFlds = ''
     m.sql.cx.fetchClass = ''
     m.sql.cx.type  = ''
     return
endProcedue sqlReset

/*--- execute a query from sql, with one resultset -------------------*/
sqlRxQuery: procedure expose m.
parse arg cx, src, feVa, retOk
     call sqlReset cx
     s1 = ''
     if pos(left(feVa, 1), '?:') < 1 then
         s1 = 'into :M.SQL.'cx'.D'
     res = sqlExec('prepare s'cx s1 'from :src', retOk)
     if res < 0 then
         return res
     call sqlRxFetchVars cx, feVa
     call sqlExec 'declare c'cx 'cursor for s'cx
     res = sqlExec('open c'cx, retOk)
     if res < 0 then
         return res
     m.sql.cx.updateCount = sqlErrd.3
     m.sql.cx.resultSet = cx
     return res
endProcedure sqlRxQuery

/*--- prepare a query from sql, with one resultset -------------------*/
sqlQueryPrepare: procedure expose m.
parse arg cx, src, feVa, retOk
     call sqlReset cx
     s1 = ''
     if feVa == '' | feVa = 'd' then
         s1 = 'into :M.SQL.'cx'.D'
     res = sqlExec('prepare s'cx s1 'from :src', retOk)
     if res < 0 then
         return res
     call sqlRxFetchVars cx, feVa
     call sqlExec 'declare c'cx 'cursor for s'cx
     return sqlExec('describe input s'cx 'into :M.SQL.'cx'.I')
endProcedure sqlQueryPrepare

sqlQueryExecute: procedure expose m.
parse arg cx retOk
    do ix=1 to arg()-1
        call sqlDASet cx , 'I', ix, arg(ix+1)
        end
     res = sqlExec('open c'cx 'using descriptor :M.SQL.'cx'.I', retOk)
     if res < 0 then
         return res
     m.sql.cx.updateCount = sqlErrd.3
     m.sql.cx.resultSet = cx
     return res
endProcedure sqlQueryExecute

/*--- fetch next row to m.dst.* at end return false ------------------*/
sqlRxFetch: procedure expose m.
parse arg cx, dst, retOk
    fetCode = sqlExec('fetch c'cx 'into' m.sql.cx.fetchVars, 100 retOk)
    if fetCode == 100 then
        return 0
    if fetCode < 0 then
        return fetCode
    interpret m.sql.cx.fetchCode
    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 sqlExec('execute immediate :src', retOk)
        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, retOk)
        end
    if fun == 'DECLARE'  then do
        if 'GLOBAL' == translate(word(substr(src, bx), 2)) then
            return sqlExec('execute immediate :src', retOk)
        end
    res = sqlExec(src, retOk)
    if wordPos(fun, 'DELETE INSERT UPDATE') > 0 then
        m.sql.cx.updateCount = sqlErrd.3
    return res
endProcedure sqlRxUpdate

/*-- prepare an update -----------------------------------------------*/
salUpdatePrepare: procedure expose m.
parse arg cx, src, retOk
    res = sqlExec('prepare s'cx 'from :src', retOk)
    return sqlExec('describe input s'cx 'into :M.SQL.'cx'.I')
endProcedure salUpdatePrepare

/*-- execute a prepared update with the given arguments --------------*/
sqlUpdExecute: procedure expose m.
parse arg cx retOk
    do ix=1 to arg()-1
        call sqlDASet cx , 'I', ix, arg(ix+1)
        end
    res = sqlExec('execute s'cx 'using descriptor :M.SQL.'cx'.I',
                  , retOk)
    m.sql.cx.updateCount = sqlErrd.3
    return res
endProcedure sqlUpdExecute

/*-- execute a query, update or call ---------------------------------*/
sqlExecute: procedure expose m.
parse arg cx, src, retOk
    f = translate(word(src, 1))
    bx = pos('(', f)
    if bx > 0 then
        f = left(f, max(1, bx-1))
    m.sql.cx.fun = f
    if f == 'SELECT' | f == 'WITH' | f == '(' 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

/*-- fetch all rows to stem and close --------------------------------*/
sqlFetch2St: procedure expose m.
parse arg cx, dst, retOK
    do sx=1 while sqlFetch(cx, dst'.'sx)
       end
    res = sx-1
    m.dst.0 = sx-1
    call sqlClose cx
    return m.dst.0
endProcedure sqlFetch2St

/*-- execute a query, copy result to stem ----------------------------*/
sql2St: procedure expose m.
parse arg src, dst, feVa, retOK
    cx = m.sql_defCurs
    res = sqlQuery(cx, src, feVa, retOk)
    return sqlFetch2St(cx, dst, retOk)
endProcedure sql2St

/*-- return first column of the only row and close -------------------*/
sqlFetch2One: procedure expose m.
parse arg cx, dst, retNone
    f1 = sqlFetch(cx, dst)
    if f1 then
        f2 = sqlFetch(cx, dst'.2')
    call sqlClose cx
    if \ f1 then
        if retNone \== '' then
            return substr(retNone, 2)
        else
            call err 'sqlFetch2One: no row returned'
    else if f2 then
        call err 'sqlFetch2One: more than 1 row'
    if m.sql.cx.fetchFlds == '' then do
        c1 = substr(word(m.sql.cx.fetchVars, 1), 2)
        res = value(c1)
        return res
        end
    c1 = word(m.sql.cx.fetchFlds, 1)
    return m.dst.c1
endProcedure sqlFetch2One

/*-- execute a query and return first column of the only row
           if > 1 row fail, if 0 rows return arg(3) or fail ----------*/
sql2One: procedure expose m.
parse arg src, dst, feVa, retOk, retNone
    cx = m.sql_defCurs
    call sqlQuery cx, src, feVa, retOk
    return sqlFetch2One(cx, dst, retNone)
endProcedure sql2One

/*--- describe table and return sqlDA --------------------------------*/
sqlDescribeTable: procedure expose m.
parse upper arg tb, force
    if force == 1 | \ datatype(m.sql.table.tb.sqlD, 'n') then
         call sqlExec 'describe table :tb into :M.SQL.TABLE.'tb
    return 'SQL.TABLE.'tb
endProcedure sqlDescribeTable

/*--- return select column list for table tb
      omit Blobs (blobMax < 0) or substr(blob, 1, blobMax) ----------*/
sqlColList: procedure expose m.
parse arg tb al, blobMax
    sd = sqlDescribeTable(tb)
    bs = ''
    lst = ''
    if al \== '' & right(al, 1) \== '.' then
        al = al'.'
    do sx=1 to m.sd.sqld
        if wordPos(m.sd.sx.sqlType, '404 405 408 409 412 413') < 1 then
            lst = lst',' al || m.sd.sx.sqlName
        else do
            bs = bs m.sd.sx.sqlName
            if blobMax >= 0 then
                lst = lst', length('al || m.sd.sx.sqlName')' ,
                                          m.sd.sx.sqlName'Len' ,
                     || ', substr('al  || m.sd.sx.sqlName ,
                     || ', 1,' blobMax')' m.sd.sx.sqlName
            end
        end
    m.sd.colList = substr(lst, 3)
    m.sd.blobs = strip(bs)
    return substr(lst, 3)
endProcedure sqlColList

/*--- use describe output to generate column names,
        ''         use names from ca (rexxified)
        nms+       use names, check ca for null values
        ?('?'?nm)+ use names, check for null if preceeded by ?
        :...       use string as is
                fetchVariables and sqlNull names ---------------------*/
sqlRxFetchVars: procedure expose m.
parse arg cx, src, cd
    st = 'SQL.'cx'.COL'
    if abbrev(src, ':') then do
        m.sql.cx.fetchVars = src
        m.sql.cx.fetchCode = cd
        m.sql.cx.fetchFlds = ''
        return
        end
    m.sql.cx.fetchVars = ''
    if abbrev(src, '?') then do
        call err  implement + rxFetchVars ?
        r = substr(src, 2)
        do wx=1 to words(src)
            cn = word(src, wx)
            if abbrev(cn, '?') then
                call sqlRexxAddVar substr(cn, 2), 0, 1
            else
                call sqlRexxAddVar cn, 0, 0
            end                              ????????????? */
        end
    else if src <> '' then do
        ff = src
        end
    else do
        ff = ''
        do kx=1 to m.sql.cx.d.sqlD
             ff = sqlNiceVarsAdd(ff, m.sql.cx.d.kx.sqlName)
             end
        end
    m.sql.cx.fetchFlds = ff
    if m.sql.cx.d.sqlD <> words(ff) then
        call err 'sqlRxFetchVars sqlD='m.sql.cx.d.sqlD ,
                '<>' words(ff) 'fields of' ff
    sNu = ''
    sFe = ''
    do kx=1 to m.sql.cx.d.sqlD
        nm = word(ff, kx)
        sFe = sFe', :m.dst.'nm
        if m.sql.cx.d.kx.sqlType // 2 then do
            sFe = sFe' :m.dst.'nm'.sqlInd'
            sNu = sNu 'if m.dst.'nm'.sqlInd < 0 then' ,
                   'm.dst.'nm '= m.sqlNull;'
            end
        end
    m.sql.cx.fetchVars = substr(sFe, 3)
    m.sql.cx.fetchCode = sNu cd
    return
endProcedure sqlRxFetchVars
/* ????????????
sqlRexxAddVar:
parse arg nm, nicify, hasNulls
     nm = sqlAddVar(st, nm, nicify)
     if \ hasNulls then
          vrs = vrs', :m.dst.'nm
     else do
         vrs = vrs', :m.dst.'nm ':m.dst.'nm'.sqlInd'
         sNu = sNu 'if m.dst.'nm'.sqlInd < 0 then' ,
                    'm.dst.'nm '= m.sqlNull;'
         end
    return
endSubroutine sqlRexxAddVar   ?????? */

sqlNiceVarsAdd: procedure expose m.
parse arg old, nx rest
    upper nx
    cx = verifId(nx)
    if cx > 0 then /* avoid bad characters for classNew| */
        nx = left(nx, cx-1)
    if nx <> '' & wordPos(nx, old) < 1 0 then
        return old nx
    else
        return old  'COL' || (words(old) + 1)
endProcedure sqlNiceVarsAdd

/*--- 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

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

/*--- execute sql thru the dsnRexx interface -------------------------*/
sqlExec: /* no procedure, to keep variables sql... */
    parse arg ggSqlStmt, ggRetOk
    m.sql_HaHi = ''
    address dsnRexx 'execSql' ggSqlStmt
    /* say 'sqlCode' sqlCode 'rc' rc 'for' ggSqlStmt ggNo */
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, ggRetOk, ggSqlStmt)
    return sqlCode
endProcedure sqlExec

sqlExecMsg: procedure expose m.
parse arg sql
    sc = sqlExec(sql, '*')
    return sqlMsgLine(sc, , sql)

sqlErrorHandler: procedure expose m. ,
                   sqlCode sqlErrMc sqlErrP sqlErrD. sqlWarn. sqlState
parse arg drC, retOk, verb rest
    if drC == 0 then
        return 'return 0'
    if pos('-', retOK) < 1 then
        retOK = retOk m.sql_retOk
    if wordPos(drC, '1 -1') < 1 then do
        eMsg = "'dsnRexx rc="drC"' sqlmsg()"
        end
    else if pos('*', retOK) > 0 | wordPos(sqlCode, retOK) > 0 then do
        if sqlCode < 0 & pos('say', retOK) > 0 then
            return "call outNl errMsg(' }'sqlMsg())"
        else
            return ''
        end
    else do
        upper verb
        if verb == 'DROP' then do
            if (sqlCode == -204 | sqlCode == -458) ,
                           & wordPos('dne', retok) > 0 then
                return 'return' sqlCode
            if sqlCode = -672 & wordPos('rod', retok) > 0 then do
                hahi = m.sql_HaHi || sqlMsgLine(sqlCode,
                                   , 'tb='sqlErrMc ,verb rest)'\n'
                haHi = haHi || sqlExecMsg('alter table' SqlErrMc ,
                        'drop restrict on drop')
                call sqlExec verb rest
                m.sql_HaHi = hahi
                return ''
                end
            end
        if drC < 0 then
            eMsg = "sqlmsg()"
        else if (sqlCode<>0 | sqlWarn.0 ^==' ') & pos('w',retOK)<1 then
            return "call outNl errMsg(' }'sqlMsg()); return" sqlCode
        else
            return ''
        end
    if wordPos('rb', retok) > 0 then
        eMsg = eMsg " || '\n"sqlExecMsg('rollback')"'"
    if wordPos('ret', retok) < 1 then
        return "call err" eMsg
    m.sql_errRet = 1
    return 'call outNl' eMsg
endProcedure sqlErrorHandler

sqlMsgLine: procedure expose m. sqlErrD.
parse arg res, cnt, src, plus
    verb = translate(word(src, 1))
    if datatype(res, 'n') then
        res = 'sqlCode' res
    if cnt \== '' then do
        res = res',' cnt
        vx = wordPos(translate(word(src,1)), 'DELETE INSERT UPDATE')
        if datatype(cnt, 'n') then
            if vx > 0 then
               res = res 'rows' word('deleted inserted updated', vx)
            else if cnt <> 0 then
                res = res 'rows updated'
        end
    if plus \== '' then
        res = res',' plus
    if abbrev(res, ', ') then
        res = substr(res, 3)
    if src \== '' then do
        ll = 75 - length(res)
        aa = strip(src)
        if length(aa) > ll then
            aa = space(aa, 1)
        if length(aa) > ll then
           aa = left(aa,  ll-3)'...'
        res = res':' aa
        end
    return res
endProcedure sqlMsgLine

/*--- 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

/*--- 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
    ggFrom = 'ggSqlStmt'
    ggW1 = translate(word(ggSqlStmt, 1))
    ggW2 = translate(word(ggSqlStmt, 2))
    if ggW1 == 'PREPARE' then
        ggFrom = sqlHostVarFind(ggSt, 'FROM')
    else if ggW1 ggW2 == 'EXECUTE IMMEDIATE' then
        ggFrom = sqlHostVarFind(ggSt, 1)
    ggPos = 0
    if datatype(sqlErrd.5, 'n') & sqlErrd.5 > 0 then do
        ggPos = sqlErrd.5
        ggRes = ggRes || sqlMsgSrcPos(value(ggFrom), sqlErrd.5)
        end
    if ggFrom == 'ggSqlStmt' then do
        ggRes = ggRes'\nsql =' sqlShorten(ggSqlStmt, 2000, ggPos)
        end
    else do
        ggRes = ggRes'\nsql =' sqlShorten(value(ggFrom), 2000, ggPos)
        ggRes = ggRes'\nstmt =' sqlShorten(ggSqlStmt, 2000)
        end
    ggPref = '\nwith'
    do ggXX=1 to m.ggSt.0
        if ggFrom = m.ggVa.ggXX then
            iterate
        ggRes = ggRes || ggPref m.ggBe.ggXX ':'m.ggVa.ggXX ,
                      '=' sqlShorten(value(m.ggVa.ggXX), 210)
        ggPref = '\n    '
        end
    if m.sqlSuMsg == 1 | (m.sqlSuMsg == 2 & m.sql_csmhost \== '') then
        ggRes = ggRes'\nsubsys =' m.sql_dbSys ,
             || ', host =' m.sql_csmhost
    return  ggRes
endSubroutine sqlMsg

sqlShorten: procedure expose m.
parse arg txt, maxL, pos
    if length(txt) <= maxL then
        return txt
    if \ datatype(pos, 'n') | pos < 1 then
        pos = 1
    ex = pos + min(60, maxL%7)
    if ex <= maxL - 4 then
        return left(txt, maxL-4) '...'
    if ex >= length(txt) then
        return left(txt, 67) '...\n'substr(txt, length(txt)-maxL+72)
    else
        return left(txt, 67) '...\n'substr(txt, ex-maxL+76, maxL-75) ,
                       '...'
endProcedure sqlShorten
/*--- 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 nop
    else if rc = 4 then say 'warn linkPgm dsnTiar rc' rc 'sqlCa' ca
    else                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: procedure expose m. ,
                   sqlCode sqlErrMc sqlErrP sqlErrD. sqlWarn. sqlState
    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 = 1
    do cnt
        cx = pos(':', src, cx) + 1
        if cx < 2 then
           leave
        if pos(substr(src, cx, 1), m.ut_rxN1) > 0 then
            iterate
        ex = verify(src, m.ut_rxDot, 'n', cx)
        if ex < 1 then
            m.st.var.sx = substr(src, cx)
        else
            m.st.var.sx = substr(src, cx, ex - cx)
        if m.st.var.sx == '' | length(m.st.var.sx) > 100 then
            iterate
                       /* 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.ut_alfa) > 0
            end
        if bB < bE & bB >= 0 then
            m.st.bef.sx = substr(src, bB+1, bE-bB)
        else
            m.st.bef.sx = ''
        sx = sx + 1
        end
    m.st.0 = sx-1
    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 sqlRx  end   **************************************************/
/* copy csi begin    ***************************************************
     csi interface: see dfs managing catalogs chapt. 11
         returncode/reason see message IDC3009I
**********************************************************************/
/*--- specify dsn mask and fields to start a csi catalog search --------
      arguments:
          m       objectPointer
          dsnMask specifies the dsns with wildcards:
              %  1 character
              *  0 - n character in one level
              ** 0 - n levels
          fields a (space separated) list of field names -------------*/
csiOpen: procedure expose m.
parse arg m, dsnMask, fields
    m.m.fld.0 = words(fields)
    ffix = d2c(m.m.fld.0, 2)
    do x=1 to m.m.fld.0
        m.m.fld.x = translate(word(fields, x))
        ffix = ffix || left(m.m.fld.x, 8)
        end
    if dsnMask \== '' & right(dsnMask, 1) \== ' ' ,
          & pos('*', dsnMask) < 1 & length(dsnMask) < 42 then
        dsnMask = dsnMask'.**'
    m.m.filt = left(dsnMask, 149) ,
             || left('Y', 3) ,        /* resume offset 149      */
             || ffix                  /* csiNumEn offset 152    */

    WORKLEN = 1024 * 64
    m.m.work = D2C(WORKLEN,4) || COPIES('00'X,WORKLEN-4)
    m.m.pos = workLen + 1
    return
endProcedure csiOpen

/*--- put the next dsn into m.o and m.o.* (for other fields)
      return 1 if next dsn exists 0 otherwise ------------------------*/
csiNext: procedure expose m.
parse arg m, o
    usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET AMOUNT OF WORK AREA USED */
    px = m.m.pos
    do forever
        if px > usedL then do
            if substr(m.m.filt, 150, 1) \== 'Y' then do
                m.m.pos = px
                m.o = ''
                return 0
                end
            reason = left('', 4)
            ADDRESS LINKPGM 'IGGCSI00  reason m.'m'.filt  m.'m'.work'
            if rc == 0 & substr(reason, 3, 2) == '0000'x then
                nop
            else if rc == 4 & substr(reason, 3, 2) == '0464'x then
                say 'data set entry with error'
            else
                call err 'call csi returns' rc,
                             'rc' c2d(substr(reason, 4,1)),
                             'reason' c2d(substr(reason, 3,1)),
                             'module' substr(reason, 1,2)
            usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET WORK AREA USED */
            numFD = C2D(SUBSTR(m.m.work,13,2)) /* no flds + 1 */
            if numFd <> m.m.fld.0 + 1 then
                call err 'csiNumFd' numFd 'not' m.m.fld.0 '+' 1
            px = 15
            iterate
            end
        eType =  substr(m.m.work, px+1, 1)
        m.o = strip(substr(m.m.work, px+2, 44), 't')
        flag = substr(m.m.work, px, 1)
        /* say 'eType' eType m.o 'flag' c2x(flag) */
        if eType == '0' then do
            if flag \== '00'x & flag \== '40'x then
                call err 'flag' c2x(flag) 'for catalog' m.o
            px = px + 50    /* length of catalog entry  */
            iterate
            end
        else do
            if \ abbrev(x2b(c2x(flag)), '101') then
                call err 'call csi entry flag' x2b(c2x(flag)),
                             'rc' c2d(substr(m.m.work, px+49,1)),
                             'reason' c2d(substr(m.m.work, px+48,1)),
                             'module' substr(m.m.work, px+46, 2),
                             'for entry' m.o,
                             'see qw IDC3009I'
            py = px + 46
            tl = c2d(substr(m.m.work, py, 2))
            pl = py + 4
            pf = py + m.m.fld.0 * 2 + 4
            do fx = 1 to m.m.fld.0
                fi = m.m.fld.fx
                fl = c2d(substr(m.m.work, pl, 2))
                m.o.fi = substr(m.m.work, pf, fl)
                if fi = 'MGMTCLAS' then
                    m.o.fi = substr(m.o.fi, 3, c2d(left(m.o.fi ,2)))
                else if wordPos(fi, 'COMUDSIZ NOBYTTRK') > 0 then
                    m.o.fi = utc2d(m.o.fi)
                pf = pf + fl
                pl = pl + 2
                end
            if py + tl <> pf then
                call err 'length mismatch for entry' m.o
            m.m.pos = pf
            return 1
            end
        end
endProcedure csiNext
/*--- if dsn is arcived return 'arcive'
      if dsn is tape return 'tape'
      otherwise return managment class ------------------------------*/
csiArcTape: procedure expose m.
parse arg vo, cl, dt, dsn
        if vo = '' then
            say err '||| no volume for dsn' dsn
        else if vo = 'ARCIVE' then
            res = 'arcive'
        else if cl <> '' then
            res = cl
        else if abbrev(vo, 'SHR') then
            res = 'SHR'
        else
            res = 'tape'
        if   res = 'arcive' then
            return res
      /*if   abbrev(res, 'ar') \= abbrev(dt, '00'x) , */
        if   abbrev(res, 'ta') \= abbrev(c2x(left(dt, 1)), '7') ,
           | (left(res, 1) >= 'A') \= abbrev(dt, '30'x) then
           say '||| mismatch cl' cl 'vo' vo 'dt' c2x(dt) 'dsn' dsn
        return res
endProcedure csiArcTape
/* copy csi end ******************************************************/
/* copy mbrList begin ************************************************/
mbrList: procedure expose m.
parse arg m, pds
    msk = strip(dsnGetMbr(pds))
    if msk == '*' then
        msk = ''
    parse value csmSysDsn(dsnSetMbr(pds)) with sys '/' dsn
    if sys == '*' then do
        call adrTso listDS "'"dsn"'" members
        oy = m.tso_trap.0 + 99
        mFound = 0
        mx = 0
        do ox=1 to m.tso_trap.0
            if mFound then do
                if msk \== '' then
                    if \ match(strip(m.tso_trap.ox), msk) then
                        iterate
                mx = mx +1
                m.m.mx = strip(m.tso_trap.ox)
                end
            else
                mFound = m.tso_trap.ox == '--MEMBERS--'
            end
        end
    else do
        if msk <> '' then
            msk = 'member('translate(msk, '%', '?')')'
        mbr_name.0 = -99
        call adrCsm "mbrList system("sys") dataset('"dsn"')" msk ,
                "index(' ') short"
        do mx=1 to mbr_name.0
            m.m.mx = strip(mbr_name.mx)
            end
        mx = mbr_name.0
        end
    m.m.0 = mx
    return mx
endProcedure mbrList
tstMbrList: procedure expose m.
/*
$=/tstMbrList/
    ### start tst tstMbrList ##########################################
    *** err: adrTso rc= 8 stmt=LISTDS 'A540769.TMP.TST.MBRLIST' MEMBERS+
    . .
    .    e 1: A540769.TMP.TST.MBRLIST
    .    e 2: IKJ58503I DATA SET 'A540769.TMP.TST.MBRLIST' NOT IN CATAL+
    OG
    #noPds: 0 mbrs in A540769.TMP.TST.MBRLIST
    #1: 1 mbrs in A540769.TMP.TST.MBRLIST
    1 EINS
    #0: 0 mbrs in A540769.TMP.TST.MBRLIST
    #4: 4 mbrs in A540769.TMP.TST.MBRLIST
    1 DREI
    2 FUENF
    3 VIER
    4 ZWEI
    #*IE*: 3 mbrs in A540769.TMP.TST.MBRLIST( *IE* )
    1 IE
    2 NNNIE
    3 VIER
    #*_IE*: 2 mbrs in A540769.TMP.TST.MBRLIST( *?IE* )
    1 NNNIE
    2 VIER
$/tstMbrList/
*/
    call tst t, 'tstMbrList'
 /* call tstMbrList1 "RZ2/A540769.WK.REXX(*DA?*)"  */
    pds = tstFileName('MbrList', 'r')
    da.1 = '2ine eins'
    call tstMbrList1 pds, '#noPds'
    call writeDsn pds'(eins) ::f', da., 1
    call tstMbrList1 pds, '#1'
    call adrTso "delete '"pds"(eins)'"
    call tstMbrList1 pds, '#0'
    call writeDsn pds'(zwei) ::f', da., 1
    call writeDsn pds'(drei) ::f', da., 1
    call writeDsn pds'(vier) ::f', da., 1
    call writeDsn pds'(fuenf) ::f', da., 1
    call tstMbrList1 pds, '#4'
    call writeDsn pds'(ie) ::f', da., 1
    call writeDsn pds'(nnnie) ::f', da., 1
    call tstMbrList1 pds"( *IE* )", '#*IE*'
    call tstMbrList1 pds"( *?IE* )", '#*_IE*'
    call adrTso "delete '"pds"'"
    call tstEnd t
    return
endProcedure tstMbrList
tstMbrList1: procedure expose m.
parse arg pds, txt
    call tstOut t, txt':' mbrList(tstMbrList, pds) 'mbrs in' pds
    do mx=1 to m.tstMbrList.0
        call tstOut t, mx m.tstMbrList.mx
        end
    return
endProdecure tstMbrList1
/* copy mbrList end   ************************************************/
/* copy csm begin ******************************************************
    interface to csm,
        it is integrate with adrTso, eg. dsnAlloc , 'RZ3/..' uses csm
***********************************************************************/
/*--- execute a single csmExec command ------------------------------*/
adrCsm:
    if wordPos(translate(word(arg(1), 1)), 'COPY MBRLIST') > 0 then
       ggTO = ''
    else if symbol('m.csm_timeOut') == 'VAR' then
        ggTO = 'timeout('m.csm_timeOut')'
    else
        ggTO = 'timeout(30)'
    ggStart = time()
    if adrTso('csmExec' arg(1) ggTO, '*') == 0 then
        return 0
    if pos('CSMXC43E NO CONNECTION TO S', m.tso_trap) > 0 ,
         | pos('CSMXC36E TIMEOUT LIMIT EXCEEDED DURING LOGON TO' ,
            , m.tso_trap) > 0 then
               /* CSMXC43E NO CONNECTION TO SYSPLEX:RR2 AVAILABLE
                  CSMXC36E TIMEOUT LIMIT EXCEEDED DURING LOGON TO SYS */
        m.csm_err = 'noConn'
    else if pos('IKJ56225I', m.tso_trap) > 0             ,
               & ( pos('ALREADY IN USE', m.tso_trap) > 0 ,
                 | pos('CATED TO ANOTH', m.tso_trap) > 0) then
               /* 5 IKJ56225I DATA SET A540769.TMP.TEXT ALREADY IN USE,
                  6 IKJ56225I DATA SET IS ALLOCATED TO ANOTHER JOB */
        m.csm_err = 'inUse'
    else
        m.csm_err = ''
    if pos('*', arg(2)) <= 0 & wordPos(m.tso_rc, arg(2)) <= 0 then
        call err strip('csmExec' m.csm_err) 'rc='m.tso_rc ,
            '\nstmt='subWord(m.tso_stmt, 2) m.tso_trap ,
            '\nend of csmExec, time='ggStart '-' time()
    return m.tso_rc
endProcedure adrCsm

csmDel: procedure expose m.
parse arg rz, dsn
    if dsnGetMbr(dsn) == '' then do
        call adrCsm "allocate system("rz") dataset('"dsn"')" ,
                         "disp(del) ddname(del1)"
        call adrTso 'free dd(del1)'
        end
    else do
        rr = adrCsm("mDelete system("rz") dataset('"dsnSetMbr(dsn)"')",
                          "member("dsnGetMbr(dsn)")", 8)
        if rr <> 0 then
            if pos('CSMEX77E Member:', m.tso_trap) < 1 ,
              | pos(' not found', m.tso_trap) < 1 then
                  call err 'rc='rr 'csm mDelete' rz'/'dsn':'m.tso_trap
        end
    return
endProcedure csmDel
/*--- copy members / datasets
      Vorlage: csrxUtil ----------------------------------------------*/
csmCopy: procedure expose m.
parse arg aFr, aTo, retOk
    frDD = tsoDD('csmFr*', 'a')
    frMbr = dsnGetMbr(aFr)
    if frMbr == '*' then
        fr = dsnSetMbr(aFr)
    else
        fr = aFr
    call csmAlloc fr frDD 'shr'
    toDD = tsoDD('csmTo*', 'a')
    toMbr = dsnGetMbr(aTo)
    if toMbr\== '=' then
        to = aTo
    else
        to = dsnSteMbr(aTo, frMbr)
    call csmAlloc to toDD 'shr ::D'frDD
    if frMbr \== '' & toMbr == '' & m.tso_dsOrg.toDD == 'PO' then do
        call adrTso 'free dd('toDD')'
        to = dsnSetMbr(aTo, frMbr)
        call csmAlloc to toDD 'shr'
        end
    inDD = tsoDD('csmIn*', 'a')
    if frMbr == '' & m.tso_dsOrg.frDD == 'PO' then do
        call tsoAlloc '-' inDD 'NEW ::F'
        call adrCsm "mbrList ddName("frDD") index(' ') short"
        do ix=1 to mbr_mem#
            i.ix = ' S M='mbr_name.ix
            end
        call writeDD inDD, 'I.', mbr_mem#
        call tsoCLose inDD
        end
    else do
        call adrTso 'alloc dd('inDD') dummy'
        end
    outDD = word(dsnAlloc('dd(csmO*) new ::V137'), 1)
    cmdu = 'CSMUTIL CSM,COPYREPLACE,DD(,,,,'inDD','outDD ,
               ||  ',,'frDD','toDD'),MARC(0)'
    cRc = adrTso(cmdU, '*')
    if cRc <> 0 then do
        call readDD outDD, o.
        call tsoClose outDD
        say 'rc='cRc',' o.0 'outputlines for' cmdU
        do ox=1 to o.0
            say o.ox
            end
        end
    call tsoFree frDD toDD inDD outDD
    if cRc <> 0 then
        call err 'csmCopy rc='cRc
    return cRc
endProcedure csmCopy

csmCopLOld: procedure expose m.
       /* csrxUtil geht nicht mit to(mbr), copy geht nicht fuer Load */
parse arg aFr, aTo, retOk
    if \ dsnExists(aTo) then do
            /* alloc ourself, because csrxutil forget mgmtclas */
        call dsnAlloc 'dd(csmCopy) catalog' aTo ,
            dsnLikeAtts(dsnSetMbr(aFr), 0)
        call tsoFree csmCopy
        end
    parse value csmSysDsn(aFr) with fSys '/' fDsn
    parse value csmSysDsn(aTo) with tSys '/' tDsn
    return adrTso("exec 'CSM.DIV.P0.EXEC(CSRXUTIL)'" ,
        "'COPY" fSys"/''"fDsn"'' TO" tSys"/''"tDsn"'' REPLACE'", retOk)
endProcedure csmCopLOld

csmCopyOld: procedure expose m.
parse arg aFr, aTo, retOk
    if dsnGetMbr(csnTo) \= '' & dsnGetMbr(csnTo) \= '' then do
        if dsnGetMbr(csnFr) <> dsnGetMbr(csnTo) then
            call err 'member rename' csnFr 'to' csnTo
        csnTo = dsnSetMbr(csnTo)
        end
    fr = csmSysDsn(aFr)
    frMbr = dsnGetMbr(fr)
    frDD = tsoDD('csmFr*', 'a')
    to = csmSysDsn(aTo)
    toMbr = dsnGetMbr(to, '=')
    toDD = tsoDD('csmTo*', 'a')
    call csmAlloc dsnSetMbr(fr) frDD 'shr'
    if frMbr == '' & m.tso_dsorg.frDD == 'PO' then
        if toMbr \== '=' then
            call err 'csmCopy from' fr'(*) to ps' to
        else
            frMbr = '*'
    if frMbr == '' & (toMbr \== '' & toMbr \== '=') then
        psOrLib = 'dsorg(po) dsntype(library)'
    else if frMbr \== '' & toMbr == '' then
        psOrLib = 'dsorg(ps)'
    else
        psOrLib = ''
    call csmAlloc dsnSetMbr(to) toDD 'shr ::D'frDD psOrLib
    c = 'indd('frDD') outDD('toDD')'
    if frMbr \== '*' then do
        if frMbr \== '' then
            c = c 'member('frMbr')'
        if toMbr \== '' & toMbr \== '=' then
            c = c 'newName('toMbr')'
        call adrCsm 'copy' c
        end
    else do
        call adrCsm "mbrList ddName("frDD") index(' ') short"
        do mx=1 to mbr_mem#
            call adrCsm 'copy' c 'member('mbr_name.mx')'
            end
        end
    call tsoFree frDD toDD
    return
endProcedure csmCopyOld

csmAlloc: procedure expose m.
parse arg dsn dd disp rest ':' nn, retRc
    sys = ''
    a2 = ''
    upper dd disp
    m.tso_dsn.dd = csmSysDsn(dsn)
    parse var m.tso_dsn.dd sys '/' dsn
    if disp = '' then
        disp = 'shr'
    else if words(disp) = 2 then
        disp = word(disp, 2)
    a1 = "SYSTEM("sys") DDNAME("dd")"
    if dsn == 'INTRDR' then do
        a1 = a1 'sysout(T) writer(intRdr)'
        end
    else do
        if dsn <> '' then do
            a1 = a1 "DATASET('"dsnSetMbr(dsn)"')"
            mbr = dsnGetMbr(dsn)
            if mbr <> '' then
                a1 = a1 'MEMBER('mbr')'
            end
        if abbrev(disp, 'SYSOUT(') then
            a1 = a1 disp
        else
            a1 = a1 "DISP("disp")"
        end
    nAtts = wordPos(disp, 'NEW MOD CAT') > 0 & nn \== ''
    if nAtts then
        rest = dsnCreateAtts('-'dsn , nn) rest
    cx = pos(' UCOUNT(', ' 'translate(rest))
    if cx > 0 then do
         rest = left(rest, cx) || 'nitCnt(' || substr(rest, cx+7)
         end
    cx = pos(' RECFM(', ' 'translate(rest))
    if cx > 0 then do
        cy = pos(')', rest, cx)
        rest = left(rest, cx+5) || space(substr(rest,cx+6,cy-cx-6), 0) ,
                                || substr(rest,cy)
        end
    cx = pos(' CYLINDERS ', ' 'translate(rest)' ')
    if cx > 0 then do
        rest = delStr(rest, cx+8, 1)
        end
    cx = pos(' CYL ', ' 'translate(rest)' ')
    if cx > 0 then do
        rest = insert('inder', rest, cx+2)
        end
    if retRc <> '' | nAtts | nn == '' then do
        alRc = adrCsm('allocate' a1 rest, retRc)
        m.tso_dsorg.dd = subsys_dsOrg
        return alRc
        end
    alRc = adrCsm('allocate' a1 rest, '*')
    m.tso_dsorg.dd = subsys_dsOrg
    if alRc = 0 then
        return 0
    say 'csmAlloc rc' alRc 'for' a1 rest '...trying to create'
    call csmAlloc m.tso_dsn.dd dd 'CAT' rest ':'nn
    call adrTso 'free  dd('dd')'
    return adrCsm('allocate' a1 rest)
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

csmLikeAtts: procedure expose m.
parse upper arg rz '/' dsn
    lc = adrCsm('dslist system('rz') dsnMask('dsnSetMbr(dsn)')')
    if stemsize <> 1 then
        call err 'csmLikeAtts stemSize='stemsize 'for dsn='dsn
    if abbrev(dsOrg.1, 'PO') then
        r = 'dsorg(po) dsnType(library)'
    else if abbrev(dsOrg.1, 'PS-') then
        r = 'dsorg(PS)'
    else
        r = 'dsorg('dsOrg.1')'
    r = r 'mgmtClas('mgmtClas.1')'                       ,
       /* 'dataClas('dataClas.1')'   */                  ,
          'recFM('strip(translate('1 2 3', recFm.1, '123'))')'  ,
          'lRecl('lRecl.1')'                         ,
          'space('tracksused.1','  tracks.1') tracks'
    /*    if \ datatype(tracksused.1, 'n') then do
              if \ datatype(tracks.1, 'n') then
                  r = r 'space('tracks.1',' tracks.1')'
              if \ datatype(tracks.1, 'n') then
                  tracks.1 = tracksUsed.1   */

    return r
endProcedure csmLikeAtts
/*--- execute a rexx (under tso) in another rz
           here we use rexx TPSYSIKJ which was written for
           jcl procedure RM@IKJ01
arguments
rz   which rz to run rexx
proc the (remote) procedure library to use
opt  options
cmd  the tso command to execute
----------------------------------------------------------------------*/
/*--- execute a rexx (under tso) in another rz
          directly (without TPSYSIKJ) --------------------------------*/
csmExRx: procedure expose m.
parse arg rz, proc, opt, cmd
    pStem = opt
    if pStem = '' then
        pStem ='CSMEXRX'               /* split tso cmd in linews */

    do cx=1 to (length(cmd)-1) % 68
        cmd.cx = substr(cmd, 68*cx-67,68)'-'
        end
    cmd.cx = substr(cmd, 68*cx-67)
    cmd.0 = cx
    timeout = 11
    if 0 then do
        call adrTso  'free ed(rmtSys)'  ,'*'
        call tsoFree tsoDD(rmtsPrt, 'a')
        call adrTso  'free dd(rmtsIn)','*'
        call adrTso  'free dd(sysproc)' ,'*'
        end
                                       /* alloc necessary dd */
    call dsnAlloc rz"/"proc "dd(rmSyPro) rmtDDn(sysProc)"
    call dsnAlloc rz"/tmp.tsin new dd(rmTsIn) rmtDdn(sysTsIn) ::f "
    call tsoOpen rmTsIn, 'w'           /* write tso cmd */
    call writeDD rmTsIn, cmd.
    call tsoClose rmtsin
    call dsnAlloc rz"/tmp.prt new dd(rmtsprt) rmtDdn(sysTsPrt)",
                    "::f133"
    call dsnAlloc rz"/tmp.rmt new dd(rmtSys) timeout("timeout")"
                                       /* now, run tso remote */
    call adrtso "csmappc start pgm(csmexec)" ,
           "parm('select tsocmd(''csmappc allocate plu(*.rmtSys)" ,
                 "tpname(sysikj) dealloc '')')",
           "timeout("timeOut")", '*'
    if rc <> 0 | appc_rc <> 0 then do  /* handle csm error */
        ee = 'csm tso exec rc='rc 'appc_rc='appc_rc
        say ee
        say '  rexx rz='rz 'proc='proc 'opt='opt
        say '  cmd='cmd
        call csmappcRcSay ggTsoCmd
        call readDD 'rmTsPrt', 'M.'pStem'.'
        call tsoClose rmtsPrt
        say m.pStem.0 'tso output lines'
        do px=1 to m.pStem.0
            say ' ' strip(m.pStem.px, 't')
            end
        call err ee
        end
    if opt <> '' then do               /* copy output to stem */
        call readDD 'rmTsPrt', 'M.'pStem'.'
        call tsoClose rmtsPrt
        end
    call tsoFree rmSyPro rmtsPrt rmtSys rmtsIn
    return
 endProcedure csmExRx

/*--- sys the re and result variables from csmAppcRc -----------------*/
csmappcRcSay: procedure expose appc_rc appc_reason appc_msg. ,
           appc_state_c appc_state_f
 parse arg cmd
     say 'rc='appc_rc 'reason='appc_reason ,
         'state_c='appc_state_c appc_state_f
     say '  for' cmd
     do ix=1 to appc_msg.0
         say ' ' appc_msg.ix
         end
     return appc_rc
 endProcedure csmappcRcSay

/*--- execute a single csmAppc command ------------------------------*/
csmAppc:
    return adrTso('csmAppc' arg(1), arg(2))
endProcedure csmAppc
/* copy csm end *******************************************************/
/* copy timing begin *************************************************/
timing: procedure expose m.
parse arg typ, c2, txt
    e1 = time('E')
    c1 = strip(sysvar('syscpu'))
    s1 = sysvar('syssrv')
    if typ == '' then
        return strip(f('%c ela=%5i cpu=%8.3i su=%9i' ,
            , time(), e1, c1, s1) txt)
    if symbol('m.timing_ela') \== 'VAR' then
        call err 'timing('typ',' c2',' txt') ohne ini'
    if symbol('m.timing.typ.ela') \== 'VAR' then do
        m.timing.typ.ela = 0
        m.timing.typ.cpu = 0
        m.timing.typ.su  = 0
        m.timing.typ.cnt = 0
        m.timing.typ.cn2 = 0
        if symbol('m.timing_types') == 'VAR' then
            m.timing_types = m.timing_types typ
        else
            m.timing_types = typ
        if symbol('m.timing_say') \== 'VAR' then
            m.timing_say = 0
        end
    m.timing.typ.ela = m.timing.typ.ela + e1 - m.timing_ela
    m.timing.typ.cpu = m.timing.typ.cpu + c1 - m.timing_cpu
    m.timing.typ.su  = m.timing.typ.su  + s1 - m.timing_su
    m.timing.typ.cnt = m.timing.typ.cnt + 1
    if c2 \== '' then
       m.timing.typ.cn2 = m.timing.typ.cn2 + c2
    m.timing_ela = e1
    m.timing_cpu = c1
    m.timing_su  = s1
    if m.timing_say then
            say left(typ, 10)right(m.timing.typ.cn2, 10) ,
                'ela='m.timing.typ.ela ,
                'cpu='m.timing.typ.cpu 'su='m.timing.typ.su txt
    return
endProcedure timing

timingSummary: procedure expose m.
    say 'timing summary' time()
    do tx = 1 to words(m.timing_types)
        typ = word(m.timing_types, tx)
        say left(typ, 10)right(m.timing.typ.cnt,  7)       ,
                      || right(m.timing.typ.cn2,  7)       ,
                         'cpu='right(m.timing.typ.cpu, 10) ,
                         'su='right(m.timing.typ.su, 10)
        end
    return
endProcedure timingSummary
/* copy timing end   *************************************************/
/* copy ii begin ********* Installation Info *************************/
iiini: procedure expose m.
    if m.ii_ini == 1 then
        return
    m.ii_ini = 1
    m.ii_rz = ''
    i = 'RZ0 0 T S0 RZ1 1 1 S1'  ,
        'RZX X X X2 RZY Y Y Y2 RZZ Z Z Z2'  ,
        'RQ2 Q Q Q2 RR2 R R R2 RZ2 2 B S2 RZ4 4 D S4'
    do while i <> ''
        parse var i rz m.ii_rz2c.rz m.ii_rz2plex.rz sys i
        if rz <> RZ0 & rz <> RZ1 then
            m.ii_rz = strip(m.ii_rz rz)
        m.ii_rz2Sys.rz  = sys
        m.ii_sys2rz.sys = rz
        end
    i = 'DBTF T DTF DVTB V DTB DBOC C DOC DBTC B DBT DBIA A DIA' ,
        'DE0G E DE0 DEVG M DEV DPXG I DPX DX0G X DX0' ,
        'DPYG Y DPY DPZG N DPZ' ,
        'DBOF F DOF DVBP P DBP DP2G Q DP2 DBOL O DOL DP4G U DP4'
    do while i <> ''
        parse var i db m.ii_db2c.db mbr i
        m.ii_mbr2db.mbr = db
        m.ii_db2Elar.db = wordPos(db, 'DVTB DVBP DEVG') > 0
        end
    m.ii_rz2db.rz0 = 'DBTC DBIA'
    m.ii_rz2db.rz1 = 'DBTF DVTB DBOC'
    m.ii_rz2db.rzx = 'DE0G DEVG DX0G DPXG'
    m.ii_rz2db.rzy = 'DE0G DEVG DPYG'
    m.ii_rz2db.rzz = 'DE0G DEVG DPZG'
    m.ii_rz2db.rq2 = 'DBOF DVBP DP2G'
    m.ii_rz2db.rr2 = 'DBOF DVBP DP2G'
    m.ii_rz2db.rz2 = 'DBOF DVBP DP2G'
    m.ii_rz2db.rz4 = 'DBOL DP4G'
    i = ''
    do rx=1 to words(m.ii_rz)
        rz = word(m.ii_rz, rx)
        i = i repAll(' 'space(m.ii_rz2db.rz, 1), ' ', ' 'rz'/')
        end
    m.ii_rzDb = space(i, 1)
    return
endProcedure iiIni

iiMbr2DbSys: procedure expose m.
parse upper arg mbr
    return iiLazy(ii_mbr2db, left(mbr, 3), 'member')

iiRz2C: procedure expose m.
parse upper arg rz
    return iiLazy(ii_rz2c, rz, 'rz')

iiRz2P: procedure expose m.
parse upper arg rz
    return iiLazy(ii_rz2plex, rz, 'rz')

iiRz2Dsn: procedure expose m.
parse upper arg rz
    return overlay('Z', rz, 2)

iiDBSys2C: procedure expose m.
parse upper arg db
    return iiLazy(ii_db2c, db, 'dbSys')

iiSys2RZ: procedure expose m.
parse upper arg sys
    return iiLazy(ii_sys2rz, left(sys, 2), 'sys')

iiLazy: procedure expose m.
parse arg st, key, txt
    if symbol('m.st.key') == 'VAR' then
        return m.st.key
    if m.ii_ini == 1 then
       return err('no' txt'='key 'in ii' st)
    call iiIni
    return iiLazy(st, key, txt)
endProcedure iiLazy

iiVPut:procedure expose m.
parse upper arg rz '/' db .
    call vPut 'rz', rz
    call vPut 'rzC', iiRz2C(rz)
    call vPut 'rzP', iiRz2P(rz)
    call vPut 'rzD', iiRz2Dsn(rz)
    call vPut 'dbSys', db
    call vPut 'dbSysC', iidbSys2C(db)
    call vPut 'dbSysElar', iiLazy(ii_db2Elar, db)
    return 1
endProcedure iiVPut

iiIxVPut:procedure expose m.
parse arg ix
    if ix > words(m.ii_rzDb) then
        return 0
    else
        return iiVPut(word(m.ii_rzDb, ix))
endProcedure iiIxVPut
/* copy ii end   ********* Installation Info *************************/
/* 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 tsoOpen grp, 'R'
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 tsoClose 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 m.tso_stmt, ggRet
    call outtrap m.tso_trap.
    address tso m.tso_stmt
    m.tso_rc = rc
    call outtrap off
    if m.tso_rc == 0 then
        return 0
    m.tso_trap = ''
    do ggXx=1 to min(7, m.tso_trap.0)
        m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
        end
    if m.tso_trap.0 > 7 then do
        if m.tso_trap.0 > 14 then
            m.tso_trap = m.tso_trap'\n............'
        do ggXx=max(8, m.tso_trap.0-6) to m.tso_trap.0
            m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
            end
        end
    if ggRet <> '*' & wordPos(m.tso_rc, ggRet) <= 0 then
        call err 'adrTso rc=' m.tso_rc 'stmt='m.tso_stmt m.tso_trap
    return m.tso_rc
endSubroutine 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 arg(2)
     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:  tsoOpen...'R', readDD*,  tsoClose
        write: tsoOpen...'W', writeDD*, tsoClose

        readDD returns true if data read, false at eof
        do not forget that open is mandatory to write empty file|
***********************************************************************/

/*--- open dd for read (rw='R') or write (rw='W') --------------------*/
tsoOpen: procedure expose m.
parse upper arg dd, rw
    return adrTso('execio' 0 'disk'RW dd '(open)')
return /* end tsoOpen */

/*--- close dd -----------------------------------------------------*/
tsoClose: procedure expose m.
    parse upper arg dd
    return adrTso('execio 0 diskR' dd '(finis)')
endProcedure tsoClose

/*--- 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 */

/*--- 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

/*--- 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
    parse value dsnAlloc('dd('m.m.dd')' m.m.dsn) with m.m.dd m.m.free
    call tsoOpen m.m.dd, 'R'
    return m
endProcedure readDDNxBegin

/*--- return the stem of the next line, or '' at end -----------------*/
readNx: procedure expose m.
parse arg m
    m.m.cx = m.m.cx + 1
    if m.m.cx <= m.m.0 then
        return m'.'m.m.cx
    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 stem of the curr line, '' at end --------------------*/
readNxCur: procedure expose m.
parse arg m
    if m.m.cx <= m.m.0 then
        return m'.'m.m.cx
    else
        return ''
endProcedure readNxCur

/*--- 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 tsoClose m.m.dd
    call tsoFree m.m.free
    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' | w == 'CAT' then
            di = di 'CAT'
        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 pos('(', w) > 0 then
            leave
        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.tso_trap.1 = ''
        m.tso_trap.2 = ''
        m.tso_trap.3 = ''
        res = dsnAlloc(spec, pDi, pDD, '*')
        if \ datatype(res, 'n') then
            return res
        msg = m.tso_trap.1'\n'm.tso_trap.2'\n'm.tso_trap.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 dd == '-' & pDD \== '' then
        dd = pDD
    if dd == '-' then
        dd = 'DD*'
    dd = tsoDD(dd, 'a')
    m.tso_dsn.dd = ''
    if na == '-' & di == '-' & rest = '' then
        return dd
    if di = '-' then
        if pDi == '' then
            di = 'SHR'
        else
            di = pDi
    if pos('(', na) < 1 then
        nop
    else if di = 'MOD' then
        call err 'disp mod for' na
    else
        di = 'SHR'
    if lastPos('/', na, 6) > 0 then
        rx = csmAlloc(na dd di rest, retRc)
    else
        rx = tsoAlloc(na dd di rest, retRc)
    if rx = 0 then
        return dd dd
    else
        return rx
endProcedure dsnAlloc

/*--- find a free dd with prefix dd ----------------------------------*/
tsoDD: procedure expose m.
parse upper arg dd, f, noErr
    if symbol('m.tso_ddAll') \== 'VAR' then do
        call errIni
        m.tso_ddAll = ''
        end
    if f == '-' then do
        ax = wordPos(dd, m.tso_ddAll)
        if ax > 0 then
            m.tso_ddAll = delWord(m.tso_ddAll, ax, 1)
        else if noErr \== 1 then
            call err 'tsoDD dd' dd 'not used' m.tso_ddAll
        end
    else if f <> 'A' then
        call err 'tsoDD bad fun' f
    else do
        if right(dd, 1) = '*' then do
            d0 = left(dd, length(dd)-1) || m.err.screen
            dd = d0
            do dx=1 while wordPos(dd, m.tso_ddAll) > 0
                dd = d0 || dx
                end
            end
        else if pos('?', dd) > 0 then
            dd = repAll(dd, '?', m.err.screen)
        if wordPos(dd, m.tso_ddAll) < 1 then
            m.tso_ddAll = strip(m.tso_ddAll dd)
        m.tso_dsn.dd = ''
        m.tso_dsOrg.dd = ''
        end
    return dd
endProcedure tsoDD

tsoAlloc: procedure expose m.
parse arg na dd disp rest ':' nn, retRc
    dd = translate(dd)
    c = 'alloc dd('dd')' disp
    if na == '-' then
        m.tso_dsn.dd = ''
    else if na \== 'INTRDR' then do
        c = c "DSN('"na"')"
        m.tso_dsn.dd = na
        end
    else do
        c = c "sysout(*) writer(intRdr)"
        m.tso_dsn.dd = '*intRdr'
        end
    if wordPos(disp, 'NEW MOD') > 0 & nn \== '' then
        c = c dsnCreateAtts(,nn)
    if adrTso(c rest, '*') = 0 then
        return 0
    if pos('IKJ56246I', m.tso_trap) > 0 then
        if pos('TED, FILE IN USE', m.tso_trap) > 0 then do
     /* IKJ56246I DATA SET A540769.WK.REXX NOT ALLOCATED, FILE IN USE */
        say 'tsoAlloc dd' dd 'already in use:' substr(m.tso_trap, 3)
        say '.... trying to free'
        call tsoFree dd, 1
        say '.... retrying to allocate' c rest
        if adrTso(c rest, '*') = 0 then
            return 0
        end
    if nn \= '' & wordPos(disp, 'OLD SHR') > 0 ,
          & pos('IKJ56228I', m.tso_trap) > 0 ,
          & pos(' NOT IN CATALOG ', m.tso_trap) > 0 then do
       /* IKJ56228I DATA SET A540769.XY.REXX NOT IN CATALOG OR CATAL*/
        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
    if pos('*', retRc) < 1 & wordPos(m.tso_rc, retRc) < 1 then
        call err 'tsoAlloc rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
    call sayNl 'tsoAlloc rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
    return m.tso_rc
endProcedure tsoAlloc

dsnExists: procedure expose m.
parse upper arg aDsn
    parse value csmSysDsn(aDsn) with rz '/' dsn
    if rz == '*' then
        return sysDsn("'"dsn"'") == 'OK'
    else if dsnGetMbr(dsn) == '' then do
        lc = adrCsm('dslist system('rz') dsnMask('dsn') short', 4)
        if stemsize = 0 | stemSize = 1 then
            return stemSize
        call err 'csmExists stemSize='stemsize 'for dsn='aDsn
        end
    else do
        cc = adrCsm("mbrList system("rz") dataset('"dsnSetMbr(dsn)"')",
                    "member("dsnGetMbr(dsn)") index(' ') short", 8)
        if cc <> 0 then do
            if pos(' NOT IN CATALOG\', m.tso_trap) > 0 ,
              & pos('CSMSV29E DATA SET ', m.tso_trap) > 0 then
                return 0
            return err('error in csm mbrList' aDsn m.tso_trap)
            end
        if mbr_name.0 == 0 | mbr_name.0 == 1 then
            return mbr_name.0
        call err 'csmExists mbr_mem#='mbr_name.0 'for dsn='aDsn
        end
endProcedure dsnExists

dsnLikeAtts: procedure expose m.
parse upper arg dsn, isFile
     if isFile then do
         ddDsn = m.tso_dsn.dsn
         if lastPos('/', m.tso_dsn.dsn, 4) < 1 then
              return tsoLikeAtts(dsn, 1)
         dsn = m.tso_dsn.dsn
         end
     sx = lastPos('/', dsn, 4)
     if sx < 1 then
         return tsoLikeAtts(dsn, 0)
     else if abbrev(dsn, '*/') | abbrev(dsn, sysVar('SYSNODE')'/') then
         return tsoLikeAtts(substr(dsn, sx+1), 0)
     else
         return csmLikeAtts(dsn)
endProcedure dsnLikeAtts

tsoLikeAtts: procedure expose m.
parse arg dsn, isFile
    rc = listDsi("'"dsn"'" copies('FILE', isFile) "SMSINFO")
    if rc = 0 then
        r = ''
    else if rc = 4 & sysReason = 19 then do
        r = 'UCOUNT(30)' /* 7.8.13 corrected attribute */
        say 'creating' dsn 'with multi volume' mv
        end
    else if rc ^= 0 then
        call err 'listDsi rc' rc 'reason' sysReason,
                             sysMsgLvl1 sysMsgLvl2
    if right(sysDsSms, 7) == 'LIBRARY' | abbrev(sysDsSms, 'PDS') ,
                      | sysDsOrg = 'PO' then
         r = 'dsOrg(PO) DSNTYPE(LIBRARY)' r
    else
         r = "dsOrg("sysDSorg")" r
    if sysUnits = 'TRACK' then
        sysUnits = 'TRACKS'
    return r "MGMTCLAS("sysMgmtClass")",
        "DATACLAS("sysDataClass")" ,
        "RECFM("strip(translate('1 2 3', ' 'sysREcFM, '123'))")",
        "LRECL("SYSLRECL")",
        "SPACE("sysPrimary"," sysSeconds")" ,
            sysUnits || left('S', sysUnits == 'TRACK')
    /*  "blksize("sysBLkSIZE")" removed 3.4.13: let sms do the magic */
endProcedure tsoLikeAtts

tsoFree: procedure expose m.
parse arg ddList, tryClose
    do dx=1 to words(ddList)
        dd = word(ddList, dx)
        if adrTso('free dd('dd')', '*') <> 0 then do
            if pos('IKJ56861I', m.tso_trap) > 0 & tryClose == 1 then
              if pos('NOT FREED, DATA SET IS OPEN', m.tso_trap),
                    > 0 then do
                  /* IKJ56861I  FILE A1 NOT FREED, DATA SET IS OPEN */
                say 'dataset open:' substr(m.tso_trap, 3)
                say '.... trying to close'
                if adrTso('execio 0 diskR' dd '(finis)', '*') = 0 then
                   call adrTso 'free dd('dd')', '*'
                end
            if m.tso_rc \== 0 then
                call sayNl 'rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
            end
        call tsoDD dd, '-', 1
        end
    return
endProcedure tsoFree

dsnCreateAtts: procedure expose m.
parse arg dsn, atts
    res = ''
    if dsn \== '' & \ abbrev(dsn, '-') then
        res = "dataset('"dsnSetMbr(dsn)"')"
    if abbrev(atts, ':') then do
        parse var atts a1 atts
        rl = substr(a1, 3)
        if abbrev(a1, ':F') then do
            if rl = '' then
                rl = 80
            res =  res "recfm("space(f b)") lrecl("rl")"
            end
        else if abbrev(a1, ':V') then do
            if rl = '' then
                rl = 32756
            res =  res "recfm("space(v b)") lrecl("rl")"
            end
        else if abbrev(a1, ':L') then
            res = res dsnLikeAtts(rl, 0)
        else if abbrev(a1, ':D') then
            res = res dsnLikeAtts(rl, 1)
        else
            call err 'csnCreateAtt bad :' a1
        end
    aU = ' 'translate(atts)
    hasOrg = pos(' DSORG(', aU) > 0 | pos(' DSNTYPE(', aU) > 0
    hasMbr = pos('(', dsn) > 0
    if hasMbr & \ hasOrg then
        atts = atts 'dsorg(po) dsntype(library)'
    if hasOrg | hasMbr then do
        ww = DSORG DSNTYPE
        do wx=1 to words(ww)
            do forever
                cx = pos(' 'word(ww, wx)'(', ' 'translate(res))
                if cx == 0 then
                    leave
                cy = pos(')', res, cx)
                res = delstr(res, cx, cy+1-cx)
                end
            end
        end
    res = res atts
    aU = ' 'translate(res)
    if pos(' MGMTCLAS(', aU) < 1 then
        res = res 'mgmtclas(COM#A091)'
    if pos(' SPACE(', aU) < 1 then
        res = res 'space(1, 50) cylinders'
    return res
endProcedure dsnCreateAtts
/*--- check if a dataset is archive ------------------------------------
          returns 'ok'                    if dataset on disk
                  'not'                   if dataset is not catalogued
                  'arc'                   if dataset archived
                  listDsi errorMsg        otherwise ------------------*/
dsnArc: procedure expose m.
parse upper arg dsn
    lc = listDsi("'"strip(dsn)"' noRecall")
    if lc = 0 then
        return 'ok'
    else if lc=4 & sysReason = 19 then  /* multiple volumes */
        return 'ok'
    else if lc=16 & sysReason = 5 then
        return 'notCat'
    else if lc=16 & sysReason = 9 then
        return 'arc'
    else
        return 'listDsi cc='lc', sysReason='sysReason ,
                          'm2='sysMsgLvl2', m1='sysMsgLvl1
endProcedure dsnArc
/*--- 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)'
    call tsoFree word(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)'
    call tsoFree word(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
    parse value dsnAlloc(frSpec, 'SHR', 'FRDD') with frDD frFr
    parse value dsnAlloc(toSpec, 'OLD', 'TODD') with toDD toFr
    call tsoOpen frDD, 'R'
    call tsoOpen toDD, 'W'
    cnt = 0
    do while readDD(frDD, r.)
        call writeDD toDD, r.
        cnt = cnt + r.0
        end
    call tsoClose frDD
    call tsoClose toDD
    call tsoFree frFr toFr
    if ggSay == 1 | m.debug == 1 then
       say cnt 'records copied from' frSpec 'to' to toSpec
    return
endSubroutine writeDsn
/* copy adrTso end ****************************************************/
/* copy csv begin *****************************************************/
csvIni: procedure expose m.
    if m.csv.ini == 1 then
        return
    m.csv.ini = 1
    call jIni
    call classNew "n CsvRdr u JRW, f RDR r", "m",
        , "jReset m.m.rdr = arg",
        , "jOpen call csvRdrOpen m, opt",
        , "jClose call jClose m.m.rdr; call oMutatName m, 'CsvRdr'"
    call classNew "n CsvRdrR u CsvRdr", "m",
        , "jRead return csvRdrRead(m)"
    call classNew "n CsvWrt u JRW, f RDR r", "m",
        , "jReset m.m.rdr = arg",
        , "jOpen call csvWrtOpen m, opt",
        , "jClose call jClose m.m.rdr; call oMutatName m, 'CsvWrt'"
    call classNew "n CsvWrtR u CsvWrt", "m",
        , "jRead return csvWrtRead(m)"
    return
endProcedure csvIni

/*--- create a new csvRdr --------------------------------------------*/
csvRdr: procedure expose m.
parse arg rdr
    return jReset(oNew('CsvRdr'), rdr)
endProcedure csvRdr

/*--- open csvRdr: read first line and create dataClass --------------*/
csvRdrOpen: procedure expose m.
parse arg m
    call jOpen m.m.rdr, '<'
    mr = m.m.rdr
    if jRead(mr) then do
        ff = 'f' repAll(m.mr, ',', ' v, f ') 'v'
        m.m.class = classNew("n* CsvF u" ff)
        end
    call oMutatName m, 'CsvRdrR'
    return
endProcedure csvRdrOpen

/*--- read next line and return derived object -----------------------*/
csvRdrRead: procedure expose m.
parse arg m
    mr = m.m.rdr
    do until m.mr <> ''
        if \ jRead(mr) then
            return 0
        end
    var = oNew(m.m.class)
    ff = classMet(m.m.class, 'oFlds')
    s = m'.SCAN'
    call scanSrc s, m.mr
    do fx=1
        f1 = m.ff.fx
        if scanString(s, '"') then
            m.var.f1 = m.s.val
        else do
            call scanUntil s, ','
            m.var.f1 = m.s.tok
            end
        if scanEnd(s) then
            leave
        if \ scanLit(s, ',') then
            call scanErr s, ',' expected
        end
    if fx <> m.ff.0 then
        call scanerr s, 'csv cla' m.ff.0 'fields but' cx 'in line'
    m.m = var
    return 1
endProcedure csvRdrRead

/*--- create a new csvRdr --------------------------------------------*/
csvWrt: procedure expose m.
parse arg rdr
    return jReset(oNew('CsvWrt'), rdr)
endProcedure csvWrt

/*--- open csvRdr: read first line and create dataClass --------------*/
csvWrtOpen: procedure expose m.
parse arg m
    call jOpen m.m.rdr, '<'
    m.m.class = ''
    m.m.o1    = ''
    call oMutatName m, 'CsvWrtR'
    return
endProcedure csvWrtOpen

/*--- read next line and return derived object -----------------------*/
csvWrtRead: procedure expose m.
parse arg m
    mr = m.m.rdr
    if m.m.o1 \== '' then do
        i1 = m.m.o1
        m.m.o1 = ''
        end
    else if jRead(mr) then
         i1 = m.mr
    else
        return 0
    if m.m.class == '' then do
        m.m.class = objClass(i1)
        m.m.o1 = i1
        t = ''
        ff = oFlds(i1)
        do fx=1 to m.ff.0
            t = t','m.ff.fx
            end
        m.m = substr(t, 2)
        return 1
        end
    else do
        m.m = csv4Obj(i1, oFlds(i1), 0)
        return 1
        end
endProcedure csvWrtRead

csv4obj: procedure expose m.
parse arg o, ff, hasNull, oNull
    res = ''
    do fx=1 to m.ff.0
        of1 = o || left('.', m.ff.fx \== '') || m.ff.fx
        v1 = m.of1
        if hasNull & v1 = oNull then
            res = res','
        else if v1 = '' then
            res = res',""'
        else if pos(',', v1) > 0 | pos('"', v1) > 0 then
            res = res','quote(v1, '"')
        else
            res = res','v1
        end
    return substr(res, 2)
endProcedure csv4obj
/* copy csv end   *****************************************************/
/* copy j begin *******************************************************
    the j framework
         jReset
         jOpen
         jClose
         jRead
         jWrite
***********************************************************************/
jRead: procedure expose m.
parse arg m
if arg() > 1 then call err '???  old interface'  / 0
    met = objMet(m, 'jRead')
    if m.m.jReading then
        interpret met
    else
        return err('jRead('m') but not opened r')
endProcedure jRead

jReadVar: procedure expose m.
parse arg m, var
if arg() > 2 then call err '???  old interface' / 0
    if \ jRead(m) then
        return 0
    m.var = m.m
    return 1
endProcedure jReadVar

jReadObRe: procedure expose m.
parse arg m
if arg() > 1 then call err '???  old interface' / 0
    if jRead(m) then
        return m.m
    else
        return ''
endProcedure jReadObRe

jReadO: procedure expose m.
parse arg m
if arg() > 1 then call err '???  old interface'  /0
    return jRead(m)
endProcedure jReadO

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

jWriteO: procedure expose m.
parse arg m, var
    call jWrite m, var
    return
endProcedure jWriteO

jWriteAll: procedure expose m.
parse arg m, rdr
    rdr = o2file(rdr)
    met = objMet(m, 'jWriteAll')
    if \ m.m.jWriting then
        return err('jWriteAll('m',' rdr') but not opened w')
    interpret met
    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)
        call jWrite m, m.rdr
        end
    call jClose rdr
    return
endProcedure jWriteNow

jWriteNowImplO: procedure expose m.
parse arg m, rdr
    call jOpen rdr, m.j.cRead
    do while jRead(rdr)
        call jWrite m, m.rdr
        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')')
    m.m.jReading = 0
    m.m.jWriting = 0
    m.m.jUsers = 0
    interpret objMet(m, 'jReset')
    return m
endProcedure jReset

jReset0: 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')')
    m.m.jReading = 0
    m.m.jWriting = 0
    m.m.jUsers = 0
    return m
endProcedure jReset

jOpen: procedure expose m.
parse arg m, opt
    met = objMet(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 met
            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 met
            m.m.jWriting = 1
            end
        end
    m.m.jUsers = oUsers + 1
    return m
endProcedure jOpen

jClose: procedure expose m.
parse arg m
    met = objMet(m, 'jClose')
    oUsers = m.m.jUsers
    if oUsers = 1 then do
        interpret met
        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 do
        call err '-sql in jCatLines'
        end
    f2 = fmt'%#?a%c%#? %#?e%# %& %&a'
    call jOpen m, m.j.cRead
    if \ jRead(m) then do
        call jClose m
        return f(f2'%##e')
        end
    res = f(f2'%##a', m.m)
    do while jRead(m)
        res = res || f(f2, m.m)
        end
    call jClose m
    return res
endProcedure jCatLines


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"
    c1 = classNew('n JRW u ORun, f JREADING v, f JWRITING v', 'm',
        , "new ?r m = jReset0(?new2); ?jReset; return m" ,
        , "jRead" am "jRead('m')'" ,
        , "jWrite" am "jWrite('m',' line')'" ,
        , "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")
    m.class.forceDown.c1 = c1'#new'
    c2 = classNew('n JRWDeleg u JRW', 'm',
        , "new ?r return jReset(?new1, arg)",
        , "jRead md = m.m.deleg; if \ jRead(md) then return 0;" ,
                         "m.m = m.md; return 1",
        , "jWrite  call jWrite(m.m.deleg, line)" ,
        , "jWriteAll call jWriteAll m.m.deleg, rdr",
        , "jWriteNow call jWriteNow m.m.deleg, rdr",
        , "jReset    if arg \== '' then m.m.deleg = arg;",
                                   "else call jReset m.m.deleg;",
        , "jOpen     call jOpen m.m.deleg,' opt; return m" ,
        , "jClose    call jClose m.m.deleg; return m" )
    m.class.forceDown.c2 = c2'#new'
    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  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)
    m.j.out = jOpen(oNew('JSay'), '>')
    m.j.errRead  = "return err('jRead('m') but not opened r')"
    m.j.errWrite = "return err('jWrite('m',' line') but not opened w')"
    call classNew "n JBuf u JRW, f BUF s r", "m",
        , "jOpen call jBufOpen m, opt",
        , "jClose call oMutatName m, 'JBuf'",
        , "jReset call jBufReset m, arg",
        , "jRead" m.j.errRead ,
        , "jWrite" m.j.errWrite
    call classNew "n jBufR u JBuf", "m",
        , "jRead return jBufRead(m)"
    call classNew "n JBufW u JBuf", "m",
        , "jWrite call jBufWrite m, line"
    call classNew "n JBufTxt u JBuf, f MAXL v ", "m",
        , "jReset call jBufReset m, arg; m.m.maxl = 80",
        , "jWrite call jBufWrite m, o2Text(var, m.m.maxl)"
    return
endProcedure jIni

/*--- return a JRW from rdr or in ------------------------------------*/
in2File: procedure expose m.
    parse arg m
    interpret objMet(m, 'in2File')
    return err('in2File did not return')
endProcedure in2File
      /* jstr is part of out interface --> in2Str */
in2Str: procedure expose m.
    parse arg m, fmt
    interpret objMet(m, 'in2Str')
    return err('in2Str did not return')
endProcedure in2Str

in2Buf: procedure expose m.
parse arg m
    interpret objMet(m, 'in2Buf')
    return err('in2Buf did not return')
endProcedure in2Buf

in: procedure expose m.
    if arg() > 0 then call err '??? old interface'
    r = m.j.in
    m.in_ret = jRead(r)
    m.in = m.r
    return m.in_ret
endProcedure in

inVar: procedure expose m.
parse arg var
    return jReadVar(m.j.in, var)
endProcedure inVar

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

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

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

/*--- out (part of) the lines of a stem ----------------------------*/
outSt: procedure expose m.
parse arg st, fx, tx
    do lx=word(fx 1, 1) to word(tx m.st.0, 1)
        call out m.st.lx
        end
    return st
endProcedure outSt

outX: procedure expose m.
parse arg line
    if symbol('m.tst_m') \== 'VAR' then
        call jWrite m.j.out, line
    else
        call tstOut m.tst_m, line
    return 0
endProcedure out

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

JRWDeleg: procedure expose m.
parse arg arg
    return oNew('JRWDeleg', arg)
endProcedure JRWDeleg

/*--- 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
    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
    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
    return m
endProcedure jBufReset

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

jBufWrite: procedure expose m.
parse arg m, line
    call mAdd m'.BUF', line
    return
endProcedure jBufWrite

jBufWriteStem: procedure expose m.
parse arg m, st
    ax = m.m.buf.0
    do sx=1 to m.st.0
        ax = ax + 1
        m.m.buf.ax = m.st.sx
        end
    m.m.buf.0 = ax
    return m
endProcedure jBufWrite

jBufRead: procedure expose m.
parse arg m
    nx = m.m.readIx + 1
    if nx > m.m.buf.0 then
        return 0
    m.m.readIx = nx
    m.m = m.m.BUF.nx
    return 1
endProcedure jBufRead

jBufCopy:
parse arg rdr
    b = jOpen(jBuf(), m.j.cWri)
    call jWriteNow b, rdr
    return jClose(b)
endProcedure jBufCopy

jSingle: procedure expose m.
parse arg m
    call jOpen m, '<'
    one = jRead(m)
    two = jRead(m)
    call jClose m
    if \ one then
        if arg() < 2 then
            call err 'empty file in jSingle('m')'
        else
            return arg(2)
    if two then
        call err '2 or more recs in jSingle('m')'
    return m.m
endProcedure jSingle
/* copy j end *********************************************************/
/* copy o begin ******************************************************
    an object is register for a class in o2c
    a class has a list of parents in cParent
    a class has a methodTable cMet with lazy initialization
             a method generator
        otherwise an existing method is simply copied
***********************************************************************/
oIni: procedure expose m.
    if m.o_ini == 1 then
        return
    m.o_ini = 1
    call classIni
    call classNew 'n= root u', 'm',
           , "new    ?l" ,
           , "new1   ?l" ,
           , "new2   ?l" ,
           , "oClear ?l" ,
           , "oCopy  ?l"
    return
endProcedure oIni

oMetLazy: procedure expose m.
parse arg cl, met, rest
    if met = 'new1' then do
        call mNewArea cl, 'O.'substr(cl,7)
        return  "oMutate(mNew('"cl"'), '"cl"')"
        end
    call classMet cl, 'oFlds'
    if met == 'new' then do
        return 'return' classMet(cl, 'new2')
        end
    else if met == 'new2' then do
        call classMet cl, 'oClear'
        return "classClear('"cl"'," classMet(cl, 'new1')")"
        end
    else if met == 'oClear' then do
        do fx=1 to m.cl.flds.0
            f1 = m.cl.flds.fx
            m.cl.flds_null.fx = if(m.cl.f2c.f1==m.class_W,
                            , m.o_escW, '')
            end
        m.cl.flds_null.0 = m.cl.flds.0
        return "return classClear('"cl"', m)"
        end
    else if met == 'oCopy' then do
        do sx=1 to m.cl.stms.0
            s1 = m.cl.stms.sx
            s2 = m.cl.s2c.s1
            call classMet m.cl.s2c.s1, 'oCopy'
            call classMet m.s2.1, 'oCopy'
            end
        call classMet cl, 'new1'
        return "return classCopy('"cl"', m, t)"
        end
    else
        call err 'bad method in oMetLazy('cl',' met')'
endProcedure oMetLazy

oMetRec: procedure expose m.
parse arg cl, met, rest
    gen = ''
    rx = 1
    do forever
        ry = pos('?', rest, rx)
        if ry == 0 then
            return gen || substr(rest, rx)
        gen = gen || substr(rest, rx, ry-rx)
        rx = verify(rest, m.ut_alfid, 'n', ry+1)
        if rx = 0 then
            rx = length(rest)+1
        rr = classMet(cl, substr(rest, ry+1, rx-ry-1))
        if word(rr, 1) = 'return' then
            rr = subword(rr,2)
        gen = gen  || rr
        end
endProcedure oMetRec

classInheritsOf: procedure expose m.
parse arg cl, sup
    cl = class4name(cl)
    sup = class4name(sup)
    if m.cl.inheritsOf \== 1 then do
        m.cl.inheritsOf = 1
        call classInheritsOfAdd cl, cl'.INHERITSOF'
        end
    return m.cl.inheritsOf.sup == 1
endProcedure classInheritsOf

classInheritsOfAdd: procedure expose m.
parse arg cl, trg, pa
    pa = classCycle(cl, pa)
    m.trg.cl = 1
    call assert "m.cl == 'u'"
    do cx=1 to m.cl.0
        c1 = m.cl.cx
        if m.c1 == 'u' then
            call classInheritsOfAdd c1, trg, pa
        end
    return
endProcedure classInheritsOf

classClear: procedure expose m.
parse arg cl, m
    if m.cl.flds_self then
        m.m = m.cl.flds_null.1
    do fx=1+m.cl.flds_self to m.cl.flds.0
        f1 = m.cl.flds.fx
        m.m.f1 = m.cl.flds_null.fx
        end
    if m.cl.stms_self then
        m.m.0 = 0
    do sx=1+m.cl.stms_self to m.cl.stms.0
        s1 = m.cl.stms.sx
        m.m.s1.0 = 0
        end
    return m
endProcedure classClear

classCopy: procedure expose m.
parse arg cl, m, t
    if t == '' then
        t = oMutate(mNew(cl), cl)
    else
        call oMutate t, cl
    if m.cl.flds_self then
        m.t = m.m
    do fx=1+m.cl.flds_self to m.cl.flds.0
        f1 = m.cl.flds.fx
        m.t.f1 = m.m.f1
        end
    if m.cl.stms_self then
        call classCopyStem m.cl.stms., m, t
    do sx=1+m.cl.stms_self to m.cl.stms.0
        s1 = m.cl.stms.sx
        call classCopyStem m.cl.s2c.s1, m'.'s1, t'.'s1
        end
    return t
endProcedure classCopy

classCopyStem: procedure expose m.
parse arg cl, m, t
    m.t.0 = m.m.0
    do sx=1 to m.t.0
        call classCopy cl, m'.'sx, t'.'sx
        end
    return 0
endProcedure classCopyStem

/*--- return true if src is a rexxVariable a, m.a.c etc. -------------*/
rxIsVar: procedure expose m.
parse arg src
    if pos(left(src, 1), m.ut_rxN1) > 0 then
        return 0
    else
        return verify(src, m.ut_rxId) = 0
endProcedure rxIsVar

/*--- return true if src is a rexxConstant rerpresenting its value ---*/
rxIsConst: procedure expose m.
parse arg src, vars                                                   c
    if \ rxIsVar(src) then
        return 0
    srU = translate(src)
    if srU \== src then
        return 0
    srU = '.'srU'.'
    if pos('.GG', srU) > 0 then
        return 0
    if vars == '' then
        return 1
    upper vars
    do vx=1 to words(vars)
        if pos('.'word(vars, vx)'.', vars) > 0 then
            return 0
        end
    return 1
endProcedure rxIsConst

/*--- return rexx code m.cc or mGet('cc') ----------------------------*/
rxMGet: procedure expose m.
parse arg v1, cc, vars
    if cc == '' then
        return 'm.'v1
    else if rxIsConst(cc, vars) then
        return 'm.'v1'.'cc
    else
        return 'mGet('v1 || quote('.'cc, "'")')'
endProcedure rxMGet

/*--- oCopy for classW ----------------------------------------------*/
oCopyW: procedure expose m.
trace ?r
parse arg m, t
    if t == '' then
        return m
    m.t = o2String(m)
    return oMutate(t, m.class_V)
endProcedure oCopyW

/*--- 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_V then
       call out p1 || m.m
   else if c == m.class_W then
       call out p1 || o2String(m)
   else
       call classOutDone c, m, pr, p1
   return
endProcedure objOut

/*--- recursively output (with outX:) object a with class t ---------*/
classOut: procedure expose m.
parse arg t, a, pr, p1
    return classOutDone(if(t=='',m.class_O, 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 outX(p1'done :'className(t) '@'a)
    done.t.a = 1
    if t = m.class_O then do
        if a == '' then
            return outX(p1'obj null')
        t = objClass(a, '')
        if t = '' then
            return outX(p1'obj has no class @'m.a)
        else
            return classOutDone(t, a, pr, p1'isA')
        end

    if t == m.class_V then
        return outX(p1'=' m.a)
    if t == m.class_W == 'w' then
        return outX(p1'}' substr(a, 2))
    if m.t == 'f' then
        return classOutDone(m.t.1, a'.'m.t.name, pr, p1'.'m.t.name)
    if m.t == 'r' then do
        if m.a == '' then
            return outX(p1'refTo :'className(m.t.1) '@null@')
        else
            return classOutDone(m.t.1, 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_V
        call outX 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 outX p1'stem' m.a.0
        do ux=1 to m.a.0
            call classOutDone m.t.1, 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.1, 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

/*--- create an an object of the class className
        and call it's new method ------------------------------------*/
oNew: procedure expose m.
parse arg cl, arg, arg2, arg3
    interpret classMet(class4name(cl), 'new')
endProcedure oNew

/*--- return the class of object obj --------------------------------*/
objClass: procedure expose m.
parse arg m
    if symbol('m.o.o2c.m') == 'VAR' then
         return m.o.o2c.m
    else if abbrev(m, m.o_escW) then
         return m.class_W
    else if arg() >= 2 then
        return arg(2)
    else
        return err('no class found for object' m)
endProcedure objClass

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

/*--- return the code of method me of object m or String or Null
         set m to the address and ggClass to the class ---------------*/
objMet: procedure expose m. m ggClass
parse arg m, me
    if symbol('m.o.o2c.m') == 'VAR' then
        ggClass = m.o.o2c.m
    else if abbrev(m, m.o_escW) then
        ggClass = m.class_w
    else if m \== '' then
        ggClass = m.class_S
    else
        ggClass = m.class_N
    if symbol('m.ggClass.method.me') == 'VAR' then
        return m.ggClass.method.me
    else
        return classMet(ggClass, me)
endProcedure objMet

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

oClear: procedure expose m.
parse arg m
    interpret objMet(m, 'oClear')
    return m
endProcedure oClear

/*--- copy object m of class cl to t --------------------------------*/
oClaCopy: procedure expose m.
parse arg cl, m, t
    interpret "drop cl;" classMet(cl, 'oCopy')
endProcedure oClaCopy

/*--- copy object m to t / create a new object if t=='' -------------*/
oCopy: procedure expose m.
parse arg m, t
    interpret objMet(m, 'oCopy')
endProcedure oCopy

/*--- return a new instance of a subclass of Run
        with code code in method oRun -------------------------------*/
oRunner: procedure expose m.
parse arg code
    return oNew(classNew('n* ORun u ORun, m oRun' code))
endProcedure oRunner

/*--- 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 of object m No Procedure:
        ??? optimize: class only run ???
         use from caller unprotected---------------------------------*/
oRunNP: procedure expose m.
    interpret objMet(arg(1), 'oRun')
    return
endProcedure oRunNP

/*--- run method oRun and return output in new JBuf ------------------*/
oRun2File: procedure expose m.
parse arg rn
    b = jBuf()
    call pipe '+F' , b
    call oRun rn
    call pipe '-'
    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 arg() = 1 then
        fmt = ' '
    interpret objMet(m, 'o2String')
    return err('o2String did not return')
endProcedure o2String

/*--- 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_V then
        l = m.m
    else if cl == m.class_W then
        l = substr(m, 2)
    else if cl == '?' then
        l = '@'m 'class=???'
    else do
        l = '@'m 'class='className(cl)
        ff = oFlds(m)
        do fx=1 to m.ff.0 while length(l) < maxL + 3
            if m.ff.fx == '' then
                 l = l', .='m.m
            else do
                 f1 = m.ff.fx
                 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.o_escW || str
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_V then
        return = m.m
    else if cl == m.class_W 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 address (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' (cu (',' cu)*)?
    cu = ce | c1* '%' c1* '%'? name+      (same type for each name)

    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
    m.o_escW = '!'
    call mapIni
    m.class.0 = 0
    call mapReset class_n2c  /* name to class */
    m.class_V = classNew('n v u', 'm',
                , "oCopy return oCopyV(m, t)" ,
                , "o2String return m.m",
                , "o2File return file(m.m)")
    m.class_W = classNew('n w u', 'm' ,
                , "o2String return substr(m, 2)" ,
                , "o2File return file(substr(m,2))")
    m.class_O = classNew('n o u')
    m.class_R = classNew('r')

    m.class_C = classNew('n class u')
    call classNew 'n= class u v' ,
            , 'c u u f NAME v',           /* union or class */
            , 'c f u f NAME v',           /* field          */
            , 'c s u' ,                   /* stem           */
            , 'c c u f NAME v',           /* choice         */
            , 'c r u' ,                   /* reference      */
            , 'c m u f NAME v, f MET  v'  /* method         */
    call mAdd m.class_C, classNew('s r class')
    m.class_root = classNew('n root u', 'm',
           , "f2c    ?l" ,
           , "f2x    ?l" ,
           , "oFlds  ?l" ,
           , "s2c    ?l" ,
           , "stms   ?l" ,
           , "in2Str ?r return ?o2String" ,
           , "in2File ?r return ?o2File" ,
           , "in2Buf ?r return jBufCopy(?o2File)" ,
           , "scanSqlIn2Scan ?r" ,
                   "return scanSqlReset(s, ?in2File, wOpt, sOpt)")
    m.class_S = classNew('n String u', 'm',
           , 'in2Str return m' ,
           , 'in2File return jBuf(m)',
           , 'in2Buf return jBuf(m)',
           , "scanSqlIn2Scan ?r if wOpt == '' then wOpt = 0;" ,
                   "return scanSqlReset(s, ?in2File, wOpt, sOpt)")
    m.class_N = classNew('n Null u', 'm',
           , 'in2Str return o2String(m.j.in, fmt)',
           , 'in2File return m.j.in',
           , 'in2Buf return jBufCopy(m.j.in)')
    call 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)'
    return
endProcedure classIni

classNe1: procedure expose m.
parse arg n, ty, nm, refs, io
    ky = ty','nm','space(refs, 1)','strip(io)
    if ty == 'f' & abbrev('=', nm) then do
        if words(refs) = 1 & io == '' then
            return strip(refs)
        else
            call err 'bad field name:' ky
        end
    if n then
        if symbol('m.class_k2c.ky') == 'VAR' then
            return m.class_k2c.ky
    m.class.0 = m.class.0 + 1
    n = 'CLASS.'m.class.0
    call mapAdd class_n2c, n, n
    m.n = ty
    m.n.name = nm
    m.n.met = strip(io)
    m.n.0 = words(refs)
    do rx=1 to m.n.0
        m.n.rx = mapGet(class_n2c, word(refs, rx))
        end
    if right(nm, 1) == '*' then
        nm = left(nm, length(nm)-1)substr(n, 7)
    if length(ty) \== 1 | pos(ty, 'ufscrm') < 1 then
        call err 'bad type' ty': classNe1('ky')' /0
    else if nm == '' & pos(ty, 'm') > 0 then
        call err 'empty name: classNe1('ky')'
    else if nm \== '' & ty \== 'c' & verifId(nm) > 0 then
        call err 'bad name' nm': classNe1('ky')'
    else if nm \= '' & pos(ty, 'rs') > 0 then
        call err 'name for type' ty': classNe1('ky')'
    else if (pos(ty, 'fcsr') > 0 & m.n.0 \== 1) ,
          | (    ty == 'm' & m.n.0 \== 0) then
        call err m.n.0 'bad ref count in classNe1('ky')'
    return n
endProcedure classNe1

classNew: procedure expose m.
parse arg clEx 1 ty rest
    n = ''
    nm = ''
    io = ''
    refs = ''
    if wordPos(ty, 'n n? n* n=') > 0 then do
        nmTy = right(ty, 1)
        parse var rest nm ty rest
        if nmTy = '=' then do
            if \ mapHasKey(class_n2c, nm) then
                call err 'class' nm 'not defined: classNew('clEx')'
            n = mapGet(class_n2c, nm)
            end
        if ty \== 'u' then
            call err 'class name' nm 'without u: classNew('clEx')'
        if nmTy == '?' then do
            if mapHasKey(class_n2c, nm) then
                return mapGet(class_n2c, nm)
            end
        else if nmTy == '*' & arg() == 1 then do
            if mapHasKey(class_n2c, clEx) then
                return mapGet(class_n2c, clEx)
            end
        end
    else do
        nmTy = ''
        if arg() == 1 then
            if mapHasKey(class_n2c, clEx) then
                return mapGet(class_n2c, clEx)
        if length(ty) <> 1 | pos(ty, 'ufscmr') < 1 then
            return err('bad type' ty': classNew('clEx')')
        if pos(ty, 'fcm') > 0 then
            parse var rest nm rest
        if ty == 'm' then
            io = rest
        else if pos(ty, 'fsc') > 0 | (ty == 'r' & rest \== '') then
            refs = classNew(strip(rest))
        else if ty == 'r' then
            refs = m.class_O
        end
    if ty == 'u' then do
        lx = 0
        do while lx < length(rest)
            t1 = word(substr(rest, lx+1), 1)
            cx = pos(',', rest, lx+1)
            if cx <= lx | t1 == 'm' then
                cx = length(rest)+1
            one = strip(substr(rest, lx+1, cx-lx-1))
            lx=cx
            if pos('%', word(one, 1)) < 1 then
                refs = refs classNew(one)
            else do
                parse value translate(word(one, 1), ' ', '-') ,
                      with wBe '%' wAf '%' ww
                do wx=2 to words(one)
                    refs = refs classNew(wBe word(one, wx) wAf)
                    end
                end
            end
        pref = ''
        do ax=2 to arg()
            if length(arg(ax)) == 1 & arg(ax) \== ' ' then
                pref = arg(ax)' '
            else
                refs = refs classNew(pref || arg(ax))
            end
        end
    if nmTy == '=' then do
        if m.n \== ty | ty \== 'u' then
            call err 'n= mismatch'
        do ux=1 to words(refs)
            call mAdd n, word(refs, ux)
            end
        end
    else if nmTy == '*' then
        n = classNe1(0, ty, nm'*', refs, io)
    else
        n = classNe1(nmTy == '', ty, nm, refs, io)
    if arg() == 1 then
        call mapAdd class_n2c, clEx, n
    if nmTy == '*' & m.n.name == nm'*' then
        m.n.name = nm || substr(n, 6)
    if nmTy \== '' & nmTy \== '=' then
       call mapAdd class_n2c, m.n.name, n
    return n
endProcedure classNew

/*--- return the name of a class if it exists otherwise the class ---*/
className: procedure expose m.
parse arg cl
    if \ mapHasKey(class_n2c, cl) then
        return 'notAClass:' cl
    c2 = mapGet(class_n2c, cl)
    if m.c2 = 'u' & m.c2.name \= '' then
        return m.c2.name
    else
        return cl
endProcedure className

/*--- 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

/*--- mutate object m to the class cl -------------------------------*/
oMutate: procedure expose m.
parse arg m, cl
    m.o.o2c.m = cl
    return m
endProcedure oMutate

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

/*--- find the code for a met in a class ---------------------------*/
classMet: procedure expose m.
parse arg cl, met
    if symbol('m.cl.method.met') == 'VAR' then
        return m.cl.method.met
    if symbol('m.class_n2c.cl') \== 'VAR' then
        call err 'no class classMet('cl',' met')'
    if cl \== m.class_n2c.cl then
        return classMet(m.class_n2c.cl, met)
    if symbol('m.cl.metLazy.met') == 'VAR' then do
         parse var m.cl.metLazy.met l1 lRest
         m.cl.method.met = "call err 'building lazy method" cl"#"me"'"
         if l1 == '?r' then
             m.cl.method.met = oMetRec(cl, met, lRest)
         else if l1 \== '?l' then
             call err 'implement' l1 'for lazy' className(cl)'#'met
         else if met = 'oFlds' then do
             m.cl.flds.0 = 0
             m.cl.flds_self = 0
             m.cl.stms.0 = 0
             m.cl.stms_self = 0
             call classFldAdd cl, cl
             m.cl.method.met = cl'.FLDS'
             end
         else if wordPos(met, 'f2c f2x stms s2c') > 0 then do
             call classMet cl, 'oFlds'
             if met == 'f2x' then
                 call mInverse cl'.FLDS', cl'.F2X'
             m.cl.method.met = cl'.'translate(met)
             end
         else
             m.cl.method.met = oMetLazy(cl, met, lRest)
         return m.cl.method.met
         end
    if m.cl.methods == 1 then
        return err('no method' met 'in class' className(cl))
    m.cl.methods = 1
    call classMetGen m.class_root, cl
    call classMetGen cl, cl
    return classMet(cl, met)
endProcedure classMet

classCycle: procedure expose m.
parse arg cl, pa
    if wordPos(cl, pa) < 1 then
        return pa cl
    call err implement cycle
endProcedure classCycle

classMetGen: procedure expose m.
parse arg cl, trg, pa
    pa = classCycle(cl, pa)
    if m.cl == 'm' then do
        mthd = m.cl.name
        drop m.trg.method.mthd m.trg.metLazy.mthd
        if \ abbrev(m.cl.met, '?') then
            m.trg.method.mthd = m.cl.met
        else
            m.trg.metLazy.mthd = m.cl.met
        end
    else if m.cl == 'u' then do
        do cx=1 to m.cl.0
            if wordPos(m.cl.cx,
                , m.class_V m.class_W m.class_O m.class_R) < 1 then
                call classMetGen m.cl.cx, trg, pa
            end
        end
    else if pos(m.cl, 'fscr') < 1 then
        call err 'bad cla' cl m.cl
    return
endProcedure classMetGen

classFlds: procedure expose m.
parse arg cl
    return classMet(cl, 'oFlds')
endProcedure classFlds

/*--- add the the fields of class cl to stem f ----------------------*/
classFldAdd: procedure expose m.
parse arg f, cl, nm, pa
    pa = classCycle(cl, pa)
    if cl == m.class_V | cl == m.class_W | cl == m.class_O ,
             | m.cl == 'r' then
             return classFldAdd1(f'.FLDS', f'.F2C', cl, nm,
                  , if(cl=m.class_W, m.o_escW, ''))
    if m.cl = 's' then do
        if m.cl.1 == '' then
            call err 'stem null class'
        return classFldAdd1(f'.STMS', f'.S2C', m.cl.1, nm, 0)
        end
    if m.cl = 'f' then
        return classFldAdd(f, m.cl.1, nm ,
          || left('.', m.cl.name \== '' & nm \== '') || m.cl.name, pa)
    do tx=1 to m.cl.0
        call classFldAdd f, m.cl.tx, nm, pa
        end
    return 0
endProcedure classFldAdd

classFldAdd1: procedure expose m.
parse arg fa, f2, cl, nm, null
    if symbol('m.f2.nm') == 'VAR' then
        if m.f2.nm == cl then
            return 0
        else
            return err('f2 mismatch f2='f2 Nm 'cl='cl 'old='m.f2.nm)
    m.f2.nm = cl
    if nm == '' then do
        call mMove fa, 1, 2
        m.fa.1 = ''
        call mPut fa'_SELF', 1
        end
    else do
        call mAdd fa, nm
        end
    return 0
endProcedure classFldAdd1
/* copy class 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.ut_alfDot, '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 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 = mapAdr(a, ky, 'a')
    if vv == '' then
        return err('duplicate in mapAdd('a',' ky',' val')')
    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 = mapAdr(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 mapAdr(a, ky, 'g') \== ''
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 =  mapAdr(a, ky, 'g')
    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 = mapAdr(a, ky, 'g')
    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 = 247 - 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 = mapAdr(a, ky, 'g')
            if adr \== '' then do
                ha = left(adr, length(adr) - 2)
                do i = 1 to m.ha.0
                     vv = ha'v'i
                     drop m.ha.i m.vv
                     end
                 drop m.ha.0
                 end
            end
        end
    m.st.0 = 0
    return a
endProcedure mapClear

/*--- return the value pointer for a key, '' if non existing
    f = 'g' return address if exists otherwise ''
        'p' return address if exists otherwise newly added address
        'a' return ''      if exists otherwise newly added address ---*/
mapAdr: procedure expose m.
parse arg a, ky, f
    if length(ky) + length(a) < 247 then do
        res = a'.'ky
        if symbol('m.res') == 'VAR' then
            return copies(res, f \== 'a')
        else if f == 'g' then
            return ''
        end
    else do
        len = length(ky)
        q = len % 2
        ha = a'.'len || left(ky, 80) || substr(ky,
            , len % 2 - 40, 85-length(len)-length(a)) || right(ky, 80)
        if symbol('M.ha.0') == 'VAR' then do
            do i=1 to m.ha.0
                if m.ha.i == ky then
                    return copies(ha'v'i, f \== 'a')
                end
            end
        else do
            i = 1
            end
        if f == 'g' then
            return ''
        if i > 9 then
            call err 'overflow long key' y 'in map' a 'hash' ha'.K.'i
        m.ha.0 = i
        m.ha.i = ky
        res = ha'v'i
        end
    if m.map_keys.a \== '' then
        call mAdd m.map_keys.a, ky
    return res
endProcedure mapAdr

/* copy map 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>.** and m.<mbr>_**: every rexx Module (copy) should only
               allocate these addresses to avoid address conficts
               with <mbr> the name of therexx 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
    ax = m.m_area.0 + 1
    m.m_area.0 = ax
    m.m_area.ax = nm
    if adr == '=' then
        adr = nm
    else if adr == '' then
        adr = 'M.'ax
    if symbol('m.m_2a.nm') == 'VAR' then
        call err 'area name' nm 'already used'
    if symbol('m.m_2a.adr') == 'VAR' then
        call err 'adr' adr 'for area' nm 'already used'
    m.m_2a.adr = adr
    m.m_2a.nm  = adr
    m.adr.0 = 0
    m.m_free.adr.0 = 0
    return nm
endProcedure mNewArea

mNew: procedure expose m. ggArea
parse arg name
    if symbol('m.m_2a.name') \== 'VAR' then
        call err 'area' name 'does not exists'
    adr = m.m_2a.name
    if m.m_free.adr.0 > 0 then do
        fx = m.m_free.adr.0
        m.m_free.adr.0 = fx-1
        return m.m_free.adr.fx
        end
    m.adr.0 = m.adr.0 + 1
    return adr'.'m.adr.0
endProcedure mNew

mFree: procedure expose m.
parse arg m
    adr = left(m, lastPos('.', m)-1)
    fx = m.m_free.adr.0 + 1
    m.m_free.adr.0  = fx
    m.m_free.adr.fx = m
    return ''
endProcedure mFree
/*--- iterate over all allocate elements of an area ------------------*/
mIterBegin: procedure expose m.
parse arg nm
    return m.m_2a.nm'.0'
endProcedure mIterBegin

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

/*--- get m.a --------------------------------------------------------*/
mGet: procedure expose m.
parse arg a
    return m.a
endProcedure mGet

/*--- put value v into m.a -------------------------------------------*/
mPut: procedure expose m.
parse arg a, v
    m.a = v
    return v
endProcedure mPut

/*--- 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

/*--- create the inverse map of a stem -------------------------------*/
mInverse: procedure expose m.
parse arg a, i
    do x=1 to m.a.0
        v = m.a.x
        m.i.v = x
        end
    return m.a.0
endProcedure inverse

/*--- add to m.dst.* a (sub)sequence of m.src.* ----------------------*/
mAddSt: procedure expose m.
    parse arg dst, src, fx , tx
    dx = m.dst.0
    if fx == '' then
        fx = 1
    if tx == '' then
        tx = m.src.0
    do sx = fx to tx
        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
    return mCatFT(st, 1, m.st.0, fmt)

mCatFT: procedure expose m.
parse arg st, fx, tx, fmt
    f2 = fmt'%#?a%c%#? %#?e%# %& %&a'
    if tx < fx then
        return f(f2'%##e')
    res = f(f2'%##a', m.st.fx)
    do sx=fx+1 to tx
        res = res || f(f2, m.st.sx)
        end
    return res
endProcedure mCatFT

fGenCat: procedure expose m.
parse arg s, ax
    do fx=1 ??????? until \ scanLit(s, '%,')
        f.fx = fGen(s)
        end
    if \ scanLit(s, '%)') then
        call scanErr s, 'no %) after @fGenCat%('
    if \ scanEnd(s) then
        call scanErr s, 'mGenCat not at end'
    if fx < 2 | f.2 == "''" then
        f.2 = fGen(scanSrc(f_u, '%c'))
    if fx < 3 then
        f.3 = "''"
    if fx < 4 then
        f.4 = "''"
    adr = m.s.src'%'
    if f.1 == "''" then
        m.f_gen.adr.1 = 'return' f.2
    else
        m.f_gen.adr.1 = 'return' f.1 '||' f.2
    m.f_gen.adr.2 = 'return' f.4
    if f.3 == "''" then
        return f.2
    else
        return f.3 '||' f.2
endProcedure fGenCat

mIni: procedure expose m.
    if m.m_ini == 1 then
        return
    m.m_ini = 1
    call utIni
    m.mBase64 = m.ut_alfUC || m.ut_alfLc || m.ut_digits'+-'
    m.m_area.0 = 0
    call mNewArea
    return
endProcedure mIni

/* copy m end *********************************************************/
/* copy fTab begin *****************************************************
    output Modes: t = tableMode 1 line per object
                  c = colMode   1 line per column/field of object

    lifeCycle fTab           sql

        fTabReset            sqlFTabReset
        fTabAdd    *         sqlFTabAdd *
                             sqlFTabOthers ?
        fTabGenerate
        fTabBegin                                      header lines
        fTab1 * / tTabCol *
        fTabEnd                                        trailer lines
***********************************************************************/
fTabIni: procedure expose m.
    if m.fTab_ini == 1 then
        return
    m.fTab_ini = 1
    call classIni
    m.fTab_class = classNew("n FTab u ORun, m oRun call fTab m")
    return
endProcedure fTabIni

fTabReset: procedure expose m.
parse arg m, m.m.titBef, m.m.titAft, m.m.opt
    call fTabIni
    if m.m.titBef == '' & m.m.titaft == '' then do
        m.m.titBef = 'c 1'
        m.m.titAft = '1 c'
        end
    if m.m.titBef == '-' then
        m.m.titBef = ''
    if m.m.titAft == '-' then
        m.m.titAft = ''
    m.m.generated = ''
    m.m.0 = 0
    m.m.len = 0
    m.m.cols = ''
    m.m.sqlOthers = 1
    m.m.set.0 = 0
    return oMutate(m, m.fTab_class)
endProcedure fTabReset

/* add a piece to title tx at current pos */
fTabAddTit: procedure expose m.
parse arg m, cx, tx, t1
    m.m.generated = ''
    if ty < m.m.cx.tit.0 then do
        do xx=m.m.cx.tit.0+1 to tx-1
            m.m.cx.tit.xx = ''
            end
        m.m.cx.tit.0 = tx
        end
    m.m.cx.tit.tx = t1
    return m
endProcedure fTabAddTit

/*--- set the infos for one column -----------------------------------*/
fTabSet: procedure expose m.
parse arg m, c1 aDone, f1, l1
    sx = m.m.set.0 + 1
    m.m.set.0 = sx
    m.m.set.sx = c1 aDone
    m.m.set.sx.fmt = f1
    m.m.set.sx.labelCy = l1
    m.m.set.sx.labelTi = c1
    m.m.set.c1 = sx
    return
endProcedure fTabSet

fTabAdd: procedure expose m.     /* old interface, new is ...RCT */
parse arg m, c1Done, f1, l1
    call fTabAddRCT m, c1Done, f1, , l1
    ox = m.m.0
    m.m.ox.tit.0 = max(arg()-3, 1)
    do tx=2 to m.m.ox.tit.0
        m.m.ox.tit.tx = arg(tx+3)
        end
    return
endProcedure fTabAdd

fTabAddRCT: procedure expose m.
parse arg m, rxNm aDone, f1, cyNm, tiNm
    cx = m.m.0 + 1
    m.m.generated = ''
    m.m.0 = cx
    m.m.cx.tit.0 = max(arg()-4, 1)
    m.m.cx.tit.1 = ''
    do tx=2 to m.m.cx.tit.0
        m.m.cx.tit.tx = arg(tx+4)
        end
    r1 = rxNm
    if rxNm == '' then
        r1 = '='
    else if rxNm == '=' then
        rxNm = ''
    m.m.cols = m.m.cols r1
    if words(m.m.cols) <> cx then
        call err 'mismatch of column number' cx 'col' rxNm / 0
    if length(aDone) > 1 | wordPos('<'aDone'>', '<> <0> <1>') < 1 then
        call err 'bad done' length(aDone) '<'aDone'> after rxNm' rxNm
    m.m.cx.col = rxNm
    m.m.cx.done = aDone \== 0
    if cyNm == '' then
        m.m.cx.labelCy = r1
    else
        m.m.cx.labelCy = cyNm
    if tiNm == '' then
        m.m.cx.labelTi = m.m.cx.labelCy
    else
        m.m.cx.labelTi = tiNm
    px = pos('%', f1)
    ax = pos('@', f1)
    if px < 1 | (ax > 0 & ax < px) then
        m.m.cx.fmt = f1
    else
        m.m.cx.fmt = left(f1, px-1)'@.'rxNm || substr(f1, px)
    return m
endProcedure fTabAddRCT

fTabGenerate: procedure expose m.
parse arg m, sep
    f = ''
    tLen = 0
    m.m.tit.0 = words(m.m.titBef m.m.titAft) + 5
    do tx=1 to m.m.tit.0
        m.m.tit.tx = ''
        end
    do kx=1 to m.m.0
       rxNm = m.m.kx.col
       call mPut 'F_TEMP'left('.', rxNm\=='')rxNm, m.m.kx.labelTi
       t1 = f(m.m.kx.fmt, 'F_TEMP')
       m.m.kx.len = length(t1)
       if pos(strip(t1), m.m.kx.labelTi) < 1 then
           t1 = left(left('', max(0, verify(t1, ' ') -1)) ,
                 || m.m.kx.labelTi, length(t1))
       m.m.kx.tit.1 = t1
       if kx = 1 then do
           f = m.m.kx.fmt
           end
       else do
           tLen = tLen + length(sep)
           f = f || sep || m.m.kx.fmt
           end
       m.m.kx.start = tLen+1
       do tx=1 to m.m.kx.tit.0
           if m.m.kx.tit.tx \== '' then
               if tx > 1 | pos('-', m.m.opt) < 1 then
                   m.m.tit.tx = left(m.m.tit.tx, tLen) ,
                       || strip(m.m.kx.tit.tx, 't')
               else if \ abbrev(m.m.kx.tit.tx, ' ') then
                   m.m.tit.tx = left(m.m.tit.tx, tLen, '-') ,
                       || strip(m.m.kx.tit.tx, 't')
               else
                   m.m.tit.tx = left(m.m.tit.tx, tLen, '-') ,
                          || right(strip(m.m.kx.tit.tx),
                                , length(m.m.kx.tit.tx), '-')
           end
       tLen = tLen + m.m.kx.len
       end
    m.m.len = tLen
    if pos('-', m.m.opt) > 0 then
        m.m.tit.1 = left(m.m.tit.1, tLen +3, '-')
    m.m.fmt = fCache('%.', f)

    cSta = m.m.tit.0+3          /* compute cycle titles */
    cycs = ''
    cyEq = 1
    do cEnd=cSta until kx > m.m.0
        cycs = cycs cEnd
        cx = cSta
        firstRound = 1
        do kx=1 to m.m.0
            if firstRound then
                m.m.tit.cx =  left('', m.m.kx.start-1)m.m.kx.labelCy
            else if length(m.m.tit.cx) <= m.m.kx.start - 2 then
                m.m.tit.cx = left(m.m.tit.cx, m.m.kx.start - 1) ,
                             || m.m.kx.labelCy
            else
                leave
            if cyEq then
               cyEq = translate(m.m.kx.labelCy) ,
                    = translate(m.m.kx.labelTi)
            cx = cx + 1
            if cx > cEnd then do
                cx = cSta
                firstRound = 0
                end
            end
        end
    m.m.cycles = strip(cycs)
    if cyEq & words(cycs) <=  1 then
        m.m.cycles = ''
    m.m.generated = m.m.generated't'
    return
endProcedure fTabGenerate

fTabColGen: procedure expose m.
parse arg m
    do kx=1 to m.m.0
        t = m.m.kx.labelTI
        l = if(m.m.kx.labelCy == t, , m.m.kx.labelCy)
        f = lefPad(lefPad(strip(l), 10) t, 29)
        if length(f) > 29 then
           if length(l || t) < 29 then
               f = l || left('', 29 - length(l || t))t
           else
               f = lefPad(strip(l t), 29)
        g = strip(m.m.kx.fmt)
        o = right(g, 1)
        if pos(o, 'dief') > 0 then
            f = f '@'m.m.kx.col'%12e @'m.m.kx.col'%18c'
        else if o = 'C' then
            f = f left(g, length(g)-1)'c'
        else
            f = f g
        m.m.kx.colFmt = f
        end
    m.m.generated = m.m.generated'c'
    return
endProcedure fTabColGen

fTab: procedure expose m.
parse arg m, rdr
    call fTabBegin m
    call fAll m.m.fmt, rdr
    return fTabEnd(m)
endProcedure fTab

fTabCol: procedure expose m.
parse arg m, i
    do cx=1 to m.m.0
        call out f(m.m.cx.colFmt, i)
        end
    return 0
endProcedure fTabCol

fTabBegin: procedure expose m.
parse arg m
    if pos('t', m.m.generated) < 1 then
        call fTabGenerate m, ' '
    return fTabTitles(m, m.m.titBef)
endProcedure fTabBegin

fTabEnd: procedure expose m.
parse arg m
    return fTabTitles(m, m.m.titAft)

fTabTitles: procedure expose m.
parse arg m, list
    list = repAll(list, 'c', m.m.cycles)
    do tx=1 to words(list)
        t1 = word(list, tx)
        call out m.m.tit.t1
        end
    return m
endProcedure fTabTitles

/*--- auto tables detect format from data ---------------------------*/
/*--- detect fmt from a rdr and write it formatted to stdOut --------*/
fTabAuto: procedure expose m.
parse arg m, rdr, wiTi
    if m == '' then
        m = fTabReset(f_auto, 1)
    i = in2Buf(rdr)
    if m.i.buf.0 <= 0 then
        return m
    call fTabDetect m, i'.BUF', wiTi
    return fTab(m, i)
endProcedure fTabAuto

/*--- generate format for all fields of a stem of objects -----------*/
fTabDetect: procedure expose m.
parse arg m, b
    ff = oFlds(m.b.1)
    do fx=1 to m.ff.0
        call fTabAddDetect m, m.ff.fx, b
        end
    return
endProcedure fTabDetect

/*--- generate format for all fields of a stem of objects -----------*/
sqlfTabDetect: procedure expose m.
parse arg m, b
    cx = m.m.sqlX
    ff = m.sql.cx.fetchFlds
    do fx=1 to words(ff)
        call fTabAddDetect m, word(ff, fx), b, m.sql.cx.d.fx.sqlName
        end
    return
endProcedure fTabDetect

/*--- detect format for one field in stem st ------------------------*/
fTabAddDetect: procedure expose m.
parse arg m, c1 aDone, st, cyNm, tiNm
    lMa = -1
    rMa = -1
    bMa = -1
    aDiv = 0
    nMi =  9e999
    nMa = -9e999
    eMi =  9e999
    eMa = -9e999
    eDa = 2
    dMa = -9e999
    suf = left('.', c1 \== '')c1
    do sx=1 to m.st.0
        v = mGet(m.st.sx || suf)
        lMa = max(lMa, length(strip(v, 't')))
        rMa = max(rMa, length(strip(v, 'l')))
        bMa = max(bMa, length(strip(v, 'b')))
        if \ dataType(v, 'n') then do
            if length(v) > 100 then
                aDiv = 99
            else if aDiv <=3 then
                if aDiv.v \== 1 then do
                    aDiv.v = 1
                    aDiv = aDiv + 1
                    end
            iterate
            end
        v = strip(v)
        nMi = min(nMi, v)
        nMa = max(nMa, v)
        ex = verify(v, 'eEfF', 'm')
        if ex > 0 then do
            eMa = max(eMa, substr(v, ex+1))
            eMi = min(eMi, substr(v, ex+1))
            v = left(v, ex-1)
            do while pos(left(v,1), '+-0') > 0
                v = substr(v, 2)
                end
            eDa = max(eDa, length(v) - (pos('.', v) > 0))
            end
        dx = pos('.', v)
        if dx > 0 then do
            do while right(v, 1) == 0
                v = left(v, length(v)-1)
                end
            dMa = max(dMa, length(v)-dx)
            end
        end
    if nMi > nMa | aDiv > 3 then
        newFo = '-'max(1, (lMa+0))'C'
    else if eMi <= eMa then do
        newFo = ' ' || (eDa+max(length(eMa), length(eMi))+3) ,
             || '.'||(eDa-1)'e'
        end
    else do
        be = max(length(trunc(nMi)), length(trunc(nMa)))
        if dMa <= 0 then
            newFo = max(be, bMa)'I'
        else
            newFo = max(be+1+dMa, bMa)'.'dMa'I'
        end
    call fTabAddRCT m, c1 aDone, '%'newFo, cyNm, tiNm
  /*  say c1 '????==> %'newFo */
   return newFo
endProcedure fTabAddDetect

/*--- format with units seconds ==> 3d13 oder 5.43e6 ==> 5m43 --------*/
fTime: procedure expose m.
?????????????? use f(%kd) ????????????????
fDec: procedure expose m.
?????????????? use f(%kd) ????????????????

fUnits: procedure expose m.
parse arg v, scale, aLen, aPrec, plus
    if \ dataType(v, 'n') then do
        f1 = fUnitsF1(v, scale, aLen, length(plus), length(plus), aPrec)
        return right(v, m.f1.len)
        end
    if v >= 0 then
        sign = plus
    else
        sign = '-'

    v = abs(v)  /* always get rid also of sign of -0 | */
    f1 = fUnitsF1(v, scale, aLen, length(plus), length(sign), aPrec)

    do forever
        w = format(v * m.f1.fact, , m.f1.prec)
        if pos('E-', w) > 0 then
            w = format(0, , m.f1.prec)
        if w < m.f1.lim2 then do
            if m.f1.kind == 'r' then
                x = sign || w || m.f1.unit
            else if m.f1.kind == 'm' then
                x = sign || (w % m.f1.mod) || m.f1.unit ,
                    || right(w // m.f1.mod, m.f1.len2, 0)
            else
                call err 'bad kind' m.f1.kind 'in f1' f1
            if length(x) <= m.f1.len then
                return right(x, m.f1.len)
            end
        if m.f1.next == '' then
            return left(sign, m.f1.len, '+')
        f1 = m.f1.next
        end
endProcedure fUnits

fUnitsF1: procedure expose m.
parse arg v, scale, len, pLen, sLen, aPrec
    slp = 'F_Unit.'scale'.'len'.'pLen'.'sLen'.'aPrec
    if symbol('m.slp.0') \== 'VAR' then do
        sc = 'F_Unit.'scale
        if symbol('m.sc.0') \== 'VAR' then do
            call fUnitsF1Ini1
            if symbol('m.sc.0') \== 'VAR' then
                call err 'bad scale' sc
            end

        if scale = 'd' | scale = 'b' then do
            if aPrec == '' then
                aPrec = 0
            if len = '' then
                len = aPrec + (aPrec >= 0) + 4 + pLen
            dLen = len - sLen
            l2 = '1e' || (dLen - aPrec - (aPrec > 0))
            call fUnitsF1I0 slp, 'nn', 'nn', , , , len
            do x=m.sc.min to m.sc.0
                si = fUnitsF1I0(slp, x, m.sc.x.kind, m.sc.x.unit,
                          , m.sc.x.fact, l2, len)
                m.si.lim1 = m.si.lim2 / m.si.fact
                m.si.prec = aPrec
                m.si.next = slp'.' || (x+1)
                end
            if aPrec > 0 then do
                y = x-1
                si = fUnitsF1I0(slp, x, m.sc.y.kind, m.sc.y.unit,
                          , m.sc.y.fact, ('1e' || dLen), len)
                m.si.lim1 = m.si.lim2 / m.si.fact
                m.si.prec = 0
                end
            end
        else if scale = 't' then do
            if len = '' then
                len = 5 + pLen
            dLen = len - sLen
            call fUnitsF1I0 slp, 'nn', 'nn', , , , len
            do x=m.sc.min to m.sc.0
                si = fUnitsF1I0(slp, x, m.sc.x.kind, m.sc.x.unit,
                          , m.sc.x.fact, m.sc.x.lim2, len ,
                          , m.sc.x.mod, m.sc.x.len2)
                if x = m.sc.0 - 1 then
                    m.si.lim2 = '24e' || (dLen-3)
                else if x = m.sc.0 then
                    m.si.lim2 = '1e' || (dLen-1)
                m.si.lim1 = m.si.lim2 / m.si.fact
                m.si.prec = 0
                m.si.next = slp'.' || (x+1)
                end
            end
        else
            call err implement
        x = m.slp.0
        m.slp.x.next = ''
        end
    if \ datatype(v, 'n') then
        return slp'.nn'
    do q=11 to m.slp.0-1 while v >= m.slp.q.lim1
        end
    if q = 11 & v <> trunc(v) then do
        do q=10 by -1 to m.slp.min+1 while v < m.slp.q.lim1
            end
        q = q + 1
        end
    return slp'.'q
endProcedure fUnitsF1

fUnitsF1Ini1: procedure expose m.
      /*  0    5   10    5   20 */
    iso = '    afpnum kMGTPE   '
    sc = 'F_Unit.d'
    call fUnitsF1i0 sc, 11, 'r', ' ',   1
    f = 1
    do x=1 to 6
        f = f * 1000
        call fUnitsF1i0 sc, 11+x, 'r', substr(iso, 11+x, 1), 1/f
        call fUnitsF1i0 sc, 11-x, 'r', substr(iso, 11-x, 1), f
        end
    sc = 'F_Unit.b'
    f = 1
    do x=11 to 17
        call fUnitsF1i0 sc, x, 'r', substr(iso, x, 1), 1/f
        f = f * 1024
        end
    sc = 'F_Unit.t'
    call fUnitsF1i0 sc, 11, 'm', 's', 100,   6000, , 100, 2
    call fUnitsF1i0 sc, 12, 'm', 'm',   1,   3600, ,  60, 2
    call fUnitsF1i0 sc, 13, 'm', 'h', 1/60,  1440, ,  60, 2
    call fUnitsF1i0 sc, 14, 'm', 'd', 1/3600,    , ,  24, 2
    call fUnitsF1i0 sc, 15, 'r', 'd', 1/3600/24
    return
endProcedure fUnitsF1Ini0

fUnitsF1I0: procedure expose m.
parse arg sc, ix
    si = sc'.'ix
parse arg , , m.si.kind, m.si.unit, m.si.fact,
                , m.si.lim2, m.si.len,
                , m.si.mod, m.si.len2
    if \ datatype(ix, 'n') then
        return si
    if symbol('m.sc.0') \== 'VAR' then do
        m.sc.0   = ix
        m.sc.min = ix
        end
    else do
        m.sc.0   = max(ix, m.sc.0)
        m.sc.min = min(ix, m.sc.min)
        end
    return si
endProcedure fUnitsF1I0
/* copy fTab end   ****************************************************/
/* copy f begin *******************************************************/
/*--- format with the given format ggA1, ggA2, etc. -----------------*/
f: procedure expose m.
parse arg ggFmt, ggA1, ggA2
    if symbol('M.f_gen.ggFmt') \== 'VAR' then
        call fCache ggFmt, ggFmt
    interpret m.f_gen.ggFmt
endProcedure f

fImm: procedure expose m.
parse arg ggFmt, ggA1
    interpret m.ggFmt
endProcedure fImm

/*--- compile format fmt put in the cache with address a ------------*/
fCache: procedure expose m.
parse arg a, fmt
    if a == '%.' then do
        if symbol('m.f_gen0') == 'VAR' then
            m.f_gen0 = m.f_gen0 + 1
        else
            m.f_gen0 = 1
        a = '%.'m.f_gen0
        end
    else if symbol('M.f_gen.a') == 'VAR' then
        return a
    cy = -2
    nm = ' '
    gen = ' '
    opt = 0
    do forever        /* split preprocesser clauses */
        cx = cy+3
        cy = pos('%#', fmt, cx)
        if cy < 1 then
            act = substr(fmt, cx)
        else
            act = substr(fmt, cx, cy-cx)
        do ax=1
            ay = pos('%&', act)
            if ay < 1 then
                leave
            ct = substr(act, ay+2, 1)
            if symbol('f.ct') \== 'VAR' then
                call err 'undefined %&'ct 'in format' fmt
            act = left(act, ay-1) || f.ct || substr(act, ay+3)
            if ax > 100 then
                say 'fGen' fmt nm '==>' act 'actPos' substr(fmt, cx)
            end
        if cy <> 1 & (\ opt | symbol('f.nm') \== 'VAR') then
            f.nm = act
        if cy < 1 | length(fmt) <= cy+1 then
            leave
        nm = substr(fmt, cy+2, 1)
        opt =  nm == '?'
        if pos(nm, '?;#') > 0 then do
            if nm == '#' then do
               if length(fmt) <> cy+3 then
                   call err 'fCache bad %##'nm 'in' fmt
               else if a == fmt then
                   a = left(a, cy-1)
               leave
               end
            cy = cy+1
            nm = substr(fmt, cy+2, 1)
            if nm == ';' then do
               gen = nm
               iterate
               end
            end
        if pos(nm, m.ut_alfa' ') < 1 then
            call err 'fCache bad name %#'nm 'in' fmt
        if pos(nm, gen) < 1 then
            gen = gen || nm
        end
    if symbol('m.f_s_0') \== 'VAR' | m.f_s_0 == 0 then
        m.f_s_0 = 1
    else do
        m.f_s_0 = m.f_s_0 + 1
        f_s = 'F_S_'m.f_s_0
        end
    do cx=1 to length(gen)
        nm = substr(gen, cx, 1)
        act = f.nm
        a2 = a
        if nm == ' ' then
            a2 = a
        else
            a2 = a'%##'nm
        call scanSrc f_s, act
        m.f_gen.a2 = fGen(f_s)
        if \ scanEnd(f_s) then
            call scanErr f_s, "bad specifier '"m.f_s.tok"'"
        end
    m.f_s_0 = m.f_s_0 - 1
    return a
endProcedure fCache

/*--------------------------------------------------------------------
 Format generator    should be compatible with fPrint|
 <<<< + extension of fPrint, - in fPrint but not implemented

   %%  %@ the escaped char
   ('@' argN? '.'? field)?'%' flags? width? ('.' precision)? specifier

 specifier: is the most significant one and defines the type

 - c  Character rigPad or lefPad, prec ==> substr(..., prec)
 -  C Cut %-nC = left(v,n), %nC = right(v,n) %n.mC = substr(m, n)
 - hH Characters in hex
 - iI Signed decimal integer (padded or cut)
 - eE Scientific notation (mantissa/exponent) using e character 3.92e+2
 - S  Strip (both)
 - txy time date formatting from format x to format y see fTstGen
 - kx  units Formatting x = t=time d=decimal b=binary (k) B=binary (kB)
 Flags:
 - -  Left-justify within the given field width; Right is default
 - +  print '+' before non negative numbers
 -' ' print ' ' before non negative numbers
 - /  cut to length

 preprocessor implemented in fCache
%#v   before contents of variable v (1 alfa or 1 space),
      stored at address%##v
%#?v  define variable v if not yet defined
%#;   restart of variables to generate
%&v   use of previously defined variable v
----------------------------------------------------------------------*/
fGen: procedure expose m.
parse arg f_s
    ax = 0
    cd = ''
    cp = ''
    do forever
        txt = fText(f_s)
        if txt \== '' then
            cd = cd '||' quote(txt, "'")
        if scanEnd(f_s) then
            leave
        if \ scanLit(f_s, '@') then do
            ax = ax + 1
            af = ''
            hasDot = 0
            end
        else do
            if scanWhile(f_s, '0123456789') then
                ax = m.f_s.tok
            else if ax < 1 then
                ax = 1
            hasDot = scanLit(f_s, '.')
            af = fText(f_s)
            end
        if \ scanLit(f_s, '%') then
            call scanErr f_s, 'missing %'
        call scanWhile f_s, '-+ /'
        flags = m.f_s.tok
        if scanWhile(f_s, '0123456789') then
            len = m.f_s.tok
        else
            len = ''
        if \ scanLit(f_s, '.') then
            prec  = ''
        else do
            call scanWhile f_s, '0123456789'
            prec = m.f_s.tok
            end
        call scanChar f_s, 1
        sp = m.f_s.tok
        if ax < 3 | ass.ax == 1 then
            aa = 'ggA'ax
        else do
            aa = 'arg(' || (ax+1) || ')'
            if af \== '' then do
                 cp = cp 'ggA'ax '=' aa';'
                 aa = 'ggA'ax
                 ass.ax = 1
                 end
            end
        if af \== '' | hasDot then
            aa = rxMGet(aa, af)
        if sp == 'c' then do
            if prec \== '' then
                aa = 'substr('aa',' prec')'
            if len == '' then
                cd = cd '||' aa
            else if pos('-', flags) > 0 then
                cd = cd '|| lefPad('aa',' len')'
            else
                cd = cd '|| rigPad('aa',' len')'
            end
        else if sp == 'C' then do
            if prec \== '' then do
                cd = cd '|| substr('aa',' prec
                if len == '' then
                    cd = cd')'
                else
                    cd = cd',' len')'
                end
            else if len == '' then
                cd = cd '||' aa
            else if pos('-', flags) > 0 then
                cd = cd '|| left('aa',' len')'
            else
                cd = cd '|| right('aa',' len')'
            end
        else if sp == 'H' then
            cd = cd "|| fH("aa"," len',' (pos('-', flags) > 0)')'
        else if sp == 'h' then
            cd = cd "|| translate(fH("aa", '"siL"'), 'abcdef','ABCDEF')"
        else if sp == 'i' then
            cd = cd "|| fI("aa"," len", '"flags"'," word(prec 0, 1)")"
        else if sp == 'I' then
            cd = cd "|| fI("aa"," len", '/"flags"'," word(prec 0, 1)")"
        else if sp == 'E' | sp == 'e' then do
            if len == '' then
                len = 8
            if prec = '' then
                prec = len - 6
            cd = cd "|| fE("aa"," len"," prec", '"sp"', '"flags"')"
            end
        else if sp = 'S' then
            cd = cd '|| strip('aa')'
        else if sp = 't' then do
            call scanChar f_s, 2
            cd = cd '||' fTstGen(m.f_s.tok, aa)
            end
        else if sp = 'k' then do
            call scanChar f_s, 1
            if pos(m.f_s.tok, 'tdbBiI') < 1 then
                call scanErr f_s, "bad unit type" m.f_s.tok
            if pos('+', flags) > 0 then
                pl = ", '+'"
            else if pos(' ', flags) > 0 then
                pl = ", ' '"
            else
                pl = ''
            cd = cd "|| fUnits("aa", '"m.f_s.tok"'," len"," prec pl")"
            end
  /*    else if sp = '(' then do
            if af == '' | flags \== '' | len \== 0 | prec \== '' then
                call scanErr f_s, "bad call shoud be @sub%("
            interpret "cRes = fGen"af"(f_s, ax)"
            cd = cd '||' cRes
            if \ scanLit(f_s, '%)') then
                if \ scanEnd(f_s) then
                    call scanErr f_s, '%) to end call' af 'expected'
            end     */
        else do
            call scanBack f_s, '%'sp
            leave
            end
        end
    if cd \== '' then
        return strip(cp 'return' substr(cd, 5))
    else
        return "return ''"
endProcedure fGen

fText: procedure expose m.
parse arg f_s
    res = ''
    do forever
        if scanUntil(f_s, '@%') then
            res = res || m.f_s.tok
        if \ scanLit(f_s, '%%', '%@') then
            return res
        res = res || substr(m.f_s.tok, 2)
        end
endProcedure fText

fAll: procedure expose m.
parse arg fmt, rdr
    i = jOpen(in2File(rdr), '<')
    do while jRead(i)
        call out f(fmt, m.i)
        end
    call jClose i
    return
endProcedure fAll

/*--- format character2hex (if not sql null) -------------------------*/
fH: procedure expose m.
parse arg v, l, leftJ
    if v \== m.sqlNull then
        v = c2x(v)
    if length(v) > l then
        return v
    else if leftJ \== 1 then
        return right(v, l)
    else
        return left(v, l)
endProcedure fH

/*--- format integer or fixPoint Decimal -----------------------------*/
fI: procedure expose m.
parse arg v, l, flags, d
    if \ datatype(v, 'n') then
        return fRigLeft(strip(v), l, flags)
    v = format(v, , d, 0)
    if pos('+', flags) > 0 then
        if \ abbrev(v, '-') then
            v = '+'v
    if length(v) > l then
        if pos('/', flags) > 0 then
            return left('', l, '*')
        else
            return v
    return fRigLefPad(v, l, flags)
endProcedure fI

/*--- format with exponent l=total output len
                           d=number of digits after . in mantissa
                           c=exponent character
                           flags: - to ouput text left justified
    differences: exponent is always printed but without +
                 overflow ==> expand exponent, *****, 0e-999 ---------*/
fE: procedure expose m.
parse arg v, l, d, c, flags
    if \ datatype(v, 'n') then
        return fRigLeft(v, l, flags)
    if pos(' ', flags) < 1 then
        if v >=  0 then
            if pos('+', flags) > 0 then
                return '+'substr(fE(v, l, d, c, ' 'flags), 2)
            else
                return substr(fE(v, l+1, d+1, c, ' 'flags), 2)
    x = format(v, 2, d, 7, 0)
    m = 2 + d + (d>0)
    call assert "length(x) == m+9", 'm x length(x)'
    if substr(x, m+1) = '' then
        return left(x, m)c || left('', l-m-1, 0)
    call assert "substr(x, m+1, 1) == 'E'","exponenent in x not at m"
    y = verify(x, '0', 'n',  m+3)
    call assert 'y>0'
    if substr(x, m+1, 2) == 'E+' then do
        if m+10-y <= l-m-1 then
             return left(x,m)c || right(x, l-m-1)
        z = l - 4 - (m+10-y)
        end
    else if substr(x, m+1, 2) == 'E-' then do
        if m+10-y <= l-m-2 then
             return left(x,m)c'-'right(x, l-m-2)
        z = l - 5 - (m+10-y)
        end
    else
        call err 'bad x' x
    if z >= -1 & max(0, z) < d then
        return fE(v, l, max(0, z), c, flags)
    else if substr(x, m+1, 2) == 'E-' then
        return left(x,1)'0'c'-'left('', l-4, 9)
    else
        return left('', l, '*')
endProcedure fE

/*--- right or left with truncation ----------------------------------*/
fRigLeft: procedure expose m
parse arg s, len, flags
    if length(s) = len then
        return s
    else if pos('-', flags) > 0 | length(s) > len then
        return left(s, len)
    else
        return right(s, len)
endProcedure fRigLefPad

/*--- right or left pad without truncation --------------------------*/
fRigLefPad: procedure expose m
parse arg s, len, flags
    if pos('-', flags) > 0 then
        if length(strip(s, 't')) >= len then
            return strip(s, 't')
        else
            return left(s, len)
    else
        if length(strip(s, 'l')) >= len then
            return strip(s, 'l')
        else
            return right(s, len)
endProcedure fRigLefPad

/*--- generate timestamp formats: from format c to format d ----------*/
fTstGen: procedure expose m.
parse arg c 2 d, s
             /* special L = LRSN in Hex
                        l = lrsn (6 or 10 Byte) */

    if c == 'L' then
        return fTstGen('S'd, 'timeLRSN2LZT('s')')
    if c == 'l' then
        return fTstGen('S'd, 'timeLRSN2LZT(c2x('s'))')
    cd = c || d
    if symbol('m.f_tstFo.c') \== 'VAR' ,
         | symbol('m.f_tstFo.d') \== 'VAR' then do
         if m.f_tstIni == 1 then
             call err "bad timestamp from or to format '"cd"'"
        m.f_tstIni = 1
        a = 'F_TSTFO.'
                      /* Y: A = 2010, M:B=Januar,
                         H=Stunde A=0 B=10 C=20
                         jkl = julian day
                         yyyy-mm-dd-hh.mm.ss.ffffff    */
        call mPut a'S', 'yz34-56-78-hi.mn.st.abcdef'
        call mPut a's', 'yz34-56-78-hi.mn.st'
        call mPut a' '
        call mPut a'D', 'yz345678'
        call mPut a'd',   '345678'
        call mPut a't',            'hi.mn.st'
        call mPut a'T',            'hi:mn:st.abcdef'
        call mPut a'E', '78.56.yz34'
        call mPut a'e', '78.56.34'
        call mPut a'Y',      'YM78'
        call mPut a'M',    'M78himns'
        call mPut a'A',    'A8himnst'
        call mPut a'H',           'Himnst'
        call mPut a'n', 'yz345678 hi:mn:st'
        call mPut a'N', 'yz345678 hi:mn:st.abcdef'
        return fTstGen(cd, s)
        end

    if pos('(', s) > 0 | length(s) > 20 then
        return "fImm('F_GEN."fCache('%t'cd, '%t'cd)"'," s")"
    if c == ' ' then do
        if pos(d, 'SN') > 0 then
            o = "Ndate('S') time('L')"
        else if pos(d, 'sMAn ') > 0 then
            o = "ndate('S') time()"
        else if pos(d, 'DdEeY') > 0 then
            o = "Ddate('S')"
        else if pos(d, 'tH') > 0 then
            o = "ttime()"
        else if pos(d, 'T') > 0 then
            o = "Ttime('L')"
        else
            call err 'fTstGen implement d='d
        parse var o o1 2 o2
        return "fImm('F_GEN."fCache('%t'o1 || d, '%t'o1 || d)"',"o2")"
        end
    f = m.f_tstFo.c
    if d = ' ' then
        d = 's'
    t = m.f_tstFo.d
    if f == t then
        return s
    r = "translate("quote(t, "'")"," s "," quote(f, "'")")"

    if pos('yz34', t) > 0 & pos('yz34', f) < 1 then do
        if pos('34', f) > 0 then
            r = "overlay('20'," r"," pos('yz', t)")"
        else if pos('Y', f) > 0 then
            r = fTstGY2Year(r, "substr("s"," pos('Y', f)", 1)", 4,
                , pos('yz34', t))
        else
            r = "overlay('0000'," r"," pos('yz34', t)")"
        end
    else if pos('34', t) > 0 & pos('34', f) < 1 then do
        if pos('Y', f) > 0 then
            r = fTstGY2Year(r, "substr("s"," pos('Y', f)", 1)", 2,
                   , pos('34', t))
        else
            r = "overlay('00'," r"," pos('34', t)")"
        end
    if pos('Y', t) > 0 & pos('Y', f) < 1 then do
        if pos('yz34', f) > 0 then
            r = fTstGYear2Y(r, "substr("s"," pos('yz34', f)", 4)" ,
                   , pos('Y', t))
        else if pos('34', f) > 0 then
            r = fTstGYear2Y(r, "substr("s"," pos('34', f)", 2)" ,
                   , pos('Y', t))
        else
            r = "overlay('?'," r"," pos('Y', t)")"
        end
    if pos('M', t) > 0 & pos('M', f) < 1 then do
        if pos('56', f) > 0 then
            r = fTstG02A(r, "substr("s"," pos('56', f)", 2)" ,
                   , pos('M', t))
        else
            r  = "overlay('?'," r"," pos('M', t)")"
        end
    if pos('56', t) > 0 & pos('56', f) < 1 then
        if pos('M', f) > 0 then
            r = fTstGA2N2(r, "substr("s"," pos('M', f)", 1)" , "-1",
                   , pos('56', t))
        else
            r = "overlay('01'," r"," pos('56', t)")"
    if pos('78', t) > 0 & pos('78', f) < 1 then
        r = "overlay('01'," r"," pos('78', t)")"
    if pos('A8', t) > 0 & pos('A8', f) < 1 then
        if pos('78', f) > 0 then
            r = fTstG02A(r, "substr("s"," pos('78', f)", 1)" ,
                   , pos('A8', t))
        else
            r = "overlay('A1'," r"," pos('A8', t)")"
    if pos('hi', t) > 0 & pos('hi', f) < 1 then do
        if pos('H', f) > 0 then
            r = fTstGA2N1(r, "substr("s"," pos('H', f)", 1)" , "-1",
                   , pos('hi', t))
        else
            r = "overlay('00'," r"," pos('hi', t)")"
        end
    if pos('i', t) > 0 & pos('i', f) < 1 then
        r = "overlay('0'," r"," pos('i', t)")"
    if pos('H', t) > 0 & pos('H', f) < 1 then do
        if pos('hi', f) > 0 then
            r = fTstG02A(r, "substr("s"," pos('hi', f)", 1)" ,
                   , pos('H', t))
        else
            r  = "overlay('A'," r"," pos('H', t)")"
        end
    if pos('mn', t) > 0 & pos('mn', f) < 1 then
        r = "overlay('00'," r"," pos('mn', t)")"
    if pos('st', t) > 0 then do
        if pos('st', f) < 1 then
            if pos('s', f) > 0 then
                r = "overlay('0'," r"," pos('t', t)")"
            else
                r = "overlay('00'," r"," pos('st', t)")"
        end
    else if pos('s', t) > 0 & pos('s', f) < 1 then
        r = "overlay('0'," r"," pos('s', t)")"
    if pos('abcdef', t) > 0 & pos('abcdef', f) < 1 then
        r = "overlay('000000'," r"," pos('abcdef', t)")"
    return r
endProcedure fTstGen

/*--- from Number overlay Character A=0, B=1 etc.. ------------------*/
fTstG02A: procedure expose m.
parse arg r, n, tx
     return "overlay(substr(m.ut_alfUc," n "+ 1, 1)," r"," tx")"

/*--- from year overlay Y-Character  A=2010 etc..--------------------*/
fTstGYear2Y: procedure expose m.
parse arg r, y, tx
     return fTstG02A(r, "("y "+ 10) // 20", tx)

/*--- from character A=0 overlay 1 digit number ---------------------*/
fTstGA2N1: procedure expose m.
parse arg r, y, dlt, tx
     return "overlay(right(pos("y", m.ut_alfUc)" dlt", 1)," r"," tx")"

/*--- from character A=0 overlay 2 digit number ---------------------*/
fTstGA2N2: procedure expose m.
parse arg r, y, dlt, tx
     return "overlay(right('00' || (pos("y", m.ut_alfUc)" dlt"), 2)",
                    "," r"," tx")"

/*--- from Y yearCharacter overlay 2 or 4 digit year ----------------*/
fTstGY2Year: procedure expose m.
parse arg r, y, len, tx
     if m.f_tstY2ini \== 1 then do  /* generate lookup table */
          m.f_tstY2ini = 1
          n = left(f('%t  '), 4)
          do i = n-15 to n+4   /* 20 values: from -15 years to + 4 */
              j = left(f('%tDY', i'0101'), 1)
              m.f_tstY24.j = i
              m.f_tstY22.j = right(i, 2)
           /* say j m.f_tstY22.j m.f_tstY24.j  */
              end
          end
     if wordPos(len, 2 4) < 1 then
         call err "fTstGYear2Y bad len" len
     return "overlay(mGet('F_TSTY2"len".'"y")," r"," tx")"
endProcedure fTstGY2Year

fWords: procedure expose m.
parse arg fmt, wrds
    f2 = fmt'%#?a%c%#? %#?e%# %& %&a'
    if wrds = '' then
        return f(f2'%##e')
    res = f(f2'%##a', word(wrds, 1))
    do wx=2 to words(wrds)
        res = res || f(f2, word(wrds, wx))
        end
    return res
endProcedure fWords

/* copy f end   *******************************************************/
/* copy err begin *** errorhandling, messages, help    ****************/
errIni: procedure expose m.
    if m.err.ini == 1 then
        return
    m.err.ini     = 1
    m.err.handler = ''
    m.err.cleanup = ';'
    m.err.opt     = ''
    parse source m.err.os .
    m.err.ispf    = 0
    m.err.screen  = 0
    if m.err.os \== 'LINUX' then do
        address tso 'profile MsgId'   /* brauchen wir in tsoAlloc| */
        if sysVar('sysISPF') = 'ACTIVE' then do
            m.err.ispf = 1
            address ispExec 'vget zScreen shared'
            m.err.screen = zScreen
            end
        end
    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 & m.err.ispf then
        address ispExec '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 stackHistory
    if ggOpt == '' & m.err.handler \== '' then
        interpret m.err.handler
    call errSay 'f}'ggTxt
    call errCleanup
    if ggOpt == '' then
        ggOpt = m.err.opt
    upper ggOpt
    if pos('T', ggOpt) > 0  then do
        trace ?r
        say 'trace ?r in err'
        end
    if pos('H', ggOpt) > 0 & m.err.eCat == 'f' then
        x = show + stackHistory + by + bad + arithmetic + conversion
    call errSay ' }errorhandler exiting with exit(12)'
    exit errSetRc(12)
endSubroutine err

/*--- set rc for ispf: -------------------------------------------------
    if a cmd is run by ispStart, its RC is ignored,
         but ISPF passes the value of the shared variable zIspfRc
         back as return code
----------------------------------------------------------------------*/
errSetRc: procedure expose m.
parse arg zIspfRc
    if m.err.ispf then
        address ispExec vput 'zIspfRc' shared
    return zIspfRc
endProcedure errSetRc

/*--- error routine: user message cleanup exit -----------------------*/
errAddCleanup: procedure expose m.
parse arg code
    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
    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
    errCleanup = m.err.cleanup
    if errCleanup <> ';' then do
        m.err.cleanup = ';'
        say 'err cleanup begin' errCleanup
        interpret errCleanup
        say 'err cleanup end' errCleanup
        end
    if symbol('m.tso_ddAll') == 'VAR' then
        call tsoFree m.tso_ddAll, 1
    return
endProcedure errCleanup

/*--- say an errorMessage msg with pref and linesplits --------------*/
errSay: procedure expose m.
parse arg msg
    return sayNl(errMsg(msg))

/*--- prefix an errormessage with pref,
          split it into lines at \n to stem m.err -------------------*/
errMsg: procedure expose m.
parse arg msg
    m.err.eCat = 'f'
    do while substr(msg, 2, 1) == '}'
        parse var msg m.err.eCat '}' msg
        end
    res = msg
    if m.err.eCat <> '' then do
       pTxt = ',error,fatal error,input error,s-}scanErr,warning,'
 /*    pTxt = ',error,fatal error,input error,syntax error,warning,' */
       px = pos(','m.err.eCat, pTxt)
       if px < 1 then do
           m.err.eCat = 'f'
           px = pos(','m.err.eCat, pTxt)
           end
       res = substr(pTxt, px+1, pos(',', pTxt, px+2)-px-1)
       if substr(res, 3, 1) == '}' then
           parse var res 2 opt 3 br 4 res
       if opt == '-' then
           res = res msg
       else do
           parse source . . s3 .              /* current rexx */
           res = res 'in' s3':' msg
           end
       end
    return res
endProcedure errMsg

splitNL: procedure expose m.
parse arg st, msg
    bx = 1
    do lx=1 to 20
        ex = pos('\n', msg, bx)
        if ex < bx then
            leave
        m.st.lx = substr(msg, bx, ex-bx)
        bx = ex+2
        end
    m.st.lx = substr(msg, bx)
    m.st.0 = lx
    return st
endProcedure splitNL

outNL: procedure expose m.
parse arg msg
    bx = 1
    do lx=1 to 20
        ex = pos('\n', msg, bx)
        if ex < bx then
            leave
        call out substr(msg, bx, ex-bx)
        bx = ex+2
        end
    call out substr(msg, bx)
    return
endProcedure outNL

/*--- say (part of) the lines of a stem ----------------------------*/
sayNl: procedure expose m.
parse arg msg
    bx = 1
    do lx=1 to 20
        ex = pos('\n', msg, bx)
        if ex < bx then
            leave
        say strip(substr(msg, bx, ex-bx), 't')
        bx = ex+2
        end
    say strip(substr(msg, bx), 't')
    return 0
endProcedure sayNl

/*--- say a trace message if m.trace is set --------------------------*/
debug: procedure expose m.
parse arg msg
    if m.debug == 1 then
        say 'debug:' msg
    return
endProcedure debug

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

/*--- assert that the passed rexx expression evaluates to true -------*/
assert:
    interpret 'if ('arg(1)') == 1 then return 1'
    interpret 'call err' quote('assert failed' arg(1) ':' arg(2) '==>'),
                        arg(2)
endProcedure assert

/*--- abend with Message after displaying help -----------------------*/
errHelp: procedure expose m.
parse arg msg
    call errSay 'i}'msg
    call help 0
    call err 'i}'msg
endProcedure errHelp

/*--- display the first comment block of the source as help ----------*/
help: procedure expose m.
parse arg doClear
    if doClear \== 0 then
        address tso 'clear'
    parse source . . s3 .
    say right(' help for rexx' s3, 72, '*')
    do lx=1 while pos('/*', sourceLine(lx)) < 1
        if lx > 10 then
            return err('initial commentblock not found for help')
        end
    doInc = 1
    ho = m.err.helpOpt
    do lx=lx+1 to sourceline() while pos('*/', sourceline(lx)) = 0
        li = strip(sourceLine(lx), 't')
        cx = lastPos('{', li)
        if cx > 0 then do
            if length(ho) = 1 then
                doInc = cx = length(li) | pos(ho, li, cx+1) > 0
            li = left(li, cx-1)
            end
        if doInc then
            say li
        end
    say right(' end help for rexx' s3, 72, '*')
    return 4
endProcedure help

/* copy err end   *****************************************************/
/* copy ut begin  *****************************************************/
utIni: procedure expose m.
    if m.ut_ini == 1 then
        return
    m.ut_ini = 1
    m.ut_digits = '0123456789'
    m.ut_alfLC  = 'abcdefghijklmnopqrstuvwxyz'
    m.ut_alfUC  = translate(m.ut_alfLc)
    m.ut_Alfa   = m.ut_alfLc || m.ut_alfUC
    m.ut_alfNum = m.ut_alfa || m.ut_digits
    m.ut_alfDot = m.ut_alfNum || '.'
    m.ut_alfId  = m.ut_alfNum'_' /* avoid rexx allowed @ # $ ¬ . | ? */
    m.ut_alfIdN1 = m.ut_digits    /* not as first character */
    m.ut_rxId   = m.ut_AlfNum'@#$?' /* charset puff mit ¬*/
    m.ut_rxDot  = '.'m.ut_rxId
    m.ut_rxN1   = '.0123456789'
    m.ut_alfPrint = m.ut_alfNum'+-*/=()¢!{}<> .:,;?|''"%&#@$£\_'
    return
endProcedure utIni
/*--- 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 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 ------------------------------*/
utTime: procedure expose m.
    return time() 'ela='time('E') 'cpu='sysvar('syscpu'),
            'su='sysvar('syssrv')

/*--- 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

/*--- left without truncation ----------------------------------------*/
lefPad: procedure expose m
parse arg s, len
    if length(strip(s, 't')) >= len then
        return strip(s, 't')
    return left(s, len)
endProcedure lefPad

/*--- right without truncation ---------------------------------------*/
rigPad: procedure expose m
parse arg s, len
    if length(strip(s, 'l')) >= len then
        return strip(s, 'l')
    return right(s, len)
endProcedure rigPad

/*--- 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

/*--- translate to lowercase -----------------------------------------*/
ut2lc: procedure expose m.
parse arg s
    return translate(s, m.ut_alfLc, m.ut_alfUc)

/*--- verify an id ---------------------------------------------------*/
verifId: procedure expose m.
    parse arg src, extra
    if pos(left(src, 1), m.ut_alfIdN1) > 0 then
        return 1
    else
        return verify(src, m.ut_alfId || extra, 'n')

/*--- 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
    do ax = 2 by 2 to arg()
        src = repAl2(src, src, arg(ax), arg(ax+1))
        end
    return src
endProcedure repAll

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

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

utc2d: procedure expose m.
parse arg ch
    cx = length(ch) // 3
    if cx = 0 then
        cx = 3
    res = c2d(left(ch, cx))
    do cx=cx+1 by 3 to length(ch)
        res = res * 16777216 + c2d(substr(ch, cx, 3))
        end
    return res

utInter: procedure expose m.
    interpret arg(1)
    return
endProcedure utInter

/* copy ut end ********************************************************/
}¢--- A540769.WK.REXX(WSH41) cre=2015-01-11 mod=2015-09-25-11.48.46 A540769 ----
/* rexx **********end;**************************************************
  wsh: walter's rexx shell                                   version 4.1
  interfaces:                                                   18. 2.15
      edit macro: for adhoc evaluation or programming
              either block selection: q or qq and b or a
              oder mit Directives ($#...) im Text
      wsh i:  tso interpreter
      batch:  input in dd wsh
      docu:   http://chw20025641/host/db2wiki/pmwiki.php?n=Main.Wsh
      syntax: http://chw20025641/host/db2wiki/pmwiki.php?n=Main.WshSyn
|||achtung $@.sqlRdr() funktioniert nicht nur $@..¢sqlRdr() $!
|||    sqlSel schreib !$#out |||||
|||    einheitliches sql select/rdr syntax in wsh (mit ftab oder ohne|)
|||sql select aus rz2 muss wie csmExRx erfolgen (via WSH) ||||

--- history ------------------------------------------------------------
16. 1.15 f: get/put read/write in/out Object/Strings transparent weiter
*********/ /*** end of help ********************************************
17.11.14 f: iirz2p ==> plex Buchstaben
17.06.14 f: %tS %tT und %tN mit MicroSekunden
16.06.14 csmCopy auch für LoadModule usw.
30.05.14 fix sql4obj fuer rcm profex
14.04.14 class vor obj, mit lazy
19.03.14 ii = installation Info
 9.01.14 walter: redesign formatting (fmt eliminiert), csm.div.p0.exec
 3.12.13 walter: db2 interface radikal geputzt
 3.10.13 walter: uCount fuer TSO <-> unitCount fuer Csm
23. 9.13 walter: ws2 syntax
 6. 2.13 w.keller csmCopy aendert mgmtClas für neue lokale Files
23. 1.13 w.keller sqlErrHandler sowie sqlRx und sql
11. 6.12 w.keller sqlUpdComLoop
23. 5.12 w.keller fix sqlStmt: drop accepts -204
31. 3.12 w.keller sql Query interface incl. sql über CSM
10. 2.12 w.keller div catTb* und eLong
 2. 6.11 w.keller sql error with current location and dsnTiar
 2. 5.11 w.keller sqlStmt etc..
16. 3.11 w.keller basic new r '' ==> r m.class_O
28. 2.11 w.keller compFile by exprBlock, fix $@do forever, |
 7. 2.11 w.keller cleanup block / with sqlPush....
 2. 2.11 w.keller simplified syntax, ast for blocks, ? for help
19. 6.10 w.keller open/close stacked mit jUsers, framed eliminiert
19. 5.10 w.keller class wird direkt erzeugt ohne temporary
18. 4.10 w.keller scanUtilInto ==> testCase noch einbauen
17. 4.10 w.keller oMutate in fileTsoList eingebaut
14. 1.10 w.keller batch interface no longer dies on openIfNotYet
                  CSM.RZ1.P0.EXEC korrigiert
***********************************************************************/

/*--- main code wsh --------------------------------------------------*/
    call errReset 'hI'
    m.myLib  = 'A540769.WK.REXX'
    m.myVers = 'v41 18. 2.15'
    call wshLog
    parse arg spec
    isEdit = 0
    if spec = '' & m.err.ispf then do /* z/OS edit macro */
        isEdit = adrEdit('macro (spec) NOPROCESS', '*') == 0
        if isEdit then do
            call adrEdit '(d) = dataset'
            call adrEdit '(m) = member'
            m.editDsn = dsnSetMbr(d, m)
            if spec = '' & abbrev(m.editDsn, 'A540769.WK.REXX(WS') then
                spec = 't'
            end
        end
    if spec = '?' then
        return help()
    if translate(word(spec, 1)) == 'T' then
        return wshTst(subword(spec, 2))
    f1 = spec
    rest = ''
    inp = ''
    out = ''
    call wshIni
    if m.err.os == 'TSO' then do
        if isEdit then do
            parse value wshEditBegin(spec) with inp out
            end
        else if sysvar('sysEnv') = 'FORE' then do
            end
        else do
            inp = file('dd(wsh)')
            useOut = listDsi('OUT FILE')
            if \ (useOut = 16 & sysReason = 2) then
                out = file('dd(out)')
            end
        end
    else if m.err.os == 'LINUX' then do
        inp = file('&in')
        out = file('&out')
        end
    else
        call err 'implement wsh for os' m.err.os
    m.wshInfo = 'compile'
    m.wsh_exitCC = 0
    call compRun spec, inp, out, wshInfo
    if isEdit then
        call wshEditEnd
exit m.wsh_exitCC

wshLog: procedure expose m.
parse arg msg, st
    lNm = 'tss.ska.db2.wshlog'
    f1 = dsnAlloc('dd(log) mod' lNm '::f', , , '*')
    if datatype(f1, 'n') then do
        lN2 = lNm'.R' || ( random() // 19)
        f1 = dsnAlloc('dd(log) old' lN2 '::f', , , '*')
        if datatype(f1, 'n') then do
            say 'could not allocate log' lNm lN2
            return
            end
        end
    parse source . . s3 .
    o.1 = m.myLib'('s3')' word(m.myVers, 1) sysvar(sysnode) ,
            'j='mvsvar('symdef', 'jobname') ,
             'u='userid() date('s') time()
    if msg <> '' then
        o.2 = left(msg, 80)
    ox = 1 + (msg <> '')
    if st <> '' then do sx=1 to m.st.0
        ox = ox+1
        o.ox = left(m.st.sx, 80)
        end
    call writedd log, o., ox
    call tsoClose log
    call tsoFree log
    return
endProcedure wshLog
/*--- test hook ----------------------------------------------------*/
wshHook_T: procedure expose m.
parse arg cmp
trace ?r
    rest = strip(scanLook(m.cmp.scan))
    call jClose m.cmp.scan
    return wshTst(rest)
endProcedure wshHook_t

wshTst: procedure expose m.
parse arg rest
    m.tst_csm = 1
    if rest = '' then do /* default */
        say funits(3e7, 'd')
        call err tstEnd
        call csmcopy 'CMN.DIV.P0.DB2J.#000197.LLB' ,
                   , 'RZ1/A540769.TST.LXB'
        return 0
        call csmcopy 'RZ1/A540769.TST.PS' ,
                   , 'RZ4/A540769.TST.PO3(EINS)'
        return 0
        call csmcopy 'RZ1/A540769.TST.LCTL(BBB)',
                   , 'RZ4/A540769.TST.PS'
        return 0
        call csmcopy 'A540769.WK.LLB' ,
                   , 'RZ1/A540769.TST.LLB'
        call csmCopL 'RZ4/A540769.WK.JCL(QZ*)',
                   , 'RZ1/A540769.TST.yCL'
        return 0
        call tstfTst
        call sqlConnect DBAF
        call catColCom 'cmnBatch', 'DSN_PGROUP_TABLE',
                     , 'cmnBatch', 'DSN_PGROUP_TABLE_new'
        call sqlDisConnect DBAF
        return 0
        end
    c = ''
    do wx=1 to words(rest)
        c = c 'call tst'word(rest, wx)';'
        end
    if wx > 2 then
        c = c 'call tstTotal;'
    say 'wsh interpreting' c
    interpret c
    exit 0
endProcedure wshTst

/*--- i hook: interpret user input: rexx, expr, data or shell -------*/
wshHook_I: procedure expose m.
parse arg cmp
    inp = strip(scanLook(m.cmp.scan))
    call scanClose m.cmp.scan
    mode = '*'
    do forever
        if pos(left(inp, 1), '/;:*@.-=') > 0 then
            parse var inp mode 2 inp
        if mode == '/' then
            exit 0
        mode = translate(mode, ';', ':')
        if inp <> '' then do
            say 'as' mode 'interpreting' inp
            if mode = ';' then
                interpret inp
            else if mode = '*' then
                interpret 'say' inp
            else do
                call errReset 'h', 'say "******* intercepting error";',
                    'call errSay ggTxt; return "";'
                call oRun compile(comp(jBuf(inp)), mode)
                call errReset 'h'
                end
            end
        say 'enter' mode 'expression, / for end, ; or * for Rexx' ,
                                                 '@ . - =  for wsh'
        parse pull inp
        end
endProcedure wshInter

/*--- sql hook -----------------------------------------------------*/
wshHook_S: procedure expose m.
parse arg cmp
    s = m.cmp.scan
    ki = '='
    call scanVerify s, m.comp_chSpa
    if scanVerify(s, m.comp_chKind) then
        ki = left(m.s.tok, 1)
    call scanChar s
    rest = m.s.tok
    call scanNl s
    dbSy = word(rest, 1)
    if abbrev(dbSy, '-') | \ (length(dbSy) = 4 ,
                   | (length(dbsy) = 8 & substr(dbSy,4,1) == '/')) then
        dbSy = ''
    else
        rest = subWord(rest, 2)
    res = compAST(cmp, 'P', ' f', '',
        , compAstAddOp(cmp, compUnit(cmp, ki, '$#'), '@'))
    call mAdd res, compAst(cmp, 'c', "call sqlConnect '"dbSy"'",
        "; if \ sqlStmts( , 'rb ret', '"rest"') then m.wsh_exitCC=8" ,
        "; call sqlDisConnect;" )
     return res
endProcedure wshHook_s


wshEditBegin: procedure expose m.
parse arg spec
    dst = ''
    li = ''
    m.wsh.editHdr = 0
    pc = adrEdit("process dest range Q", 0 4 8 12 16)
    if pc = 16 then
        call err 'bad range must be q'
    if pc = 0 | pc = 8 then do
        call adrEdit "(rFi) = lineNum .zFrange"
        call adrEdit "(rLa) = lineNum .zLrange"
    /*  say 'range' rFi '-' rLa */
        end
    else do
        rFi = ''
    /*  say 'no range' */
        end
    if pc = 0 | pc = 4 then do
        call adrEdit "(dst) = lineNum .zDest"
    /*  say 'dest' dst */
        dst = dst + 1
        end
    else do
    /*  say 'no dest' */
        if adrEdit("find first '$#out' 1", 4) = 0 then do
            call adrEdit "(dst) = cursor"
    /*      say '$#out' dst   */
            call adrEdit "(li) = line" dst
            m.wsh.editHdr = 1
            end
        end
    m.wsh.editDst = dst
    m.wsh.editOut = ''
    if dst \== '' then do
        m.wsh.editOut = jOpen(jBufTxt(), '>')
        if m.wsh.editHdr then
            call jWrite m.wsh.editOut, left(li, 50) date('s') time()
        end
    if rFi == '' then do
        call adrEdit "(zLa) = lineNum .zl"
        if adrEdit("find first '$#' 1", 4) = 0 then do
            call adrEdit "(rFi) = cursor"
            call adrEdit "(li) = line" rFi
            if abbrev(li, '$#out') | abbrev(li, '$#end') then
                rFi = 1
            if rFi < dst & dst \== '' then
                rLa = dst-1
            else
                rLa = zLa
            end
        else do
            rFi = 1
            rLa = zLa
            end
        end
    /*  say 'range' c1 'rc' pc':' rFi '-' rLa   'after' dst */

    m.wsh.editIn = jOpen(jBuf(), m.j.cWri)
    do lx=rFi to rLa
        call adrEdit "(li) = line" lx
        call jWrite m.wsh.editIn, li
        end
    call errReset 'h',
             , 'return wshEditErrH(ggTxt, ' rFi',' rLa')'
    return jClose(m.wsh.editIn) m.wsh.editOut
endProcedure wshEditBegin

wshEditEnd: procedure expose m.
    call errReset 'h'
    if m.wsh.editOut == '' then
        return 0
    call jClose(m.wsh.editOut)
    lab = wshEditInsLinSt(m.wsh.editDst, 0, , m.wsh.editOut'.BUF')
    call wshEditLocate max(1, m.wsh.editDst-7)
    return 1
endProcedure wshEditEnd

wshEditLocate: procedure
parse arg ln
    call adrEdit '(la) = linenum .zl'
    call adrEdit 'locate '  max(1, min(ln, la - 37))
    return
endProcedure wshEditLocate

wshEditErrH: procedure expose m.
parse arg ggTxt, rFi, rLa
    call errCleanup
    call errReset 'h'
    call splitNl err, errMsg(' }'ggTxt)
    call mMove err, 1, 2
    isScan = 0
    if wordPos("pos", m.err.4) > 0 ,
        & pos(" in line ", m.err.4) > 0 then do
        parse var m.err.4 "pos " pos .     " in line " lin":"
        if pos = '' then do
            parse var m.err.4 " line " lin":"
            pos = 0
            end
        isScan = lin \== ''
        end
    m.err.1 = '***' m.wshInfo 'error ***'
    if m.wshInfo=='compile' & isScan then do
        do sx=1 to m.err.0
            call out m.err.sx
            end
        lab = rFi + lin
        if pos \= '' then
            lab = wshEditInsLin(lab, 'msgline', right('*',pos))
        lab = wshEditInsLinSt((rFi+lin),0, 'msgline', err)
        call wshEditLocate rFi+lin-25
        end
    else do
        if m.wsh.editOut \== '' then do
            do sx=1 to m.err.0
                call jWrite m.wsh.editOut, m.err.sx
                end
            lab = wshEditInsLinSt(m.wsh.editDst, 0, ,
                , m.wsh.editOut'.BUF')
            call wshEditInsLinSt m.wsh.editDst, m.wsh.editHdr,
                , msgline, err
            call wshEditLocate max(1, m.wsh.editDst-7)
            end
        else do
            do sx=1 to m.err.0
                say m.err.sx
                end
            end
        end
    call errCleanup
    exit
endSubroutine wshEditErrH

wshEditInsLinCmd: procedure
parse arg wh
    if dataType(wh, 'n')  then do
        if adrEdit("label" wh "= .a", 0 8 12) \= 12 then
            return 'line_before .a ='
        else
            return 'line_after .zl ='
        end
    else if left(wh, 1) == '.' then
        return 'line_before' wh '='
    else
        return wh
endProcedure wshEditInsLinCmd

wshEditInsLin: procedure
parse arg wh, type
    cmd = wshEditInsLinCmd(wh)
    do ax=3 to arg()
        li = strip(arg(ax), 't')
  /*    if li == '' then   ???? leere Zeilen doch anzeigen
            iterate                 ????? */
        if translate(type) = 'MSGLINE' then do while length(li) > 72
            sx = lastPos(' ', li, 72)
            if sx < 10 then
                sx = 72
            one = left(li, sx)
            li = '    'strip(substr(li, sx))
            call adrEdit cmd type "(one)"
            end
        call adrEdit cmd type "(li)", 0 4
        end
    return cmd
endProcedure wshEditInsLin

wshEditInsLinSt: procedure expose m.
parse arg wh, pl, type, st
    if wh == '' then do
        do ox=1 to m.st.0
            say m.st.ox
            end
        return ''
        end
    wh = wh + pl
    cmd = wshEditInsLinCmd(wh)
    do ax=1 to m.st.0
        call wshEditInsLin cmd, type, m.st.ax
        end
    return cmd
endProcedure wshEditInsLinSt


/*** end wsh, begin all copies ****************************************/
/* copy tstAll begin  *************************************************/
/*----------- neu, noch versorgen |||||-------------------------------*/
tstRts: procedure expose m.
    call wshIni
    call sqlConnect dbaf
    call sqlQuery 3, "select * from sysibm.sysTableSpaceSTats" ,
                    "where dbName = 'MF01A1A' and name = 'A150A'",
                    "order by partition  asc"
    do while sqlFetch(3, rr)
        say f('@.DBNAME%-8C.@NAME%-8C @PARTITION %4C' ,rr)
        end
    call sqlDisconnect
endProcedure tstRts

tstWiki:
    call mapReset docs, 'k'
    call addFiles docs, 'n', '/media/wkData/literature/notes'
    call addFiles docs, 'd', '/media/wkData/literature/docs'

    in = jOpen(file('wiki.old'), '<')
    out = jOpen(file('wiki.new'), '>')
    abc = '(:abc: %l%'
    do cx=1 to length(m.ut_alfLC)
        c1 = substr(m.ut_alfLC, cx, 1)
        abc = abc '¢¢#'c1 '|' c1'!!'
        end
    call jWrite out, abc ':)'
    inTxt = 0
    li = m.i
    do lx=1 while jReadVar(in, i)
        if 0 then
            say length(m.i) m.i
        if m.i = '' then
            iterate
        li = m.i
        do forever
            bx = pos('¢=', li)
            if bx < 1 then
                leave
            ex = pos('=!', li)
            if ex <= bx then
                call err '=! before ¢= in' lx li
            li = left(li, bx-1)substr(li,bx+2, ex-bx-2)substr(li,ex+2)
            end
        li = strip(li)
        if abbrev(li, '|') then do
            w = word(substr(li, 2), 1)
            call jWrite out, '¢¢#'w'!! {$:abc}'
            call jWrite out, '|||' substr(li, 2)
            inTxt=1
            iterate
            end
        if \ inTxt then do
            call jWrite out, li
            iterate
            end
        if \ (abbrev(li, '->') | abbrev(li, '#') ,
                | abbrev(li, '¢')) then do
            call jWrite out, '-<' li
            iterate
            end
        cx = 1
        if substr(li, cx, 2) == '->' then
            cx = verify(li, ' ', 'n', cx+2)
        hasCross = substr(li, cx, 1) == '#'
        if hasCross then
            cx = verify(li, ' ', 'n', cx+1)
        ex = verify(li, '!:\, ', 'm', cx)
        ex = ex - (substr(li, ex, 1) \== '!')
        hasBr = substr(li, cx, 1) == '¢'
        if \ hasBr then
            w = substr(li, cx, ex+1-cx)
        else if substr(li, ex, 1) == '!' then
            w = substr(li, cx+1, ex-1-cx)
        else
            call err 'br not closed' substr(w, cx+1, ex-1-cx) 'in' lx li
        hasPdf = right(w, 4) == '.pdf'
        if hasPdf then
            w = left(w, length(w)-4)
        if verify(w, '#?', 'm') > 0 then do
            w = translate(w, '__', '#?')
            say '*** changing to' w 'in' lx li
            end
        o = '-< {def+'w'}'
        o = '-< ¢¢'w'!!'
        k = translate(w)
        if k.k == 1 then
            say '*** doppelter key' k 'in:' lx left(li,80)
        k.k = 1
        dT = ''
        if mapHasKey(docs, k) then do
            parse value mapGet(docs, k) with dT dC dN
            call mapPut docs, k, dT (dC+1) dN
            do tx=1 to length(dT)
                t1 = substr(dT, tx, 1)
                o = o '¢¢Lit'translate(t1)':'word(dN, tx) '|' t1 '!!'
                end
            end
        qSeq = 'nd'
        qq = left(qSeq, 1)
        qx = 0
        do forever
            qx = pos('@'qq, li, qx+1)
            if qx < 1 then do
                qq = substr(qSeq, 1 + pos(qq, qSeq), 1)
                qx=0
                if qq = '' then
                    leave
                else
                    iterate
                end
            if pos(qq, dT) < 1 then do
                say '*** @'qq 'document not found:' lx li
                iterate
                end
            do qb = qx-1 by -1 while substr(li, qb, 1) == ' '
                end
            do qe = qx+2 by 1 while substr(li, qe, 1) == ' '
                end
            if substr(li, qb, 1) == '.' & substr(li, qe, 1) == '.' then
                li = left(li, qb)substr(li, qe+1)
            else
                li = left(li, qb) substr(li, qe)
            end
        o = o':' strip(substr(li, ex+1+(substr(li,ex+1,1)==':')))
        if 0 then say left(li, 30) '==>' left(o, 30)
        call jWrite out, o
        end
    dk = mapKeys(docs)
    do dx=1 to m.dk.0
        parse value mapGet(docs, m.dk.dx) with dT dC dN
        if dC < 1 then
            say '*** document not used:' dT dC dn
        end
    call jClose in
    call jClose out
    return
endProcedure tstWiki

addFiles: procedure expose m.
parse arg m, ty, file
    fl = jOpen(fileList(file(file)), '<')
    do while jRead(fl)
        nm = substr(m.fl, lastPos('/', m.fl)+1)
        k = translate(left(nm, pos('.', nm)-1))
        if \ mapHasKey(m, k) then do
            call mapAdd m, k, ty 0 nm
            end
        else do
            parse value mapGet(m, k) with dT dC dN
            call mapPut m, k, dT || ty 0 dN nm
            end
        end
    call jClose fl
    return
endProcedure addFiles

tstAll: procedure expose m.
    say 'tstAll ws2 25.2.13...............'
    call tstBase
    call tstComp
    call tstDiv
    if m.err.os = 'TSO' then do
        call tstZos
        call tstTut0
        end
    return 0
endProcedure tstAll

/****** tstZos ********************************************************/
tstZOs:
    call tstTime
    call tstII
    call sqlIni
    call tstSqlRx
    call tstSql
    if m.tst_csm \== 0 then
        call tstSqlCsm
    call scanIni
    call tstSqlC
    call tstSqlCsv
    call tstSqlRxUpd
    call tstSqlUpd
    call tstSqlUpdPre
    call tstSqlE
    call tstSqlB
    call tstSqlO1
    call tstSqlO2
    call tstSqlStmt
    call tstSqlStmts
    call tstSqlUpdComLoop
    call tstSqls1
    call tstSqlO
    call tstSqlFTab
    call tstSqlFTab2
    call tstSqlFTab3
    call tstSqlFTab4
    call tstsql4obj
    call tstTotal
    return
endProcedure tstZOs

tstWshBatch:
    call adrTso 'alloc dd(WSH) shr dsn(WK.TEXT(WSHBATCH))'
    call wshBatch
    return adrTso('free dd(WSH)')

tstLmdTiming:
parse arg lev
    say timing() lev
    call lmdBegin abc, lev
    c = 0
    do while lmdNext(abc, st.)
        c = c + st.0
        end
    call lmdEnd   abc
    say timing() lev 'with group - without reading' c
    call adrIsp 'lmdinit listid(lmdId) level('lev')'
    do c=0 while adrIsp('lmdlist listid(&lmdId) dataset(abc)', 8) = 0
        end
    call adrIsp 'lmdfree listid(&lmdId)'
    say timing() lev 'with list' c
    return
endProcedure lmdTiming

tstCsi: procedure expose m.
    if 0 then do
        call lmd 'A540769.*K'
        call tstCsiCla 'A540769.WK.REXX'
        call tstCsiCla 'A540769.AAA.DATASETS'
        call tstCsiCla 'A540769.RRR.DATASETS'
        end
    if 0 then do
        call tstCsiOpNx 'A540769.WK.*E*'
        call tstCsiOpNx 'A540769.AAA.DATASETS'
        call tstCsiOpNx 'A540769.RRR.DATASETS'
        end
    if 1 then do
        call tstCsiNxCl 'A540769.WK.**'
        call tstCsiNxCl 'DBTF.M*.**'
        call tstCsiNxCl 'DBTF.BE*.**'
        end
    return
tstCsi: procedure expose m.
 /* call lmd 'A540769.*K' */
    call tstCsi1   'A540769.WK.REXX'
    call tstCsi1   'A540769.AAA.DATASETS'
    call tstCsi1   'A540769.RRR.DATASETS'
    return

tstCsiCla:
parse arg ds
    say ds '-->' csiCla(ds)
    return

tstCsiOpNx: procedure expose m.
parse arg ds
    m = 'NUE123'
    s = 'res89'
    flds = 'devtyp volser mgmtclas comudsiz NOBYTTRK UDATASIZ HARBA'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    do while csiNext(m, s)
        say m.s     'dev' c2x(m.s.devTyp) ,
                    'vol' m.s.volSer 'cla' m.s.mgmtclas,
                    'comuDsiz' m.s.comuDsiz 'noBytTrk' m.s.noBytTrk,
                    'udatasiz' c2x(m.s.udatasiz) ,
                    'harba' c2x(m.s.harba)
        end
    return

tstCsiNxCl: procedure expose m.
parse arg ds
    m = 'ABC123'
    s = 'efg89'
    flds = 'devtyp volser mgmtclas'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    say timing() 'begin'
    do i=1 while csiNext(m, s)
        nn = csiArcTape(m.s.volser, m.s.mgmtClas, m.s.devtyp, m.s)
/*      oo = csiCla(strip(m.s))
        if oo <> nn then
            say nn '<>' oo m.s
 */     if i // 1000 = 0 then
            say timing() i nn m.s
        end
    say timing() (i-1) nn m.s
    return
endProcedure tstCsiNxCl

listCatClass: procedure expose m.
parse upper arg dsn
    rt = adrTso("listcat volume entry('"dsn"')", 4)
    /* say 'listct rc =' rt 'lines' m.tso_trap.0 */
    cl = ''
    vo = ''
    if word(m.tso_trap.1, 3) \== dsn then
        call out 'err ??? for dsn' dsn 'bad first line' m.tso_trap.1
    else if pos('NOT FOUND', m.tso_trap.1) > 0 then
        return 'notFound'
    else if word(m.tso_trap.1, 1)\== 'NONVSAM' then
        call out 'err ??? for dsn' dsn 'bad first line' m.tso_trap.1
    do tx=2 to m.tso_trap.0 while vo = '' ,
              & left(m.tso_trap.tx, 1) = ' '
     /* say m.tso_trap.tx */
        p = pos('MANAGEMENTCLASS-', m.tso_trap.tx)
        if p > 0 then
            vo = strip(word(substr(m.tso_trap.tx, p+16), 1), 'l', '-')
        p = pos('VOLSER--', m.tso_trap.tx)
        if p > 0 then
            vo = strip(word(substr(m.tso_trap.tx, p+6), 1), 'l', '-')
        p = pos('DEVCLASS--', m.tso_trap.tx)
            dt = strip(word(substr(m.tso_trap.tx, p+8), 1), 'l', '-')
        end
   /*  say 'lc' cl 'vo' vo 'dt' dt 'dsn' dsn */
    if vo = '' then
        call out '??? err no volume for dsn' dsn
    else if vo = 'ARCIVE' then
        res =  'arcive'
    else if cl = '' then
        res = 'tape'
    else
        res = cl
    if   abbrev(res, 'ar') \= abbrev(dt, "X'0") ,
       | abbrev(res, 'ta') \= abbrev(dt, "X'7") ,
       | (left(res, 1) >= 'A') \= abbrev(dt, "X'3") then
       call out 'err ??? mismatch cl' cl 'vo' vo 'dt' dt 'dsn' dsn
    return res
endProcedure listCatClass

/****** tstDiv ********************************************************/
tstDiv:
    call tstSort
    call tstMat
    call tstMatch
    call tstTotal
    return
endProcedure tstDiv


tstSort: procedure expose m.
    call tstSortComp
    call tstSortComp '<<='
    call tstSortComp 'm.aLe <<= m.aRi'
    call tstSortComp 'if 1 then cmp = m.aLe <<= m.aRi; else call err sd'
    return
endProcedure tstSort

tstSortComp: procedure expose m.
parse arg cmp
/*
$=/tstSort/
    ### start tst tstSort #############################################
    sort 29  c ACHT DREI DREIZEHN EINS ELF FUENF M.I.25 M.I.26 M.I.27 M+
    ..I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI ZWOELF 0 1 1 1 2 2+
    . 3 3 4 4
    sort 22  c ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27 M.I.29 NEUN VIERZ+
    EHN ZEHN ZWOELF 0 1 1 1 2 2 3 3 4 4
    sort 15  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1 1 2 2 3 3 4 4
    sort 8  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1
    sort 1  M.I.29
    sortWords(also als a 05 4 1e2, cmp) a als also 05 1e2 4
    sortWords(also als a 05 4, cmp) a als also 05 4
    sortWords(also als a 05, cmp) a als also 05
    sortWords(also als a, cmp) a als also
    sortWords(also als, cmp) als also
    sortWords(also, cmp) also
    sortWords(, cmp) .
    sortWords(also als a 05 4 1e2, <) a als also 4 05 1e2
    sortWords(also als a 05 4 1e2, >) 1e2 05 4 also als a
$/tstSort/ */
/*
$=/tstSortAscii/
    ### start tst tstSortAscii ########################################
    sort 29  0 1 1 1 2 2 3 3 4 4 ACHT DREI DREIZEHN EINS ELF FUENF M.I.+
    25 M.I.26 M.I.27 M.I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI Z+
    WOELF c
    sort 22  0 1 1 1 2 2 3 3 4 4 ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27+
    . M.I.29 NEUN VIERZEHN ZEHN ZWOELF c
    sort 15  0 1 1 1 2 2 3 3 4 4 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 8  0 1 1 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 1  M.I.29
$/tstSortAscii/ */
    say '### start with comparator' cmp '###'
    if m.err.os == 'LINUX' then
        call tst t, "tstSortAscii"
    else
        call tst t, "tstSort"
    call mAdd mCut(i, 0), eins, zwei, drei, vier, fuenf, sechs,
        ,sieben, acht, neun, zehn, elf, zwoelf, dreizehn, vierzehn
    call mAdd i, 1, 2, 3, 4, 4, 3, 2, 1, 0, 1,
        , 'M.I.25', 'M.I.26', 'M.I.27', 'c', 'M.I.29'
    do yy = m.i.0 by -1 to 1

        do x = 0 to yy
            m.i.0 = x
            call sort i, o, cmp
            m = ''
            la = ''
            if x <> m.o.0 then
                call err 'size mismatch' x '<>' m.o.0
            do y=1 to m.o.0
                m = m m.o.y
                if \ (la << m.o.y) then
                    call err 'sort mismatch' yy x y '\' la '<<' m.o.y
                end
            end
        if yy // 7 = 1 then
           call tstOut t, 'sort' yy m
        do x = 2 to yy
            x1 = x-1
            m.i.x1 = m.i.x
            end
        end
    wi = 'also als a 05 4 1e2'
    do l=words(wi) by -1 to 0
        call tstOut t, 'sortWords('subWord(wi, 1, l)', cmp)' ,
                        sortWords(subWord(wi, 1, l), cmp)
        end
    call tstOut t, 'sortWords('wi', <)' sortWords(wi, '<')
    call tstOut t, 'sortWords('wi', >)' sortWords(wi, '>')
    call tstEnd t
    return
endProcedure tstSort
tstMatch: procedure expose m.
/*
$=/tstMatch/
    ### start tst tstMatch ############################################
    match(eins, e?n*) 1 1 2,i,s trans(E?N*) EiNs
    match(eins, eins) 1 1 0 trans(EINS) EINS
    match(e1nss, e?n*) 1 1 2,1,ss trans(?*) 1ss
    match(eiinss, e?n*) 0 0 -9 trans(E?N*) .
    match(einss, e?n *) 0 0 -9 trans(E?N *) .
    match(ein s, e?n *) 1 1 2,i,s trans(E?N *) EiN s
    match(ein abss  , ?i*b*) 1 1 3,e,n a,ss   trans(?I*B*) eIn aBss  .
    match(ein abss wie gehtsssxdirx und auch , ) 0 0 -9 trans() .
    match(ies000, *000) 1 1 1,ies trans(*000) ies000
    match(xx0x0000, *000) 1 1 1,xx0x0 trans(*000) xx0x0000
    match(000x00000xx, 000*) 1 1 1,x00000xx trans(000*) 000x00000xx
    match(000xx, *0*) 1 1 2,00,xx trans(ab*cd*ef) ab00cdxxef
    match(abcdef, *abcdef*) 1 1 2,, trans(*ABCDEF*) ABCDEF
    match(abcdef, **abcdef***) 1 1 5,,,,, trans(**ABCDEF***) ABCDEF
    match(abcdef, *cd*) 1 1 2,ab,ef trans(*CD*) abCDef
    match(abcdef, *abc*def*) 1 1 3,,, trans(*ABC*DEF*) ABCDEF
    match(abcdef, *bc*e*) 1 1 3,a,d,f trans(*BC*E*) aBCdEf
    match(abcdef, **bc**ef**) 1 1 6,a,,d,,, trans(**BC**EF**) aBCdEF
$/tstMatch/
*/
    call tst t, "tstMatch"
    call scanIni
    call tstOut t, tstMatch1('eins', 'e?n*'                         )
    call tstOut t, tstMatch1('eins', 'eins'                         )
    call tstOut t, tstMatch1('e1nss', 'e?n*', '?*'                  )
    call tstOut t, tstMatch1('eiinss', 'e?n*'                       )
    call tstOut t, tstMatch1('einss', 'e?n *'                       )
    call tstOut t, tstMatch1('ein s', 'e?n *'                       )
    call tstOut t, tstMatch1('ein abss  ', '?i*b*'                  )
    call tstOut t, tstMatch1('ein abss wie gehtsssxdirx und auch ' )
    call tstOut t, tstMatch1('ies000', '*000'                       )
    call tstOut t, tstMatch1('xx0x0000', '*000'                     )
    call tstOut t, tstMatch1('000x00000xx', '000*'                  )
    call tstOut t, tstMatch1('000xx', '*0*', 'ab*cd*ef'             )
    call tstOut t, tstMatch1('abcdef', '*abcdef*'                   )
    call tstOut t, tstMatch1('abcdef', '**abcdef***'                )
    call tstOut t, tstMatch1('abcdef', '*cd*'                       )
    call tstOut t, tstMatch1('abcdef', '*abc*def*'                  )
    call tstOut t, tstMatch1('abcdef', '*bc*e*'                     )
    call tstOut t, tstMatch1('abcdef', '**bc**ef**'                 )
    call tstEnd t
return

tstMatch1:
parse arg w, m, m2
    r = 'match('w',' m')' match(w, m) matchVars(w, m, vv) m.vv.0
    do x=1 to m.vv.0
        r = r','m.vv.x
        end
    if m2 = '' then
        m2 = translate(m)
    r = r 'trans('m2')' matchRep(w, m, m2)
    return r
endProcedure tstMatch1

tstIntRdr: procedure expose m.
    i.1 = "//A540769J JOB (CP00,KE50),'DB2 REO',"
    i.2 = "//         MSGCLASS=T,TIME=1440,"
    i.3 = "//         NOTIFY=&SYSUID,REGION=0M,SCHENV=DB2"
    i.4 = "//*MAIN CLASS=LOG"
    i.5 = "//S1       EXEC PGM=IEFBR14"
    call writeDsn 'RR2/intRdr', i., 5, 1
    return
endProcedure tstIntRdr

tstII: procedure expose m.
/*
$=/tstII/
    ### start tst tstII ###############################################
    iiRz2C(RZ2)       2
    *** err: no rz=R?Y in ii II_RZ2C
    iiRz2C(R?Y)       0
    iiRz2C(RZY)       Y
    iiDbSys2C(de0G)   E
    *** err: no dbSys=D??? in ii II_DB2C
    iiDbSys2C(d???)   0
    iiDbSys2C(DBOF)   F
    iiSys2RZ(S27)     RZ2
    iiMbr2DbSys(DBP5) DVBP
    ii_rz             RZX RZY RZZ RQ2 RR2 RZ2 RZ4
    ii_rz2db.rzx      DE0G DEVG DX0G DPXG
    rr2/dvbp    RR2 R p=R d=RZ2, db DVBP P 1
    iiixVPut 1: RZ2 2 p=B d=RZ2, db DBOF F 0
    iiixVPut 1: RZ2 2 p=B d=RZ2, db DVBP P 1
    iiixVPut 1: RZ2 2 p=B d=RZ2, db DP2G Q 0
$/tstII/
*/
    call tst t, 'tstII'
    call tstOut t, 'iiRz2C(RZ2)      '  iiRz2C(RZ2)
    call tstOut t, 'iiRz2C(R?Y)      '  iiRz2C(R?Y)
    call tstOut t, 'iiRz2C(RZY)      '  iiRz2C(RZY)
    call tstOut t, 'iiDbSys2C(de0G)  '  iiDbSys2C('de0G')
    call tstOut t, 'iiDbSys2C(d???)  '  iiDbSys2C('d???')
    call tstOut t, 'iiDbSys2C(DBOF)  '  iiDbSys2C('DBOF')
    call tstOut t, 'iiSys2RZ(S27)    '  iiSys2RZ(S27)
    call tstOut t, 'iiMbr2DbSys(DBP5)'  iiMbr2DbSys(DBP5)
    call tstOut t, 'ii_rz            '  m.ii_rz
    call tstOut t, 'ii_rz2db.rzx     '  m.ii_rz2db.rzx
    call pipeIni
    call iiVPut 'rr2/ DvBp  '
    call tstOut t, 'rr2/dvbp   ' ,
             vGet('rz') vget('rzC') 'p='vget('rzP') 'd='vget('rzD'),
             || ', db' vGet('dbSys') vGet('dbSysC') vGet('dbSysElar')
    w1 = wordPos('RZ2/DBOF', m.ii_rzDb)
    do wx=w1 to w1+2
        call tstOut t, 'iiixVPut' iiIxVPut(wx)':' ,
             vGet('rz') vget('rzC') 'p='vget('rzP') 'd='vget('rzD'),
             || ', db' vGet('dbSys') vGet('dbSysC') vGet('dbSysElar')
        end
    call tstEnd t
    return
endProcedure tstII

tstTime: procedure
/*         Winterzeit dez 2011
$=/tstTime/
    ### start tst tstTime #############################################
    Lrsn2Lzt(C5E963363741) 2010-05-01-11.34.55.789008
    Achtung: output ist abhaengig von Winter/Sommerzeit und LeapSecs
    timeZone 3600.00000 leapSecs 25.0000000
    2jul(2011-03-31-14.35.01.234567)  11090
    Lrsn2Gmt(C5E963363741) 2010-05-01-10.35.20.789008
    Lrsn2Lzt(C5E963363741) 2010-05-01-11.34.55.789008
    gmt2Lrsn(2011-03-31-14.35.01.234567) C78D87B86E38
    lzt2Lrsn(2011-03-31-14.35.01.234567) C78D7A670B7C
    Lrsn2Gmt(gmt2Lrsn(2011-03-31-14.35.01.234567) 2011-03-31-14.35.01.2+
    34560
    gmt2Lrsn(Lrsn2Gmt(C5E963363741) C5E963363741
    Lrsn2LZt(LZt2Lrsn(2011-03-31-14.35.01.234567) 2011-03-31-14.35.01.2+
    34560
    LZt2Stc(Lrsn2LZt(C5E963363741) C5E963363741
    Lrsn2uniq(C5E963363741) CTNR6S7T back C5E963363740
$/tstTime/
*/
    call jIni
    call tst t, 'tstTime'
    t1 = '2011-03-31-14.35.01.234567'
    s1 = 'C5E963363741'
    call out 'Lrsn2Lzt('s1')' timeLrsn2Lzt(s1)
    call out ,
     'Achtung: output ist abhaengig von Winter/Sommerzeit und LeapSecs'
    call out 'timeZone' m.time_Zone * m.time_StckUnit ,
             'leapSecs' m.time_Leap * m.time_StckUnit
    call timeReadCvt 1
    call out '2jul('t1') ' time2jul(t1)
    call out 'Lrsn2Gmt('s1')' timeLrsn2Gmt(s1)
    call out 'Lrsn2Lzt('s1')' timeLrsn2Lzt(s1)
    call out 'gmt2Lrsn('t1')' timeGmt2Lrsn(t1)
    call out 'lzt2Lrsn('t1')' timeLzt2Lrsn(t1)
    call out 'Lrsn2Gmt(gmt2Lrsn('t1')' timeLrsn2Gmt(timeGmt2Lrsn(t1))
    call out 'gmt2Lrsn(Lrsn2Gmt('s1')' timeGmt2Lrsn(timeLrsn2Gmt(s1))
    call out 'Lrsn2LZt(LZt2Lrsn('t1')' timeLrsn2LZt(timeLZt2Lrsn(t1))
    call out 'LZt2Stc(Lrsn2LZt('s1')'  timeLZt2Lrsn(timeLrsn2LZt(s1))
    call out 'Lrsn2uniq('s1')' timeLrsn2Uniq(s1) ,
                        'back' timeUniq2Lrsn(timeLrsn2Uniq(s1))
    call tstEnd t
    return
endProcedure tstTime
tstMat: procedure expose m.
/*
$=/tstMat/
    ### start tst tstMat ##############################################
    .   0 sqrt  0 isPrime 0 nxPrime    3 permut 1 > 1 2 3 4 5
    .   1 sqrt  1 isPrime 0 nxPrime    3 permut 2 > 2 1 3 4 5
    .   2 sqrt  1 isPrime 1 nxPrime    3 permut 3 > 1 3 2 4 5
    .   3 sqrt  1 isPrime 1 nxPrime    3 permut 3 > 2 3 1 4 5
    .   4 sqrt  2 isPrime 0 nxPrime    5 permut 3 > 3 2 1 4 5
    .   5 sqrt  2 isPrime 1 nxPrime    5 permut 3 > 3 1 2 4 5
    .   6 sqrt  2 isPrime 0 nxPrime    7 permut 4 > 1 2 4 3 5
    .   7 sqrt  2 isPrime 1 nxPrime    7 permut 4 > 2 1 4 3 5
    .   8 sqrt  2 isPrime 0 nxPrime   11 permut 4 > 1 3 4 2 5
    .   9 sqrt  3 isPrime 0 nxPrime   11 permut 4 > 2 3 4 1 5
    .  10 sqrt  3 isPrime 0 nxPrime   11 permut 4 > 3 2 4 1 5
    .  11 sqrt  3 isPrime 1 nxPrime   11 permut 4 > 3 1 4 2 5
    .  12 sqrt  3 isPrime 0 nxPrime   13 permut 4 > 1 4 3 2 5
    .  13 sqrt  3 isPrime 1 nxPrime   13 permut 4 > 2 4 3 1 5
    .  14 sqrt  3 isPrime 0 nxPrime   17 permut 4 > 1 4 2 3 5
    .  15 sqrt  3 isPrime 0 nxPrime   17 permut 4 > 2 4 1 3 5
    .  16 sqrt  4 isPrime 0 nxPrime   17 permut 4 > 3 4 1 2 5
    .  17 sqrt  4 isPrime 1 nxPrime   17 permut 4 > 3 4 2 1 5
    .  18 sqrt  4 isPrime 0 nxPrime   19 permut 4 > 4 2 3 1 5
$/tstMat/
$/tstMat/
*/
    call tst t, 'tstMat'
    q = 'tst_Mat'
    do qx=1 to 20
        m.q.qx = qx
        end
    do i=0 to 18
        call permut q, i
        call tstOut t, right(i,4) 'sqrt' right(sqrt(i), 2) ,
        'isPrime' isPrime(i) 'nxPrime' right(nxPrime(i), 4) ,
            'permut' m.q.0 '>' m.q.1 m.q.2 m.q.3 m.q.4 m.q.5
        end
    call tstEnd t
    return
endProcedure tstMat

/****** tstSql ********************************************************/
tstSqlUtils: procedure expose m.
    call sqlConnect 'DBAF'
    id = 'A540769.dsnUtils'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "TEMPLATE TCOPYD",
            "DSN('&SSID..&JO..&US..P&PART(2)..D&DATE(3)..T&TIME.')",
            "DATACLAS(NULL12) MGMTCLAS(COM#A011) STORCLAS(FAR$N)",
            "SPACE (150,3750) TRK UNCNT 59;",
         "listdef abc include tablespace DA540769.A002* partlevel;",
         "listdef mf  include tablespace MF01A1A.A110A partlevel;",
         "copy list abc copyddn(tcopyd) shrlevel change;"
    st = translate(st)
    call sqlExec "call SYSPROC.DSNUTILS ( :id, :rst,",
                   ":st,:retcode, :ANY" copies(',:e,:e,:z',12) ")"
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.DSNUTILS'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    exit
endProcedure tstSqlUtils

tstSqlStored: procedure expose m.
    call sqlConnect 'DBAF'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "abc"
    call sqlExec "call SYSPROC.COMMAND ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.COMMAND'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStored

tstSqlStoredWK: procedure expose m.
    call sqlConnect 'DBAF'
 /* st = "direct wkUtiSub"
    rst = 'NO'
    say 'before call st='st 'rst='rst
    call sqlExec "call A540769.WKUTILSUB ( :st, :rst)"
    say 'after call st='st 'rst='rst
 */ rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    prc = 'DB2UTIL.DB2UTIL'
    st = "DA540769.A2*" /*  DA540769.A1*" */
    say  "call" prc "("st", ...)"
    call sqlExec "call" prc "(:st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE' prc
 /* say 'results' results  */
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say ''
    say '***** utility output'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say '***** end output'
    call sqlDisconnect
    return 0
endProcedure tstSqlStoredWK

tstSqlStoredSQL1: procedure expose m.
    call sqlConnect 'DBIA'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "DA540769.A2* DA540769.A1*"
    call sqlExec "call A540769.WKSQL1 ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE A540769.WKUTIL'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStoredSQL1

tstSqlTriggerTiming:
    parse upper arg tb ni
    m.noInsert = ni == 0
    cnt = 200000
    if tb = '' then
        TB = 'GDB9998.TWK511TRI'
    call sqlConnect dbaf
    say timing() 'noInsert' m.noInsert 'tb' tb
    call sql2St 'select max(pri) MX from' tb, cc
    if m.cc.1.mx == m.sqlNull then
        m.cc.1.mx = 0
    von = m.cc.1.mx + 1
    bis = m.cc.1.mx + cnt
    say m.cc.0 'max' m.cc.1.mx 'von' von 'bis' bis 'count' cnt tb
    if right(tb, 2) = 'A1' then do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long, par)' ,
                   'values (?, ?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    else do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long)' ,
                   'values (?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    /*  call sqlLn 5,,, 'select * from' tb 'where pri >=' von */
    call sqlCommit
    say timing()
    call sqlDisconnect
    return
endProcedure tstSqlTriggerTiming

tstSqlRx: procedure expose m.
/*
$=/tstSqlRx/
    ### start tst tstSqlRx ############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s7 into :M.SQL.7.D from :src
    .    e 3: with into :M.SQL.7.D = M.SQL.7.D
    fetchA 1 ab=m.abcdef.123.AB=abc ef=efg
    fetchA 0 ab=m.abcdef.123.AB=abc ef=efg
    fetchB 1 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchB 0 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
    fetchBT 1 SYSTABLES
    fetchBT 0 SYSTABLES
    fetchBi 1 SYSINDEXES
    fetchBi 0 SYSINDEXES
$/tstSqlRx/ */
    call jIni
    call tst t, "tstSqlRx"
    call sqlRxConnect
    cx = 7
    call sqlRxQuery cx, 'select * from sysdummy'
    call sqlRxQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlRxFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    call sqlRxClose cx
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlRxQuery cx, sql, 'AB CD EF GH'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlRxFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    call sqlRxClose cx
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlRxQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlRxFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    call sqlRxClose cx
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call sqlQueryPrepare cx, "select name" ,
                        "from sysibm.sysTables" ,
                        "where creator = 'SYSIBM' and name = ?",':m.nm'
    call sqlQueryExecute cx, 'SYSTABLES'
    call out 'fetchBT' sqlRxFetch(cx) m.nm
    call out 'fetchBT' sqlRxFetch(cx) m.nm
    call sqlRxClose cx
    call sqlQueryExecute cx, 'SYSINDEXES'
    call out 'fetchBi' sqlRxFetch(cx) m.nm
    call out 'fetchBi' sqlRxFetch(cx) m.nm
    call tstEnd t
    call sqlRxDisconnect
    return
endProcedure tstSqlRx

tstSql: procedure expose m.
/*
$=/tstSql/
    ### start tst tstSql ##############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s7 into :M.SQL.7.D from :src
    .    e 3: with into :M.SQL.7.D = M.SQL.7.D
    fetchA 1 ab=m.abcdef.123.AB=abc ef=efg
    fetchA 0 ab=m.abcdef.123.AB=abc ef=efg
    fetchB 1 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchB 0 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
    sql2St 1 st.0=1
    sql2St:1 a=a b=2 c=--- d=d
    sql2One a
    sql2One a=a b=2 c=--- d=d
    fetchBT 1 SYSTABLES
    fetchBT 0 SYSTABLES
    fetchBi 1 SYSINDEXES
    fetchBi 0 SYSINDEXES
$/tstSql/ */
    call jIni
    call tst t, "tstSql"
    call sqlConnect
    cx = 7
    call sqlQuery cx, 'select * from sysdummy'
    call sqlQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    call sqlClose cx
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlQuery cx, sql, 'AB CD EF GH'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    call sqlClose cx
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    call sqlClose cx
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call out 'sql2St' sql2St(sql, st) 'st.0='m.st.0
    do i=1 to m.st.0
        call out 'sql2St:'i ,
            'a='m.st.i.a 'b='m.st.i.b 'c='m.st.i.c 'd='m.st.i.d
        end
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    drop m.st.a m.st.b m.st.c m.st.d m.st.0
    call out 'sql2One' sql2One(sql, st)
    call out 'sql2One' ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
    drop m.st.a m.st.b m.st.c m.st.d m.st.0
    call sqlQueryPrepare cx, "select name" ,
                        "from sysibm.sysTables" ,
                        "where creator = 'SYSIBM' and name = ?",':m.nm'
    call sqlQueryExecute cx, 'SYSTABLES'
    call out 'fetchBT' sqlFetch(cx) m.nm
    call out 'fetchBT' sqlFetch(cx) m.nm
    call sqlClose cx
    call sqlQueryExecute cx, 'SYSINDEXES'
    call out 'fetchBi' sqlFetch(cx) m.nm
    call out 'fetchBi' sqlFetch(cx) m.nm
    call tstEnd t
    call sqlDisconnect
    return
endProcedure tstSql

tstSqlCsm: procedure expose m.
/*
$=/tstSqlCsm/
    ### start tst tstSqlCsm ###########################################
    *** err: SQLCODE = -204: S100447.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: subsys = DE0G, host = RZZ
    *** err: implement sqlCmsQuery fetchVars ? or : :m.dst.ab, :m.dst.ef
    fetchA 0 ab=m.abcdef.123.AB=M.abcdef.123.AB ef=M.abcdef.123.EF
    fetchA 0 ab=m.abcdef.123.AB=M.abcdef.123.AB ef=M.abcdef.123.EF
    fetchB 1 ab=a cd=2 ef=--- ind=M.abc.Def.123.EF.SQLIND gh=d ind=M.ab+
    c.Def.123.GH.SQLIND
    fetchB 0 ab=a cd=2 ef=--- ind=M.abc.Def.123.EF.SQLIND gh=d ind=M.ab+
    c.Def.123.GH.SQLIND
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
$/tstSqlCsm/ */
    call pipeIni
    call tst t, "tstSqlCsm"
    call sqlConnect 'RZZ/DE0G'
    cx = 7
    call sqlCsmQuery cx, 'select * from sysdummy'
    call sqlCsmQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlCsmFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlCsmQuery cx, sql, 'AB CD EF GH'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlCsmFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlCsmQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlCsmFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call tstEnd t
    call sqlDisconnect
    return
endProcedure tstsqlCsm

tstSqlCSV: procedure expose m.
/*
$=/tstSqlCSV/
    ### start tst tstSqlCSV ###########################################
    NAME,CREATOR,MITCOM,MITQUO,MITNU,COL6
    SYSTABLES,SYSIBM  ,"a,b","a""b",1,8
    SYSTABLESPACE,SYSIBM  ,"a,b","a""b",---,8
    SYSTABLESPACESTATS,SYSIBM,"a,b","a""b",---,6
$/tstSqlCSV/ */
    call csvIni
    call scanReadIni
    call sqlConnect
    call tst t, "tstSqlCSV"
    r = csvWrt(sqlRdr("select name, creator, 'a,b' mitCom",
         ", 'a""b' mitQuo" ,
         ", case when name='SYSTABLES' then 1 else null end mitNu" ,
         ",length(creator)" ,
              "from sysibm.sysTables" ,
              "where creator = 'SYSIBM' and name like 'SYSTABLES%'",
              "fetch first 3 rows only"))
    call pipeWriteAll r
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlCsv

tstSqlB: procedure expose m.
/*
$=/tstSqlB/
    ### start tst tstSqlB #############################################
    #jIn 1# select strip(name) "tb", strip(creator) cr
    #jIn 2# , case when name = 'SYSTABLES' then 1 else null end
    #jIn 3# from sysibm.sysTables
    #jIn 4# where creator = 'SYSIBM' and name like 'SYSTABLES%'
    #jIn 5# .
    #jIn 6# order by name
    #jIn 7# fetch first 3 rows only
    #jIn eof 8#
    dest1.fet: SYSTABLES SYSIBM 1
    dest2.fet: SYSTABLESPACE SYSIBM ---
    dest3.fet: SYSTABLESPACESTATS SYSIBM ---
$/tstSqlB/ */
    call pipeIni
    call tst t, "tstSqlB"
    cx = 9
    call sqlConnect
    call jIni
    call mAdd mCut(t'.IN', 0),
      , 'select strip(name) "tb", strip(creator) cr' ,
      ,     ", case when name = 'SYSTABLES' then 1 else null end" ,
      ,   "from sysibm.sysTables" ,
      ,   "where creator = 'SYSIBM' and name like 'SYSTABLES%'", ,
      ,   "order by name",
      ,   "fetch first 3 rows only"
     call sqlQuery cx, in2Str(,' ')
     do qx=1 while sqlFetch(cx, 'dest'qx'.fet')
         dst = 'dest'qx'.fet'
         call out dst':' m.dst.tb m.dst.cr m.dst.col3
         drop m.dst.tb m.dst.cr m.dst.col3
         end
     call sqlClose cx
     call sqlDisconnect
     call tstEnd t
     return
endProcedure tstSqlB

tstSqlFTab: procedure expose m.
/*
$=/tstSqlFTab/
    ### start tst tstSqlFTab ##########################################
    UPDATESTATSTIME----------------NACTIVE------NPAGES-EXTENT-LOADRLAST+
    TIME--------------REORGLASTTIME--------------REORGINSERT-REORGDELET+
    E-REORGUPDATE-REORGUNCLUS-REORGDISORG-REORGMASSDE-REORGNEARIN-REORG+
    FARIND-STATSLASTTIME--------------STATSINSERT-STATSDELETE-STATSUPDA+
    TE-STATSMASSDE-COPYLASTTIME---------------COPYUPDATED-COPYCHANGES-C+
    OPYUP-COPYUPDATETIME-------------I---DBID---PSID-PARTIT-INSTAN---SP+
    ACE-TOTALRO-DATASIZ-UNCOMPR-DBNAME-------NAME---------REORGCL-REORG+
    SC-REORGHA-HASHLASTUS-DRI-L-STATS01---
    --- modified
    allg vorher                     others vorher
    db-------ts---------part---inst-UPDATESTATSTIME----------------NACT+
    IVE------NPAGES-EXTENT-LOADRLASTTIME--------------REORGLASTTIME----+
    ----------REORGINSERT-REORGDELETE-REORGUPDATE-REORGUNCLUS-REORGDISO+
    RG-REORGMASSDE-REORGNEARIN-REORGFARIND-STATSLASTTIME--------------S+
    TATSINSERT-STATSDELETE-STATSUPDATE-STATSMASSDE-COPYLASTTIME--------+
    -------COPYUPDATED-COPYCHANGES-COPYUP-COPYUPDATETIME-------------I-+
    --DBID---PSID---SPACE-TOTALRO-DATASIZ-UNCOMPR-REORGCL-REORGSC-REORG+
    HA-HASHLASTUS-DRI-L-STATS01---
    db-------ts---------part---inst-UPDATESTATSTIME----------------NACT+
    IVE------NPAGES-EXTENT-LOADRLASTTIME--------------REORGLASTTIME----+
    ----------REORGINSERT-REORGDELETE-REORGUPDATE-REORGUNCLUS-REORGDISO+
    RG-REORGMASSDE-REORGNEARIN-REORGFARIND-STATSLASTTIME--------------S+
    TATSINSERT-STATSDELETE-STATSUPDATE-STATSMASSDE-COPYLASTTIME--------+
    -------COPYUPDATED-COPYCHANGES-COPYUP-COPYUPDATETIME-------------I-+
    --DBID---PSID---SPACE-TOTALRO-DATASIZ-UNCOMPR-REORGCL-REORGSC-REORG+
    HA-HASHLASTUS-DRI-L-STATS01---
    allg nachher                    others nachher
    DBNAME                   INSTANCE                                  +
    .    NPAGES                                        REORGLASTTIME   +
    .                                   REORGUPDATES                   +
    .     REORGMASSDELETE                     STATSLASTTIME            +
    .                          STATSUPDATES                            +
    .           COPYUPDATEDPAGES               COPYUPDATETIME          +
    .            PSID                   DATASIZE                REORGSC+
    ANACCESS            DRIVETYPE
    .         NAME                   UPDATESTATSTIME                   +
    .                 EXTENTS                                          +
    .            REORGINSERTS                        REORGUNCLUSTINS   +
    .                  REORGNEARINDREF                                 +
    .   STATSINSERTS                        STATSMASSDELETE            +
    .                        COPYCHANGES                               +
    .    IBMREQD         SPACE                   UNCOMPRESSEDDATASIZE  +
    .  REORGHASHACCESS        LPFACILITY
    .                  PARTITION                                NACTIVE+
    .                        LOADRLASTTIME                             +
    .                        REORGDELETES                        REORGD+
    ISORGLOB                      REORGFARINDREF                       +
    .              STATSDELETES                        COPYLASTTIME    +
    .                                   COPYUPDATELRSN                 +
    .     DBID                  TOTALROWS               REORGCLUSTERSEN+
    S        HASHLASTUSED     STATS01
$/tstSqlFTab/
*/
    call pipeIni
    call tst t, 'tstSqlFTab'
    call sqlConnect
    call sqlQuery 17, 'select * from sysibm.sysTablespaceStats' ,
                "where dbName = 'xxxDB06' and name = 'SYSTSTAB'"
    call sqlFTabOpts fTabReset(abc, 1, ,'-'), 17,  12
    call sqlFTabDef      abc, 492, '%7e'
    call sqlFTabOthers abc
    call sqlfTab abc
    call sqlClose 17
    call out '--- modified'
    call sqlQuery 17, 'select * from sysibm.sysTablespaceStats' ,
                "where dbName = 'xxxDB06' and name = 'SYSTSTAB'"
    call sqlFTabOpts fTabReset(abc, 2 1, 1 3 'c', '-'), 17,  12
    call sqlFTabDef      abc, 492, '%7e'
    call sqlFTabAdd      abc, DBNAME, '%-8C', 'db', 'allg vorher'  ,
                                                  , 'allg nachher'
    call sqlFTabAdd      abc, NAME  , '%-8C', 'ts'
    call sqlFTabAdd      abc, PARTITION , , 'part'
    call sqlFTabAdd      abc, INSTANCE  , , 'inst'
    ox = m.abc.0 + 1
    call sqlFTabOthers abc
    call fTabAddTit      abc, ox, 2,             'others vorher'
    call fTabAddTit      abc, ox, 3,             'others nachher'
    call sqlFTab abc
    call sqlClose 17
    call tstEnd t
    call sqlDisConnect
    return
endProcedure tstSqlFTab

tstSqlFTab2: procedure expose m.
/*
$=/tstSqlFTab2/
    ### start tst tstSqlFTab2 #########################################
    Und Eins Oder
    .          zw aber
    Und Eins---------------zw aber---
    . und eins                22223
    . und eins                22224
    Und Eins---------------zw aber---
    Und Eins Oder
    .          zw aber
    a-------------b---
    aaa         222
    a-------------b---
    --- row 1 ---------------------------------------------------------+
    -------------
    .           Und Eins Oder       und eins
    .           zw aber            2.2223000e04              22223
    --- row 2 ---------------------------------------------------------+
    -------------
    .           Und Eins Oder       und eins
    .           zw aber            2.2224000e04              22224
    --- end of 2 rows -------------------------------------------------+
    -------------
$/tstSqlFTab2/
*/
    call pipeIni
    call tst t, 'tstSqlFTab2'
    call sqlConnect
    sq1 = 'select '' und eins'' "Und Eins Oder"',
             ', 22222 + row_number() over() "zw aber" ',
            'from sysibm.sysTables fetch first 2 rows only'
    call sqlQuery 17, sq1
    call sqlFTab sqlFTabOthers(sqlfTabReset(tstSqlFtab2, 17))
    call sqlClose 17
    sq2 =             'select ''aaa'' "a", 222 "b"' ,
            'from sysibm.sysTables fetch first 1 rows only'
    call sqlQuery 17, sq2
    call sqlFTab sqlFTabOthers(sqlfTabReset(tstSqlFtab2, 17))
    call sqlClose 17
    call sqlQuery 15, sq1
    call sqlFTabCol sqlFTabOthers(sqlfTabReset(tstSqlFtab5, 15))
    call sqlClose 15
    call tstEnd t
    call sqlDisConnect
    return
endProcedure tstSqlFTab2
tstSqlFTab3: procedure expose m.
/*
$=/tstSqlFTab3/
    ### start tst tstSqlFTab3 #########################################
    Und Eins Oder
    .          zw aber
    Und Eins--z---
    . und eins 1
    . und eins 2
    Und Eins--z---
    Und Eins Oder
    .          zw aber
    a-----b---
    aaa 222
    a-----b---
$/tstSqlFTab3/
*/
    call pipeIni
    call tst t, 'tstSqlFTab3'
    call sqlConnect
    sq1 = 'select '' und eins'' "Und Eins Oder"',
             ', row_number() over() "zw aber" ',
            'from sysibm.sysTables fetch first 2 rows only'
    r = jOpen(sqlRdr(sq1), '<')
    f = sqlRdrfTabReset(r, 'tstSqFTab3')
    b = in2Buf(r)
    call sqlFTabDetect f, b'.BUF'
    call fTab f, b
    call jClose r
    sq2 =             'select ''aaa'' "a", 222 "b"' ,
            'from sysibm.sysTables fetch first 1 rows only'
    call sqlQuery 17, sq2
    f = sqlfTabReset('tstSqFTab3t', 17)
    st = 'tstSqFTab3st'
    call sqlFetch2St 17, st
    s2 = 'tstSqFTab3s2'
    do sx=1 to m.st.0
        m.s2.sx = st'.'sx
        end
    m.s2.0 = m.st.0
    call sqlFTabDetect f, s2
    call fTabBegin f
    do sx=1 to m.st.0
        call out f(m.f.fmt, st'.'sx)
        end
    call fTabEnd f
    call tstEnd t
    call sqlDisConnect
    return
endProcedure tstSqlFTab3

tstSqlFTab4: procedure expose m.
/*
$=/tstSqlFTab4/
    ### start tst tstSqlFTab4 #########################################
    a
    1
    1 rows fetched: select 1 "a" from sysibm.sysDummy1
    sqlCode -204: drop table gibt.EsNicht
    a
    2
    1 rows fetched: select 2 "a" from sysibm.sysDummy1
    *** err: SQLCODE = -104: ILLEGAL SYMBOL "Y". SOME SYMBOLS THAT MIGH+
    T
    .    e 1:     BE LEGAL ARE: , FROM INTO
    .    e 2: src select x frm y
    .    e 3:   >              <<<pos 14 of 14<<<
    .    e 4: sql = select x frm y
    .    e 5: stmt = prepare s10 into :M.SQL.10.D from :src
    .    e 6: with into :M.SQL.10.D = M.SQL.10.D
    sqlCode -104: select x frm y
    a
    3
    1 rows fetched: select 3 "a" from sysibm.sysDummy1
    dy  => 1
    a
    1
    1 rows fetched: select 1 "a" from sysibm.sysDummy1
    sqlCode -204: drop table gibt.EsNicht
    a
    2
    1 rows fetched: select 2 "a" from sysibm.sysDummy1
    SQLCODE = -104: ILLEGAL SYMBOL "Y". SOME SYMBOLS THAT MIGHT
    .    BE LEGAL ARE: , FROM INTO
    src select x frm y
    .  >              <<<pos 14 of 14<<<
    sql = select x frm y
    stmt = prepare s10 into :M.SQL.10.D from :src
    with into :M.SQL.10.D = M.SQL.10.D
    sqlCode 0: rollback
    ret => 0
$/tstSqlFTab4/
*/
    call pipeIni
    call tst t, 'tstSqlFTab4'
    call sqlConnect
    b = jBuf('select 1 "a" from sysibm.sysDummy1;' ,
         , 'drop table gibt.EsNicht;' ,
         , 'select 2 "a" from sysibm.sysDummy1;',
         , ' select x frm y;',
         , 'select 3 "a" from sysibm.sysDummy1;')
    call tstout t, 'dy  =>' sqlsOut(scanSqlStmtRdr(b, 0))
    call tstout t, 'ret =>' sqlsOut(scanSqlStmtRdr(b, 0), 'rb ret')
    call tstEnd t
    call sqlDisConnect
    return
endProcedure tstSqlFTab4

tstSql4Obj: procedure expose m.
/*
$=/tstSql4Obj/
    ### start tst tstSql4Obj ##########################################
    tstR: @tstWriteoV2 isA :tstClass-1 = -11
    tstR:  .a2i = -11
    tstR:  .b3b = b3
    tstR:  .D4 = D4-11+D4++++.
    tstR:  .fl5 = -111.1
    tstR:  .ex6 = -.111e-11
    insert into cr.insTb -- tstClass-1
    .   ( , a2i, b3b, D4, fl5, ex6
    .   ) values .
    .   ( -11, -11, 'b3', 'D4-11+D4++++', -111.1, -.111e-11
    .   ) ; .
    insert into cr.insTbHex -- tstClass-1
    .   ( , a2i, b3b, D4, fl5, ex6
    .   ) values .
    .   ( -11, -11, 'b3', x'C40760F1F14EC4F44E4E4E4E', -111.1, -.111e-1+
    1
    .   ) ; .
    tstR: @tstWriteoV4 isA :tstClass-2
    tstR:  .c = c83
    tstR:  .a2i = 83
    tstR:  .b3b = b3b8
    tstR:  .D4 = D483+D4++++++++++++++++++++++++++++++++++++++++++++++++
    .++++++++++++++++++++++++++++++.
    tstR:  .fl5 = .183
    tstR:  .ex6 = .11183e-8
    insert into cr.insTb -- tstClass-2
    .   ( c, a2i, b3b, D4, fl5, ex6
    .   ) values .
    .   ( 'c83', 83, 'b3b8'
    .   , 'D483+D4++++++++++++++++++++++++++++++++++++++++++++++++++++++
    '
    .   || '++++++++++++++++++++++++'
    .   , .183, .11183e-8
    .   ) ; .
    insert into cr.insTbHex -- tstClass-2
    .   ( c, a2i, b3b, D4, fl5, ex6
    .   ) values .
    .   ( 'c83', 83, 'b3b8'
    .   , x'C407F8F34EC4F44E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E'
    .   || '++++++++++++++++++++++++++++++++'
    .   || x'314E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E'
    .   , .183, .11183e-8
    .   ) ; .
$/tstSql4Obj/
*/
    call pipeIni
    call tst t, 'tstSql4Obj'
    call pipe '+N'
    call tstDataClassOut '. c3 a2i i b3b c5 D4 c23 fl5 f8n2 ex6 e9n3',
        , -11, -11
    call tstDataClassOut 'c c3 a2i i b3b c5 D4 c93 fl5 f8n2 ex6 e9n3',
        , 83, 83
    call pipe 'P|'
    do cx=1 while in()
        i = m.in
        call mAdd t'.'trans, className(objClass(i)) 'tstClass-'cx
        call out i
        call sql4Obj i, 'cr.insTb'
        m.i.d4 = overlay('07'x, m.i.d4, 2)
        if length(m.i.d4) >= 62 then
            m.i.d4 = overlay('31'x, m.i.d4, 62)
        call sql4Obj i, 'cr.insTbHex'
        end
    call pipe '-'
    call tstEnd t
    return
endProcedure tstSql4Obj
tstSqlC: procedure expose m.
call sqlIni
/*
$=/tstSqlCRx/
    ### start tst tstSqlCRx ###########################################
    *** err: SQLCODE = -104: ILLEGAL SYMBOL "?". SOME SYMBOLS THAT MIGH+
    T
    .    e 1:     BE LEGAL ARE: . <IDENTIFIER> JOIN INNER LEFT RIGHT FU+
    LL CROSS ,
    .    e 2:     HAVING GROUP
    .    e 3: src select * from sysibm?sysDummy1
    .    e 4:   >    >>>pos 21 of 30>>>
    .    e 5: sql = select * from sysibm?sysDummy1
    .    e 6: stmt = prepare s9 into :M.SQL.9.D from :src
    .    e 7: with into :M.SQL.9.D = M.SQL.9.D
    *** err: SQLCODE = -204: NONONO.SYSDUMMY1 IS AN UNDEFINED NAME
    .    e 1: sql = select * from nonono.sysDummy1
    .    e 2: stmt = prepare s9 into :M.SQL.9.D from :src
    .    e 3: with into :M.SQL.9.D = M.SQL.9.D
    sys  ==> server CHSKA000DP4G    .
    fetched a1=abc, i2=12, c3=---
    .  I1 C2
    .   1 eins
    2222 zwei
$/tstSqlCRx/
$=/tstSqlCCsm/
    ### start tst tstSqlCCsm ##########################################
    *** err: SQLCODE = -104: ILLEGAL SYMBOL "?". SOME SYMBOLS THAT MIGH+
    T
    .    e 1:     BE LEGAL ARE: . <IDENTIFIER> JOIN INNER LEFT RIGHT FU+
    LL CROSS ,
    .    e 2:     HAVING GROUP
    .    e 3: src select * from sysibm?sysDummy1
    .    e 4:   >    >>>pos 21 of 30>>>
    .    e 5: sql = select * from sysibm?sysDummy1
    .    e 6: subsys = DE0G, host = RZZ
    *** err: SQLCODE = -204: NONONO.SYSDUMMY1 IS AN UNDEFINED NAME
    .    e 1: sql = select * from nonono.sysDummy1
    .    e 2: subsys = DE0G, host = RZZ
    sys RZZ/DE0G ==> server CHROI00ZDE0G    .
    fetched a1=abc, i2=12, c3=---
    .  I1 C2
    .   1 eins
    2222 zwei
$/tstSqlCCsm/ */
    sql1 = "select 1 i1, 'eins' c2 from sysibm.sysDummy1" ,
        "union all select 2222 i1, 'zwei' c2 from sysibm.sysDummy1"
    do tx=1 to 1 +  (m.tst_Csm \== 0)
        if tx = 1 then do
            call tst t, "tstSqlCRx"
            sys = ''
            end
        else do
            call tst t, "tstSqlCCsm"
            sys =  'RZZ/DE0G'
            end
        call sqlConnect sys
        cx = 9
        call sqlQuery cx, 'select * from sysibm?sysDummy1'
        call sqlQuery cx, 'select * from nonono.sysDummy1'
        call sqlQuery cx, "select 'abc' a1, 12 i2, current server srv",
                     ", case when 1=0 then 1 else null end c3",
                 "from sysibm.sysDummy1"
        do while sqlFetch(cx, dst)
            call out 'sys' sys '==> server' m.dst.srv
            call out 'fetched a1='m.dst.a1', i2='m.dst.i2', c3='m.dst.c3
            end
        call fTabAuto , sqlRdr(sql1)
        call sqlDisconnect
        call tstEnd t
        end
    return
endProcedure tstSqlC

tstSqlUpd: procedure expose m.
/*
$=/tstSqlUpd/
    ### start tst tstSqlUpd ###########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table (update session.dgtt  set c2 = 'u' +
    || c2)
    stmt = prepare s9 into :M.SQL.9.D from :src
    with into :M.SQL.9.D = M.SQL.9.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstSqlUpd/ */
    call tst t, "tstSqlUpd"
    cx = 9
    qx = 3
    call sqlConnect
    call sqlUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call sqlUpdate,"insert into session.dgtt" ,
                    "values(1, 'eins', '2012-04-01 06.07.08')"
    call sqlUpdate,"insert into session.dgtt" ,
                    "values(2, 'zwei', '2012-02-29 15:44:33.22')"
    call out 'insert updC' m.sql..updateCount
    call sqlUpdate,"insert into session.dgtt" ,
                      "select i1+10, 'zehn+'||strip(c2), t3+10 days",
                           "from session.dgtt"
    call out 'insert select updC' m.sql..updateCount
    call sqlQuery cx, 'select d.*' ,
               ', case when mod(i1,2) = 1 then 1 else null end grad' ,
               'from session.dgtt d'
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlClose cx
    call sqlQuery cx, "select * from final table (update session.dgtt",
                   " set c2 = 'u' || c2)"
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSqlUpd
tstSqlUpdPre: procedure expose m.
/*
$=/tstSqlUpdPre/
    ### start tst tstSqlUpdPre ########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table ( update session.dgtt set c2 = ? ||+
    . c2)
    stmt = prepare s5 into :M.SQL.5.D from :src
    with into :M.SQL.5.D = M.SQL.5.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstSqlUpdPre/ */
    call tst t, "tstSqlUpdPre"
    cx = 5
    qx = 3
    call sqlConnect
    call sqlUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call salUpdatePrepare 5, "insert into session.dgtt" ,
                                   "values (?, ?, ?)"
    call sqlUpdExecute 5, 1, 'eins', '2012-04-01 06.07.08'
    call sqlUpdExecute 5, 2, 'zwei', '2012-02-29 15:44:33.22'
    call out 'insert updC' m.sql.5.updateCount
    call salUpdatePrepare 5,"insert into session.dgtt" ,
                      "select i1+?, 'zehn+'||strip(c2), t3+? days",
                           "from session.dgtt"
    call sqlUpdExecute 5, 10, 10
    call out 'insert select updC' m.sql.5.updateCount
    call sqlQueryPrepare cx, 'select d.*' ,
               ', case when mod(i1,2) = ? then 0+? else null end grad' ,
               'from session.dgtt d'
    call sqlQueryExecute cx, 1, 1
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlClose cx
    call sqlQueryPrepare cx, "select * from final table (" ,
              "update session.dgtt set c2 = ? || c2)"
    call sqlQueryExecute cx, "u"
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSqlUpdPre
tstsqlRxUpd: procedure expose m.
/*
$=/tstsqlRxUpd/
    ### start tst tstsqlRxUpd #########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table (update session.dgtt set c2 = 'u' +
    || c2)
    stmt = prepare s9 into :M.SQL.9.D from :src
    with into :M.SQL.9.D = M.SQL.9.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstsqlRxUpd/ */
    call tst t, "tstsqlRxUpd"
    cx = 9
    qx = 3
    call sqlRxConnect
    call sqlRxUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call sqlRxUpdate,"insert into session.dgtt" ,
                    "values(1, 'eins', '2012-04-01 06.07.08')"
    call sqlRxUpdate,"insert into session.dgtt" ,
                    "values(2, 'zwei', '2012-02-29 15:44:33.22')"
    call out 'insert updC' m.sql..updateCount
    call sqlRxUpdate,"insert into session.dgtt" ,
                      "select i1+10, 'zehn+'||strip(c2), t3+10 days",
                           "from session.dgtt"
    call out 'insert select updC' m.sql..updateCount
    call sqlRxQuery cx, 'select d.*' ,
               ', case when mod(i1,2) = 1 then 1 else null end grad' ,
               'from session.dgtt d'
    do qx=qx+1 while sqlRxFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlRxClose cx
    call sqlRxQuery cx, "select * from final table",
                 "(update session.dgtt set c2 = 'u' || c2)"

    do qx=qx+1 while sqlRxFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlRxClose cx
    call sqlRxDisconnect
    call tstEnd t
    return
endProcedure tstsqlRxUpd

tstSqlE: procedure expose m.
/*
$=/tstSqlE/
    ### start tst tstSqlE #############################################
    *** err: SQLCODE = -713: THE REPLACEMENT VALUE FOR CURRENT SCHEMA I+
    S
    .    e 1:     INVALID
    .    e 2: sql = set current schema = 'sysibm'
    .    e 3: stmt = execute immediate :src
    -713 set schema ''
    0 set schema
    0 select
    fetch=1 SYSIBM
$/tstSqlE/
*/
    call sqlConnect
    call tst t, "tstSqlE"
    call tstOut t, sqlExecute(3, "set current schema = 'sysibm'") ,
                                 "set schema ''"
    call tstOut t, sqlExecute(3, " set current schema = sysibm ") ,
                                 "set schema"
    call tstOut t, sqlExecute(3, " select current schema c"      ,
                                      "from sysibm.sysDummy1") 'select'
    call tstOut t, 'fetch='sqlFetch(3, aa) m.aa.c
    call sqlClose 3
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlE
tstSqlO: procedure expose m.
/*
$=/tstSqlO/
    ### start tst tstSqlO #############################################
    sqlCode 0: set current schema = A540769
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s10 into :M.SQL.10.D from :src
    .    e 3: with into :M.SQL.10.D = M.SQL.10.D
    sqlCode -204: select * from sysdummy
    REQD=Y col=123 case=--- .sqlInd:-1 col5=anonym geburri=1956-04-01+
    -06.00.00.000000
$/tstSqlO/
*/
    call sqlConnect
    call scanWinIni
    call tst t, "tstSqlO"
    call sqlStmts 'set current schema = A540769';
    call sqlStmts 'select * from sysdummy';
    r = sqlRdr( ,
          "select d.*, 123, timestamp('01.04.1956','06:00:00')" ,
                             '"geburri walter",',
                    'case when 1=0 then 1 else null end caseNull,',
                    "'anonym'" ,
               'from sysibm.sysdummy1 d')
    call jOpen r, '<'
    do while jRead(r)
        o = m.r
        call out 'REQD='m.o.IBMREQD 'col='m.o.col2,
                  'case='m.o.CASENULL '.sqlInd:'m.o.caseNull.sqlInd,
                  'col5='m.o.col5,
                  'geburri='m.o.GEBURRI
        end
    call jClose r
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlO

tstSqlUpdComLoop: procedure expose m.
/*
$=/tstSqlUpdComLoop/
    ### start tst tstSqlUpdComLoop ####################################
    sqlCode 0: declare global temporary table session.dgtt (i1 int) on +
    commit ....
    sqlCode 0, 123 rows inserted: insert into session.dgtt select row_n+
    umber()....
    CNT
    123
    1 rows fetched: select count(*) cnt from session.dgtt
    123 rows deleted, 10 commits: delete from session.dgtt d where i1 i+
    n (sele....
    C
    0
    1 rows fetched: select count(*) cnt from session.dgtt
$/tstSqlUpdComLoop/ */
    call pipeIni
    call tst t, "tstSqlUpdComLoop"
    call sqlConnect
    call sqlsOut "declare global temporary table session.dgtt",
                           "(i1 int) on commit preserve rows"
    call sqlsOut "insert into session.dgtt",
       "select row_number() over() from sysibm.sysTables",
           "fetch first 123 rows only"
    call sqlsOut "select count(*) cnt from session.dgtt"
    call out sqlUpdComLoop("delete from session.dgtt d where i1 in",
       "(select i1 from session.dgtt fetch first 13 rows only)")
    call sqlsOut "select count(*) cnt from session.dgtt"
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlUpdComLoop

tstSqlO1: procedure expose m.
/*
$=/tstSqlO1/
    ### start tst tstSqlO1 ############################################
    tstR: @tstWriteoV2 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART
    tstR: @tstWriteoV3 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART_HIST
    tstR: @tstWriteoV4 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLES
    tstR: @tstWriteoV5 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLESPACE
    --- writeAll
    tstR: @tstWriteoV6 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART
    tstR: @tstWriteoV7 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART_HIST
    tstR: @tstWriteoV8 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLES
    tstR: @tstWriteoV9 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLESPACE
$/tstSqlO1/
*/
    call sqlConnect
    call tst t, "tstSqlO1"
    qr = sqlRdr("select strip(creator) cr, strip(name) tb",
                     "from sysibm.sysTables",
                     "where creator='SYSIBM' and name like 'SYSTABL%'",
                     "order by 2 fetch first 4 rows only")
    call jOpen qr, m.j.cRead
    call mAdd t.trans, className(m.qr.type) '<tstSqlO1Type>'
    do while jRead(qr)
        abc = m.qr
        if m.qr.rowCount = 1 then do
            cx = m.qr.cursor
            end
        call out abc
        end
    call jClose qr
    call out '--- writeAll'
    call pipeWriteAll qr
    call tstEnd t
    return 0
endProcedure tstSqlO1

tstSqlO2: procedure expose m.
/*
$=/tstSqlO2/
    ### start tst tstSqlO2 ############################################
    CR     TB                RR
    SYSIBM SYSTABLEPART       1
    SYSIBM SYSTABLEPART_HIST  4
    SYSIBM SYSTABLES          9
    SYSIBM SYSTABLESPACE     16
$/tstSqlO2/
*/
    call sqlConnect
    call pipeIni
    call tst t, "tstSqlO2"
    call pipe '+N'
    call out    "select strip(creator) cr, strip(name) tb,"
    call out         "(row_number()over())*(row_number()over()) rr"
    call out         "from sysibm.sysTables"
    call out         "where creator='SYSIBM' and name like 'SYSTABL%'"
    call out         "order by 2 fetch first 4 rows only"
    call pipe 'N|'
    call sqlSel
    call pipe 'P|'
    call fTabAuto fTabReset(abc, 1)
    call pipe '-'
    call tstEnd t
    return 0
endProcedure tstSqlO2
tstSqlS1: procedure expose m.
/*
$=/tstSqlS1/
    ### start tst tstSqlS1 ############################################
    select c, a from sysibm.sysDummy1
    tstR: @tstWriteoV2 isA :<cla sql c a>
    tstR:  .C = 1
    tstR:  .A = a
    select ... where 1=0
    tstR: @ obj null
$/tstSqlS1/
*/
    call sqlIni
    call tst t, "tstSqlS1"
    call sqlConnect
    s1 = jSingle( ,
        sqlRdr("select count(*) c, 'a' a from sysibm.sysdummy1"))
    call mAdd t.trans, className(objClass(s1)) '<cla sql c a>'
    call out 'select c, a from sysibm.sysDummy1'
    call tstWrite t, s1
    call out 'select ... where 1=0'
    call tstWrite t, jSingle( ,
        sqlRdr("select*from sysibm.sysdummy1 where 1=0"), '')
    call tstEnd t
    return
endProcedure tstSqlS1
tstSqlStmt: procedure expose m.
/*
$=/tstSqlStmt/
    ### start tst tstSqlStmt ##########################################
    *** err: SQLCODE = -713: THE REPLACEMENT VALUE FOR CURRENT SCHEMA I+
    S
    .    e 1:     INVALID
    .    e 2: sql = set current schema = 'sysibm'
    .    e 3: stmt = execute immediate :src
    sqlCode -713: set current schema = 'sysibm'
    sqlCode 0: set current schema =  sysibm
    tstR: @tstWriteoV2 isA :<sql?sc>
    tstR:  .C = SYSIBM
    tstR: @tstWriteoV3 isA :<sql?sc>
    tstR:  .C = SYSIBM
$/tstSqlStmt/
*/
    call sqlConnect
    call tst t, "tstSqlStmt"
    cn = className(classNew('n* Sql u f%v  C'))
    call mAdd t.trans, cn '<sql?sc>'
    call sqlStmts "set current schema = 'sysibm'"
    call sqlsOut "    set current schema =  sysibm "
    call sqlsOut "   select current schema c  from sysDummy1", , 'o'
    call sqlsOut "  (select current schema c from sysDummy1)", , 'o'
    call tstEnd t
    return
endProcedure tstSqlStmt
tstSqlStmts: procedure expose m.
/*
$=/tstSqlStmts/
   ### start tst tstSqlStmts #########################################
   *** err: SQLCODE = -104: ILLEGAL SYMBOL "BLABLA". SOME SYMBOLS THAT
   .    e 1:     MIGHT BE LEGAL ARE: <ERR_STMT> <WNG_STMT> GET SQL SAV+
   EPOINT HOLD
   .    e 2:     FREE ASSOCIATE
   .    e 3: src blabla
   .    e 4:   > <<<pos 1 of 6<<<
   .    e 5: sql = blabla
   sqlCode -104: blabla
   sqlCode 0: set current schema=  sysIbm
   c
   1
   1 rows fetched: select count(*) "c" from sysDummy1 with   ur
   c
   1
   1 rows fetched: select count(*) "c" from sysDummy1 with   ur
   #jIn 1# set current -- sdf
   #jIn 2# schema = s100447;
   #jIn eof 3#
   sqlCode 0: set current schema = s100447
$/tstSqlStmts/ */
    call jIni
    call sqlConnect
    call scanReadIni
    call scanWinIni
    call tst t, "tstSqlStmts"
    call sqlStmts "blabla ;;set current schema=  sysIbm "
    b = jBuf('select count(*) "c" from sysDummy1 --com' ,
             ,'with  /* comm */ ur;')
    call sqlStmts b
    call sqlStmts b
    call mAdd mCut(t'.IN', 0), 'set current -- sdf', 'schema = s100447;'
    call sqlStmts
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlStmts

tstCatTb:     /* ???????????????????? tkr kopieren und testen */
/*
$=/tstCatTb/
    ### start tst tstCatTb ############################################
    ..
    select * from sysibm.SYSDUMMY1  .
    IBMREQD
    I .
    Y .
    I .
    IBMREQD
$/tstCatTb/
*/
    call sqlConnect
    call tst t, 'tstCatTb'
    call sqlCatTb 'sysDummy1'
    call sqlCatTb 'SYSTableSpaceStats',
             , "name = 'A403A1' and dbName = 'DA540769'"
    call tstEnd t
    return
endProcedure tstCatTb

tstSqlDisDb: procedure expose m.
    call sqlDsn di, 'DP4G', '-dis db(*) sp(*)' ,
                    'restrict advisory limit(*)', 12
    m.oo.0 = 0
    call sqlDisDb oo, di
    say 'di.0' m.di.0 '==> oo.0' m.oo.0
    trace ?r
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE)
    say 'DB2PDB6.RR2HHAGE  ==>' ix m.oo.ix.sta
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE, 3)
    say 'DB2PDB6.RR2HHAGE.3  ==>' ix m.oo.ix.sta
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE,22)
    say 'DB2PDB6.RR2HHAGE.22 ==>' ix m.oo.ix.sta
    return
endProcedure tstSqlDisDb

/****** tstComp ********************************************************
    test the wsh compiler
***********************************************************************/
tstComp: procedure expose m.
    call compIni
    call tstCompDataConst
    call tstCompDataVars
    call tstCompShell
    call tstCompPrimary
    call tstCompExpr
    call tstCompFile
    call tstCompStmt
    call tstCompDir
    call tstCompObj
    call tstCompORun
    call tstCompORu2
    call tstCompORuRe
    call tstCompDataIO
    call tstCompPipe
    call tstCompPip2
    call tstCompRedir
    call tstCompComp
    call tstCompColon
    call tstCompTable
    call tstCompSyntax
    if m.err.os == 'TSO' then
        call tstCompSql
    call tstTotal
    return
endProcedure tstComp

tstComp1: procedure expose m.
parse arg ty nm cnt
    c1 = 0
    if cnt = 0 | cnt = '+' then do
        c1 = cnt
        cnt = ''
        end
    call jIni
    src = jBuf()
    call jOpen src, m.j.cWri
    do sx=2 to arg()
        call jWrite src, arg(sx)
        end
    call tstComp2 nm, ty, jClose(src), , c1, cnt
    return
endProcedure tstComp1

tstComp2: procedure expose m.
parse arg nm, spec, src, compSt
    call compIni
    call tst t, nm, compSt
    if src == '' then do
        src = jBuf()
        call tst4dp src'.BUF', mapInline(nm'Src')
        end
    m.t.moreOutOk = abbrev(strip(arg(5)), '+')
    oldErr = m.err.count
    cmp = comp(src)
    call out 'compile' spec',' (m.src.buf.0) 'lines:' m.src.buf.1
    r = compile(cmp, spec)
    noSyn = m.err.count = oldErr
    coErr = m.t.err
    say if( noSyn, "compiled", "*** syntaxed") r ":" objMet(r, 'oRun')

    cnt = 0
    do ax = 5 to max(arg(), 5) while m.t.err = coErr & noSyn
        a1 = strip(arg(ax))
        if a1 == '' & arg() >= 5 then
            iterate
        if abbrev(a1, '+') then do
            m.t.moreOutOk = 1
            a1 = strip(substr(a1, 2))
            end
        if datatype(a1, 'n') then
            cnt = a1
        else if a1 \== '' then
            call err 'tstComp2 bad arg('ax')' arg(ax)
        if cnt = 0 then do
            call mCut 'T.IN', 0
            call out "run without input"
            end
        else  do
            call mAdd mCut('T.IN', 0),
                ,"eins zwei drei", "zehn elf zwoelf?",
                , "zwanzig 21 22 23 24 ... 29|"
            do lx=4 to cnt
                call mAdd 'T.IN', left(lx "line" lx, lx+9, '-')';'
                end
            call out "run with" cnt "inputs"
            end
        m.t.inIx = 0
        call oRun r
        end
    call tstEnd t
    return
endProcedure tstComp2

tstCompDataConst: procedure expose m.
/*
$=/tstCompDataConst/
    ### start tst tstCompDataConst ####################################
    compile =, 8 lines:       Lline one, $** asdf
    run without input
    .      Lline one, .
    line two..
    line threecontinued on 4
    line five  fortsetzung
    line six   fortsetzung
$/tstCompDataConst/ */
    call tstComp1 '= tstCompDataConst',
        , '      Lline one, $** asdf',
        , 'line two.',
        , 'line three$*+ bla bla' ,
        , 'continued on 4',
        , 'line five $*( und so',
        , 'weiter $abc $? $''$*)'' $"$*)" und weiter $*) fortsetzung',
        , 'line six  $*( und $*( $** $*( so',
        , 'weiter $abc $? $*)'' $"$*)" und weiter $*) fortsetzung'

/*
$=/tstCompDataConstBefAftComm1/
    ### start tst tstCompDataConstBefAftComm1 #########################
    compile =, 3 lines:     $*(anfangs com.$*)       $*(plus$*) $** x
    run without input
    the only line;
$/tstCompDataConstBefAftComm1/ */
    call tstComp1 '= tstCompDataConstBefAftComm1',
        , '    $*(anfangs com.$*)       $*(plus$*) $** x',
        , 'the only line;',
        , '      $*(end kommentar$*)              '

/*
$=/tstCompDataConstBefAftComm2/
    ### start tst tstCompDataConstBefAftComm2 #########################
    compile =, 11 lines:     $*(anfangs com.$*)       $*(plus$*) $*+ x
    run without input
    the first non empty line;
    .      .
    befor an empty line with comments;
$/tstCompDataConstBefAftComm2/ */

    call tstComp1 '= tstCompDataConstBefAftComm2',
        , '    $*(anfangs com.$*)       $*(plus$*) $*+ x',
        , '    $*(forts Zeile com.$*)       $*(plus$*) $** x',
        , ' $*(comment 2. Zeile$*) $*(plus$*) $*+ x',
        , ' $*(forts Zeile com.$*) $*(plus$*) $** x',
        , 'the first non empty line;',
        , '      ',
        , 'befor an empty line with comments;',
        , ' $*(comment 4. Zeile$*) $*(plus$*) $*+ x',
        , ' $*(forts 4.Zeile com.$*) $*(plus$*) $** x',
        , '      $*(end kommentar$*)              $*+',
        , ' $*(forts end com.$*) $*(plus$*) $** x'
     return
endProcedure tstCompDataComm

tstCompDataVars: procedure expose m.
/*
$=/tstCompDataVars/
    ### start tst tstCompDataVars #####################################
    compile =, 5 lines:       Lline one, $** asdf
    run without input
    .      Lline one, .
    lline zwei output
    lline 3 .
    variable v1 = valueV1 ${v1}= valueV1; .
    .      $-{""$v1} = valueV1; .
$/tstCompDataVars/ */
    call tstComp1 '= tstCompDataVars',
        , '      Lline one, $** asdf',
        , '   $$ lline zwei output',
        , 'lline 3 $=v1= valueV1 ' ,
        , 'variable v1 = $v1 $"${v1}=" ${  v1  }; ',
        , '      $"$-{""""$v1} =" $-{$""$"v1"}; '
    return
endProcedure tstCompDataVars

tstCompShell: procedure expose m.
/*
$=/tstCompShell3/
    ### start tst tstCompShell3 #######################################
    compile @, 8 lines: call tstOut "T",  "abc" $-¢2*3$! "efg"$-¢2*3$!"+
    hij"
    run without input
    abc 6 efg6hij
    insert into A540769x.tqt002 with n(n) as ( select 4 from sysibm.s
    insert into A540769x.tqt002 with n(n) as ( select 4 from sysibm.s  +
    .   union all .
    abc 6 efg6hij
$/tstCompShell3/ */
    call tstComp1 '@ tstCompShell3',
        , 'call tstOut "T",  "abc" $-¢2*3$! "efg"$-¢2*3$!"hij"',
        , 'ix=3' ,
        , 'call tstOut "T","insert into A540769x.tqt002" ,',
        ,     '"with n(n) as ( select" $-¢ix+1$! "from sysibm.s"',
        , 'call tstOut "T","insert into A540769x.tqt002"  ,  ',
        ,    '"with n(n) as ( select" $-¢ix+1$! "from sysibm.s" , ' ,
        ,    '"    union all "' ,
        , '$$ abc $-¢2*3$! efg$-¢2*3$!hij',
/*
$=/tstCompShell/
    ### start tst tstCompShell ########################################
    compile @, 12 lines:   $$  Lline one, $** asdf
    run without input
    Lline one,
    lline zwei output
    v1 = valueV1 ${v1}= valueV1|
    REXX OUT L5 CONTINUED L6 CONTINUED L7
    L8 ONE
    L9 TWO
    valueV1
    valueV1 valueV2
    out  valueV1 valueV2
    SCHLUSS
$/tstCompShell/ */
    call tstComp1 '@ tstCompShell',
        , '  $$  Lline one, $** asdf',
        , '   $$ lline zwei output',
        , '        $=v1= valueV1 ' ,
        , '$$       v1 = $v1 $"${v1}=" ${  v1  }| ' ,
        , 'call out rexx out l5, ' ,
        , '     continued l6 , ' ,
        , '     continued l7   ' ,
        , 'call out l8 one    ' ,
        , 'call out l9 two$=v2=valueV2  ',
        , '$$- $v1  $$- $v1 $v2   ',
        , 'call out   "out "     $v1 $v2   ',
        , '$$-   schluss    '
/*
$=/tstCompShell2/
    ### start tst tstCompShell2 #######################################
    compile @, 13 lines: $@do j=0 to 1 $@¢ $$ do j=$j
    run without input
    do j=0
    after if 0 $@¢ $!
    after if 0 $=@¢ $!
    do j=1
    if 1 then $@¢ a
    a2
    if 1 then $@=¢ b
    b2
    after if 1 $@¢ $!
    after if 1 $=@¢ $!
    end
$/tstCompShell2/ */
    call tstComp1 '@ tstCompShell2',
        , '$@do j=0 to 1 $@¢ $$ do j=$j' ,
        ,     'if $j then $@¢ ',
        ,          '$$ if $j then $"$@¢" a $$a2' ,
        ,          '$!',
        ,     'if $j then $@=¢ ',
        ,          '$$ if $j then $"$@=¢" b $$b2' ,
        ,          '$!',
        ,     'if $j then $@¢ $!' ,
        ,     '$$ after if $j $"$@¢ $!"' ,
        ,     'if $j then $@=¢ $!' ,
        ,     '$$ after if $j $"$=@¢ $!"' ,
        ,     '$!',
        , '$$ end'
    return
endProcedure tstCompShell

tstCompPrimary: procedure expose m.
    call compIni
/*
$=/tstCompPrimary/
    ### start tst tstCompPrimary ######################################
    compile =, 16 lines: Strings $"$""$""""$""" $'$''$''''$'''
    run without input
    Strings $"$""$" $'$''$'
    rexx $-¢ 3 * 5 $! = 15
    rexx $-¢ 5 * 7 $! = 35
    rexx $-// 7 * 11 $// = 77
    rexx $-/abcEf/ 11 * 13 $/abcEf/ = 143
    data  line three line four  bis hier
    shell line five line six bis hier
    var get   v1 value Eins, v1 value Eins .
    var isDef v1 1, v2 0 .
    #jIn eof 1#
    var read  >1 0 rr undefined
    #jIn eof 2#
    var read  >2 0 rr undefined
    no call abc$-¢4*5$! $-¢efg$-¢6*7$! abc20 EFG42
    brackets $-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$! 1000
    run with 3 inputs
    Strings $"$""$" $'$''$'
    rexx $-¢ 3 * 5 $! = 15
    rexx $-¢ 5 * 7 $! = 35
    rexx $-// 7 * 11 $// = 77
    rexx $-/abcEf/ 11 * 13 $/abcEf/ = 143
    data  line three line four  bis hier
    shell line five line six bis hier
    var get   v1 value Eins, v1 value Eins .
    var isDef v1 1, v2 0 .
    #jIn 1# eins zwei drei
    var read  >1 1 rr eins zwei drei
    #jIn 2# zehn elf zwoelf?
    var read  >2 1 rr zehn elf zwoelf?
    no call abc$-¢4*5$! $-¢efg$-¢6*7$! abc20 EFG42
    brackets $-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$! 1000
$/tstCompPrimary/ */
    call vRemove 'v2'
    call tstComp1 '= tstCompPrimary 3',
        , 'Strings $"$""$""""$"""' "$'$''$''''$'''",
        , 'rexx $"$-¢ 3 * 5 $! =" $-¢ 3 * 5 $!' ,
        , 'rexx $"$-¢ 5 * 7 $! =" $-¢ 5 * 7 $!' ,
        , 'rexx $"$-// 7 * 11 $// =" $-// 7 * 11 $//' ,
        , 'rexx $"$-/abcEf/ 11 * 13 $/abcEf/ ="',
            '$-/abcEf/ 11 * 13 $/abcEf/' ,
        , 'data $-=¢ line three',
        , 'line four $! bis hier'  ,
        , 'shell $-@¢ $$ line five',
        , '$$ line six $! bis hier' ,
        , '$= v1  =   value Eins  $=rr=undefined $= eins = 1 ',
        , 'var get   v1 $v1, v1 ${  v1  } ',
        , 'var isDef v1 ${? v${  eins  }  }, v2 ${?v2 } ',
        , 'var read  >1 ${> rr} rr $rr' ,
        , 'var read  >2 ${> rr} rr $rr',
        , 'no call $"abc$-¢4*5$! $-¢efg$-¢6*7$!"',
            'abc$-¢4*5$! $-¢efg$-¢6*7$!$!',
        , 'brackets $"$-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$!"',
            '$-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$!'
    return
endProcedure tstCompPrimary

tstCompExpr: procedure expose m.
    call compIni
/*
$=/tstCompExprStr/
    ### start tst tstCompExprStr ######################################
    compile -, 3 lines: $=vv=vvStr
    run without input
    vv=vvStr
    o2String($.-vv)=vvStr
$/tstCompExprStr/ */
    call tstComp1 '- tstCompExprStr',
        , '$=vv=vvStr' ,
        , '"vv="$vv' ,
        , '$"o2String($.-vv)="o2String($.-vv)'
/*
$=/tstCompExprObj/
    ### start tst tstCompExprObj ######################################
    compile ., 5 lines: $=vv=vvStr
    run without input
    vv=
    vvStr
    s2o($.vv)=
    vvStr
$/tstCompExprObj/ */
    call tstComp1 '. tstCompExprObj',
        , '$=vv=vvStr' ,
        , '"!vv="', '$.-vv',
        , '$."s2o($.vv)="', 's2o($-vv)'
/*
$=/tstCompExprDat/
    ### start tst tstCompExprDat ######################################
    compile =, 4 lines: $=vv=vvDat
    run without input
    vv=vvDat
    $.-vv= !vvDat
    $.-¢"abc"$!=!abc
$/tstCompExprDat/ */
    call tstComp1 '= tstCompExprDat',
        , '$=vv=vvDat' ,
        , 'vv=$vv',
        , '$"$.-vv=" $.-vv',
        , '$"$.-¢""abc""$!="$.-¢"abc"$!'

/*
$=/tstCompExprRun/
    ### start tst tstCompExprRun ######################################
    compile @, 3 lines: $=vv=vvRun
    run without input
    vv=vvRun
    o2string($.-vv)=vvRun
$/tstCompExprRun/ */
    call tstComp1 '@ tstCompExprRun',
        , '$=vv=vvRun' ,
        , 'call out "vv="$vv',
        , 'call out $"o2string($.-vv)="o2string($.-vv)'
/*
$=/tstCompExprCon/
    ### start tst tstCompExprCon ######################################
    compile #, 2 lines: $$ in # drinnen
    run without input
    $$ in # drinnen
    call out "vv="$vv
$/tstCompExprCon/
$=/tstCompExprCo2/
    ### start tst tstCompExprCo2 ######################################
    compile #, 3 lines: $$ in # drinnen
    run without input
    $$ in # drinnen
    call out "vv="$vv
    nacgh $#@
$/tstCompExprCo2/
*/
    call tstComp1 '# tstCompExprCon',
        , '$$ in # drinnen' ,
        , 'call out "vv="$vv'

    call tstComp1 '# tstCompExprCo2',
        , '$$ in # drinnen' ,
        , 'call out "vv="$vv',
        , '$#@ $$ nacgh $"$#@"'

    return
endProcedure tstCompExpr

tstCompStmt: procedure expose m.
/*
$=/tstCompStmt1/
    ### start tst tstCompStmt1 ########################################
    compile @, 8 lines: $= v1 = value eins  $= v2  =- 3*5*7 .
    run without input
    data v1 value eins v2 105
    eins
    zwei
    drei
    vier
    fuenf
    elf
    . zwoelf  dreiZ  .
    . vierZ .
    fuenfZ
    lang v1 value eins v2 945
    oRun ouput 1
$/tstCompStmt1/ */
    call pipeIni
    call compIni
    call vPut 'oRun', oRunner('call out "oRun ouput" (1*1)')
    call vRemove 'v2'
    call tstComp1 '@ tstCompStmt1',
        , '$= v1 = value eins  $= v2  =- 3*5*7 ',
        , '$$ data v1 $v1 v2 ${   v2  }',
        , '$$eins $@¢$$ zwei $$ drei  ',
        , '   $@¢   $!  $@//   $// $@/q r s /   $/q r s /',
             '       $@/eins/ $@¢ $$vier $! $/eins/ $! $$fuenf',
        , '$$elf $@=¢$@=¢ zwoelf  dreiZ  $!  ',
        , '   $@=¢   $! $@=¢ $@=¢ vierZ $! $! $! $$fuenfZ',
        , '$$- "lang v1" $v1 "v2" ${v2}*9',
        , '$@oRun'
/*
$=/tstCompStmt2/
    ### start tst tstCompStmt2 ########################################
    compile @, 1 lines: $@for qq $$ loop qq $qq
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    loop qq eins zwei drei
    #jIn 2# zehn elf zwoelf?
    loop qq zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    loop qq zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
$/tstCompStmt2/ */
    call tstComp1 '@ tstCompStmt2 3',
        , '$@for qq $$ loop qq $qq'

/*
$=/tstCompStmt3/
    ### start tst tstCompStmt3 ########################################
    compile @, 9 lines: $$ 1 begin run 1
    2 ct zwei
    ct 4 mit assign .
    run without input
    1 begin run 1
    3 run 3 ctV = ct 4 assign ctV|
    run 5 procCall $@prCa
    out in proc at 8
    run 6 vor call $@prCa
    out in proc at 8
    9 run end
    run with 3 inputs
    1 begin run 1
    3 run 3 ctV = ct 4 assign ctV|
    run 5 procCall $@prCa
    out in proc at 8
    run 6 vor call $@prCa
    out in proc at 8
    9 run end
$/tstCompStmt3/ */
    call tstComp1 '@ tstCompStmt3 3',
        , '$$ 1 begin run 1',
        , '$@ct $$ 2 ct zwei',
        , '$$ 3 run 3 ctV = $ctV|',
        , '$@ct $@=¢ct 4 mit assign $=ctV = ct 4 assign ctV $!',
        , '$$ run 5 procCall $"$@prCa" $@prCa',
        , '$$ run 6 vor call $"$@prCa"',
        , '$@prCa',
        , '$@proc prCa $$out in proc at 8',
        , '$$ 9 run end'

/*
$=/tstCompStmt4/
    ### start tst tstCompStmt4 ########################################
    compile @, 4 lines: $=eins=vorher
    run without input
    eins vorher
    eins aus named block eins .
$/tstCompStmt4/ */
    call tstComp1 '@ tstCompStmt4 0',
        , '$=eins=vorher' ,
        , '$$ eins $eins' ,
        , '$=/eins/aus named block eins $/eins/' ,
        , '$$ eins $eins'
/*
$=/tstCompStmtDo/
    ### start tst tstCompStmtDo #######################################
    compile @, 2 lines: ti=0$@do y=3 to 4 $@do 2 $@¢
    run without input
    y=3 ti1 z=7
    y=3 ti1 z=8
    y=3 ti2 z=7
    y=3 ti2 z=8
    y=4 ti3 z=7
    y=4 ti3 z=8
    y=4 ti4 z=7
    y=4 ti4 z=8
$/tstCompStmtDo/ */
    call tstComp1 '@ tstCompStmtDo', 'ti=0$@do y=3 to 4 $@do 2 $@¢',
     ,    'ti = ti + 1',
        '$@do $*(sdf$*) z $*(sdf$*) =7 to 8 $$ y=$y ti$-¢ti$! z=$z $!'

/*
$=/tstCompStmtDo2/
    ### start tst tstCompStmtDo2 ######################################
    compile @, 7 lines: $$ $-=/sqlSel/
    run without input
    select 1 abc select 2 abc after table .
$/tstCompStmtDo2/ */
    call tstComp1 '@ tstCompStmtDo2',
         , '$$ $-=/sqlSel/',
         ,     '$=ty = abc ',
         ,     '$@do tx=1 to 2 $@=/table/',
         ,          'select $tx $ty',
         , '$/table/',
         ,     '$=ty = abc',
         ,     'after table',
         '$/sqlSel/'
/*
$=/tstCompStmtWith/
    ### start tst tstCompStmtWith #####################################
    compile @, 3 lines: $@with $.vA $$ fEins=$FEINS fZwei=$FZWEI va&fEi+
    ns=${vA&FEINS}
    run without input
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
    fEins=2Eins fZwei=2Zwei va&fEins=1Eins
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
$/tstCompStmtWith/
*/
    cl = classNew('n? tstStmtWith u f FEINS v, f FZWEI v')
    v1 = onew(cl)
    m.v1.feins = '1Eins'
    m.v1.fzwei = '1Zwei'
    v2 = oNew(cl)
    m.v2.feins ='2Eins'
    m.v2.fzwei ='2Zwei'
    call vPut 'vA', v1
    call vPut 'vB', v2
    stmt = '$$ fEins=$FEINS fZwei=$FZWEI va&fEins=${vA&FEINS}'
    call tstComp1 '@ tstCompStmtWith',
         , '$@with $.vA' stmt ,
         , '$@with $vA $@¢' stmt ,
         , '$@with $vB ' stmt stmt '$!'
     return
endProcedure tstCompStmt

tstCompSyntax: procedure expose m.
    call tstCompSynPrimary
    call tstCompSynAss
    call tstCompSynRun
    return
endProcedure tstCompSyntax

tstCompSynPrimary: procedure expose m.

/*
$=/tstCompSynPri1/
    ### start tst tstCompSynPri1 ######################################
    compile @, 1 lines: a $ =
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $ =
    .    e 2: pos 3 in line 1: a $ =
    *** err: no method oRun in class String
$/tstCompSynPri1/ */
    call tstComp1 '@ tstCompSynPri1 +', 'a $ ='

/*
$=/tstCompSynPri2/
    ### start tst tstCompSynPri2 ######################################
    compile @, 1 lines: a $. {
    *** err: scanErr primary block or expression expected expected
    .    e 1: last token  scanPosition . {
    .    e 2: pos 4 in line 1: a $. {
    *** err: scanErr primary block or expression expected expected
    .    e 1: last token  scanPosition . {
    .    e 2: pos 4 in line 1: a $. {
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $. {
    .    e 2: pos 3 in line 1: a $. {
    *** err: no method oRun in class String
$/tstCompSynPri2/ */
    call tstComp1 '@ tstCompSynPri2 +', 'a $. {'

/*
$=/tstCompSynPri3/
    ### start tst tstCompSynPri3 ######################################
    compile @, 1 lines: b $-  ¢  .
    *** err: scanErr primary block or expression expected expected
    .    e 1: last token  scanPosition -  ¢
    .    e 2: pos 4 in line 1: b $-  ¢
    *** err: scanErr primary block or expression expected expected
    .    e 1: last token  scanPosition -  ¢
    .    e 2: pos 4 in line 1: b $-  ¢
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $-  ¢
    .    e 2: pos 3 in line 1: b $-  ¢
    *** err: no method oRun in class String
$/tstCompSynPri3/ */
    call tstComp1 '@ tstCompSynPri3 +', 'b $-  ¢  '

/*
$=/tstCompSynPri4/
    ### start tst tstCompSynPri4 ######################################
    compile @, 1 lines: a ${ $*( sdf$*) } =
    *** err: scanErr var name expected
    .    e 1: last token  scanPosition } =
    .    e 2: pos 17 in line 1: a ${ $*( sdf$*) } =
$/tstCompSynPri4/ */
    call tstComp1 '@ tstCompSynPri4 +', 'a ${ $*( sdf$*) } ='

/*
$=/tstCompSynFile/
    ### start tst tstCompSynFile ######################################
    compile @, 1 lines: $@.<$*( co1 $*) $$abc
    *** err: scanErr primary, block or expression expected
    .    e 1: last token  scanPosition .<$*( co1 $*) $$abc
    .    e 2: pos 3 in line 1: $@.<$*( co1 $*) $$abc
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@.<$*( co1 $*) $$abc
    .    e 2: pos 1 in line 1: $@.<$*( co1 $*) $$abc
    *** err: no method oRun in class String
$/tstCompSynFile/ */
    call tstComp1 '@ tstCompSynFile +', '$@.<$*( co1 $*) $$abc'

    return
endProcedure tstCompSynPrimary

tstCompSynAss: procedure expose m.

/*
$=/tstCompSynAss1/
    ### start tst tstCompSynAss1 ######################################
    compile @, 1 lines: $=
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 3 in line 1: $=
$/tstCompSynAss1/ */
    call tstComp1 '@ tstCompSynAss1 +', '$='

/*
$=/tstCompSynAss2/
    ### start tst tstCompSynAss2 ######################################
    compile @, 2 lines: $=   .
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $=
$/tstCompSynAss2/ */
    call tstComp1 '@ tstCompSynAss2 +', '$=   ', 'eins'

/*
$=/tstCompSynAss3/
    ### start tst tstCompSynAss3 ######################################
    compile @, 2 lines: $=   $$
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition $$
    .    e 2: pos 6 in line 1: $=   $$
$/tstCompSynAss3/ */
    call tstComp1 '@ tstCompSynAss3 +', '$=   $$', 'eins'

/*
$=/tstCompSynAss4/
    ### start tst tstCompSynAss4 ######################################
    compile @, 1 lines: $=   eins
    *** err: scanErr = expected in assignment after $= var
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $=   eins
$/tstCompSynAss4/ */
    call tstComp1 '@ tstCompSynAss4 +', '$=   eins'

/*
$=/tstCompSynAss5/
    ### start tst tstCompSynAss5 ######################################
    compile @, 1 lines: $=  abc eins $$ = x
    *** err: scanErr = expected in assignment after $= var
    .    e 1: last token  scanPosition eins $$ = x
    .    e 2: pos 9 in line 1: $=  abc eins $$ = x
$/tstCompSynAss5/ */
    call tstComp1 '@ tstCompSynAss5 +', '$=  abc eins $$ = x'

/*
$=/tstCompSynAss6/
    ### start tst tstCompSynAss6 ######################################
    compile @, 1 lines: $=  abc =
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $=  abc =
$/tstCompSynAss6/ */
    call tstComp1 '@ tstCompSynAss6 +', '$=  abc ='

/*
$=/tstCompSynAss7/
    ### start tst tstCompSynAss7 ######################################
    compile @, 1 lines: $=  abc =..
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 11 in line 1: $=  abc =..
$/tstCompSynAss7/ */
    call tstComp1 '@ tstCompSynAss7 +', '$=  abc =.'
    return
endProcedure tstCompSynAss

tstCompSynRun: procedure expose m.

/*
$=/tstCompSynRun1/
    ### start tst tstCompSynRun1 ######################################
    compile @, 1 lines: $@
    *** err: scanErr primary, block or expression expected
    .    e 1: last token  scanPosition .
    .    e 2: atEnd after line 1: $@
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@
    .    e 2: pos 1 in line 1: $@
    *** err: no method oRun in class String
$/tstCompSynRun1/ */
    call tstComp1 '@ tstCompSynRun1 +', '$@'

/*
$=/tstCompSynRun2/
    ### start tst tstCompSynRun2 ######################################
    compile @, 1 lines: $@=
    *** err: scanErr primary, block or expression expected
    .    e 1: last token  scanPosition =
    .    e 2: pos 3 in line 1: $@=
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@=
    .    e 2: pos 1 in line 1: $@=
$/tstCompSynRun2/ */
    call tstComp1 '@ tstCompSynRun2 +', '$@='

/*
$=/tstCompSynRun3/
    ### start tst tstCompSynRun3 ######################################
    compile @, 1 lines: $@: und
    *** err: scanErr bad kind : in compExpr
    .    e 1: last token  scanPosition und
    .    e 2: pos 5 in line 1: $@: und
    *** err: no method oRun in class Null
$/tstCompSynRun3/ */
    call tstComp1 '@ tstCompSynRun3 +', '$@: und'

/*
$=/tstCompSynFor4/
    ### start tst tstCompSynFor4 ######################################
    compile @, 1 lines: $@for
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $@for
$/tstCompSynFor4/ */
    call tstComp1 '@ tstCompSynFor4 +', '$@for'

/*
$=/tstCompSynFor5/
    ### start tst tstCompSynFor5 ######################################
    compile @, 2 lines: $@for
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $@for
$/tstCompSynFor5/ */
    call tstComp1 '@ tstCompSynFor5 +', '$@for', a

/*
$=/tstCompSynFor6/
    ### start tst tstCompSynFor6 ######################################
    compile @, 2 lines: a
    *** err: scanErr variable or named block after for
    .    e 1: last token  scanPosition .
    .    e 2: pos 15 in line 2:  b $@for   $$q
$/tstCompSynFor6/ */
    call tstComp1 '@ tstCompSynFor6 +', 'a', ' b $@for   $$q'

/*
$=/tstCompSynFor7/
    ### start tst tstCompSynFor7 ######################################
    compile @, 3 lines: a
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 11 in line 2:  b $@for a
$/tstCompSynFor7/ */
    call tstComp1 '@ tstCompSynFor7 +', 'a', ' b $@for a', '  $$q'

/*
$=/tstCompSynCt8/
    ### start tst tstCompSynCt8 #######################################
    compile @, 3 lines: a
    *** err: scanErr ct statement expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 8 in line 2:  b $@ct
$/tstCompSynCt8/ */
    call tstComp1 '@ tstCompSynCt8 +', 'a', ' b $@ct', '  $$q'

/*
$=/tstCompSynProc9/
    ### start tst tstCompSynProc9 #####################################
    compile @, 2 lines: a
    *** err: scanErr var or namedBlock expected after proc
    .    e 1: last token  scanPosition .
    .    e 2: pos 15 in line 2:  b $@proc  $$q
$/tstCompSynProc9/ */
    call tstComp1 '@ tstCompSynProc9 +', 'a', ' b $@proc  $$q'

/*
$=/tstCompSynProcA/
    ### start tst tstCompSynProcA #####################################
    compile @, 2 lines: $@proc p1
    *** err: scanErr proc statement expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $@proc p1
$/tstCompSynProcA/ */
    call tstComp1 '@ tstCompSynProcA +', '$@proc p1', '  $$q'

/*
$=/tstCompSynCallB/
    ### start tst tstCompSynCallB #####################################
    compile @, 1 lines: $@% ¢roc p1$!
    *** err: scanErr primary, block or expression expected
    .    e 1: last token  scanPosition % ¢roc p1$!
    .    e 2: pos 3 in line 1: $@% ¢roc p1$!
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@% ¢roc p1$!
    .    e 2: pos 1 in line 1: $@% ¢roc p1$!
    *** err: no method oRun in class String
$/tstCompSynCallB/ */
    call tstComp1 '@ tstCompSynCallB +', '$@% ¢roc p1$!'

/*
$=/tstCompSynCallC/
    ### start tst tstCompSynCallC #####################################
    compile @, 1 lines: $@%¢call roc p1 !
    *** err: scanErr ending $! expected after ¢
    .    e 1: last token  scanPosition .
    .    e 2: atEnd after line 1: $@%¢call roc p1 !
$/tstCompSynCallC/ */
    call tstComp1 '@ tstCompSynCallC +', '$@%¢call roc p1 !'

/*
$=/tstCompSynCallD/
    ### start tst tstCompSynCallD #####################################
    compile @, 2 lines: $@^¢call( $** roc
    *** err: scanErr ending $! expected after ¢
    .    e 1: last token  scanPosition )
    .    e 2: pos 13 in line 2:  $*( p1 $*) )
$/tstCompSynCallD/ */
    call tstComp1 '@ tstCompSynCallD +',
        ,'$@^¢call( $** roc' , ' $*( p1 $*) )'
    return
endProcedure tstCompSynRun

tstCompObj: procedure expose m.
    call tstReset t
    call oIni
    cl = classNew('n? tstCompCla u v, f FEINS v, f FZWEI v')
    do rx=1 to 10
        o = oNew(cl)
        m.tstComp.rx = o
        m.o = 'o'rx
        if rx // 2 = 0 then do
            m.o.fEins = 'o'rx'.1'
            m.o.fZwei = 'o'rx'.fZwei'rx
            end
        else do
            m.o.fEins = 'o'rx'.fEins'
            m.o.fZwei = 'o'rx'.2'
            end
        call mAdd 'T.TRANS', m.tstComp.rx '<o'rx'>'
        end

/*
$=/tstCompObjRef/
    ### start tst tstCompObjRef #######################################
    compile @, 13 lines: o1=m.tstComp.1
    run without input
    out .$"string" o1
    string
    out . o1
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .{ o2 }
    tstR: @<o2> isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    out .¢ o3 $!
    tstR: @<o3> isA :tstCompCla = o3
    tstR:  .FEINS = o3.fEins
    tstR:  .FZWEI = o3.2
    out .¢ o4 $!
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    out ./-/ o5 $/-/
    tstR: @<o5> isA :tstCompCla = o5
    tstR:  .FEINS = o5.fEins
    tstR:  .FZWEI = o5.2
$/tstCompObjRef/ */
m.ttt=1
    call tstComp1 '@ tstCompObjRef' ,
        , 'o1=m.tstComp.1',
        , 'o2 = m.tstComp.2' ,
        , '$$ out $".$""string""" o1 $$."string"',
        , '$$ out . o1 $$. o1',
        , '$$ out .{ o2 } $$.¢ o2 $!',
        , '$$ out .¢ o3 $"$!" $$.¢ ', ' m.tstComp.3 ', ' $!',
        , '$$ out .¢ o4 $"$!" $$.¢ ', ' m.tstComp.4 ', ' $!',
        , '$$ out ./-/ o5 $"$/-/" $$./-/  m.tstComp.5 ', ' $/-/'

/*
$=/tstCompObjRefPri/
    ### start tst tstCompObjRefPri ####################################
    compile @, 9 lines: $$ out .$"$.{o1}" $$.¢ m.tstComp.1 $!
    run without input
    out .$.{o1}
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .$.-{o2}
    <o2>
    out .$.={o3}
    . m.tstComp.3 .
    out .$.@{out o4}
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    tstWriteO kindOf ORun oRun end   >>>
    out .$.@¢$$abc $$efg$!
    tstWriteO kindOf ORun oRun begin <<<
    abc
    efg
    tstWriteO kindOf ORun oRun end   >>>
    out .$.@¢o5$!
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o5> isA :tstCompCla = o5
    tstR:  .FEINS = o5.fEins
    tstR:  .FZWEI = o5.2
    abc
    tstWriteO kindOf ORun oRun end   >>>
$/tstCompObjRefPri/ */
    call tstComp1 '@ tstCompObjRefPri' ,
        , '$$ out .$"$.{o1}" $$.¢ m.tstComp.1 $!',
        , '$$ out .$"$.-{o2}" $$.-¢ m.tstComp.2 $!',
        , '$$ out .$"$.={o3}" $$.=¢ m.tstComp.3 $!',
        , '$$ out .$"$.@{out o4}" $$.@@¢ call out m.tstComp.4 $!',
    , '$$ out .$"$.@¢$$abc $$efg$!" $$. $.@@¢ $$abc ', ' ', ' $$efg $!',
        , '$$ out .$"$.@¢o5$!" $$. $.@@¢ $$. m.tstComp.5', '$$abc $!'

/*
$=/tstCompObjRefFile/
    ### start tst tstCompObjRefFile ###################################
    compile @, 7 lines: $$ out .$".<.¢o1!" $$.<.¢ m.tstComp.1 $!
    run without input
    out ..<.¢o1!
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .<$.-{o2}
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o2> isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$.<{o3}
    tstWriteO kindOf JRW jWriteNow begin <<<
    . m.tstComp.3 .
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$.<@{out o4}
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$<@¢$$abc $$efg$!
    tstWriteO kindOf JRW jWriteNow begin <<<
    abc
    efg
    tstWriteO kindOf JRW jWriteNow end   >>>
$/tstCompObjRefFile/ */

    call tstComp1 '@ tstCompObjRefFile' ,
        , '$$ out .$".<.¢o1!" $$.<.¢ m.tstComp.1 $!',
        , '$$ out .$"<$.-{o2}" $$<.¢ m.tstComp.2 $!',
        , '$$ out .$"$.<{o3}" $$<=¢ m.tstComp.3 $!',
        , '$$ out .$"$.<@{out o4}" $$<@¢ call out m.tstComp.4 $!',
    , '$$ out .$"$<@¢$$abc $$efg$!" $$<@¢ $$abc ', ' ', ' $$efg $!'

/*
$=/tstCompObjFor/
    ### start tst tstCompObjFor #######################################
    compile @, 2 lines: $@do rx=1 to 3 $$. m.tstComp.rx
    run without input
    FEINS=o1.fEins FZWEI=o1.2
    FEINS=o2.1 FZWEI=o2.fZwei2
    FEINS=o3.fEins FZWEI=o3.2
$/tstCompObjFor/
*/
    call tstComp1 '@ tstCompObjFor' ,
        , '$@do rx=1 to 3 $$. m.tstComp.rx' ,
        , '$| $@forWith witx $$ FEINS=$FEINS FZWEI=$FZWEI'

/*
$=/tstCompObjRun/
    ### start tst tstCompObjRun #######################################
    compile @, 4 lines: $$ out .$"$@¢o1!" $$@¢ $$. m.tstComp.1 $!
    run without input
    out .$@¢o1!
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstWriteO kindOf ORun oRun end   >>>
    out .$<@¢$$abc $$efg$!
    tstWriteO kindOf JRW jWriteNow begin <<<
    abc
    efg
    tstWriteO kindOf JRW jWriteNow end   >>>
$/tstCompObjRun/ */
    call tstComp1 '@ tstCompObjRun' ,
        , '$$ out .$"$@¢o1!" $$@¢ $$. m.tstComp.1 $!',
    , '$$ out .$"$<@¢$$abc $$efg$!" $$<@¢ $$abc ', ' ', ' $$efg $!'

    m.t.trans.0 = 0
/*
$=/tstCompObj/
    ### start tst tstCompObj ##########################################
    compile @, 6 lines: o1=m.tstComp.1
    run without input
    out . o1
    tstR: @tstWriteoV1 isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .{ o2 }
    tstR: @tstWriteoV2 isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    out .¢ o1, o2!
    tstR: @tstWriteoV1 isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstR: @tstWriteoV2 isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
$/tstCompObj/ */
    call tstComp1 '@ tstCompObj' ,
        , 'o1=m.tstComp.1',
        , 'o2 = m.tstComp.2' ,
        , '$$ out . o1 $$. o1',
        , '$$ out .{ o2 } $$.¢ o2 $!',
    , '$$ out .¢ o1, o2!$; $@<.¢  m.tstComp.1  ', '  m.tstComp.2  $!'
    return
    m.t.trans.0 = 0
endProcedure tstCompObj

tstCompORun: procedure expose  m.
/*
$=/tstCompORun/
    ### start tst tstCompORun #########################################
    compile @, 6 lines: $@oRun
    run without input
    oRun arg=1, v2=, v3=, v4=
    oRun arg=1, v2=, v3=, v4=
    oRun arg=2, v2=-{1 arg only} oder?, v3=, v4=
    oRun arg=2, v2=!.{1 obj only} oder?, v3=, v4=
    oRun arg=3, v2={2 args}, v3=und zwei?, v4=
    oRun arg=4, v2={3 args}, v3=zwei, v4=und drei?
$/tstCompORun/ */
    call compIni
    call vPut 'oRun', oRunner('parse arg , v2, v3, v4;',
        'call tstOut t, "oRun arg="arg()", v2="v2", v3="v3", v4="v4' )
    call tstComp1 '@ tstCompORun',
        , '$@oRun', '$@%¢oRun$!' ,
        , '    $@%¢oRun $"-{1 arg only}" oder?$!' ,
        , '    $@%¢oRun - $.".{1 obj only}" ''oder?''$! $=v2=zwei' ,
        , '    $@%¢oRun - $"{2 args}", "und" $v2"?"$!' ,
        , '    $@%¢oRun - $"{3 args}", $v2, "und drei?"$!'
    return
endProcedure tstCompORun

tstCompORu2: procedure expose  m.
/*
$=/tstCompORu2/
    ### start tst tstCompORu2 #########################################
    compile @, 6 lines: $@oRun
    run without input
    oRun arg=1, v2=, v3=, v4=
    oRun arg=1, v2=, v3=, v4=
    oRun arg=2, v2=eins, zwei, drei, v3=, v4=
    oRun arg=2, v2=eins, zwei, drei, v3=, v4=
    oRun arg=4, v2=-eins, v3=zwei, v4=DREI
    oRun arg=4, v2=-eins, v3=zwei, v4=DREI
$/tstCompORu2/ */
    call compIni
    call vPut 'oRun', oRunner('parse arg , v2, v3, v4;',
        'call tstOut t, "oRun arg="arg()", v2="v2", v3="v3", v4="v4' )
    call tstComp1 '@ tstCompORu2',
        , '$@oRun', '$@%oRun',
        , '$@% oRun  eins, zwei, drei' ,
        , '$@%¢ oRun eins, zwei, drei $!',
        , '$@% oRun  - "-eins", "zwei", drei' ,
        , '$@%¢ oRun - "-eins", "zwei", drei $!'
    return
endProcedure tstCompORu2

tstCompORuRe: procedure expose  m.
/*
$=/tstCompORuRe/
    ### start tst tstCompORuRe ########################################
    compile @, 9 lines: $$ primary $-^oRuRe eins, zwei
    run without input
    primary oRuRe(arg=1, v2=, v3=) eins, zwei
    oRuRe(arg=2, v2=expr, zwei, v3=)
    oRuRe(arg=3, v2=-expr, v3=zwei)
    oRuRe(arg=2, v2=block, zwei, v3=)
    oRuRe(arg=3, v2=-block, v3=zwei)
$/tstCompORuRe/ */
    call compIni
    call vPut 'oRuRe', oRunner('parse arg , v2, v3;',
        'return "oRuRe(arg="arg()", v2="v2", v3="v3")"' )
    call tstComp1 '@ tstCompORuRe',
        , '$$ primary $-^oRuRe eins, zwei' ,
        , '$$-^ oRuRe expr, zwei',
        , '$$-^ oRuRe - "-expr", "zwei"',
        , '$$-^¢oRuRe block, zwei$!' ,
        , '$$-^¢',, 'oRuRe - "-block", "zwei"' , , '$!'
    return
endProcedure tstCompORuRe

tstCompDataIO: procedure expose m.
/*
$=/tstCompDataHereData/
    ### start tst tstCompDataHereData #################################
    compile =, 13 lines:  herdata $@#/stop/    .
    run without input
    . herdata .
    heredata 1 $x
    heredata 2 $y
    nach heredata
    . herdata ¢ .
    heredata 1 xValue
    heredata 2 yValueY
    nach heredata ¢
    . herdata { .
    HEREDATA 1 xValue
    heredata 2 yValueY
    nach heredata {
$/tstCompDataHereData/ */
    call tstComp1 '= tstCompDataHereData',
        , ' herdata $@#/stop/    ',
        , 'heredata 1 $x',
        , 'heredata 2 $y',
        , '$/stop/ $$ nach heredata',
        , ' herdata ¢ $@=/stop/    ',
        , '$=x=xValue$=y=yValueY',
        , 'heredata 1 $x',
        , 'heredata 2 $y',
        , '$/stop/ $$ nach heredata ¢',
        , ' herdata { $@/st/',
        , '; call out heredata 1 $x',
        , '$$heredata 2 $y',
        , '$/st/ $$ nach heredata {'
/*
$=/tstCompDataIO/
    ### start tst tstCompDataIO #######################################
    compile =, 5 lines:  input 1 $@.<-=¢$dsn $*+.
    run without input
    . input 1 .
    readInp line 1                       .
    readInp line 2                       .
    . nach dsn input und nochmals mit & .
    readInp line 1                       .
    readInp line 2                       .
    . und schluiss..
$/tstCompDataIO/ */
    dsn = word(tstPdsMbr(tstFilename('lib37', 'r'), 'readInp'), 1)
    dsnFB = strip(dsn tstFB('::F37', 0))
    b = jBuf(tstFB('readInp line 1', 37),
            ,tstFB('readInp line 2', 37))
    extFd = tstFB('&dsn('dsn') dd(xyz)', 0)
    extFd = tstFB(dsn 'dd(xyz) ::f', 0)
    if extFd = '' then
        extFd = dsn
    call jCat file(dsnFB), b
    call vPut 'dsn', dsn
    say 'dsn' dsn 'extFD' extFD'?'
    call tstComp1 '= tstCompDataIO',
        , ' input 1 $@.<-=¢$dsn $*+',
        , tstFB('::f', 0) '$!',
        , ' nach dsn input und nochmals mit & ' ,
        , '         $@.<'extFD,
        , ' und schluiss.'
    return
endProcedure tstCompDataIO

tstObjVF: procedure expose m.
parse arg v, f
    obj  = oNew(classNew('n? TstClassVF u v, f FLD1 v'))
    m.obj = if(f=='','val='v, v)
    m.obj.fld1 = if(f=='','FLD1='v, f)
    return obj
endProcedure tstObjVF

tstCompFile: procedure expose m.
/*
$=/tstCompFileBloSrc/
    $=vv=value-of-vv
    ###file from empty # block
    $@<#¢
        $!
    ###file from 1 line # block
    $@<#¢
    the only $ix+1/0 line $vv
    $!
    ###file from 2 line # block
    $@<#¢
        first line /0 $*+ no comment
        second and last line $$ $wie
    $!
    ===file from empty = block
    $@<=¢     $*+ comment
        $!
    ===file from 1 line = block
    $@<=¢ the only line $!
    ===file from 2 line = block
    $@<=¢ first line$** comment
        second and last line  $!
    ---file from empty - block
    $@<-/s/
        $/s/
    ---file from 1 line - block
    $@<-/s/ the only "line" (1*1) $/s/
    ---file from 2 line = block
    $@<-// first "line" (1+0)
        second   and   "last  line" (1+1)  $//
    ...file from empty . block
    $@<.¢
        $!
    ...file from 1 line . block
    $@<.¢ tstObjVF('v-Eins', '1-Eins') $!
    ...file from 2 line . block
    $@<.¢ tstObjVF('v-Elf', '1-Elf')
        tstObjVF('zwoelf')  $!
    ...file from 3 line . block
    $@<.¢ tstObjVF('einUndDreissig')
            s2o('zweiUndDreissig' o2String($.-vv))
            tstObjVF('dreiUndDreissig')  $!
    @@@file from empty @ block
    $@<@¢
        $!
    $=noOutput=before
    @@@file from nooutput @ block
    $@<@¢ nop
        $=noOutput = run in block $!
    @@@nach noOutput=$noOutput
    @@@file from 1 line @ block
    $@<@¢ $$. tstObjVF('w-Eins', 'w1-Eins') $!
    @@@file from 2 line @ block
    $@<@¢ $$. tstObjVF('w-Elf', 'w1-Elf')
        y='zwoelf' $$- y  $!
    @@@file from 3 line @ block
    $@<@¢ $$. tstObjVF('w einUndDreissig')    $$ +
    zweiUndDreissig $$ 33 $vv$!
    {{{ empty ¢ block
    $@<¢     $!
    {{{ empty ¢ block with comment
    $@<¢    $*+ abc
          $!
    {{{ one line ¢ block
    $@<¢ the only $"¢...$!" line $*+.
        $vv $!
    {{{ one line -¢ block
    $@<-¢ the only $"-¢...$!"  "line" $vv  $!
    {{{ empty #¢ block
    $@<#¢
$!
    {{{ one line #¢ block
    $@<#¢ the only $"-¢...$!"  "line" $vv $¢vv${x}$!
$!
$/tstCompFileBloSrc/ */
/*
$=/tstCompFileBlo/
    ### start tst tstCompFileBlo ######################################
    compile =, 72 lines: $=vv=value-of-vv
    run without input
    ###file from empty # block
    ###file from 1 line # block
    the only $ix+1/0 line $vv
    ###file from 2 line # block
    first line /0 $*+ no comment
    second and last line $$ $wie
    ===file from empty = block
    ===file from 1 line = block
    . the only line .
    ===file from 2 line = block
    . first line
    second and last line  .
    ---file from empty - block
    ---file from 1 line - block
    THE ONLY line 1
    ---file from 2 line = block
    FIRST line 1
    SECOND AND last  line 2
    ...file from empty . block
    ...file from 1 line . block
    tstR: @tstWriteoV1 isA :TstClassVF = v-Eins
    tstR:  .FLD1 = 1-Eins
    ...file from 2 line . block
    tstR: @tstWriteoV2 isA :TstClassVF = v-Elf
    tstR:  .FLD1 = 1-Elf
    tstR: @tstWriteoV3 isA :TstClassVF = val=zwoelf
    tstR:  .FLD1 = FLD1=zwoelf
    ...file from 3 line . block
    tstR: @tstWriteoV4 isA :TstClassVF = val=einUndDreissig
    tstR:  .FLD1 = FLD1=einUndDreissig
    zweiUndDreissig value-of-vv
    tstR: @tstWriteoV5 isA :TstClassVF = val=dreiUndDreissig
    tstR:  .FLD1 = FLD1=dreiUndDreissig
    @@@file from empty @ block
    @@@file from nooutput @ block
    @@@nach noOutput=run in block
    @@@file from 1 line @ block
    tstR: @tstWriteoV6 isA :TstClassVF = w-Eins
    tstR:  .FLD1 = w1-Eins
    @@@file from 2 line @ block
    tstR: @tstWriteoV7 isA :TstClassVF = w-Elf
    tstR:  .FLD1 = w1-Elf
    zwoelf
    @@@file from 3 line @ block
    tstR: @tstWriteoV8 isA :TstClassVF = val=w einUndDreissig
    tstR:  .FLD1 = FLD1=w einUndDreissig
    zweiUndDreissig
    33 value-of-vv
    {{{ empty ¢ block
    {{{ empty ¢ block with comment
    {{{ one line ¢ block
    . the only ¢...$! line value-of-vv .
    {{{ one line -¢ block
    THE ONLY -¢...$! line value-of-vv
    {{{ empty #¢ block
    {{{ one line #¢ block
    . the only $"-¢...$!"  "line" $vv $¢vv${x}$!
$/tstCompFileBlo/ */
    call tstComp2 'tstCompFileBlo', '='
    m.t.trans.0 = 0

/*
$=/tstCompFileObjSrc/
    $=vv=value-vv-1
    $=fE=<¢ $!
    $=f2=. $.<.¢s2o("f2 line 1" o2String($.-vv))
         tstObjVF("f2 line2") $!
    ---empty file $"$@<$fE"
    $@fE
    ---file with 2 lines $"$@<$f2"
    $@.<.f2
    $=vv=value-vv-2
    ---file with 2 lines $"$@<$f2"
    $@.<.f2
    $= dsn =- word(tstPdsMbr(tstFilename('libvb', 'r'), 'fileObj'),1) +
                tstFB('::V', 0)
    $@¢
        fi=jOpen(file($dsn),'>')
        call jWrite fi, 'line one on' $"$dsn"
        call jWrite fi, 'line two on' $"$dsn"
        call jClose fi
    $!
    ---file on disk out
    $@<-dsn
$/tstCompFileObjSrc/ */
/*
$=/tstCompFileObj/
    ### start tst tstCompFileObj ######################################
    compile =, 20 lines: $=vv=value-vv-1
    run without input
    ---empty file $@<$fE
    ---file with 2 lines $@<$f2
    f2 line 1 value-vv-1
    tstR: @tstWriteoV1 isA :TstClassVF = val=f2 line2
    tstR:  .FLD1 = FLD1=f2 line2
    ---file with 2 lines $@<$f2
    f2 line 1 value-vv-1
    tstR: @tstWriteoV1 isA :TstClassVF = val=f2 line2
    tstR:  .FLD1 = FLD1=f2 line2
    ---file on disk out
    line one on $dsn
    line two on $dsn
$/tstCompFileObj/ */
    call tstComp2 'tstCompFileObj', '='

    return
endProcedure tstCompFile

tstCompPipe: procedure expose m.
/*
$=/tstCompPipe1/
    ### start tst tstCompPipe1 ########################################
    compile @, 1 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    (1 eins zwei drei 1)
    #jIn 2# zehn elf zwoelf?
    (1 zehn elf zwoelf? 1)
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    (1 zwanzig 21 22 23 24 ... 29| 1)
    #jIn eof 4#
$/tstCompPipe1/ */
    call tstComp1 '@ tstCompPipe1 3',
        , ' call pipePreSuf "(1 ", " 1)"'
/*
$=/tstCompPipe2/
    ### start tst tstCompPipe2 ########################################
    compile @, 2 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    ¢2 (1 eins zwei drei 1) 2!
    ¢2 (1 zehn elf zwoelf? 1) 2!
    ¢2 (1 zwanzig 21 22 23 24 ... 29| 1) 2!
$/tstCompPipe2/ */
    call tstComp1 '@ tstCompPipe2 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| call pipePreSuf "¢2 ", " 2!"'

/*
$=/tstCompPipe3/
    ### start tst tstCompPipe3 ########################################
    compile @, 3 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <3 ¢2 (1 eins zwei drei 1) 2! 3>
    <3 ¢2 (1 zehn elf zwoelf? 1) 2! 3>
    <3 ¢2 (1 zwanzig 21 22 23 24 ... 29| 1) 2! 3>
$/tstCompPipe3/ */
    call tstComp1 '@ tstCompPipe3 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| call pipePreSuf "¢2 ", " 2!"',
        , ' $| call pipePreSuf "<3 ", " 3>"'

/*
$=/tstCompPipe4/
    ### start tst tstCompPipe4 ########################################
    compile @, 7 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <3 ¢222 ¢221 ¢21 ¢20 (1 eins zwei drei 1) 20! 21! 221! 222! 3>
    <3 ¢222 ¢221 ¢21 ¢20 (1 zehn elf zwoelf? 1) 20! 21! 221! 222! 3>
    <3 ¢222 ¢221 ¢21 ¢20 (1 zwanzig 21 22 23 24 ... 29| 1) 20! 21! 221!+
    . 222! 3>
$/tstCompPipe4/ */
    call tstComp1 '@ tstCompPipe4 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| $@¢    call pipePreSuf "¢20 ", " 20!"',
        ,        ' $| call pipePreSuf "¢21 ", " 21!"',
        ,        ' $| $@¢      call pipePreSuf "¢221 ", " 221!"',
        ,                 ' $| call pipePreSuf "¢222 ", " 222!"',
        ,     '$!     $! ',
        , ' $| call pipePreSuf "<3 ", " 3>"'
    return
endProcedure tstCompPipe


tstCompPip2: procedure expose m.
/*
$=/tstCompPip21/
    ### start tst tstCompPip21 ########################################
    compile @, 3 lines:  $<¢ zeile eins .
    run without input
    (1  zeile eins  1)
    (1    zeile zwei  1)
    run with 3 inputs
    (1  zeile eins  1)
    (1    zeile zwei  1)
$/tstCompPip21/ */
    call tstComp1 '@ tstCompPip21 3',
        , ' $<¢ zeile eins ' ,
        , '   zeile zwei $!' ,
        , ' call pipePreSuf "(1 ", " 1)"'
/*
$=/tstCompPip22/
    ### start tst tstCompPip22 ########################################
    compile @, 3 lines: if ${>i1} then $@¢
    run without input
    #jIn eof 1#
    nachher
    run with 3 inputs
    #jIn 1# eins zwei drei
    <zeile 1: eins zwei drei>
    <zwei>
    nachher
$/tstCompPip22/ */
    call tstComp1 '@ tstCompPip22 3',
        , 'if ${>i1} then $@¢'          ,
        , ' $$ zeile 1: $i1 $$ zwei $| call pipePreSuf "<",">" $!',
        , ' $$ nachher '
    return
endProcedure tstCompPip2

tstCompRedir: procedure expose m.
/*
$=/tstCompRedir/
    ### start tst tstCompRedir ########################################
    compile @, 6 lines:  $=eins=<@¢ $@for vv $$ <$vv> $! .
    run without input
    #jIn eof 1#
    output eins .
    output piped zwei .
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    output eins <eins zwei drei> <zehn elf zwoelf?> +
    <zwanzig 21 22 23 24 ... 29|>
    output piped zwei ab<eins zwei drei>yz ab<zehn elf zwoelf?>yz a+
    b<zwanzig 21 22 23 24 ... 29|>yz
$/tstCompRedir/ */
    call pipeIni
    call vRemove 'eins'  /* alte Variable loswerden */
    dsn = word(tstPdsMbr(tstFilename('libvb', 'r'), 'redir1'), 1)
    call vPut 'dsn', dsn
    say  'dsn' $dsn
    call tstComp1 '@ tstCompRedir 3' ,
        , ' $=eins=<@¢ $@for vv $$ <$vv> $! ',
        , ' $$ output eins $-=¢$.eins$! $; ',
        , ' $@for ww $$b${ww}y ' ,
        , '    $>-= $-¢ $dsn $! 'tstFB('::v', 0),
        ,         '$| call pipePreSuf "a", "z" $<.eins' ,
        , ' $; $$ output piped zwei $-=¢$@<$-dsn$!'
/*
$=/tstCompRedi2/
    ### start tst tstCompRedi2 ########################################
    compile @, 12 lines: call mAdd t.trans, $var "dsnTestRedi"
    run without input
    >1<dsnTestRedi currTimeRedi
    >2<$"dsnTestRedi" currTimeRedi
    >3<$"dsnTestRedi" ::v currTimeRedi
    >4<$-var" currTimeRedi
    >5<$dsnTestRedi" currTimeRedi
$/tstCompRedi2/
*/
    call vPut 'var', tstFileName('compRedi', 'r')
    call vPut 'tst', translate(date()'+'time()'+testRedi2', '_', ' ')
    call tstComp1 '@ tstCompRedi2 ' ,
        , 'call mAdd t.trans, $var "dsnTestRedi"',
        , 'call mAdd t.trans, $tst "currTimeRedi"',
        , '$<> $>'vGet('var') '::v $$ $">1<'vGet('var')'" $tst',
        , '$<> $<'vGet('var') '    $@ call pipeWriteAll' ,
        , '$<> $>$"'vGet('var')' ::v" $$ $">2<$""'vGet('var')'""" $tst',
        , '$<> $<$"'vGet('var') '"   $@ call pipeWriteAll',
   , '$<> $>$"'vGet('var')'" ::v $$ $">3<$""'vGet('var')'"" ::v" $tst',
        , '$<> $<$"'vGet('var') '"   $@ call pipeWriteAll',
        , '$<> $>-var  $$ $">4<$"-var" $tst',
        , '$<> $<-var  $@ call pipeWriteAll',
        , '$<> $>$var ::v $$ $">5<$"$var" $tst',
        , '$<> $<$var  $@ call pipeWriteAll'
    return
endProcedure tstCompRedir

tstCompComp: procedure expose m.
/*
$=/tstCompCompShell/
    ### start tst tstCompCompShell ####################################
    compile @, 5 lines: $$compiling shell $; $= rrr =. $.^compile $<@#/+
    aaa/
    run without input
    compiling shell
    running einmal
    RUN 1 COMPILED einmal
    #jIn eof 1#
    running zweimal
    RUN 1 COMPILED zweimal
    #jIn eof 2#
    run with 3 inputs
    compiling shell
    running einmal
    RUN 1 COMPILED einmal
    #jIn 1# eins zwei drei
    compRun eins zwei dreieinmal
    #jIn 2# zehn elf zwoelf?
    compRun zehn elf zwoelf?einmal
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    compRun zwanzig 21 22 23 24 ... 29|einmal
    #jIn eof 4#
    running zweimal
    RUN 1 COMPILED zweimal
    #jIn eof 5#
$/tstCompCompShell/ */
    call tstComp1 '@ tstCompCompShell 3',
        ,  "$$compiling shell $; $= rrr =. $.^compile $<@#/aaa/",
        ,  "call out run 1*1*1 compiled $cc;" ,
            "$@for v $$ compRun $v$cc" ,
        ,  "$/aaa/ $;",
        ,  "$=cc=einmal $$ running $cc $@rrr",
        ,  "$=cc=zweimal $$ running $cc $@rrr"
/*
$=/tstCompCompData/
    ### start tst tstCompCompData #####################################
    compile @, 5 lines: $$compiling data $; $= rrr =. $.^¢compile = +
    =$!  $<@#/aaa/
    run without input
    compiling data
    running einmal
    call out run 1*1*1 compiled einmal
    running zweimal
    call out run 1*1*1 compiled zweimal
    run with 3 inputs
    compiling data
    running einmal
    call out run 1*1*1 compiled einmal
    running zweimal
    call out run 1*1*1 compiled zweimal
$/tstCompCompData/ */
    call tstComp1 '@ tstCompCompData 3',
        ,  "$$compiling data $; $= rrr =. $.^¢compile = =$!  $<@#/aaa/",
        ,  "call out run 1*1*1 compiled $cc",
        ,  "$/aaa/ $;",
        ,  "$=cc=einmal $$ running $cc $@rrr",
        ,  "$=cc=zweimal $$ running $cc $@rrr"
    return
endProcedure tstCompComp

tstCompDir: procedure expose m.
/*
$=/tstCompDirSrc/
  'in src v1='$v1
  $#@ call out 'src @ out v1='$v1
  $#. $*(komm$*) s2o('src . v1=')
       $.-v1
  $#-
    'src - v1='$v1
  $#=
    src = v1=$v1
$/tstCompDirSrc/

$=/tstCompDir/
    ### start tst tstCompDir ##########################################
    compile @call out 'before v1='$v1 $=v1=eins $#. s2o('. v1='$-v1) $#+
    @ call out '@ v1='$v1 $#= = v1=$v1 $#- '- v1='$v1 $#-, 8 lines: 'in+
    . src v1='$v1
    run without input
    before v1=v1Before
    .. v1=eins
    @ v1=eins
    = v1=eins .
    - v1=eins
    in src v1=eins
    src @ out v1=eins
    src . v1=
    eins
    src - v1=eins
    src = v1=eins
$/tstCompDir/ */
    call compIni
    call vPut 'v1', 'v1Before'
    call tstComp2 'tstCompDir', "@call out 'before v1='$v1 $=v1=eins" ,
        "$#. s2o('. v1='$-v1) $#@ call out '@ v1='$v1" ,
        "$#= = v1=$v1 $#- '- v1='$v1 $#-"
/*
$=/tstCompDirPiSrc/
  zeile 1 v1=$v1
  zweite Zeile vor $"$@$#-"
  $#@  $@proc pi2 $@-¢
  $'zeile drei nach $@$#- v1='v1
  vierte und letzte Zeile $!
$/tstCompDirPiSrc/ */
/*
$=/tstCompDirPi/
    ### start tst tstCompDirPi ########################################
    compile @call pipePreSuf '<','>' $=v1=eiPi $<.pi2 $#=, 5 lines: ze+
    ile 1 v1=$v1
    run without input
    <zeile drei nach $@$#- v1=V1>
    <VIERTE UND LETZTE ZEILE>
    zeile 1 v1=eiPi
    zweite Zeile vor $@$#-
$/tstCompDirPi/ */
    call tstComp2 'tstCompDirPi',
            , "@call pipePreSuf '<','>' $=v1=eiPi $<.pi2 $#="
    return
endProcedure tstCompDir

tstCompColon: procedure expose m.
/*
$=/tstCompColon1/
    ### start tst tstCompColon1 #######################################
    compile :, 12 lines: vA = valueVonA
    run without input
    vA = valueVonA
    vA=valueVonA vB=valueVonB vC=valueVonC
    vC=valueVonC vD=valueVonD vE=valueVonvE
    vF=6
$/tstCompColon1/ */
    call tstComp1 ': tstCompColon1',
        , 'vA = valueVonA' ,
        , ' $$ vA = $vA' ,
        , '        * kommentar ' ,
        , '=vB=- "valueVonB"' ,
        , '=/vC/valueVonC$/vC/' ,
        , ' $$ vA=$vA vB=$vB vC=$vC' ,
        , '$=/vD/valueVonD' ,
        , '$/vD/ vE=valueVonvE' ,
        , '        * kommentar ' ,
        , ' $$ vC=$vC vD=$vD vE=$vE',
        , 'vF=- 2*3 $=vG=@@¢ $$ vF=$vF$!' ,
        , '@vG'

/*
$=/tstCompColon2/
    ### start tst tstCompColon2 #######################################
    compile :, 7 lines: ix=0
    run without input
    #jIn eof 1#
    proc p1 arg(2) total 0 im argumentchen
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <<for 1 -> eins zwei drei>>
    <<for 2 -> zehn elf zwoelf?>>
    <<for 3 -> zwanzig 21 22 23 24 ... 29|>>
    proc p1 arg(2) total 3 im argumentchen
$/tstCompColon2/

*/
    call tstComp1 ': tstCompColon2 3',
        , 'ix=0' ,
        , 'for v @:¢ix=- $ix+1',
        , ' $$ for $ix -> $v' ,
        , '! | @¢call pipePreSuf "<<",">>"',
        , '$! @%¢p1 total $ix im argumentchen$!',
        , 'proc @:/p1/$$- "proc p1 arg(2)" arg(2)' ,
        , '/p1/'
/*
$=/tstCompColon3/
    ### start tst tstCompColon3 #######################################
    compile :, 11 lines: tc3Eins=freeVar1
    run without input
    tc3Eins=freeVar1 o2&tc3Eins= o2&tc3Zwei=
    tc3Eins=freeVar1 o2&tc3Eins=with3Eins o2&tc3Zwei=with3Zwei
    tc3Eins=freeVar1 o2&tc3Eins=ass4Eins o2&tc3Zwei=with5Zwei
    o3&tc3Eins=ass4Eins o3&tc3Zwei=with5 o3 Zwei
    tc3Eins=freeVar1 o2&tc3Eins=ass4Eins o2&tc3Zwei=with5Zwei
$/tstCompColon3/
*/
    call classNew 'n? TstCompColon3 u f tc3Eins v, f tc3Zwei v'
    showO2 = 'tc3Eins=$tc3Eins' ,
            'o2&tc3Eins=${o2&tc3Eins} o2&tc3Zwei=${o2&tc3Zwei}'
    showO3 = 'o3&tc3Eins=${o3&tc3Eins} o3&tc3Zwei=${o3&tc3Zwei}'
    call tstComp1 ': tstCompColon3',
        , 'tc3Eins=freeVar1' ,
     , 'o2 =. oNew("TstCompColon3")' ,
        , '$$' showO2 ,
        , 'with $o2 $@:¢tc3Eins = with3Eins',
        ,     'tc3Zwei = with3Zwei',
        ,    '! $$' showO2 ,
        , '{o2&tc3Eins} = ass4Eins',
        , 'with $o2 $=tc3Zwei = with5Zwei',
        , '$$' showO2 ,
        , 'with o3 =. oCopy($o2) $=tc3Zwei = with5 o3 Zwei',
        , '$$' showO3 '$$' showO2
    return
endProcedure tstCompColon

tstCompTable: procedure expose m.
/*
$=/tstCompTable1/
    ### start tst tstCompTable1 #######################################
    compile :, 6 lines: table $*( sdf $*)   .
    run without input
    tstR: @tstWriteoV2 isA :<TstCT1Class>
    tstR:  .fEins = v1
    tstR:  .fZwei = valueZwei
    tstR:  .fDrei = undDrei
    zweite
    tstR: @tstWriteoV3 isA :<TstCT1Class>
    tstR:  .fEins = w1
    tstR:  .fZwei = wZwei
    tstR:  .fDrei = wwwDrei
$/tstCompTable1/

 */
    call wshIni
    cl = classNew('n* CompTable u f fEins v, f fZwei v, f fDrei v')
    call tstComp1 ': tstCompTable1',
        , 'table $*( sdf $*)   ' ,
        , 'fEins   fZwei $*(....$*) fDrei  ' ,
        , '$@¢call mAdd t.trans, className("'cl'") "<TstCT1Class>"$!',
        , '    v1     valueZwei undDrei     ' ,
        , '$$ zweite',
        , ' w1 wZwei                    wwwDrei     '


/*
$=/tstCompWithNew/
    ### start tst tstCompWithNew ######################################
    compile :, 12 lines: withNew nn $*( sdf $*)  $@:¢  .
    run without input
    tstR: @tstWriteoV2 isA :<TstCT1Class>
    tstR:  .fEins = withNewValue fEins
    tstR:  .fZwei = withNewValue fZwei
    tstR:  .fDrei = withNewValuel drei
    tstR: @tstWriteoV3 isA :<TstCT1Class>
    tstR:  .fEins = withNewValue fEinsB
    tstR:  .fZwei = withNewValue fZweiB
    tstR:  .fDrei = withNewValue fDreiB
    tstR: @tstWriteoV4 isA :<TstCT1Class>
    tstR:  .fEins = withValue fEinsC
    tstR:  .fZwei = .
    tstR:  .fDrei = withValue fDreiC
$/tstCompWithNew/
*/
    call tstComp1 ': tstCompWithNew',
        , 'withNew nn $*( sdf $*)  $@:¢  ' ,
        , 'fEins = withNewValue fEins' ,
        , 'fZwei = withNewValue fZwei' ,
        , '$@¢call mAdd t.trans, className("'cl'") "<TstCT1Class>"',
        , '$@:¢   fDrei = withNewValuel drei $! $!! $$ $nn' ,
        , 'withNew out $*( sdf $*)  $@:¢  ' ,
        , 'fEins = withNewValue fEinsB' ,
        , 'fZwei = withNewValue fZweiB',
        , 'fDrei = withNewValue fDreiB',
        , '! with out =. oNew('cl') $@:¢',
        , 'fEins = withValue fEinsC' ,
        , 'fDrei = withValue fDreiC $!'
    return
endProcedure tstCompTable

tstCompSql: procedure expose m.
/*
$=/tstCompSqlSrc/
$@=¢
   select strip(creator) cr, strip(name) tb,
            (row_number()over())*(row_number()over()) rr
       from sysibm.sysTables
       where creator='SYSIBM' and name like 'SYSTABL%'
       order by 2 fetch first 4 rows only
$!
$| call sqlSel
$| call fTabAuto
$/tstCompSqlSrc/
$=/tstCompSql/
    ### start tst tstCompSql ##########################################
    compile @, 9 lines: $@=¢
    run without input
    CR     TB                RR
    SYSIBM SYSTABLEPART       1
    SYSIBM SYSTABLEPART_HIST  4
    SYSIBM SYSTABLES          9
    SYSIBM SYSTABLESPACE     16
$/tstCompSql/
$=/tstCompSqlFTabSrc/
$$ select 'ahaaaax' ahaCol, 'buuuuh' buhhhh from sysibm.sysDummy1
$| $@. vPut('lc', sqlRdr(scanSqlIn2Stmt()))
$| call fTab  sqlFTabOthers(sqlRdrFTabReset($.lc, tstCompSql1))
$<>
$$ select 'aOh' ahaOhne, 'buuVar' buhVar from sysibm.sysDummy1
$| call sqlSel
$| t2 = fTabReset(sqlRdrFTabReset( , tstCompS2), '2 1', '2 c', '-')
   ox = m.t2.0 + 1
   call sqlFTabOthers t2
   call fTab fTabAddTit(t2, ox, 2, '-----')
$<>
$$ select 'aOh' aDrei, 'buuDre' buhDrei from sysibm.sysDummy1
$| call sql2Tab
$/tstCompSqlFTabSrc/
$=/tstCompSqlFTab/
    ### start tst tstCompSqlFTab ######################################
    compile @, 13 lines: $$ select 'ahaaaax' ahaCol, 'buuuuh' buhhhh fr+
    om sysibm.sysDummy1
    run without input
    AHACOL--BUHHHH---
    ahaaaax buuuuh
    AHACOL--BUHHHH---
    -----
    AHA-BUHVAR---
    aOh buuVar
    -----
    AHAOHNE
    .    BUHVAR
    ADREI
    .    BUHDREI
    ADR-BUHDRE---
    aOh buuDre
    ADR-BUHDRE---
    ADREI
    .    BUHDREI
$/tstCompSqlFTab/
    ### start tst tstCompSql ##########################################
*/
    call sqlConnect
    call tstComp2 'tstCompSql', '@'
    call tstComp2 'tstCompSqlFTab', '@'
    call sqlDisConnect
    return
endProcedure tstCompSql
tstTut0: procedure expose m.
/*
$=/tstTut01Src/
$#=
$*+>.fSub()                               Kommentar
$*+>~tmp.jcl(t)                           Kommentar
$*+@=¢                                    Kommentar
$=subsys=DP4G
$=db=DA540769
$=ts=A977A
$*+@<~wk.jcl(jc)                          Kommentar
??* ?-¢sysvar(sysnode) date() time()?!ts=$ts 10*len=$-¢length($ts)*10$!
//P02     EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769C.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
  $@¢if right($ts, 2) == '7A' then $@=¢
    FULL YES
  $! else
    $$ $''    FULL NO
  $!
    SHRLEVEL CHANGE
$*+!                                      Kommentar
$#out original/src                                 20150212 12:49:04
$/tstTut01Src/
$=/tstTut01/
    ### start tst tstTut01 ############################################
    compile , 28 lines: $#=
    run without input
    ??* ?-¢sysvar(sysnode) date() time()?!ts=A977A 10*len=50
    //P02     EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A540769C.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977A*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    FULL YES
    SHRLEVEL CHANGE
$/tstTut01/
$=/tstTut02Src/
$#@
$**>.fSub()
$**@¢
$=subsys=DP4G
$=db=DA540769
$=jx=0
$@do tx = 976 to 977 $@=¢
    $=ts=A$tx
    $=jx=- $jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$**!
$#out original/src
$/tstTut02Src/
$=/tstTut02/
    ### start tst tstTut02 ############################################
    compile , 28 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut02/
$=/tstTut03Src/
$#@
$=subsys=DP4G
$@:¢table
      db         ts
      DGDB9998   A976
      DA540769   A977
$!
$** $| call fTabAuto
$**    $#end
$|
$=jx=0
$@forWith o $@=¢
    $=jx=- $jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$#out original/src
$/tstTut03Src/
$=/tstTut03/
    ### start tst tstTut03 ############################################
    compile , 31 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut03/
$=/tstTut04Src/
$#@
$=subsys=DP4G
$=db=DA540769
call sqlConnect $subsys
$@=¢  select dbName  db , tsName  ts
          from sysibm.sysTables
          where creator = 'SYSIBM' and name like 'SYSINDEXPAR%'
          order by name desc
$!
$| call sqlSel
$** $| call fTabAuto
$** $#end
$|
$=jx=0
$@forWith o $@=¢
    $=jx=- $jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$TS    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $DB.$TS*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
call sqlDisConnect
$#out original/src
$/tstTut04Src/
$=/tstTut04/
    ### start tst tstTut04 ############################################
    compile , 35 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CSYSHIST     EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DSNDB06 .SYSHIST *   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CSYSTSIPT    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DSNDB06 .SYSTSIPT*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut04/
$=/tstTut05Src/
$#:
subsys = DP4G
lst =<:¢withNew out :¢
    db = DGDB9998
    ts =<:¢table
             ts
             A976
             A977
    $!
    db = DA540769
    <|/ts/
             ts
             A976
             A975
    /ts/
!
$** $$. $lst
$** $@ct $@¢$=tool =. $.compile@ $<~.wk.rexx(wshtut06)$!
$** $@$tool
$@do sx=1 to ${lst.0} $@¢
    $=db = ${lst.$sx.db}
    $** $$. ${lst.$sx}
    $@do tx=1 to ${lst.$sx.ts.0} $@=¢
        $*+ $$. ${lst.$sx.ts.$tx}
        $=ts= ${lst.$sx.ts.$tx.ts}
        $@¢ say $-=¢subsys $subsys db $db ts $ts $! $!
        $@copy()
        $!
    $!
$@ct $@¢
cl = classNew('n? DbTsList u s' ,
     classNew('n? DbTs u f db v, f ts s' ,
     classNew('n? Ts u f ts v')))
$=lst=. oNew(cl)
$!
$@proc copy $@=¢
$@ct $=jx=0
$=jx=- $jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$#out original/src
$/tstTut05Src/
$=/tstTut05/
    ### start tst tstTut05 ############################################
    compile , 56 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407693 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407693.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407694 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA975    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407694.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A975*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut05/
   tstTut06   ==> platz für copy aus tut05
$=/tstTut07Src/
$**$>.fEdit()
call sqlConnect dp4g
$@:¢table
   ts
VTCASHFLOW
VTCASHFLOWAMOUNT
VTINSTRFLATDATA
$!
$| $@=¢
    select name, statstime, strip(dbName) || '.' || strip(tsName) dbts
       from sysibm.sysTables
       where creator = 'VDPS2' and name in
  $=co=(
  $@forWith t $@=¢
                                           $co '$ts'
      $=co=,
  $!
                                           )
$!
$| call sqlSel
$** $| call fTabAuto
$|
$=jx=0
$@forWith t $@=¢
$=jx=- $jx+1
//A540769$jx JOB (CP00,KE50),
//       'CATALOG',MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//STEP$jx EXEC PGM=DSNUTILB,TIME=1440,
//   PARM=(DP4G,'A540769$jx.RUNSTA'),
//   REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//UTPRINT  DD SYSOUT=*
//SYSOUT   DD SYSOUT=*
//RNPRIN01 DD SYSOUT=*
//SYSIN DD *
  LISTDEF LST#STA   INCLUDE TABLESPACE $DBTS
   OPTIONS EVENT (ITEMERROR, SKIP)

   RUNSTATS TABLESPACE LIST LST#STA
         SHRLEVEL CHANGE
           INDEX(ALL KEYCARD)
           REPORT YES UPDATE ALL
$!
call sqlDisconnect
$#out original/src
$/tstTut07Src/
$=/tstTut07/
$=/tstTut07/
    ### start tst tstTut07 ############################################
    compile , 47 lines: $**$>.fEdit()
    run without input
    //A5407691 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP1 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407691.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV27A1T.VDPS329
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
    //A5407692 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP2 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407692.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV28A1T.VDPS390
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
    //A5407693 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP3 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407693.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV21A1T.VDPS004
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
$/tstTut07/
$=/tstTut08Src/
$/tstTut08Src/
$=/tstTut08/
$/tstTut08/
*/
    call sqlIni
    call sqlDisconnect '*'
    call tstComp2 'tstTut01'
    call tstComp2 'tstTut02'
    call tstComp2 'tstTut03'
    if m.err.os == 'TSO' then do
        call tstComp2 'tstTut04'
        /* call tstComp2 'tstTut05' */
     /* call tstComp2 'tstTut07'  ???? anderes Beispiel ???? */
        end
    call tstTotal
    return
endProcedure tstTut0
/****** tstBase ********************************************************
     test the basic classes
***********************************************************************/
tstBase: procedure expose m.
    call tstTstSay
    call tstM
    call tstUtc2d
    call tstMap
    call tstMapVia
    call tstClass
    call tstClass2
    call tstClass3
    call tstClass4
    call scanIni
    call tstO
    call oIni
    call tstF
    call tstFWords
    call tstFtst
    call tstMCat
    call tstOEins
    call jIni
    call tstJSay
    call tstJ
    call tstJ2
    call tstScanSqlStmt
    call catIni
    call tstCat
    call pipeIni
    CALL TstEnv
    CALL TstEnvCat
    call tstPipe
    call tstPipeS
    call tstEnvVars
    call tstvWith
    call tstTotal
    call tstPipeLazy
    call tstEnvClass
    if m.tst_csm then
        call tstDsnEx
    call tstFile
    call tstFileList
    call tstMbrList
    call tstFE
    call tstFTab
    call tstFmt
    call tstfUnits
    call tstCsv
    call tstTotal
    call tstSb
    call tstSb2
    call tstScan
    call ScanReadIni
    call tstScanRead
    call tstScanUtilInto
    call tstScanWin
    call tstScanSQL
    call tstTotal
    return
endProcedure tstBase

/*--- test the tst Module, check the output visually  ----------------*/
tstTstSay: procedure expose m.
    call tstIni
    oldErr = m.tst.err
    oldNames = m.tst.errNames
    say '+++ tstTstSay start with' oldErr 'totErrs and',
            m.tst.tests 'tests'
/*
$=/tstTstSayEins/
    ### start tst tstTstSayEins #######################################
    test eins einzige testZeile
$/tstTstSayEins/
$=/tstTstSayZwei/
    ### start tst tstTstSayZwei #######################################
    zwei 1. testZeile
    zwei 2. und letzte testZeile
$/tstTstSayZwei/
$=/tstTstSayDrei/
    ### start tst tstTstSayDrei #######################################
    drei 1. testZeile vor empty Zeile
    ..
    drei 3. testZeile vor 10 space
    .          .
    drei 5. testZeile ziemlich lang 66                                 +
    .                                77  5   10   15++++++++++++++++++++
    .+++++++++++++++++++++++++++++++++++++++++++.
$/tstTstSayDrei/
*/
    call tst x, 'tstTstSayEins'
    call tstOut x, "test eins einzige testZeile"
    call tstEnd x, 'err 0'


    call tst x, 'tstTstSayZwei'
    call tstOut x, "zwei 1. testZeile"
    call tstOut x, "zwei 2. und letzte testZeile"
    call tstEnd x, 'err 0'

    call tst x, 'tstTstSayZwei'
    call tstOut x, "zwei 1. testZeile "    /* ein space zuviel */
    call tstOut x, "zwei 2. und letzte testZeile"
    call tstOut x, "zwei 3. zuviel"
    call tstEnd x, 'err 3'

    call tst y, 'tstTstSayDrei'
    call tstOut y, 'drei 1. testZeile vor empty Zeile'
    call tstOut y, ''
    call tstOut y, 'drei 3. testZeile vor 10 space'
    call tstOut y, left('', 10)
    call tstOut y, 'drei 5. testZeile ziemlich lang',
                left(66, 66) left('77  5   10   15', 77, '+')
    call tstEnd y, 'err 0'
    if m.y.err <> 0 then
        call err '+++ tstTstSay errs' m.x.err 'expected' 0
    if m.tst.err <> oldErr + 3 then
        call err '+++ tstTstSay totErrs' m.tst.err 'expected' 3
    say '+++ tstTstSay end Ok with' m.tst.err 'totErrs and' ,
        m.tst.tests 'tests'
    m.tst.err = oldErr
    m.tst.errNames = oldNames
    return
endProcedure tstTstSay

tstMark: procedure expose m.
parse arg m, msg
    if symbol('m.m') == 'VAR' then
        m.m = msg';' m.m
    else
        m.m = msg 'new'
    return m
endProcedure tstMark
tstM: procedure expose m.
/*
$=/tstMa/
    ### start tst tstMa ###############################################
    mNew() 1=newM1 2=newM2
    mNew(tst...) 2=2 new 3=4; 3; 1 new 4=5 new
    iter 4; 3; 1 new
    iter 2 new
    iter 5 new
$/tstMa/
*/
    call tst t, 'tstMa'
    m1 = mNew()
    m2 = mNew()
    m.m1 = 'newM1'
    m.m2 = 'newM2'
    call tstOut t, 'mNew() 1='m.m1 '2='m.m2
    call mNewArea 'tst'm1
    t1 = tstMark(mNew('tst'm1), '1')
    t2 = tstMark(mNew('tst'm1), '2')
    call mFree tstMark(t1, '3')
    t3 = tstMark(mNew('tst'm1), '4')
    t4 = tstMark(mNew('tst'm1), '5')
    call tstOut t, 'mNew(tst...) 2='m.t2 '3='m.t3 '4='m.t4
    i = mIterBegin('tst'm1)
    do while assNN('i', mIter(i))
        call tstOut t, 'iter' m.i
        end
    call tstEnd t
/*
$=/tstM/
    ### start tst tstM ################################################
    symbol m.b LIT
    symbol m.a LIT
    mAdd a A.2
    mAdd a A.3
    m.a: 3: 1=eins 2=zwei 3=drei 4=M.A.4
    m.c: 5: 1=c vorAddSt a 2=eins 3=zwei 4=drei 5=c nacAddSt a 6=M.C.6
$/tstM/ */


    drop m.b m.a m.a.0 m.a.1 m.a.2
    call tst t, 'tstM'
    call tstOut t, 'symbol m.b' symbol('m.b')
    m.b = 1
    call tstOut t, 'symbol m.a' symbol('m.a')
    call tstOut t, 'mAdd a' mAdd(mCut(a, 0), 'eins', 'zwei')
    call tstOut t, 'mAdd a' mAdd(a, 'drei')
    call tstOut t, 'm.a:' m.a.0': 1='m.a.1 '2='m.a.2 '3='m.a.3 '4='m.a.4
    call mAdd mCut(c, 0), 'c vorAddSt a'
    call mAddSt c, a
    call mAdd c, 'c nacAddSt a'
    call tstOut t, 'm.c:' m.c.0': 1='m.c.1 '2='m.c.2 '3='m.c.3,
                    '4='m.c.4 '5='m.c.5 '6='m.c.6
    call tstEnd t
    return
endProcedure tstM

tstMCat: procedure expose m.
/*
$=/tstMCat/
    ### start tst tstMCat #############################################
    mCat(0, )                     =;
    mCat(0, 1)                    =;
    mCat(0, 112222)               =;
    mCat(0, 3%#a1%c2)             =;
    mCat(0, 4%#a1%c2@%c333)       =;
    mCat(0, 5%#a1%c2@%c3@%c4)     =;
    mCat(1, )                     =eins;
    mCat(1, 1)                    =eins;
    mCat(1, 112222)               =eins;
    mCat(1, 3%#a1%c2)             =1eins2;
    mCat(1, 4%#a1%c2@%c333)       =1eins2eins333;
    mCat(1, 5%#a1%c2@%c3@%c4)     =1eins2eins3eins4;
    mCat(2, )                     =einszwei;
    mCat(2, 1)                    =eins1zwei;
    mCat(2, 112222)               =eins112222zwei;
    mCat(2, 3%#a1%c2)             =1eins231zwei2;
    mCat(2, 4%#a1%c2@%c333)       =1eins2eins33341zwei2zwei333;
    mCat(2, 5%#a1%c2@%c3@%c4)     =1eins2eins3eins451zwei2zwei3zwei4;
    mCat(3, )                     =einszweidrei;
    mCat(3, 1)                    =eins1zwei1drei;
    mCat(3, 112222)               =eins112222zwei112222drei;
    mCat(3, 3%#a1%c2)             =1eins231zwei231drei2;
    mCat(3, 4%#a1%c2@%c333)       =1eins2eins33341zwei2zwei33341drei2dr+
    ei333;
    mCat(3, 5%#a1%c2@%c3@%c4)     =1eins2eins3eins451zwei2zwei3zwei451d+
    rei2drei3drei4;
$/tstMCat/ */
    call pipeIni
    call scanIni
    call tst t, "tstMCat"
     m.qq.1 = "eins"
     m.qq.2 = "zwei"
     m.qq.3 = "drei"
     do qx = 0 to 3
         m.qq.0 = qx
         call tstMCat1 qx
         call tstMCat1 qx, '1'
         call tstMCat1 qx, '112222'
         call tstMCat1 qx, '3%#a1%c2'
         call tstMCat1 qx, '4%#a1%c2@%c333'
         call tstMCat1 qx, '5%#a1%c2@%c3@%c4'
         end
     call tstEnd t
     return
endProcedure tstMCat

tstMCat1: procedure expose m.
parse arg m.qq.0, fmt
    call out left("mCat("m.qq.0"," fmt")", 30)"="mCat(qq, fmt)";"
return
endProcedure tstMCat1

tstMap: procedure expose m.
/*
$=/tstMap/
    ### start tst tstMap ##############################################
    mapNew m keys m-keys 0
    map m zwei --> 2
    map m Zwei is not defined
    map stem m-keys 4
    map m eins --> 1
    map m zwei --> 2
    map m drei --> 3
    map m vier --> 4
    *** err: duplicate in mapAdd(m, eins, 1)
    map m zwei is not defined
    q 2 zw dr
    map stem Q 2
    map Q zw --> 2Q
    map Q dr --> 3Q
    map stem m 3
    map m eins --> 1
    map m zwei --> 2PUT
    map m vier --> 4PUT
    *** err: duplicate in mapAdd(m, zwei, 2ADDDUP)
    tstMapLong eins keys 3
    tstMapLong zweiMal keys 48
    tstMapLong dreiMal keys 93
    tstMapLong vier    keys 138
    tstMapLong <fuenf> keys 188
    tstMap clear keys 0
    inline1 3
    inline1 1 ==    inline1 eins==
    inline1 2 ====
    inline1 3 ==    inline1 drei==
    inline2 1 1 ==    inline2 eins==
    inline3 ?
$/tstMap/ */
/*
$=/tstMapInline1/
    inline1 eins

    inline1 drei
$/tstMapInline1/ */
/*
$=/tstMapInline2/
    inline2 eins
$/tstMapInline2/ */

    call tst t, 'tstMap'
    m = mapNew('K')
    ky = mapKeys(m)
    call mAdd t'.TRANS', m 'm', ky 'm-keys'
    call tstOut t, 'mapNew' m 'keys' ky m.ky.0
    call mapAdd m, 'eins', 1
    call mapAdd m, 'zwei', 2
    call mapAdd m, 'drei', 3
    call mapAdd m, 'vier', 4
    call tstMapShow m, 'zwei'
    call tstMapShow m, 'Zwei'
    call tstMapShowSt m, mapKeys(m)
    call mapAdd m, 'eins', 1
    call mapReset m, '='
    call tstMapShow m, 'zwei'
    call mapAdd m, 'eins', 1
    call mapAdd m, 'zwei', 2
    call mapPut m, 'zwei', 2Put
    call mapPut m, 'vier', 4Put
    call mapReset q, '='
    call mapAdd q, 'zw', 2q
    call mapAdd q, 'dr', 3q
    call tstOut t, 'q' m.q.0 m.q.1 m.q.2
    call tstMapShowSt q, mapKeys(q)
    call tstMapShowSt m, mapKeys(m)
    call mapAdd m, 'zwei', 2addDup
    call tstMapLong m, 'eins'      ,201, 2000, -2, 2
    call tstMapLong m, 'zweiMal'   ,201, 2000, -2, 2
    call tstMapLong m, 'dreiMal'   ,201, 2000,  2,-2
    call tstMapLong m, 'vier   '   ,2010, 201, -2, 2
    call tstMapLong m, '<fuenf>'   ,2010, 201,  2,-2
    call mapClear m
    keys = mapKeys(m)
    call tstOut t, 'tstMap clear keys' m.keys.0
    i = mapInline('tstMapInline1')
    call tstOut t, 'inline1' m.i.0
    do x=1 to m.i.0
        call tstOut t, 'inline1' x '=='m.i.x'=='
        end
    i = mapInline('tstMapInline2')
    call tstOut t, 'inline2' m.i.0 '1 =='m.i.1'=='
    call tstOut t, 'inline3' mapInline('tstMapInline3', 'r')'?'
    call tstEnd t
    return
endProcedure tstMap

tstMapLong: procedure expose m.
parse arg m, w, f1, t1, f2, t2
    if f1 < t1 then
        b1 = 201
    else
        b1 = -201
    if f2 < t2 then
        b2 = 1
    else
        b2 = -1
    lo = copies(w, 2100 % length(w))
    keys = mapKeys(m)
    keyCn = m.keys.0
    call tstOut t, 'tstMapLong' w 'keys' keyCn
    do x = f1 by b1 to t1
        do y = x+f2 by b2 to x+t2
            k = left(lo, y)
            if mapHasKey(m, k) then
                call err 'mapLong hasKey before' w y
            call mapAdd m, k, w y
            if \ mapHasKey(m, k) then
                call err 'mapLong \ hasKey after' w y
            if mapGet(m, k) \== w y then
                call err 'mapLong \ get <> ' w y
            keys = mapKeys(m)
            if keyCn + 1 \= m.keys.0 then
                call err 'mapLong keys .0 <> ' w y
            keyCn = m.keys.0
            if k \== m.keys.keyCn then
                call err 'mapLong keys . ' keyCn '<>' w y
            end
        end
    return
endProcedure tstMapLong

tstMapVia: procedure expose m.
/*
$=/tstMapVia/
    ### start tst tstMapVia ###########################################
    map M K --> A
    mapVia(m, K)      A
    *** err: missing m.A at 3 in mapVia(M, K|)
    mapVia(m, K|)     M.A
    mapVia(m, K|)     valAt m.a
    mapVia(m, K|)     valAt m.a
    *** err: missing m.A.aB at 5 in mapVia(M, K|aB)
    mapVia(m, K|aB)   M.A.aB
    mapVia(m, K|aB)   valAt m.A.aB
    *** err: missing m.valAt m.a at 4 in mapVia(M, K||)
    mapVia(m, K||)    M.valAt m.a
    mapVia(m, K||)    valAt m.valAt m.a
    mapVia(m, K||F)   valAt m.valAt m.a.F
$/tstMapVia/ */
    call tst t, 'tstMapVia'
    u = 'A.aB'
    v = 'valAt m.a'
    drop m.a m.u m.v m.v.f
    call mapReset m, 'K'
    call mapAdd m, k, a
    call tstMapShow m, k
    call tstOut t, 'mapVia(m, K)     ' mapVia(m, 'K')
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    m.a = v
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    call tstOut t, 'mapVia(m, K|aB)  ' mapVia(m, 'K|aB')
    m.u = 'valAt m.'u
    call tstOut t, 'mapVia(m, K|aB)  ' mapVia(m, 'K|aB')
    call tstOut t, 'mapVia(m, K||)   ' mapVia(m, 'K||')
    v = m.a
    m.v = 'valAt m.'v
    m.v.f = 'valAt m.'v'.F'
    call tstOut t, 'mapVia(m, K||)   ' mapVia(m, 'K||')
    call tstOut t, 'mapVia(m, K||F)  ' mapVia(m, 'K||F')
    call tstEnd t
    return
endProcedure tstMapVia

tstMapShow: procedure expose m.
parse arg a, key
    if mapHasKey(a, key) then
        call tstOut t, 'map' a key '-->' mapGet(a, key)
    else
        call tstOut t, 'map' a key 'is not defined'
    return
endProcedure tstMapShow

tstMapShowSt: procedure expose m.
parse arg a, st
    call tstOut t, 'map stem' st m.st.0
    do wx=1 to m.st.0
        call tstMapShow a, m.st.wx
        end
    return
endProcedure tstMapShow

tstClass2: procedure expose m.
/*
$=/tstClass2/
    ### start tst tstClass2 ###########################################
    @CLASS.10 :class = u
    . choice u union
    .  .NAME = class
    . stem 8
    .  .1 refTo @CLASS.4 :class = u
    .   choice u union
    .    .NAME = v
    .   stem 3
    .    .1 refTo @CLASS.1 :class = m
    .     choice m union
    .      .NAME = oCopy
    .      .MET = return oCopyV(m, t)
    .     stem 0
    .    .2 refTo @CLASS.2 :class = m
    .     choice m union
    .      .NAME = o2String
    .      .MET = return m.m
    .     stem 0
    .    .3 refTo @CLASS.3 :class = m
    .     choice m union
    .      .NAME = o2File
    .      .MET = return file(m.m)
    .     stem 0
    .  .2 refTo @CLASS.13 :class = c
    .   choice c union
    .    .NAME = u
    .   stem 1
    .    .1 refTo @CLASS.12 :class = u
    .     choice u union
    .      .NAME = .
    .     stem 1
    .      .1 refTo @CLASS.11 :class = f
    .       choice f union
    .        .NAME = NAME
    .       stem 1
    .        .1 refTo @CLASS.4 done :class @CLASS.4
    .  .3 refTo @CLASS.14 :class = c
    .   choice c union
    .    .NAME = f
    .   stem 1
    .    .1 refTo @CLASS.12 done :class @CLASS.12
    .  .4 refTo @CLASS.16 :class = c
    .   choice c union
    .    .NAME = s
    .   stem 1
    .    .1 refTo @CLASS.15 :class = u
    .     choice u union
    .      .NAME = .
    .     stem 0
    .  .5 refTo @CLASS.17 :class = c
    .   choice c union
    .    .NAME = c
    .   stem 1
    .    .1 refTo @CLASS.12 done :class @CLASS.12
    .  .6 refTo @CLASS.18 :class = c
    .   choice c union
    .    .NAME = r
    .   stem 1
    .    .1 refTo @CLASS.15 done :class @CLASS.15
    .  .7 refTo @CLASS.21 :class = c
    .   choice c union
    .    .NAME = m
    .   stem 1
    .    .1 refTo @CLASS.20 :class = u
    .     choice u union
    .      .NAME = .
    .     stem 2
    .      .1 refTo @CLASS.11 done :class @CLASS.11
    .      .2 refTo @CLASS.19 :class = f
    .       choice f union
    .        .NAME = MET
    .       stem 1
    .        .1 refTo @CLASS.4 done :class @CLASS.4
    .  .8 refTo @CLASS.23 :class = s
    .   choice s union
    .   stem 1
    .    .1 refTo @CLASS.22 :class = r
    .     choice r union
    .     stem 1
    .      .1 refTo @CLASS.10 done :class @CLASS.10
$/tstClass2/

$=/tstClass2oo/
    ### start tst tstClass2 ###########################################
    @CLASS.4 isA :class = u
    . choice u union
    .  .NAME = class
    .  stem 7
    .   .1 refTo @CLASS.1 :class = u
    .    choice u union
    .     .NAME = v
    .     stem 2
    .      .1 refTo @CLASS.20 :class = m
    .       choice m union
    .        .NAME = o2String
    .        .MET = return m.m
    .      .2 refTo @CLASS.108 :class = m
    .       choice m union
    .        .NAME = o2File
    .        .MET = return file(m.m)
    .   .2 refTo @CLASS.5 :class = c
    .    choice c union
    .     .NAME = u
    .     .CLASS refTo @CLASS.6 :class = u
    .      choice u union
    .       .NAME = .
    .       stem 2
    .        .1 refTo @CLASS.7 :class = f
    .         choice f union
    .          .NAME = NAME
    .          .CLASS refTo @CLASS.1 done :class @CLASS.1
    .        .2 refTo @CLASS.8 :class = s
    .         choice s .CLASS refTo @CLASS.9 :class = r
    .          choice r .CLASS refTo @CLASS.4 done :class @CLASS.4
    .   .3 refTo @CLASS.10 :class = c
    .    choice c union
    .     .NAME = f
    .     .CLASS refTo @CLASS.11 :class = u
    .      choice u union
    .       .NAME = .
    .       stem 2
    .        .1 refTo @CLASS.7 done :class @CLASS.7
    .        .2 refTo @CLASS.12 :class = f
    .         choice f union
    .          .NAME = CLASS
    .          .CLASS refTo @CLASS.9 done :class @CLASS.9
    .   .4 refTo @CLASS.13 :class = c
    .    choice c union
    .     .NAME = s
    .     .CLASS refTo @CLASS.12 done :class @CLASS.12
    .   .5 refTo @CLASS.14 :class = c
    .    choice c union
    .     .NAME = c
    .     .CLASS refTo @CLASS.11 done :class @CLASS.11
    .   .6 refTo @CLASS.15 :class = c
    .    choice c union
    .     .NAME = m
    .     .CLASS refTo @CLASS.16 :class = u
    .      choice u union
    .       .NAME = .
    .       stem 2
    .        .1 refTo @CLASS.7 done :class @CLASS.7
    .        .2 refTo @CLASS.17 :class = f
    .         choice f union
    .          .NAME = MET
    .          .CLASS refTo @CLASS.1 done :class @CLASS.1
    .   .7 refTo @CLASS.18 :class = c
    .    choice c union
    .     .NAME = r
    .     .CLASS refTo @CLASS.12 done :class @CLASS.12
$/tstClass2oo/ */

    call classIni
    call tst t, 'tstClass2'
    call classOut m.class_C, m.class_C
    call tstEnd t
    return
endProcedure tstClass2

tstClass3: procedure expose m.
/*
$=/tstClass3/
    ### start tst tstClass3 ###########################################
    met v#o2String return m.m
    met w#o2String return substr(m, 2)
    met w#o2String return substr(m, 2)
    *** err: no method nonono in class w
    met w#nonono 0
    met class#oCopy return classCopy('<class class>', m, t)
    t1 4 flds FV, FR
    clear q1 FV= FR= FW=! FO=
    orig R1 FV=valFV FR=refFR FW=!valFW FO=obj.FO
    copy <s1> FV=valFV FR=refFR FW=!valFW FO=obj.FO
    t2 2 flds , EINS.ZWEI
    clear q2 EINS.ZWEI= val=
    orig R2 EINS.ZWEI=valR2.eins.zwei val=valR2Self
    copy <s2> EINS.ZWEI=valR2.eins.zwei val=valR2Self
    t3 0 flds M.<class tst...Tf33>.FLDS.1, M.<class tst...Tf33>.FLDS.2
    clear q3 s1.0=0
    orig R3 s1.0=1 s1.1=M.R3.S1.1 s1.1.f1=M.R3.S1.1.F1 s1.1.s2.0=2 s1.1+
    ..s2.1.f2=M.R3.S1.1.S2.1.F2 s1.1.s2.2.f2=M.R3.S1.1.S2.2.F2
    copy <s3> s1.0=1 s1.1=M.R3.S1.1 s1.1.f1=M.R3.S1.1.F1 s1.1.s2.0=2 s1+
    ..1.s2.1.f2=M.R3.S1.1.S2.1.F2 s1.1.s2.2.f2=M.R3.S1.1.S2.2.F2
$/tstClass3/ */

    call oIni
    call tst t, 'tstClass3'
    call mAdd t.trans, m.class_C '<class class>'
    call tstOut t, 'met v#o2String' classMet(m.class_V, 'o2String')
    call tstOut t, 'met w#o2String' classMet(m.class_W, 'o2String')
    call tstOut t, 'met w#o2String' classMet(m.class_W, 'o2String')
    call tstOut t, 'met w#nonono' classMet(m.class_W, 'nonono')
    call tstOut t, 'met class#oCopy' classMet(m.class_C, 'oCopy')
    all =  classNew('n? tstClassTf31 u f FV v, f FR r, f FW w, f FO o'),
           classNew('n? tstClassTf32 u f EINS f ZWEI v, v') ,
           classNew('n? tstClassTf33 u f S1' classNew('s u v, f F1 v,',
                           'f S2 s f F2 v'))
    call mAdd t.trans, word(all, 3) '<class tst...Tf33>'
    m.r1.fv = 'valFV'
    m.r1.fr = 'refFR'
    m.r1.fw = '!valFW'
    m.r1.fo = 'obj.FO'
    m.r2    = 'valR2Self'
    m.r2.eins.zwei  = 'valR2.eins.zwei'
    m.r3.s1.0 = 1
    m.r3.s1.1.s2.0 = 2
    o.1 = "q 'FV='m.q.FV 'FR='m.q.fr 'FW='m.q.fw 'FO='m.q.fo"
    o.2 = "q 'EINS.ZWEI='m.q.EINS.zwei 'val='m.q"
    o.3 = "q 's1.0='m.q.s1.0"
    p.1 = o.1
    p.2 = o.2
    p.3 = "q 's1.0='m.q.s1.0 's1.1='m.q.s1.1 's1.1.f1='m.q.s1.1.f1" ,
            "'s1.1.s2.0='m.q.s1.1.s2.0 's1.1.s2.1.f2='m.q.s1.1.s2.1.f2",
                                      "'s1.1.s2.2.f2='m.q.s1.1.s2.2.f2"
    do tx=1 to words(all)
        t1 = word(all, tx)
        u1 = classFlds(t1)
        q = 'q'tx
        call tstOut t, 't'tx m.u1.0 'flds' m.u1.1',' m.u1.2
        call utInter("m='"q"';" classMet(t1, 'oClear'))
        interpret "call tstOut t, 'clear'" o.tx
        q = 'R'tx
        interpret "call tstOut t, 'orig'" p.tx
        q = utInter("m='"q"';t='';" classMet(t1, 'oCopy'))
        call mAdd t.trans, q '<s'tx'>'
        interpret "call tstOut t, 'copy'" p.tx
        end
    call tstEnd t
    return
endProcedure tstClass3

tstClass: procedure expose m.
/*
$=/tstClass/
    ### start tst tstClass ############################################
    Q u =className= tstClassTf12
    Q.eins.zwei v ==> M.Q.eins.zwei
    *** err: bad type v: classNew(v tstClassTf12)
    *** err: missing key in mapGet(CLASS_N2C, 0)
    R u =className= uststClassTf12
    R u =className= uststClassTf12in
    R u =className= tstClassTf12
    R.eins.zwei v ==> M.R.eins.zwei
    R s =stem.0= 2
    R.1 r ==> M.R.1 :CLASS.8
    R.1 u =className= tstClassTf12
    R.1.eins.zwei v ==> M.R.1.eins.zwei
    R.2 r ==> M.R.2 :CLASS.8
    R.2 u =className= tstClassTf12
    R.2.eins.zwei v ==> M.R.2.eins.zwei
    S u =className= TstClass7
    S s =stem.0= 2
    S.1 u =className= TstClass7s
    S.1.eins v ==> M.S.1.eins
    S.1 m =met-metA--> say "metA"
    S.1 m =met-metB--> say "metB"
    S.2 u =className= TstClass7s
    S.2.zwei v ==> M.S.2.zwei
    S.2 m =met-metA--> say "metA"
    S.2 m =met-metB--> say "metB"
    class of mutate qq tstClassTf12
$/tstClass/ */

    f = 'eins.zwei'
    e = 'eins'
    z = 'zwei'
    drop m.q.f m.r.f m.r.0 m.r.1 m.r.1.f m.r.2 m.r.2.f
    drop m.s.0 m.s.1 m.s.1.e m.s.2 m.s.2.z
    call classIni
    call tst t, 'tstClass'
    t1  =  classNew('n? tstClassTf12 u f eins f zwei v')
    call tstClassOut t, t1, q
    z = m.class.0
    if class4name('tstClassB', '') == '' then do
        t2 = classNew('n tstClassB u n tstClassC u tstClassTf12,',
            's u v tstClassTf12')
        end
    else do /*  the second time we would get a duplicate error */
        call tstOut t, '*** err: bad type v: classNew(v tstClassTf12)'
        call tstOut t, '*** err: missing key in mapGet(CLASS_N2C, 0)'
        end
    t2 = classNew('n? uststClassTf12 u' ,
           'n? uststClassTf12in u tstClassTf12',
        , classNew('s u r, tstClassTf12'))
    m.r.0 = 2
    call tstClassOut t, t2, r
    t3 = classNew('n? TstClass7 u s',
         classNew('n? TstClass7s u c 1 f eins v, c 2 f zwei v',
            ,'m', 'metA say "metA"', 'metB say "metB"'))
    m.s.0 = 2
    m.s.1 = 1
    m.s.2 = 2
    call tstClassOut t, t3, s
    call oMutatName qq, 'tstClassTf12'
    tt = objClass(qq)
    call tstOut t, 'class of mutate qq' className(tt)
    call tstEnd t
    return
endProcedure tstClass

tstClassOut: procedure expose m.
parse arg o, t, a
    if wordPos(t, m.class_V m.class_W m.class_O) > 0 then
        return tstOut(o, a m.t.name '==>' m.a)
    if m.t == 'r' then
        return tstOut(o, a m.t '==>' m.a ':'if(m.t.0==0,'',m.t.1))
    if m.t == 'u' & m.t.name \== '' then
        call tstOut o, a m.t '=className=' m.t.name
    if m.t == 'f' then
        return tstClassOut(o, m.t.1, a'.'m.t.name)
    if m.t = 'u' then do
        do ux=1 to m.t.0
            call tstClassOut o, m.t.ux, a
            end
        return 0
        end
    if m.t = 's' then do
        call tstOut o, a m.t '=stem.0=' m.a.0
        do ux=1 to m.a.0
            call tstClassOut o, m.t.1, a'.'ux
            end
        return 0
        end
    if m.t = 'c' then do
        if m.t.name = m.a then
            call tstClassOut o, m.t.1, a
         return 0
        end
    if m.t = 'm' then
        return tstOut(o, a m.t '=met-'m.t.name'-->' m.t.met)
    call err 'bad class type' m.t
endProcedure tstClassOut

tstClass4: procedure expose m.
parse arg
/*
$=/tstClass4/
    ### start tst tstClass4 ###########################################
    f 1 eins
    f 2 zwei
    f 3 drei
    f 4 vier
    f 5 acht
    s 1 fuenf
    s 2 sechs
    s 3 sie
$/tstClass4/
*/
    call classIni
    call tst t, 'tstClass4'
    x = classNew('n* TstClass4a u f eins v, f%v zwei drei, f vier v',
                             ', f%s-v fuenf sechs sie, f acht v')
    ff = classFlds(x)
    do fx=1 to m.ff.0
        call tstOut t, 'f' fx m.ff.fx
        end
    st = classMet(x, 'stms')
    do sx=1 to m.st.0
        call tstOut t, 's' sx m.st.sx
        end
    call tstEnd t
    return
endProcedure tstClass4

tstO: procedure expose m.
/*
$=/tstO/
    ### start tst tstO ################################################
    o1.class ---
    o1.class <class T..1>
    o1#met1 metEins
    o1#met2 metZwei
    o1#new return classClear('<class T..1>', oMutate(mNew('<class T..1>+
    '), '<class T..1>'))
$/tstO/
*/
    call mIni
    call tst t, 'tstO'
    call oIni
    c1 = classNew('n? TstOCla1 u', 'm', 'met1 metEins', 'met2 metZwei')
    call mAdd t.trans, c1 '<class T..1>'
    o1 = 'tst_o1'
    call tstOut t, 'o1.class' objClass(o1, '---')
    o1 = oMutate('o1', c1)
    call tstOut t, 'o1.class' objClass(o1, '---')
    call tstOut t, 'o1#met1' objMet(o1, 'met1')
    call tstOut t, 'o1#met2' objMet(o1, 'met2')
    call tstOut t, 'o1#new' objMet(o1, 'new')
    call tstEnd t
    return
endProcedure tstO


tstOEins: procedure expose m.
/*
$=/tstOEins/
    ### start tst tstOEins ############################################
    class method calls of TstOEins
    .  met Eins.eins M
     flds of <obj e of TstOEins> FEINS, FZWEI
     methodcalls of object e of TstOEins
    .  met Eins.eins <obj e of TstOEins>
    .  met Eins.zwei <obj e2 of TstOEins>
    *** err: no method nein in class TstOEins
    *** err: no method nein in class String
    class method calls of TstOEins
    .  met Elf.zwei M
    flds of <obj f of TstOElf> FEINS, FZWEI, FELF
    methodcalls of object f of TstOElf
    .  met Eins.eins <obj f of TstOElf>
    .  met Elf.zwei <obj f of TstOElf>
    .  met Elf.drei <obj f of TstOElf>
    oCopy c1 of class TstOEins, c2
    C1 u =className= TstOEins
    C1.FEINS v ==> M.C1.FEINS
    C1.FZWEI v ==> M.C1.FZWEI
    C1 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C1 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    C2 u =className= TstOEins
    C2.FEINS v ==> M.C1.FEINS
    C2.FZWEI v ==> M.C1.FZWEI
    C2 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C2 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    oCopy c3 of class TstOElf, c4
    C4 u =className= TstOElf
    C4 u =className= TstOEins
    C4.FEINS v ==> M.C3.FEINS
    C4.FZWEI v ==> M.C3.FZWEI
    C4 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C4 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    C4.FELF r ==> M.C3.FELF :<class O>
    C4 m =met-zwei--> call tstOut t, "  met Elf.zwei" m
    C4 m =met-drei--> call tstOut t, "  met Elf.drei" m
    tEinsDop <class TstOEins>
    oRun 7*3 21
    oRun 12*12 144
$/tstOEins/ */
    call scanIni
    call tst t, 'tstOEins'
    tEins = classNew('n? TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
    call mAdd t.trans, tEins '<class TstOEins>' ,
                   , m.class_o '<class O>'
    call tstOut t, 'class method calls of TstOEins'
    interpret classMet('TstOEins', 'eins')
    e = oNew('TstOEins')
    e2 = oNew('TstOEins')
    call mAdd t.trans, e '<obj e of TstOEins>'
    call mAdd t.trans, e2 '<obj e2 of TstOEins>'
    call tstOut t, 'flds of' e mCat(oFlds(e), ', ')
    call tstOut t, 'methodcalls of object e of TstOEins'
    call tstOmet e, 'eins'
    call tstOmet e2, 'zwei'
    call tstOmet e, 'nein'
    call tstOmet 'noObj', 'nein'
    tElf = classNew('n? TstOElf u TstOEins, f FELF r', 'm',
        , 'zwei call tstOut t, "  met Elf.zwei" m',
        , 'drei call tstOut t, "  met Elf.drei" m')
    call tstOut t, 'class method calls of TstOEins'
    interpret classMet('TstOElf', 'zwei')
    f = oNew('TstOElf')
    call mAdd t.trans, f '<obj f of TstOElf>'
    call tstOut t, 'flds of' f mCat(oFlds(f), ', ')
    call tstOut t, 'methodcalls of object f of TstOElf'
    call tstOmet f, 'eins'
    call tstOmet f, 'zwei'
    call tstOmet f, 'drei'
    call oMutatName c1, 'TstOEins'
    call tstOut t, 'oCopy c1 of class TstOEins, c2'
    call tstClassOut t, tEins, c1
    call oCopy c1, c2
    call tstClassOut t, tEins, c2
    call tstOut t, 'oCopy c3 of class TstOElf, c4'
    call oMutatName c3, 'TstOElf'
    call oCopy c3, c4
    call tstClassOut t, tElf, c4

/*    tEinsDop = classNew('n TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
*/ tEinsDop = tEins
    call tstOut t, 'tEinsDop' tEinsDop
    e3 = oNew('TstOEins')
    if e3 <<= e | e3 <<= e2 then
        call err 'doppelt reinitialised objects'
    rr = oRunner('return 7 * 3')
    call tstOut t, 'oRun 7*3' oRun(rr)
    r12 = oRunner('return 12 * 12')
    call tstOut t, 'oRun 12*12' oRun(r12)
    call tstEnd t
    return
endProcedure tstOEins

tstOmet: procedure expose m.
parse arg m, met
    interpret objMet(m, met)
    return
endProcedure tstOmet

tstJSay: procedure expose m.
/*
$=/tstJSay/
    ### start tst tstJSay #############################################
    *** err: bad option openArg in jOpen(<obj j of JRW>, openArg)
    *** err: jWrite(<obj j of JRW>, writeArg) but not opened w
    *** err: can only write JSay.jOpen(<obj s of JSay>, <)
    *** err: jWrite(<obj s of JSay>, write s vor open) but not opened+
    . w
    *** err: can only read JRWEof.jOpen(<obj e of JRWEof>, >)
    *** err: jRead(<obj e of JRWEof>) but not opened r
    read e vor open 0 m.xx valueBefore
    read e nach open 0 m.xx valueBefore
    out eins
    #jIn 1# tst in line 1 eins ,
    out zwei in 1 vv=readAdrVV
    #jIn 2# tst in line 2 zwei ;   .
    out drei in 1 vv=readAdrVV Schluss
$/tstJSay/ */

    call jIni
    call tst t, 'tstJSay'
    jrw = oNew('JRW')
    call mAdd t'.TRANS', jrw '<obj j of JRW>'
    call jOpen jrw, 'openArg'
    call jWrite jrw, 'writeArg'
    s = oNew('JSay')
    call mAdd t'.TRANS', s '<obj s of JSay>'
    call jOpen s, m.j.cRead
    s = oNew('JSay')
    call mAdd t'.TRANS', s '<obj s of JSay>'
    call jWrite s, 'write s vor open'
    call jOpen s, '>'
    call jWrite s, 'write s nach open'
    e = oNew('JRWEof')
    call mAdd t'.TRANS', e '<obj e of JRWEof>'
    call jOpen e, '>'
    e = oNew('JRWEof')
    call mAdd t'.TRANS', e '<obj e of JRWEof>'
    m.xx = 'valueBefore'
    call tstOut t, 'read e vor open' jReadVar(e, xx) 'm.xx' m.xx
    call jOpen e, m.j.cRead
    call tstOut t, 'read e nach open' jReadVar(e, xx) 'm.xx' m.xx
    call out 'out eins'
    vv = 'readAdrVV'
    m.vv = 'readAdrVVValueBefore'
    call out 'out zwei in' in() 'vv='vv
    m.vv = 'readAdrVVValueBefore'
    call out 'out drei in' inVar(vv) 'vv='vv 'Schluss'
    call tstEnd t
    return
endProcedure tstJSay

tstJ: procedure expose m.
/*
$=/tstJ/
    ### start tst tstJ ################################################
    out eins
    #jIn 1# tst in line 1 eins ,
    1 in() tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    2 in() tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    3 in() tst in line 3 drei .schluss..
    #jIn eof 4#
    in() 3 reads vv VV
    line buf line one
    line buf line two
    line buf line three
    line buf line four
    *** err: jWrite(<buf b>, buf line five while reading) but not opene+
    d w
$/tstJ/ */

    call jIni
    call tst t, "tstJ"
    b = jOpen(jBuf(), '>')
    call mAdd t'.TRANS', b '<buf b>'
    call out 'out eins'
    do lx=1 by 1 while in()
        call out lx 'in()' m.in
        end
    call out 'in()' (lx-1) 'reads vv' vv
    call jOpen b, '>'
    call jWrite b, 'buf line one'
    call jClose b
    call mAdd m.b.stem, 'buf line two', 'buf line three'
    call jWrite b, 'buf line four'
    call jClose b
    call jOpen b, m.j.cRead
    do while jRead(b)
        call out 'line' m.b
        end
    call jWrite b, 'buf line five while reading'
    call jClose b
    call tstEnd t
    return
endProcedure tstJ

tstJ2: procedure expose m.
/*
$=/tstJ2/
    ### start tst tstJ2 ###############################################
    class1 <Tst?1 class> <Tst?1 name>
    class2 <Tst?1 class> <Tst?1 name>
    class3 <Tst?1 class> <Tst?1 name>
    b read EINS feld eins, ZWEI feld zwei, DREI feld drei
    b read EINS feld eins, ZWEI feld zwei 2, DREI feld drei
    c read EINS feld eins, ZWEI feld zwei, DREI drei cat 1
    tstR: @tstWriteoV3 isA :<Tst?1 name>
    tstR:  .EINS = feld eins
    tstR:  .ZWEI = feld zwei
    tstR:  .DREI = drei cat 1
    c read EINS feld eins, ZWEI feld zwei 2, DREI drei cat 2
    tstR: @tstWriteoV4 isA :<Tst?1 name>
    tstR:  .EINS = feld eins
    tstR:  .ZWEI = feld zwei 2
    tstR:  .DREI = drei cat 2
$/tstJ2/ */

    call jIni
    call tst t, "tstJ2"
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call mAdd t'.TRANS', ty '<Tst?1 class>'
    call mAdd t'.TRANS', m.ty.name '<Tst?1 name>'
    call tstOut t, 'class1' ty m.ty.name
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call tstOut t, 'class2' ty m.ty.name
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call tstOut t, 'class3' ty m.ty.name
    call oMutate qq, ty
    m.qq.eins = 'feld eins'
    m.qq.zwei = 'feld zwei'
    m.qq.drei = 'feld drei'
    b = jBuf()
    call jOpen b, '>'
    call jWrite b, oCopy(qq)
    m.qq.zwei = 'feld zwei 2'
    call jWrite b, qq
    call jOpen jClose(b), m.j.cRead
    c = jOpen(jBuf(), '>')
    do xx=1 while jRead(b)
        res = m.b
        call out 'b read EINS' m.res.eins', ZWEI' m.res.zwei,
                                     || ', DREI' m.res.drei
        m.res.drei = 'drei cat' xx
        call jWrite c, res
        end
    call jOpen jClose(c), m.j.cRead
    do while jRead(c)
        ccc = m.c
        call out 'c read EINS' m.ccc.eins', ZWEI' m.ccc.zwei,
                                     || ', DREI' m.ccc.drei
        call out ccc
        end
    call tstEnd t
    return
endProcedure tstJ2

tstCat: procedure expose m.
/*
$=/tstCat/
    ### start tst tstCat ##############################################
    catRead 1 line 1
    catRead 2 line 2
    catRead 3 line 3
    appRead 1 line 1
    appRead 2 line 2
    appRead 3 line 3
    appRead 4 append 4
    appRead 5 append 5
$/tstCat/ */
    call catIni
    call tst t, "tstCat"
    i = cat(jBuf('line 1', 'line 2'), jBuf('line 3'))
    call jOpen i, m.j.cRead
    do lx=1 by 1 while jRead(i)
        call tstOut t, 'catRead' lx m.i
        end
    call jOpen jClose(i), m.j.cApp
    call jWrite i, 'append 4'
    call jWrite i, 'append 5'
    call jOpen jClose(i), m.j.cRead
    do lx=1 by 1 while jRead(i)
        call tstOut t, 'appRead' lx m.i
        end
    call tstEnd t
    return
endProcedure tstCat

tstEnv: procedure expose m.
    call pipeIni
/*
$=/tstEnv/
    ### start tst tstEnv ##############################################
    before pipeBeLa
    after pipeEnd
    *** err: jWrite(<jBuf c>, write nach pop) but not opened w
    #jIn 1# tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    before writeNow 2 c --> std
    before writeNow 1 b --> c
    b line eins
    b zwei |
    nach writeNow 1 b --> c
    add nach pop
    after push c only
    tst in line 1 eins ,
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    nach writeNow 2 c --> std
    *** err: jWrite(<jBuf c>, ) but not opened w
$/tstEnv/ */

    call tst t, "tstEnv"
    c = jBuf()
    call mAdd t'.TRANS', c '<jBuf c>'
    call out 'before pipeBeLa'
    b = jBuf("b line eins", "b zwei |")
    call pipe '+Ff', c, b
    call out 'before writeNow 1 b --> c'
    call pipeWriteNow
    call out 'nach writeNow 1 b --> c'
    call pipe '-'
    call out 'after pipeEnd'
    call jWrite c, 'write nach pop'
    call mAdd c'.BUF', 'add nach pop'
    call pipe '+A', c
    call out 'after push c only'
    call pipeWriteNow
    call pipe '-'
    call pipe '+f', , c
    call out 'before writeNow 2 c --> std'
    call pipeWriteNow
    call out 'nach writeNow 2 c --> std'
    call pipe '-'
    call jWrite c
    call tstEnd t
    return
endProcedure tstEnv

tstEnvCat: procedure expose m.
    call pipeIni
/*
$=/tstEnvCat/
    ### start tst tstEnvCat ###########################################
    c1 contents
    c1 line eins |
    before writeNow 1 b* --> c*
    b1 line eins|
    b2 line eins
    b2 zwei |
    c2 line eins |
    after writeNow 1 b* --> c*
    c2 contents
    c2 line eins |
$/tstEnvCat/ */

    call tst t, "tstEnvCat"

    b0= jBuf()
    b0= jBuf()
    b1= jBuf("b1 line eins|")
    b2 = jBuf("b2 line eins", "b2 zwei |")
    c1 = jBuf("c1 line eins |")
    c2 = jBuf("c2 line eins |")
    call pipe '+Af', c1, b0, b1, b2, c2
    call out 'before writeNow 1 b* --> c*'
    call pipeWriteNow
    call out 'after writeNow 1 b* --> c*'
    call pipe '-'
    call out 'c1 contents'
    call pipe '+f' , , c1
    call pipeWriteNow
    call pipe '-'
    call pipe '+f' , , c2
    call out 'c2 contents'
    call pipeWriteNow
    call pipe '-'
    call tstEnd t
    return
endProcedure tstEnvCat

tstPipe: procedure expose m.
    call pipeIni
/*
$=/tstPipe/
    ### start tst tstPipe #############################################
    .+0 vor pipeBegin
    #jIn 1# tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    .+7 nach pipeLast
    ¢7 +6 nach pipe 7!
    ¢7 +2 nach pipe 7!
    ¢7 +4 nach nested pipeLast 7!
    ¢7 (4 +3 nach nested pipeBegin 4) 7!
    ¢7 (4 (3 +1 nach pipeBegin 3) 4) 7!
    ¢7 (4 (3 tst in line 1 eins , 3) 4) 7!
    ¢7 (4 (3 tst in line 2 zwei ;    3) 4) 7!
    ¢7 (4 (3 tst in line 3 drei .schluss. 3) 4) 7!
    ¢7 (4 (3 +1 nach writeNow vor pipe 3) 4) 7!
    ¢7 (4 +3 nach preSuf vor nested pipeLast 4) 7!
    ¢7 +4 nach preSuf vor nested pipeEnd 7!
    ¢7 +5 nach nested pipeEnd vor pipe 7!
    ¢7 +6 nach writeNow vor pipeLast 7!
    .+7 nach writeNow vor pipeEnd
    .+8 nach pipeEnd
$/tstPipe/ */

    say 'x0' m.pipe.0
    call tst t, 'tstPipe'
    call out '+0 vor pipeBegin'
    say 'x1' m.pipe.0
    call pipe '+N'
    call out '+1 nach pipeBegin'
    call pipeWriteNow
    call out '+1 nach writeNow vor pipe'
    call pipe 'N|'
    call out '+2 nach pipe'
    call pipe '+N'
    call out '+3 nach nested pipeBegin'
    call pipePreSuf '(3 ', ' 3)'
    call out '+3 nach preSuf vor nested pipeLast'
    call pipe 'P|'
    call out '+4 nach nested pipeLast'
    call pipePreSuf '(4 ', ' 4)'
    call out '+4 nach preSuf vor nested pipeEnd'
    call pipe '-'
    call out '+5 nach nested pipeEnd vor pipe'
    call pipe 'N|'
    call out '+6 nach pipe'
    call pipeWriteNow
    say 'out +6 nach writeNow vor pipeLast'
    call out '+6 nach writeNow vor pipeLast'
    call pipe 'P|'
    call out '+7 nach pipeLast'
    call pipePreSuf '¢7 ', ' 7!'
    call out '+7 nach writeNow vor pipeEnd'
    call pipe '-'
    call out '+8 nach pipeEnd'
    say 'xx' m.pipe.0
    call tstEnd t
    return
endProcedure tstPipe

tstPipeS: procedure expose m.
/*
$=/tstPipeS/
    ### start tst tstPipeS ############################################
    eine einzige zeile
    nach all einzige Zeile
    select strip(creator) cr, strip(name) tb,
    (row_number()over())*(row_number()over()) rr
    from sysibm.sysTables
$/tstPipeS/
*/
    call tst t, "tstPipeS"
    call pipe '+s',, 'eine einzige zeile'
    call pipeWriteAll
    call out 'nach all einzige Zeile'
    call pipe 's',,
              , "select strip(creator) cr, strip(name) tb," ,
              ,      "(row_number()over())*(row_number()over()) rr" ,
              ,      "from sysibm.sysTables"
    call pipeWriteAll
    call pipe '-'
    call tstEnd t
    return
endProcedure tstPipeS

tstEnvVars: procedure expose m.
    call pipeIni
/*
$=/tstEnvVars/
    ### start tst tstEnvVars ##########################################
    put v1 value eins
    v1 hasKey 1 get TST.ADR1
    v2 hasKey 0
    one to theBur
    two to theBuf
    v1=TST.ADR1 o=TST.ADR1
    v3=v3WieGehts? o=v3WieGehts?
    v4=!v4WieGehts? o=!v4WieGehts?
    o o0=<o0>
    s o0=<o0>
    o o0=<o0>
    s o0=<o0>
    o0&fSt0=rexx o0.fSt0 o=rexx o0.fSt0
    o0&fRe0=!rexx o0.fRe0 o=!rexx o0.fRe0
    o0&=rexx o0-value o=rexx o0-value
    o o0=<o0>
    s o0=<o0>
    o0&fSt0=put o0.fSt0 o=put o0.fSt0
    o0&fRe0=!putO o0.fRe0 o=!putO o0.fRe0
    o0&=put o0-value o=put o0-value
$/tstEnvVars/
$=/tstEnvVars1/
    ### start tst tstEnvVars1 #########################################
    m.o1=put-o1-value m.o1.fStr=put-o1.fStr m.o1.fRef=<o0>
    o o1=<o1> s o1=<o1>
    o1&fStr=put-o1.fStr o=put-o1.fStr
    o1&=put-o1-value o=put-o1-value
    o1&fRef=<o0> o=<o0>
    o1&fRef>fSt0=put o0.fSt0 o=put o0.fSt0
    o1&fRef>fRe0=!putO o0.fRe0 o=!putO o0.fRe0
    m.o1&fNest.fSt0= put-o1.fNest.fSt0 m.o1&fNest.fRe0= !put-o1&fNest.f+
    Re0
    o1&fNest.fSt0=put-o1.fNest.fSt0 o=put-o1.fNest.fSt0
    o1&fNest&fRe0=!put-o1&fNest.fRe0 o=!put-o1&fNest.fRe0
$/tstEnvVars1/
$=/tstEnvVars2/
    ### start tst tstEnvVars2 #########################################
    o2=<o2> getO(o2)=<o2> getO(o2&fRef)=<o1>
    o2&fRef>fStr=put-o1.fStr o=put-o1.fStr
    o2&fRef>=put-o1-value o=put-o1-value
    o2&fRef>fRef=<o0> o=<o0>
    o2&fRef>fRef>fSt0=put o0.fSt0 o=put o0.fSt0
    o2&fRef>fRef>fRe0=!putO o0.fRe0 o=!putO o0.fRe0
    o2&fRef>fNest.fSt0=put-o1.fNest.fSt0 o=put-o1.fNest.fSt0
    o2&fRef>fNest&fRe0=!put-o1&fNest.fRe0 o=!put-o1&fNest.fRe0
$/tstEnvVars2/
$=/tstEnvVarsS/
    ### start tst tstEnvVarsS #########################################
    oS=<oS> oS&fStS=<put oS.fStS>
    oS&fStV.0=1 oS&fStV.1=<put oS.fStV.1>
    m.oS.fStR.0=2 .2=!<put oS.fStR.2>
    oS&fStR.0=2 .1=!<put oS.fStR.1> .2=!<put oS.fStR.2>
    m.oS.0=9876 .1234=<put oS.1234>
    *** err: undefined var oS&12
    oS&0=9876 .12=M. .1234=<put oS.1234>
$/tstEnvVarsS/
$=/tstEnvVars3/
    ### start tst tstEnvVars3 #########################################
    m.<o0>=*o0*val vGet(<o0>>)=*o0*val
    m.<o0>.fSt0=*o0.fSt0*val vGet(<o0>>fSt0)=*o0.fSt0*val
    m.<o0>.fRe0=<o1> vGet(<o0>>fRe0)=<o1>
    m.<o1>=*o1*val vGet(<o0>>fRe0>)=*o1*val
    m.<o1>.fStr=*o1.fStr*val vGet(<o0>>fRe0>fStr)=*o1.fStr*val
    m.V.tstEnvVar0=<o0> vGet(tstEnvVar0)=<o0>
    m.V.tstEnvVar0=<o0> vGet(tstEnvVar0&)=<o0>
    m.<o0>=*o0*val vGet(tstEnvVar0&>)=*o0*val
    m.<o0>.fSt0=*o0.fSt0*val vGet(tstEnvVar0&fSt0)=*o0.fSt0*val
    m.<o0>.fRe0=<o1> vGet(tstEnvVar0&fRe0)=<o1>
    m.<o1>=*o1*val vGet(tstEnvVar0&fRe0>)=*o1*val
    m.<o1>.fStr=*o1.fStr*val vGet(tstEnvVar0&fRe0>fStr)=*o1.fStr*val
    m.<o1>.fVar=tstEnvVar2 vGet(tstEnvVar0&fRe0>fVar)=tstEnvVar2
    m.<o2>=*o2*val vGet(tstEnvVar0&fRe0>fVar&)=<o2>
    m.<o2>=*o2*val vGet(tstEnvVar0&fRe0>fVar&>)=*o2*val
    m.<o2>.fStr=*o2.fStr*val vGet(tstEnvVar0&fRe0>fVar&fStr)=*o2.fStr*v+
    al
    m.<o0>=*o0*put2 vGet(<o0>>)=*o0*put2
    m.<o0>.fSt0=*o0.fSt0*put2 vGet(<o0>>fSt0)=*o0.fSt0*put2
    m.<o1>=*o0>fRe0>put2 vGet(<o0>>fRe0>)=*o0>fRe0>put2
    m.<o1>.fStr=*o0>fRe0>fStr*put2 vGet(<o0>>fRe0>fStr)=*o0>fRe0>fStr*p+
    ut2
    m.<o0>=*v0&>*put3 vGet(tstEnvVar0&>)=*v0&>*put3
    m.<o0>.fSt0=*v0&fSt0*put3 vGet(tstEnvVar0&fSt0)=*v0&fSt0*put3
    m.<o1>=*v0&fRe0>*put3 vGet(tstEnvVar0&fRe0>)=*v0&fRe0>*put3
    m.<o1>.fStr=*v0&fRe0>fStr*put3 vGet(tstEnvVar0&fRe0>fStr)=*v0&fRe0>+
    fStr*put3
    m.<o2>=*v0&fRe0>fVar&>*put3 vGet(tstEnvVar0&fRe0>fVar&>)=*v0&fRe0>f+
    Var&>*put3
    m.<o2>.fStr=*v0&fRe0>fVar&fStr*put3 vGet(tstEnvVar0&fRe0>fVar&fStr)+
    =*v0&fRe0>fVar&fStr*put3
$/tstEnvVars3/
 */
    c0 = classNew('n? TstEnvVars0 u f fSt0 v, f = v, f fRe0 r')
    c1 = classNew('n? TstEnvVars1 u f fStr v,f fRef r' ,
              ', f fNest TstEnvVars0, f = v, f fVar v')
    o0 = oNew(c0)
    o1 = oNew(c1)
    o2 = oNew(c1)
    call tst t, "tstEnvVars3"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'
    fSt0 = 'fSt0'
    fRe0 = 'fRe0'
    fStr = 'fStr'
    fRef = 'fRef'
    fVar = 'fVar'
    v0 = 'tstEnvVar0'
    v2 = 'tstEnvVar2'
    m.o0 = '*o0*val'
    m.o0.fSt0 = '*o0.fSt0*val'
    m.o0.fRe0 = o1
    m.o1 = '*o1*val'
    m.o1.fStr = '*o1.fStr*val'
    m.o1.fRef = o2
    m.o1.fVar = v2
    m.o2 = '*o2*val'
    m.o2.fStr = '*o2.fStr*val'
    m.v.v0 = o0
    m.v.v2 = o2
    call tstEnvVarsMG o0, o0'>'
    call tstEnvVarsMG o0'.'fSt0, o0'>'fSt0
    call tstEnvVarsMG o0'.'fRe0, o0'>'fRe0
    call tstEnvVarsMG o1, o0'>'fRe0'>'
    call tstEnvVarsMG o1'.'fStr, o0'>'fRe0'>'fStr
    call tstEnvVarsMG v'.'v0, v0
    call tstEnvVarsMG v'.'v0, v0'&'
    call tstEnvVarsMG o0, v0'&>'
    call tstEnvVarsMG o0'.'fSt0, v0'&'fSt0
    call tstEnvVarsMG o0'.'fRe0, v0'&'fRe0
    call tstEnvVarsMG o1, v0'&'fRe0'>'
    call tstEnvVarsMG o1'.'fStr, v0'&'fRe0'>'fStr
    call tstEnvVarsMG o1'.'fVar, v0'&'fRe0'>'fVar
    call tstEnvVarsMG o2, v0'&'fRe0'>'fVar'&'
    call tstEnvVarsMG o2, v0'&'fRe0'>'fVar'&>'
    call tstEnvVarsMG o2'.'fStr, v0'&'fRe0'>'fVar'&fStr'
    call vPut o0'>', '*o0*put2'
    call tstEnvVarsMG o0, o0'>'
    call vPut o0'>'fSt0, '*o0.fSt0*put2'
    call tstEnvVarsMG o0'.'fSt0, o0'>'fSt0
    call vPut o0'>'fRe0'>', '*o0>fRe0>put2'
    call tstEnvVarsMG o1, o0'>'fRe0'>'
    call vPut o0'>'fRe0'>'fStr, '*o0>fRe0>fStr*put2'
    call tstEnvVarsMG o1'.'fStr, o0'>'fRe0'>'fStr
    call vPut v0'&>', '*v0&>*put3'
    call tstEnvVarsMG o0, v0'&>'
    call vPut v0'&'fSt0, '*v0&fSt0*put3'
    call tstEnvVarsMG o0'.'fSt0, v0'&'fSt0
    call vPut v0'&'fRe0'>', '*v0&fRe0>*put3'
    call tstEnvVarsMG o1, v0'&'fRe0'>'
    call vPut v0'&'fRe0'>'fStr, '*v0&fRe0>fStr*put3'
    call tstEnvVarsMG o1'.'fStr, v0'&'fRe0'>'fStr
    call vPut v0'&'fRe0'>'fVar'&>', '*v0&fRe0>fVar&>*put3'
    call tstEnvVarsMG o2, v0'&'fRe0'>'fVar'&>'
    call vPut v0'&'fRe0'>'fVar'&fStr', '*v0&fRe0>fVar&fStr*put3'
    call tstEnvVarsMG o2'.'fStr, v0'&'fRe0'>'fVar'&fStr'
    call tstEnd t, "tstEnvVars"
    call tst t, "tstEnvVars"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'
    call vRemove 'v2'
    m.tst.adr1 = 'value eins'
    put1 = vPut('v1', oMutate(tst'.'adr1, m.class_V))
    call tstOut t, 'put v1' m.put1
    call tstOut t, 'v1 hasKey' vHasKey('v1') 'get' vGet('v1')
    call tstOut t, 'v2 hasKey' vHasKey('v2')
    if 0 then
        call tstOut t, 'v2 get'    vGet('v2')
    call vPut 'theBuf', jBuf()
    call pipe '+F' , vGet('theBuf')
    call out 'one to theBur'
    call out 'two to theBuf'
    call pipe '-'
    call pipe '+f',, vGet('theBuf')
    call pipeWriteNow
    call pipe '-'
    call tstOut t, 'v1='vGet('v1') 'o='vGet('v1')
    call vPut 'v3', 'v3WieGehts?'
    call tstOut t, 'v3='vGet('v3') 'o='vGet('v3')
    call vPut 'v4', s2o('v4WieGehts?')
    call tstOut t, 'v4='vGet('v4') 'o='vGet('v4')

    call vPut 'o0', o0
    call tstOut t, 'o o0='vGet('o0')
    call tstOut t, 's o0='vGet('o0')
    fSt0 = 'fSt0'
    fRe0 = 'fRe0'
    m.o0 = 'rexx o0-value'
    m.o0.fSt0 = 'rexx o0.fSt0'
    m.o0.fRe0 = s2o('rexx o0.fRe0')
    call tstOut t, 'o o0='vGet('o0')
    call tstOut t, 's o0='vGet('o0')
    call tstOut t, 'o0&fSt0='vGet('o0&fSt0') 'o='vGet('o0&fSt0')
    call tstOut t, 'o0&fRe0='vGet('o0&fRe0') 'o='vGet('o0&fRe0')
    call tstOut t, 'o0&='vGet('o0&>') 'o='vGet('o0&>')
    call vPut 'o0&>', 'put o0-value'
    call vPut 'o0&fSt0', 'put o0.fSt0'
    call vPut 'o0&fRe0', s2o('putO o0.fRe0')
    call tstOut t, 'o o0='vGet('o0')
    call tstOut t, 's o0='vGet('o0')
    call tstOut t, 'o0&fSt0='vGet('o0&fSt0') 'o='vGet('o0&fSt0')
    call tstOut t, 'o0&fRe0='vGet('o0&fRe0') 'o='vGet('o0&fRe0')
    call tstOut t, 'o0&='vGet('o0&>') 'o='vGet('o0&>')

    call tstEnd t
    call tst t, "tstEnvVars1"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'

    call vPut 'o1', o1
    call vPut 'o1&>', 'put-o1-value'
    call vPut 'o1&fStr', 'put-o1.fStr'
    call vPut 'o1&fRef', vGet('o0')
    call tstOut t, 'm.o1='m.o1 'm.o1.fStr='mGet(o1'.fStr'),
         'm.o1.fRef='mGet(o1'.fRef')
    call tstOut t, 'o o1='vGet('o1') 's o1='vGet('o1')
    call tstOut t, 'o1&fStr='vGet('o1&fStr') 'o='vGet('o1&fStr')
    call tstOut t, 'o1&='vGet('o1&>') 'o='vGet('o1&>')
    call tstOut t, 'o1&fRef='vGet('o1&fRef') 'o='vGet('o1&fRef')
    call tstOut t, 'o1&fRef>fSt0='vGet('o1&fRef>fSt0') ,
        'o='vGet('o1&fRef>fSt0')
    call tstOut t, 'o1&fRef>fRe0='vGet('o1&fRef>fRe0'),
         'o='vGet('o1&fRef>fRe0')

    call vPut 'o1&fNest.fSt0', 'put-o1.fNest.fSt0'
    call vPut 'o1&fNest.fRe0', s2o('put-o1&fNest.fRe0')
    call tstOut t, 'm.o1&fNest.fSt0=' mGet(o1'.fNest.fSt0') ,
            'm.o1&fNest.fRe0=' mGet(o1'.fNest.fRe0')
    call tstOut t, 'o1&fNest.fSt0='vGet('o1&fNest.fSt0'),
         'o='vGet('o1&fNest.fSt0')
    call tstOut t, 'o1&fNest&fRe0='vGet('o1&fNest.fRe0'),
         'o='vGet('o1&fNest.fRe0')
    call tstEnd t

    call tst t, "tstEnvVars2"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'
    call vPut 'o2', o2
    call vPut 'o2&fRef', vGet('o1')
    call tstOut t, 'o2='o2 'getO(o2)='vGet('o2'),
        'getO(o2&fRef)='vGet('o2&fRef')

    call tstOut t, 'o2&fRef>fStr='vGet('o2&fRef>fStr'),
         'o='vGet('o2&fRef>fStr')
    call tstOut t, 'o2&fRef>='vGet('o2&fRef>'),
     'o='vGet('o2&fRef>')

    call tstOut t, 'o2&fRef>fRef='vGet('o2&fRef>fRef') ,
        'o='vGet('o2&fRef>fRef')
    call tstOut t, 'o2&fRef>fRef>fSt0='vGet('o2&fRef>fRef>fSt0') ,
        'o='vGet('o2&fRef>fRef>fSt0')
    call tstOut t, 'o2&fRef>fRef>fRe0='vGet('o2&fRef>fRef>fRe0'),
         'o='vGet('o2&fRef>fRef>fRe0')
    call tstOut t, 'o2&fRef>fNest.fSt0='vGet('o2&fRef>fNest.fSt0'),
         'o='vGet('o2&fRef>fNest.fSt0')
    call tstOut t, 'o2&fRef>fNest&fRe0='vGet('o2&fRef>fNest.fRe0'),
         'o='vGet('o1&fNest.fRe0')
    call tstEnd t

    cS = classNew('n? TstEnvVarsS u f fStS v,f fStV s v, f fStR s r',
        ', f fNeS s TstEnvVars0, f = s v')
    oS = oNew(cS)
    call vPut 'oS', oS
    oT = oNew(cS)
    call tst t, "tstEnvVarsS"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>',
        , oS '<oS>', oT '<oT>'
    call mPut oS'.fStS', '<put oS.fStS>'
    call tstOut t, 'oS='vGet('oS') 'oS&fStS='vGet('oS&fStS')
    call mPut oS'.fStV.1', '<put oS.fStV.1>'
    call mPut oS'.fStV.0', 1
    call tstOut t, 'oS&fStV.0='vGet('oS&fStV.0'),
         'oS&fStV.1='vGet('oS&fStV.1')
    call mPut oS'.fStR.1', s2o('<put oS.fStR.1>')
    call mPut oS'.fStR.2', s2o('<put oS.fStR.2>')
    call mPut oS'.fStR.0', 2
    call tstOut t, 'm.oS.fStR.0='mGet(oS'.fStR.0'),
     '.2='mGet(oS'.fStR.2')
    call tstOut t, 'oS&fStR.0='vGet('oS&fStR.0'),
         '.1='vGet('oS&fStR.1') '.2='vGet('oS&fStR.2')
    call mPut oS'.1234', '<put oS.1234>'
    call mPut oS'.0', 9876
    call mPut oS'.fStR.0', 2
    call tstOut t, 'm.oS.0='mGet(oS'.0'),
     '.1234='mGet(oS'.1234')
    call tstOut t, 'oS&0='vGet('oS&0'),
         '.12='vGet('oS&12') '.1234='vGet('oS&1234')
    call tstEnd t
    return
endProcedure tstEnvVars
tstEnvVarsMG: procedure expose m.
parse arg m, g
     call tstOut t, 'm.'m'='m.m 'vGet('g')='vGet(g)
     return

tstvWith: procedure expose m.
/*
$=/tstEW2/
    ### start tst tstEW2 ##############################################
    tstK1             TSTEW1
    tstK1&            !get1 w
    tstK1&f1          get1.f1 v
    tstK1&f2          !get1.f2 w
    tstK1&F3          get1.f3 v
    ttstK1&F3.FEINS   get1.f3.fEins v
    tstK1&F3.FZWEI    !get1.f3.fZwei w
    tstK1&F3.FDREI o  !get1.f3.fDrei w
    tstK1&F3.FDREI    !get1.f3.fDrei w
    tstK1&F3.1        !get1.f3.1 w
    tstK1&F3.2        TSTEW1
    tstK1&F3.2>F1     get1.f1 v
    tstK1&F3.2>F3.2>F2 !get1.f2 w
    *** err: undefined var F1
    F1          M..
    F1          get1.f1 v
    f2          !get1.f2 w
    F3          get1.f3 v
    F3.FEINS    get1.f3.fEins v
    F3.FZWEI    !get1.f3.fZwei w
    F3.FDREI o  !get1.f3.fDrei w
    F3.1        !get1.f3.1 w
    pu1 F1      get1.f1 v
    pu2 F1      get2.f1 v
    po-2 F1     get1.f1 v
    *** err: undefined var F1
    po-1 F1     M..
$/tstEW2/  */
    call pipeIni
    c0 = classNew('n? TstEW0 u f FEINS v,f FZWEI w, f FDREI r,v,s r')
    call classMet c0, 'oFlds' /* new would do it, but we donot use it */
    cl = classNew('n? TstEW u r TstEW0, f F1 v, f F2 r, f F3 TstEW0')
    call classMet cl, 'oFlds' /* new would do it, but we donot use it */
    call oMutate tstEW1, cl
    m.tstEW1    = s2o('get1 w')
    m.tstEW1.f1 = 'get1.f1 v'
    m.tstEW1.f2 = s2o('get1.f2 w')
    m.tstEW1.f3 = 'get1.f3 v'
    m.tstEW1.f3.fEins = 'get1.f3.fEins v'
    m.tstEW1.f3.fZwei = s2o('get1.f3.fZwei w')
    m.tstEW1.f3.fDrei = s2o('get1.f3.fDrei w')
    m.tstEW1.f3.0 = 3
    m.tstEW1.f3.1 = s2o('get1.f3.1 w')
    m.tstEW1.f3.2 = tstEW1
    m.tstEW1.f3.3 = s2o('get1.f3.3 w')
    call oMutate tstEW2, cl
    m.tstEW2    = s2o('get2 w')
    m.tstEW2.f1 = 'get2.f1 v'
    m.tstEW2.f2 = s2o('get2.f2 w')
    call vPut 'tstK1', tstEW1

    call tst t, 'tstEW2'
    call tstOut t, 'tstK1            ' vGet('tstK1')
    call tstOut t, 'tstK1&           ' vGet('tstK1&>')
    call tstOut t, 'tstK1&f1         ' vGet('tstK1&F1')
    call tstOut t, 'tstK1&f2         ' vGet('tstK1&F2')
    call tstOut t, 'tstK1&F3         ' vGet('tstK1&F3')
    call tstOut t, 'ttstK1&F3.FEINS  ' vGet('tstK1&F3.FEINS')
    call tstOut t, 'tstK1&F3.FZWEI   ' vGet('tstK1&F3.FZWEI')
    call tstOut t, 'tstK1&F3.FDREI o ' vGet('tstK1&F3.FDREI')
    call tstOut t, 'tstK1&F3.FDREI   ' vGet('tstK1&F3.FDREI')
    call tstOut t, 'tstK1&F3.1       ' vGet('tstK1&F3.1')
    call tstOut t, 'tstK1&F3.2       ' vGet('tstK1&F3.2')
    call tstOut t, 'tstK1&F3.2>F1    ' vGet('tstK1&F3.2>F1')
    call tstOut t, 'tstK1&F3.2>F3.2>F2' ,
                                vGet('tstK1&F3.2>F3.2>F2')
    call tstOut t, 'F1         ' vGet('F1')
    call vWith '+', tstEW1
    call tstOut t, 'F1         ' vGet('F1')
    call tstOut t, 'f2         ' vGet('F2')
    call tstOut t, 'F3         ' vGet('F3')
    call tstOut t, 'F3.FEINS   ' vGet('F3.FEINS')
    call tstOut t, 'F3.FZWEI   ' vGet('F3.FZWEI')
    call tstOut t, 'F3.FDREI o ' vGet('F3.FDREI')
    call tstOut t, 'F3.1       ' vGet('F3.1')
    call tstOut t, 'pu1 F1     ' vGet('F1')
    call vWith '+', tstEW2
    call tstOut t, 'pu2 F1     ' vGet('F1')
    call vWith '-'
    call tstOut t, 'po-2 F1    ' vGet('F1')

    call vWith '-'
    call tstOut t, 'po-1 F1    ' vGet('F1')
    call tstEnd t
/*
$=/tstEW3/
    ### start tst tstEW3 ##############################################
    .          s c3&F1          = v(c3&f1)
    *** err: null address at &FEINS in c3&F1&FEINS
    *** err: undefined var c3&F1&FEINS
    .          s c3&F1&FEINS    = M..
    *** err: null address at &FEINS in c3&F3&FEINS
    *** err: null address at &FEINS in c3&F3&FEINS
    *** err: undefined var c3&F3&FEINS
    .          s c3&F3&FEINS    = M..
    .          s c3&F3.FEINS    = val(c3&F3.FEINS)
    *** err: undefined var c3&FEINS
    .          s c3&FEINS       = M..
    getO c3&
    aft Put   s c3&>FEINS      = v&&fEins
    Push c3   s F3.FEINS       = val(c3&F3.FEINS)
    aftPut=   s F3.FEINS       = pushPut(F3.FEINS)
    push c4   s F1             = v(c4&f1)
    put f2    s F2             = put(f2)
    put ..    s F3.FEINS       = put(f3.fEins)
    popW c4   s F1             = v(c3&f1)
    *** err: undefined var F1
    popW c3   s F1             = M..
    .          s F222           = f222 pop stop
$/tstEW3/
*/
    call tst t, 'tstEW3'
    c3 = oNew('TstEW')
    call mAdd t.trans, c3 '<c3>'
    m.c3.f1 = 'v(c3&f1)'
    call vPut 'c3', c3
    call tstEnvSG , 'c3&F1'
    call tstEnvSG , 'c3&F1&FEINS'
    call tstEnvSG , 'c3&F3&FEINS'
    call vPut 'c3&F3.FEINS', 'val(c3&F3.FEINS)'
    call tstEnvSG , 'c3&F3.FEINS'
    call tstEnvSG , 'c3&FEINS'
    call tstOut t,  'getO c3&', vGet('c3&')
    call vPut 'c3&>', oNew('TstEW0')
    call vPut 'c3&>FEINS', 'v&&fEins'
    call tstEnvSG 'aft Put', 'c3&>FEINS'
    call vWith '+', c3
    call tstEnvSG 'Push c3', 'F3.FEINS'
    call vPut 'F3.FEINS', 'pushPut(F3.FEINS)'
    call tstEnvSG 'aftPut=', 'F3.FEINS'

    c4 = oNew('TstEW')
    call mAdd t.trans, c4 '<c4>'
    m.c4.f1 = 'v(c4&f1)'
    call vPut f222, 'f222 no stop'
    call vWith '+',  c4
    call tstEnvSG 'push c4', f1
    call vPut f2, 'put(f2)'
    call tstEnvSG 'put f2', f2
    call vPut f222, 'f222 stopped', 1
    call vPut 'F3.FEINS', 'put(f3.fEins)'
    call tstEnvSG 'put .. ', 'F3.FEINS'
    call vWith '-'
    call tstEnvSG 'popW c4', f1
    call vWith '-'
    call vPut f222, 'f222 pop stop'
    call tstEnvSG 'popW c3', f1
    call tstEnvSG          , f222
    call tstEnd t
    return
endProcedure tstvWith

tstEnvSG: procedure expose m. t
parse arg txt, nm
    call tstOut t, left(txt,10)'s' left(nm, 15)'=' vGet(nm)
    return

tstPipeLazy: procedure expose m.
    call pipeIni
/*
$=/tstPipeLazy/
    ### start tst tstPipeLazy #########################################
    a1 vor pipeBegin loop lazy 0 writeNow *** <class TstPipeLazyBuf>
    bufOpen <
    bufClose
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    a5 vor 2 writeNow in inIx 4
    a2 vor writeNow jBuf
    jBuf line 1
    jBuf line 2
    a3 vor writeNow in inIx 1
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    a4 vor barLast inIx 4
    a6 vor barEnd inIx 4
    a7 nach barEnd lazy 0 writeNow ***
    b1 vor barBegin lazy 0 writeNow *** <class TstPipeLazyRdr>
    RdrOpen <
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    rdrClose
    b4 vor writeNow
    b2 vor writeNow rdr inIx 2
    jRead lazyRdr
    tst in line 3 drei .schluss..
    jRead lazyRdr
    b3 vor barLast inIx 4
    b5 vor barEnd inIx 4
    b6 nach barEnd lazy 0 writeNow ***
    a1 vor pipeBegin loop lazy 1 writeAll *** +
        .<class TstPipeLazyBuf>
    a5 vor 2 writeAll in inIx 0
    a2 vor writeAll jBuf
    bufOpen <
    jBuf line 1
    jBuf line 2
    bufClose
    a3 vor writeAll in inIx 0
    #jIn 1# tst in line 1 eins ,
    tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    #jIn eof 4#
    a4 vor barLast inIx 0
    a6 vor barEnd inIx 4
    a7 nach barEnd lazy 1 writeAll ***
    b1 vor barBegin lazy 1 writeAll *** <class TstPipeLazyRdr>
    b4 vor writeAll
    b2 vor writeAll rdr inIx 1
    RdrOpen <
    jRead lazyRdr
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    jRead lazyRdr
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    jRead lazyRdr
    #jIn eof 4#
    rdrClose
    b3 vor barLast inIx 1
    b5 vor barEnd inIx 4
    b6 nach barEnd lazy 1 writeAll ***
$/tstPipeLazy/ */
    call tst t, "tstPipeLazy"
    do lz=0 to 1
        if lz then
            w = 'writeAll'
        else
            w = 'writeNow'
        m.t.inIx = 1-lz
        ty = class4Name('TstPipeLazyBuf', '')
        if ty == '' then do
            ty = classNew('n TstPipeLazyBuf u JRWDeleg', 'm',
            , 'jOpen call tstOut "T", "bufOpen" opt;',
              'call jOpen m.m.deleg, opt',
            , 'jClose call tstOut "T", "bufClose";',
              'call jClose m.m.deleg')
            end
        if \ lz then
             call mAdd t'.TRANS', ty '<class TstPipeLazyBuf>'
        call out 'a1 vor pipeBegin loop lazy' lz w '***' ty
        call pipe '+N'
        call out 'a2 vor' w 'jBuf'
        b = oNew('TstPipeLazyBuf', jBuf('jBuf line 1','jBuf line 2'))
        interpret 'call pipe'w 'b'
        call out 'a3 vor' w 'in inIx' m.t.inIx
        interpret 'call pipe'w
        call out 'a4 vor barLast inIx' m.t.inIx
        call pipe 'P|'
        call out 'a5 vor 2' w 'in inIx' m.t.inIx
        interpret 'call pipe'w
        call out 'a6 vor barEnd inIx' m.t.inIx
        call pipe '-'
        call out 'a7 nach barEnd lazy' lz w '***'

        ty = class4Name('TstPipeLazyRdr', '')
        if ty == '' then
            ty = classNew('n TstPipeLazyRdr u JRW', 'm',
            , 'jOpen call tstOut "T", "RdrOpen" opt',
            , 'jRead call out "jRead lazyRdr";' ,
                  'mr = m.m.rdr; if \ jRead(mr) then return 0;',
                          "m.m = m.mr; return 1",
            , 'jClose call tstOut "T",  "rdrClose";')
        if \ lz then
             call mAdd t'.TRANS', ty '<class TstPipeLazyRdr>'

        r = oNew('TstPipeLazyRdr')
            m.r.rdr = m.j.in
        if lz then
            call mAdd t'.TRANS', r '<lazyRdr>'
     m.t.inIx = 2-lz
     call out 'b1 vor barBegin lazy' lz w '***' ty
     call pipe '+N'
     call out 'b2 vor' w 'rdr inIx' m.t.inIx
     interpret 'call pipe'w 'r'
     call out 'b3 vor barLast inIx' m.t.inIx
     call pipe 'P|'
        call out 'b4 vor' w
        interpret 'call pipe'w
        call out 'b5 vor barEnd inIx' m.t.inIx
        call pipe '-'
     call out 'b6 nach barEnd lazy' lz w '***'
        end
    call tstEnd t
    return
endProcedure tstPipeLazy

tstEnvClass: procedure expose m.
    call pipeIni
/*
$=/tstEnvClass/
    ### start tst tstEnvClass #########################################
    a0 vor pipeBegin loop lazy 0 writeNow *** TY
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    a5 vor writeNow
    a1 vor jBuf()
    a2 vor writeNow b
    tstR: @<o20 of TstEnvClass10> isA :TstEnvClass10
    tstR:  .f11 = .
    tstR:  .F12 = value F12 of o1 <o20 of TstEnvClass10>
    tstR:  .f13 = .
    WriteO o2
    tstR: @<oc0 of TstEnvClass20> isA :TstEnvClass20 = valueO2Lazy0
    tstR:  .f24 = .
    tstR:  .F25 = value F25 of o20 <o20 of TstEnvClass20>
    a3 vor writeNow
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    a4 vor barLast inIx 4
    a6 vor barEnd
    a7 nach barEnd lazy 0 writeNow ***
    a0 vor pipeBegin loop lazy 1 writeAll *** TY
    a5 vor writeAll
    a1 vor jBuf()
    a2 vor writeAll b
    tstR: @<o21 of TstEnvClass10> isA :TstEnvClass10
    tstR:  .f11 = .
    tstR:  .F12 = value F12 of o1 <o21 of TstEnvClass10>
    tstR:  .f13 = .
    WriteO o2
    tstR: @<oc1 of TstEnvClass20> isA :TstEnvClass20 = valueO2Lazy1
    tstR:  .f24 = .
    tstR:  .F25 = value F25 of o21 <o21 of TstEnvClass20>
    a3 vor writeAll
    #jIn 1# tst in line 1 eins ,
    tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    #jIn eof 4#
    a4 vor barLast inIx 0
    a6 vor barEnd
    a7 nach barEnd lazy 1 writeAll ***
$/tstEnvClass/ */

    call tst t, "tstEnvClass"
    t10 = classNew('n? TstEnvClass10 u f f11 v, f F12 v, f f13 v')
    t20 = classNew('n? TstEnvClass20 u v, f f24 v, f F25 v')
    do lz=0 to 1
        if lz then
            w = 'writeAll'
        else
            w = 'writeNow'
        m.t.inIx = 1-lz
        call out 'a0 vor pipeBegin loop lazy' lz w '***' ty
        call pipe '+N'
        call out 'a1 vor jBuf()'
        b = jOpen(jBuf(), m.j.cWri)
        o1 = oNew('TstEnvClass10')
        m.o1.F12 = 'value F12 of o1' o1
        call mAdd t'.TRANS', o1 '<o2'lz 'of TstEnvClass10>'
        call jWrite b, o1
        call jWrite b, 'WriteO o2'
        o2 = oNew('TstEnvClass20')
        m.o2 = 'valueO2Lazy'lz
        m.o2.F25 = 'value F25 of o2'lz o2
        oc = oCopy(oCopy(o2))
        call mAdd t'.TRANS', o2 '<o2'lz 'of TstEnvClass20>'
        call mAdd t'.TRANS', oc '<oc'lz 'of TstEnvClass20>'
        call jWrite b, oc
        call out 'a2 vor' w 'b'
        interpret 'call pipe'w jClose(b)
        call out 'a3 vor' w
        interpret 'call pipe'w
        call out 'a4 vor barLast inIx' m.t.inIx
        call pipe 'P|'
        call out 'a5 vor' w
        interpret 'call pipe'w
        call out 'a6 vor barEnd'
        call pipe '-'
        call out 'a7 nach barEnd lazy' lz w '***'
        end
    call tstEnd t
    m.t.trans.0 = 0
    return
endProcedure tstEnvClass

tstDsnEx: procedure expose m.
/*
$=/tstDsnEx/
    ### start tst tstDsnEx ############################################
    dsnExists(A540769.WK.rexx) 1
    dsnExists(rzZ/A540769.WK.rexx) 1
    dsnExists(A540769.WK.wk.rexxYY) 0
    dsnExists(rzZ/A540769.WK.wk.rexxYY) 0
    dsnExists(A540769.WK.rexx(wsh)) 1
    dsnExists(rzZ/A540769.WK.rexx(wsh)) 1
    dsnExists(A540769.WK.rexx(nonono)) 0
    dsnExists(rzZ/A540769.WK.rexx(nonono)) 0
    dsnExists(A540769.WK.rxxYY(nonon)) 0
    dsnExists(rzZ/A540769.WK.rxxYY(nonon)) 0
    *** err: error in csm mbrList ?ZZ/A540769.WK.RXXYY(NONON) .
    .    e 1: CSMSI77E INVALID SYSTEM NAME (MUST BE * OR A VALID NAME) +
    (COL:8)
    .    e 2: CSMSI77E?????SYSTEM=?ZZ                                  +
    .                                                                  +
    .                                                           ???????+
    ??????????
    dsnExists(qzZ/A540769.WK.rxxYY(nonon)) 0
$/tstDsnEx/
*/
    call tst t, 'tstDsnEx'
    lst = 'rexx wk.rexxYY rexx(wsh) rexx(nonono) rxxYY(nonon)'

    do lx =1 to words(lst)
         d1 = 'A540769.WK.'word(lst,lx)
         call tstOut t, 'dsnExists('d1')' dsnExists(d1)
         call tstOut t, 'dsnExists(rzZ/'d1')' dsnExists('RZZ/'d1)
         end
    call mAdd t'.TRANS', '00'x '?', '0A'x '?'
    call tstOut t, 'dsnExists(qzZ/'d1')' dsnExists('?ZZ/'d1)
    call tstEnd t
    return
endProceudre tstDsnEx

tstFile: procedure expose m.
    call catIni
/*
$=/tstFile/
    ### start tst tstFile #############################################
    write read 0 last 10 vor anfang
    write read 1 last 80  links1 1   und rechts |  ..
    write read 2 last 80 liinks2 2   und rechts |  ..
    write read 5 last 80 links5 5 rechts5
    write read 99 last 80 links99 99 rechts
    write read 100 last 80 links100 100 rechts
    write read 101 last 80 links101 101 rechts
    write read 999 last 80 links999 999 rechts
    write read 1000 last 80 links1000 1000 rechts
    write read 1001 last 80 links1001 1001 rechts
    write read 2109 last 80 links2109 2109 rechts
    out > eins 1                                                       +
    .             .
    out > eins 2 schluss.                                              +
    .             .
    buf eins
    buf zwei
    buf drei
    out > zwei mit einer einzigen Zeile                                +
    .             .
    . links1 1   und rechts |  .                                       +
    .              .
$/tstFile/ */
    call tst t, "tstFile"
    pds = tstFilename('lib', 'r')
    call tstFileWr pds, 0, ' links0', '  und rechts |  .  '
    call tstFileWr pds, 1, ' links1', '  und rechts |  .  '
    call tstFileWr pds, 2, 'liinks2', '  und rechts |  .  '
    call tstFileWr pds, 5, 'links5', 'rechts5'
    call tstFileWr pds, 99, 'links99', 'rechts'
    call tstFileWr pds, 100, 'links100', 'rechts'
    call tstFileWr pds, 101, 'links101', 'rechts'
    call tstFileWr pds, 999, 'links999', 'rechts'
    call tstFileWr pds, 1000, 'links1000', 'rechts'
    call tstFileWr pds, 1001, 'links1001', 'rechts'
    call tstFileWr pds, 2109, 'links2109', 'rechts'
    pd2 = tstFilename('li2', 'r')
    call pipeIni
    call pipe '+F', s2o(tstPdsMbr(pd2, 'eins'))
    call out tstFB('out > eins 1') /* simulate fixBlock on linux */
    call out tstFB('out > eins 2 schluss.')
    call pipe '-'
    call pipe '+F', s2o(tstPdsMbr(pd2, 'zwei'))
    call out tstFB('out > zwei mit einer einzigen Zeile')
    call pipe '-'
    b = jBuf("buf eins", "buf zwei", "buf drei")
    call pipe '+f', , s2o(tstPdsMbr(pd2, 'eins')), b,
                    ,jBuf(),
                    ,s2o(tstPdsMbr(pd2, 'zwei')),
                    ,s2o(tstPdsMbr(pds, 'wr0')),
                    ,s2o(tstPdsMbr(pds, 'wr1'))
    call pipeWriteNow
    call pipe '-'
    call tstEnd t
    return
endProcedure tstFile

/*--- simulate fixBlock 80 on linux ---------------------------------*/
tstFB: procedure expose m.
parse arg line, recL
    if m.err.os \== 'LINUX' then
        return line
    else if recL == '' then
        return left(line, 80)
    else
        return left(line, recL)
endProcedure tstFB

tstPdsMbr: procedure expose m.
parse arg pds, mbr
    if m.err.os = 'TSO' then
        return pds'('mbr') ::F'
    if m.err.os = 'LINUX' then
        return pds'.'mbr
    call err 'tstPdsMbr implement os' m.err.os
endProcedure tstPdsMbr

tstFileWR: procedure expose m.
parse arg dsn, num, le, ri
    io = file(tstPdsMbr(dsn, 'wr'num))
    call jOpen io, m.j.cWri
    do x = 1 to num /* simulate fixBlock 80 on LINUX*/
        call jWrite io, tstFB(le x ri)
        end
    call jClose io
    if num > 100 then
        call jReset io, tstPdsMbr(dsn, 'wr'num)

    call jOpen io, m.j.cRead
    m.io = 'vor anfang'
    do x = 1 to num
        if \ jRead(io) then
            call err x 'not jRead'
        else if m.io <> le x ri then
            call err x 'read mismatch' m.io
        end
    if jRead(io) then
        call err x 'jRead but should be eof 1'
    if jRead(io) then
        call err x'+1 jjRead but should be eof 2'
    call jClose io
    call tstOut t, 'write read' num 'last' length(m.io) strip(m.io,'t')
    return
endProcedure tstFileWr

tstFileList: procedure expose m.
    call catIni
/*
$=/tstFileList/
    ### start tst tstFileList #########################################
    empty dir
    filled dir
    <<pref 2 List>>eins
    <<pref 2 List>>zwei
    <<pref 2 List>>drei
    <<pref 2 List>>vier
    filled dir recursive
    <<pref 2 List>>eins
    <<pref 2 List>>zwei
    <<pref 2 List>>drei
    <<pref 2 List>>vier
    <<pref 1 vier>>eins
    <<pref 1 vier>>zwei
    <<pref 1 vier>>drei
$/tstFileList/ */
/*
$=/tstFileListTSO/
    ### start tst tstFileListTSO ######################################
    empty dir
    filled dir
    <<pref 2 LIST>>DREI
    <<pref 2 LIST>>EINS
    <<pref 2 LIST>>ZWEI
    filled dir recursive
    <<pref 2 LIST>>DREI
    <<pref 2 LIST>>EINS
    <<pref 1 VIER>>DREI
    <<pref 1 VIER>>EINS
    <<pref 1 VIER>>ZWEI
    <<pref 2 LIST>>ZWEI
$/tstFileListTSO/ */
    if m.err.os = 'TSO' then
        call tst t, "tstFileListTSO"
    else
        call tst t, "tstFileList"
    fi = file(tstFileName('FileList', 'r'))
    call fileMkDir fi
    fl = fileList(fi)
    call tstOut t, 'empty dir'
    call jWriteNow t, fl
    call tstFileListMake t, fi, 2
    call tstOut t, 'filled dir'
    call jWriteNow t, fl
    call tstOut t, 'filled dir recursive'
    call jWriteNow t, fileList(fi, 'r')
    call tstEnd t
    return
endProcedure tstFileList

tstFileListMake: procedure expose m.
parse arg t, fi, lev
    if \ fileIsDir(fi) then
        call fileMkDir fi
    call mAdd t'.TRANS', filePath(fi) || m.file.sep ,
                '<<pref' lev right(filePath(fi),4)'>>'
    call jCat fileChild(fi, 'eins','::F'),jBuf('zeile eins','eins' lev)
    call jCat fileChild(fi, 'zwei','::F'),jBuf('zeile zwei','zwei' lev)
    call jCat fileChild(fi, 'drei','::F'),jBuf('zeile drei','drei' lev)
    if lev > 1 then
        call tstFileListMake t, fileChild(fi, 'vier'), lev-1
    return
endProcedure tstFileListMake

tstMail: procedure expose m.
do i=1 to 3
    call mailHead xy, 'mail from walter''s rexx' time() i, A540769
    call mAdd xy, 'und hier kommt der text' ,
                , 'und zeile zwei timestamp' i':' date('s') time() ,
                , left('und eine lange Zeile 159', 156, '+')159 ,
                , left('und eine lange Zeile 160', 157, '+')160 ,
                , left('und eine lange Zeile 161', 158, '+')161 ,
                , left('und eine lange Zeile ', 200, '+')203 'y ',
                , 'und kurz' ,
                , 'und Schluss'
    call mailSend xy
    call sleep 60
    end
    return
endprocedure tstMail

tstF: procedure expose m.
/*
$=/tstF/
    ### start tst tstF ################################################
    f(1 23%c345%c67%%8, eins,  zwei ) =1 23eins345 zwei 67%8;
    f(1\S23%c345%S67%%8, eins,  zwei ) =1\S23eins345zwei67%8;
    f(1 23%C345%C67%%8, eins,  zwei ) =1 23eins345 zwei 67%8;
    f(1 23%c345%S67%%8, eins,  zwei ) =1 23eins345zwei67%8;
    f(1%S2%c3@2%S4@%c5, eins,  zwei ) =1eins2 zwei 3zwei4 zwei 5;
    f(1%-2C2%3C3@2%3.2C4, eins,  zwei ) =1ei2ei 3zwe4;
    f(1@F1%c2@f2%c3@F3%c4, eins,  zwei ) =1fEins2fZwei3fDrei4;
    tstF2 _ %-9C @%5I @%8I @%+8I @%-8I -----
    _ 0             0        0       +0 0       .
    _ -1.2         -1       -1       -1 -1      .
    _ 2.34          2        2       +2 2       .
    _ -34.8765    -35      -35      -35 -35     .
    _ 567.91234   568      568     +568 568     .
    _ -8901     -8901    -8901    -8901 -8901   .
    _ 23456     23456    23456   +23456 23456   .
    _ -789012   *****  -789012  -789012 -789012 .
    _ 34e6      ***** 34000000 ******** 34000000
    _ -56e7     ***** ******** ******** ********
    _ 89e8      ***** ******** ******** ********
    _ txtli     txtli    txtli    txtli txtli   .
    _ undEinLan undEi undEinLa undEinLa undEinLa
    tstF2 _ %-9C @%5.2I @%12.2I @%+12.2I @%-12.2I -----
    _ 0          0.00         0.00        +0.00 0.00        .
    _ -1.2      -1.20        -1.20        -1.20 -1.20       .
    _ 2.34       2.34         2.34        +2.34 2.34        .
    _ -34.8765  *****       -34.88       -34.88 -34.88      .
    _ 567.91234 *****       567.91      +567.91 567.91      .
    _ -8901     *****     -8901.00     -8901.00 -8901.00    .
    _ 23456     *****     23456.00    +23456.00 23456.00    .
    _ -789012   *****   -789012.00   -789012.00 -789012.00  .
    _ 34e6      *****  34000000.00 +34000000.00 34000000.00 .
    _ -56e7     ***** ************ ************ ************
    _ 89e8      ***** ************ ************ ************
    _ txtli     txtli        txtli        txtli txtli       .
    _ undEinLan undEi undEinLanger undEinLanger undEinLanger
    tstF2 _ %-9C @%7e @% 8E @% 9.3e @% 11.4E -----
    _ 0         0.00e00  0.00E00  0.000e00  0.0000E000
    _ -1.2      -1.2e00 -1.20E00 -1.200e00 -1.2000E000
    _ 2.34      2.34e00  2.34E00  2.340e00  2.3400E000
    _ -34.8765  -3.5e01 -3.49E01 -3.488e01 -3.4877E001
    _ 567.91234 5.68e02  5.68E02  5.679e02  5.6791E002
    _ -8901     -8.9e03 -8.90E03 -8.901e03 -8.9010E003
    _ 23456     2.35e04  2.35E04  2.346e04  2.3456E004
    _ -789012   -7.9e05 -7.89E05 -7.890e05 -7.8901E005
    _ 34e6      3.40e07  3.40E07  3.400e07  3.4000E007
    _ -56e7     -5.6e08 -5.60E08 -5.600e08 -5.6000E008
    _ 89e8      8.90e09  8.90E09  8.900e09  8.9000E009
    _ txtli       txtli    txtli     txtli       txtli.
    _ undEinLan undEinL undEinLa undEinLan undEinLange
    _ 8.76e-07  8.76e-7  8.76E-7  8.760e-7  8.7600E-07
    _ 5.43e-11  5.4e-11  5.4E-11  5.43e-11  5.4300E-11
    _ -8.76e-07 -8.8e-7 -8.76E-7 -8.760e-7 -8.7600E-07
    _ -5.43e-11 -5e-011 -5.4E-11 -5.43e-11 -5.4300E-11
    tstF2 _ %-9C @%kt @%kd @%kb -----
    _ 0          0s00    0     0 .
    _ -1.2      -1s20   -1    -1 .
    _ 2.34       2s34 2340m    2 .
    _ -34.8765  -0m35  -35   -35 .
    _ 567.91234  9m28  568   568 .
    _ -8901     -2h28   -9k   -9k
    _ 23456      6h31   23k   23k
    _ -789012   -9d03 -789k -771k
    _ 34e6       394d   34M   32M
    _ -56e7     -++++ -560M -534M
    _ 89e8      +++++ 8900M 8488M
    _ txtli     txtli txtli txtli
    _ undEinLan Text? Text? Text?
    _ 8.76e-07   0s00  876n    0 .
    _ 5.43e-11   0s00   54p    0 .
    _ -8.76e-07 -0s00 -876n   -0 .
    _ -5.43e-11 -0s00  -54p   -0 .
$/tstF/ */
    call scanIni
    call tst t, 'tstF'
    call tstF1 '1 23%c345%c67%%8'
    call tstF1 '1\S23%c345%S67%%8'
    call tstF1 '1 23%C345%C67%%8'
    call tstF1 '1 23%c345%S67%%8'
    call tstF1 '1%S2%c3@2%S4@%c5'
    call tstF1 '1%-2C2%3C3@2%3.2C4'
    call tstF1 '1@F1%c2@f2%c3@F3%c4'
    nums = '0 -1.2 2.34 -34.8765 567.91234 -8901 23456' ,
                '-789012 34e6 -56e7 89e8 txtli undEinLangerText?'
    call tstF2 '_ %-9C @%5I @%8I @%+8I @%-8I', nums
    call tstF2 '_ %-9C @%5.2I @%12.2I @%+12.2I @%-12.2I', nums
    num2 = ' 8.76e-07  5.43e-11 -8.76e-07  -5.43e-11'
    call tstF2 '_ %-9C @%7e @% 8E @% 9.3e @% 11.4E', nums num2
    call tstF2 '_ %-9C @%kt @%kd @%kb', nums num2
    call tstEnd t
    return
endProcedure tstF

tstF1: procedure expose m.
parse arg fmt
    e='eins'
    z=' zwei '
    f2 = 'f2'
    m.e.f1 = 'fEins'
    m.e.f2 = 'fZwei'
    m.e.f3 = 'fDrei'
    call out "f("fmt"," e"," z") ="f(fmt, e, z)";"
    return
endProcedure tstF1

tstF2: procedure expose m.
parse arg fmt, vals
    call out 'tstF2' fmt '-----'
    do vx=1 to words(vals)
        call out f(fmt, word(vals, vx))
        end
    return
endProcedure tstF2

tstFWords: procedure expose m.
/*
$=/tstFWords/
    ### start tst tstFWords ###########################################
    ??empty??  .
    1space     .
    , %#e--    --
    %#a%9c     .
    *%#a%-7c   .
    ??empty??  eins
    1space     eins
    , %#e--    eins
    %#a%9c          eins
    *%#a%-7c   eins   .
    ??empty??  einszwei
    1space     eins zwei
    , %#e--    eins, zwei
    %#a%9c          eins     zwei
    *%#a%-7c   eins   *zwei   .
    ??empty??  einszweidrei
    1space     eins zwei drei
    , %#e--    eins, zwei, drei
    %#a%9c          eins     zwei     drei
    *%#a%-7c   eins   *zwei   *drei   .
$/tstFWords/
*/
    call scanIni
    ws = '  eins zwei   drei '
    call tst t, 'tstFWords'
    do l=0 to 3
      call tstOut t, '??empty?? ' fWords(            ,subword(ws,1,l))
      call tstOut t, '1space    ' fWords(' '         ,subword(ws,1,l))
      call tstOut t, ', %#e--   ' fWords(', %#e--'   ,subword(ws,1,l))
      call tstOut t, '%#a%9c    ' fWords('%#a%9c'    ,subword(ws,1,l))
      call tstOut t, '*%#a%-7c  ' fWords('*%#a%-7c'  ,subword(ws,1,l))
      end
    call tstEnd t
    return
endProcedure tstFWords
tstFe: procedure expose m.
/*
$=/tstFe/
  ### start tst tstFe ###############################################
  .                   1 < 1.00e00> <1.00e00>
  .                   0 < 0.00e00> <0.00e00>
  .                -2.1 <-2.10e00> <-2.1e00>
  .                  .3 < 3.00e-1> <3.00e-1>
  .             -.45678 <-4.57e-1> <-4.6e-1>
  .                 901 < 9.01e02> <9.01e02>
  .               -2345 <-2.35e03> <-2.3e03>
  .              678e90 < 6.78e92> <6.78e92>
  .              123e-4 < 1.23e-2> <1.23e-2>
  .             567e-89 < 5.7e-87> <5.7e-87>
  .              12e456 < 1.2e457> <1.2e457>
  .             78e-901 < 8e-0900> <8e-0900>
  .           2345e5789 < 2e05792> <2e05792>
  .           123e-4567 < 1e-4565> <1e-4565>
  .          8901e23456 < 9e23459> <9e23459>
  .          -123e-4567 <-1e-4565> <-0e-999>
  .          567e890123 <********> <*******>
  .       45678e-901234 < 0e-9999> <0e-9999>
  .                kurz <    kurz> <kurz   >
  .       undLangerText <undLange> <undLang>
$/tstFe/
*/
    call tst t, 'tstFe'
    vAll = '1 0 -2.1 .3 -.45678 901 -2345 678e90 123e-4' ,
            '567e-89 12e456 78e-901 2345e5789  123e-4567 8901e23456' ,
            '-123e-4567 567e890123 45678e-901234' ,
            'kurz undLangerText'
    do vx=1 to words(vAll)
        v = word(vAll, vx)
        call tstOut t, right(v, 20)  '<'fe(v, 8, 2, 'e', ' ')'>' ,
                                     '<'fe(v, 7, 1, 'e', '-')'>'
        end
    call tstEnd t
    return
endProcedure

tstFTst: procedure expose m.
/*
$=/tstFTstS/
    ### start tst tstFTstS ############################################
    1956-01-29-23.34.56.987654     SS => 1956-01-29-23.34.56.987654|
    1956-01-29-23.34.56.987654     Ss => 1956-01-29-23.34.56|
    1956-01-29-23.34.56.987654     S  => 1956-01-29-23.34.56|
    1956-01-29-23.34.56.987654     SD => 19560129|
    1956-01-29-23.34.56.987654     Sd => 560129|
    1956-01-29-23.34.56.987654     SE => 29.01.1956|
    1956-01-29-23.34.56.987654     Se => 29.01.56|
    1956-01-29-23.34.56.987654     St => 23.34.56|
    1956-01-29-23.34.56.987654     ST => 23:34:56.987654|
    1956-01-29-23.34.56.987654     SY => GB29|
    1956-01-29-23.34.56.987654     SM => B2923345|
    1956-01-29-23.34.56.987654     SH => C33456|
$/tstFTstS/
$=/tstFTsts/
    ### start tst tstFTsts ############################################
    2014-12-23-16.57.38            sS => 2014-12-23-16.57.38.000000|
    2014-12-23-16.57.38            ss => 2014-12-23-16.57.38|
    2014-12-23-16.57.38            s  => 2014-12-23-16.57.38|
    2014-12-23-16.57.38            sD => 20141223|
    2014-12-23-16.57.38            sd => 141223|
    2014-12-23-16.57.38            sE => 23.12.2014|
    2014-12-23-16.57.38            se => 23.12.14|
    2014-12-23-16.57.38            st => 16.57.38|
    2014-12-23-16.57.38            sT => 16:57:38.000000|
    2014-12-23-16.57.38            sY => EM23|
    2014-12-23-16.57.38            sM => M2316573|
    2014-12-23-16.57.38            sH => B65738|
$/tstFTsts/
$=/tstFTstD/
    ### start tst tstFTstD ############################################
    23450618                       DS => 2345-06-18-00.00.00.000000|
    23450618                       Ds => 2345-06-18-00.00.00|
    23450618                       D  => 2345-06-18-00.00.00|
    23450618                       DD => 23450618|
    23450618                       Dd => 450618|
    23450618                       DE => 18.06.2345|
    23450618                       De => 18.06.45|
    23450618                       Dt => 00.00.00|
    23450618                       DT => 00:00:00.000000|
    23450618                       DY => PG18|
    23450618                       DM => G1800000|
    23450618                       DH => A00000|
$/tstFTstD/
0----+----1----+----2----+----3
abcdefghijklmnopqrstuvwxyz
$=/tstFTstd/
    ### start tst tstFTstd ############################################
    120724                         dS => 2012-07-24-00.00.00.000000|
    120724                         ds => 2012-07-24-00.00.00|
    120724                         d  => 2012-07-24-00.00.00|
    120724                         dD => 20120724|
    120724                         dd => 120724|
    120724                         dE => 24.07.2012|
    120724                         de => 24.07.12|
    120724                         dt => 00.00.00|
    120724                         dT => 00:00:00.000000|
    120724                         dY => CH24|
    120724                         dM => H2400000|
    120724                         dH => A00000|
$/tstFTstd/
$=/tstFTstE/
    ### start tst tstFTstE ############################################
    09.12.1345                     ES => 1345-12-09-00.00.00.000000|
    09.12.1345                     Es => 1345-12-09-00.00.00|
    09.12.1345                     E  => 1345-12-09-00.00.00|
    09.12.1345                     ED => 13451209|
    09.12.1345                     Ed => 451209|
    09.12.1345                     EE => 09.12.1345|
    09.12.1345                     Ee => 09.12.45|
    09.12.1345                     Et => 00.00.00|
    09.12.1345                     ET => 00:00:00.000000|
    09.12.1345                     EY => PM09|
    09.12.1345                     EM => M0900000|
    09.12.1345                     EH => A00000|
$/tstFTstE/
$=/tstFTste/
    ### start tst tstFTste ############################################
    31.05.2467                     eS => 2024-05-31-00.00.00.000000|
    31.05.2467                     es => 2024-05-31-00.00.00|
    31.05.2467                     e  => 2024-05-31-00.00.00|
    31.05.2467                     eD => 20240531|
    31.05.2467                     ed => 240531|
    31.05.2467                     eE => 31.05.2024|
    31.05.2467                     ee => 31.05.2467|
    31.05.2467                     et => 00.00.00|
    31.05.2467                     eT => 00:00:00.000000|
    31.05.2467                     eY => OF31|
    31.05.2467                     eM => F3100000|
    31.05.2467                     eH => A00000|
$/tstFTste/
$=/tstFTstt/
### start tst tstFTstt ############################################
    12.34.56                       tS => 0000-01-01-12.34.56.000000|
    12.34.56                       ts => 0000-01-01-12.34.56|
    12.34.56                       t  => 0000-01-01-12.34.56|
    12.34.56                       tD => 00000101|
    12.34.56                       td => 000101|
    12.34.56                       tE => 01.01.0000|
    12.34.56                       te => 01.01.00|
    12.34.56                       tt => 12.34.56|
    12.34.56                       tT => 12:34:56.000000|
    12.34.56                       tY => ??01|
    12.34.56                       tM => ?0112345|
    12.34.56                       tH => B23456|
$/tstFTstt/
$=/tstFTstT/
    ### start tst tstFTstT ############################################
    23.45.06.784019                TS => 0000-01-01-23.45.06.784019|
    23.45.06.784019                Ts => 0000-01-01-23.45.06|
    23.45.06.784019                T  => 0000-01-01-23.45.06|
    23.45.06.784019                TD => 00000101|
    23.45.06.784019                Td => 000101|
    23.45.06.784019                TE => 01.01.0000|
    23.45.06.784019                Te => 01.01.00|
    23.45.06.784019                Tt => 23.45.06|
    23.45.06.784019                TT => 23.45.06.784019|
    23.45.06.784019                TY => ??01|
    23.45.06.784019                TM => ?0123450|
    23.45.06.784019                TH => C34506|
$/tstFTstT/
$=/tstFTstY/
    ### start tst tstFTstY ############################################
    FE25                           YS => 2015-04-25-00.00.00.000000|
    FE25                           Ys => 2015-04-25-00.00.00|
    FE25                           Y  => 2015-04-25-00.00.00|
    FE25                           YD => 20150425|
    FE25                           Yd => 150425|
    FE25                           YE => 25.04.2015|
    FE25                           Ye => 25.04.15|
    FE25                           Yt => 00.00.00|
    FE25                           YT => 00:00:00.000000|
    FE25                           YY => FE25|
    FE25                           YM => E2500000|
    FE25                           YH => A00000|
$/tstFTstY/
$=/tstFTstM/
    ### start tst tstFTstM ############################################
    I2317495                       MS => 0000-08-23-17.49.50.000000|
    I2317495                       Ms => 0000-08-23-17.49.50|
    I2317495                       M  => 0000-08-23-17.49.50|
    I2317495                       MD => 00000823|
    I2317495                       Md => 000823|
    I2317495                       ME => 23.08.0000|
    I2317495                       Me => 23.08.00|
    I2317495                       Mt => 17.49.50|
    I2317495                       MT => 17:49:50.000000|
    I2317495                       MY => ?I23|
    I2317495                       MM => I2317495|
    I2317495                       MH => B74950|
$/tstFTstM/
$=/tstFTstH/
    ### start tst tstFTstH ############################################
    B23456                         HS => 0000-01-01-12.34.56.000000|
    B23456                         Hs => 0000-01-01-12.34.56|
    B23456                         H  => 0000-01-01-12.34.56|
    B23456                         HD => 00000101|
    B23456                         Hd => 000101|
    B23456                         HE => 01.01.0000|
    B23456                         He => 01.01.00|
    B23456                         Ht => 12.34.56|
    B23456                         HT => 12:34:56.000000|
    B23456                         HY => ??01|
    B23456                         HM => ?0112345|
    B23456                         HH => B23456|
$/tstFTstH/
$=/tstFTstn/
    ### start tst tstFTstn ############################################
    19560423 17:58:29              nS => 1956-04-23-17.58.29.000000|
    19560423 17:58:29              ns => 1956-04-23-17.58.29|
    19560423 17:58:29              n  => 1956-04-23-17.58.29|
    19560423 17:58:29              nD => 19560423|
    19560423 17:58:29              nd => 560423|
    19560423 17:58:29              nE => 23.04.1956|
    19560423 17:58:29              ne => 23.04.56|
    19560423 17:58:29              nt => 17.58.29|
    19560423 17:58:29              nT => 17:58:29.000000|
    19560423 17:58:29              nY => GE23|
    19560423 17:58:29              nM => E2317582|
    19560423 17:58:29              nH => B75829|
$/tstFTstn/
$=/tstFTstN/
    ### start tst tstFTstN ############################################
    32102130 10:21:32.456789       NS => 3210-21-30-10.21.32.456789|
    32102130 10:21:32.456789       Ns => 3210-21-30-10.21.32|
    32102130 10:21:32.456789       N  => 3210-21-30-10.21.32|
    32102130 10:21:32.456789       ND => 32102130|
    32102130 10:21:32.456789       Nd => 102130|
    32102130 10:21:32.456789       NE => 30.21.3210|
    32102130 10:21:32.456789       Ne => 30.21.10|
    32102130 10:21:32.456789       Nt => 10.21.32|
    32102130 10:21:32.456789       NT => 10:21:32.456789|
    32102130 10:21:32.456789       NY => AV30|
    32102130 10:21:32.456789       NM => V3010213|
    32102130 10:21:32.456789       NH => B02132|
$/tstFTstN/
*/
    call scanIni
    say "f('%t  ')" f('%t  ')
    allOut = 'Ss DdEetTYMH'
    allIn  = 'S1956-01-29-23.34.56.987654' ,
             's2014-12-23-16.57.38' ,
             'D23450618' ,
             'd120724'   ,
             'E09.12.1345' ,
             'e31.05.2467' ,
             't12.34.56'  ,
             'T23.45.06.784019' ,
             'YFE25' ,
             'MI2317495' ,
             'HB23456' ,
             'n19560423*17:58:29' ,
             'N32102130*10:21:32.456789'
    do ix=1 to words(allIn)
        parse value word(allIn, ix) with iF 2 iV
        iv = translate(iv, ' ', '*')
        call tst t, "tstFTst"iF
        do ox=1 to length(allOut)
            ft = iF || substr(allOut, ox, 1)
            call tstOut t, left(iV, 30) ft  '=>' f('%t'ft, iV)'|'
            if 0 & iF = 'Y' then
                say '???' ft '>>>' mGet('F_GEN.%t'ft)
            end
        call tstEnd t
        end
    return
endProcedure tstFTst

tstFmt: procedure expose m.
    call pipeIni
/*
$=/tstFmt/
    ### start tst tstFmt ##############################################
    =   a2i b3b   d4                       fl5          ex6
    -11 -11 b3    d4-11+d4++++    -111.1000000 -1.11000e-12
    -1  -10 b     d4-10+d4+++            null1        null3
    -    -9 b3b-9 d4-9+d4+++       -11.0000000 -1.11900e-10
    -8+  -8 b3b-  d4-8+d4++        -18.0000000 -1.18000e010
    -7   -7 b3b   d4-7+d4+          -7.0000000 -1.70000e-07
    -    -6 b3    d4-6+d4           -0.1111160 -6.00000e006
    -5+  -5 b     d4-5+d                 null2        null2
    -4   -4 b3b-4 d4-4+         -11114.0000000 -1.11140e008
    -    -3 b3b-  d4-3              -0.1130000 -1.13000e-04
    -2+  -2 b3b   d4-               -0.1200000 -1.20000e001
    -1   -1 b3    d4                -0.1000000 -1.00000e-02
    0     0 b     d                      null1        null1
    1+    1 b3    d4                 0.1000000  1.00000e-02
    2++   2 b3b   d42                0.1200000  1.20000e001
    3     3 b3b3  d43+               0.1130000  1.13000e-04
    4+    4 b3b4+ d44+d          11114.0000000  1.11140e008
    5++   5 b     d45+d4                 null2        null2
    6     6 b3    d46+d4+            0.1111160  1.11116e005
    7+    7 b3b   d47+d4++           0.1111117  7.00000e-08
    8++   8 b3b8  d48+d4+++          8.0000000  1.80000e009
    9     9 b3b9+ d49+d4++++         0.9000000  1.19000e-08
    10   10 b     d410+d4++++            null1        null3
    11+  11 b3    d411+d4+++++       0.1110000  1.00000e-12
    1    12 b3b   d412+d4++++++  11112.0000000  2.00000e012
    13   13 b3b1  d               1111.3000000  1.13000e-12
    14+  14 b3b14 d4            111111.0000000  1.40000e013
    1    15 b     d41                    null2        null1
    16   16 b3    d416               6.0000000  1.16000e003
    17+  17 b3b   d417+              0.7000000  1.11170e-03
    1    18 b3b1  d418+d            11.0000000  1.11800e003
    19   19 b3b19 d419+d4            0.1190000  9.00000e-05
    20+  20 b     d420+d4+               null1        null2
    2    21 b3    d421+d4++         11.1210000  1.11000e-05
    22   22 b3b   d422+d4+++     11111.2000000  2.00000e007
    23+  23 b3b2  d423+d4++++        0.1111123  1.11230e-09
    c3L      a2i drei  d4                 fl5          ex6
    -11 -1.10E01 b3    d4-11+d   -111.1000000 -1.11000e-12
    -1  -1.00E01 b     d4-10+d          null1        null3
    -   -9.00E00 b3b-9 d4-9+d4    -11.0000000 -1.11900e-10
    -8+ -8.00E00 b3b-  d4-8+d4    -18.0000000 -1.18000e010
    -7  -7.00E00 b3b   d4-7+d4     -7.0000000 -1.70000e-07
    -   -6.00E00 b3    d4-6+d4     -0.1111160 -6.00000e006
    -5+ -5.00E00 b     d4-5+d           null2        null2
    -4  -4.00E00 b3b-4 d4-4+   -11114.0000000 -1.11140e008
    -   -3.00E00 b3b-  d4-3        -0.1130000 -1.13000e-04
    -2+ -2.00E00 b3b   d4-         -0.1200000 -1.20000e001
    -1  -1.00E00 b3    d4          -0.1000000 -1.00000e-02
    0    0.00E00 b     d                null1        null1
    1+   1.00E00 b3    d4           0.1000000  1.00000e-02
    2++  2.00E00 b3b   d42          0.1200000  1.20000e001
    3    3.00E00 b3b3  d43+         0.1130000  1.13000e-04
    4+   4.00E00 b3b4+ d44+d    11114.0000000  1.11140e008
    5++  5.00E00 b     d45+d4           null2        null2
    6    6.00E00 b3    d46+d4+      0.1111160  1.11116e005
    7+   7.00E00 b3b   d47+d4+      0.1111117  7.00000e-08
    8++  8.00E00 b3b8  d48+d4+      8.0000000  1.80000e009
    9    9.00E00 b3b9+ d49+d4+      0.9000000  1.19000e-08
    10   1.00E01 b     d410+d4          null1        null3
    11+  1.10E01 b3    d411+d4      0.1110000  1.00000e-12
    1    1.20E01 b3b   d412+d4  11112.0000000  2.00000e012
    13   1.30E01 b3b1  d         1111.3000000  1.13000e-12
    14+  1.40E01 b3b14 d4      111111.0000000  1.40000e013
    1    1.50E01 b     d41              null2        null1
    16   1.60E01 b3    d416         6.0000000  1.16000e003
    17+  1.70E01 b3b   d417+        0.7000000  1.11170e-03
    1    1.80E01 b3b1  d418+d      11.0000000  1.11800e003
    19   1.90E01 b3b19 d419+d4      0.1190000  9.00000e-05
    20+  2.00E01 b     d420+d4          null1        null2
    2    2.10E01 b3    d421+d4     11.1210000  1.11000e-05
    22   2.20E01 b3b   d422+d4  11111.2000000  2.00000e007
    23+  2.30E01 b3b2  d423+d4      0.1111123  1.11230e-09
$/tstFmt/ */

    call tst t, "tstFmt"
    b = jBuf()
    st = b'.BUF'
    call pipe '+F', b
    call tstDataClassOut '. c3 a2i i b3b c5 d4 c13 fl5 f8n2 ex6 e9n3',
        , -11, + 23
    call pipe '-'
    call fTabAuto fTabReset(abc, 1), b
    call fTabReset abc, 1
    call fTabAddDetect abc,      , st   , , 'c3L'
    call fTabAdd       abc, 'a2i', '% 8E'
    call fTabAddDetect abc, 'b3b', st   , ,'drei'
    call fTabAdd       abc, 'd4', '%-7C'
    call fTabAddDetect abc, 'fl5', st
    call fTabAddDetect abc, 'ex6', st
    call fTab abc, b
    call tstEnd t
    return
endProcedure tstFmt

tstFTab: procedure expose m.
    call pipeIni
    call scanIni
/*
$=/tstFTab/
    ### start tst tstFTab #############################################
    testData begin
    ..---------a2i-b3b------------------d4------fl5-----ex6---
    -11       -11 b3           -11+d4++++ -111.100 -1e-012
    -1        -10 b            4-10+d4+++    null1   null3
    -          -9 b3b-9        d4-9+d4+++  -11.000 -1e-010
    -8+        -8 b3b-          d4-8+d4++  -18.000 -1.2e10
    -7         -7 b3b            d4-7+d4+   -7.000 -1.7e-7
    -          -6 b3              d4-6+d4   -0.111 -6.0e06
    -5+        -5 b                d4-5+d    null2   null2
    -4         -4 b3b-4             d4-4+ ******** -1.1e08
    -          -3 b3b-               d4-3   -0.113 -1.1e-4
    -2+        -2 b3b                 d4-   -0.120 -1.2e01
    -1         -1 b3                   d4   -0.100 -1.0e-2
    0           0 b                     d    null1   null1
    1+          1 b3                   d4    0.100 1.00e-2
    2++         2 b3b                 d42    0.120 1.20e01
    3           3 b3b3               d43+    0.113 1.13e-4
    4+          4 b3b4+             d44+d ******** 1.11e08
    5++         5 b                d45+d4    null2   null2
    6           6 b3              d46+d4+    0.111 1.11e05
    7+          7 b3b            d47+d4++    0.111 7.00e-8
    8++         8 b3b8          d48+d4+++    8.000 1.80e09
    9           9 b3b9+        d49+d4++++    0.900 1.19e-8
    10         10 b            410+d4++++    null1   null3
    11+        11 b3           11+d4+++++    0.111 1.0e-12
    1          12 b3b          2+d4++++++ ******** 2.00e12
    13         13 b3b1                  d 1111.300 1.1e-12
    14+        14 b3b14                d4 ******** 1.40e13
    1          15 b                   d41    null2   null1
    16         16 b3                 d416    6.000 1.16e03
    17+        17 b3b               d417+    0.700 1.11e-3
    1          18 b3b1             d418+d   11.000 1.12e03
    19         19 b3b19           d419+d4    0.119 9.00e-5
    20+        20 b              d420+d4+    null1   null2
    2          21 b3            d421+d4++   11.121 1.11e-5
    22         22 b3b          d422+d4+++ ******** 2.00e07
    23+        23 b3b2         423+d4++++    0.111 1.11e-9
    ..---------a2i-b3b------------------d4------fl5-----ex6---
    testData end
$/tstFTab/ */

    call tst t, "tstFTab"
    b = jBuf()
    st = b'.BUF'
    call pipe '+F', b
    call tstDataClassOut '. c3 a2i i b3b c5 d4 c13 fl5 f8n2 ex6 e9n3',
        , -11, + 23
    call pipe 'P|'
    call fTabReset ft, 2 1, 1 3, '-'
    call fTabAddRCT   ft, '='   , '%-6C', '.', , 'testData begin',
                                                , 'testData end'
    call fTabAddRCT   ft, 'a2i' , '%6i'
    call fTabAddRCT   ft, 'b3b' , '%-12C'
    call fTabAddRCT   ft, 'd4'  , '%10C'
    call fTabAddRCT   ft, 'fl5' , '%8.3I'
    call fTabAddRCT   ft, 'ex6' , '%7e'
    call fTab ft
    call pipe '-'
    call tstEnd t
    return
endProcedure tstFTab

tstCSV: procedure
/*
$=/tstCSV/
    ### start tst tstCSV ##############################################
    value,value eins,value zwei
    value,"value, , eins",value zwei
    value,"","value ""zwei"" oder?"
    value,,"value ""zwei"" oder?"
$/tstCSV/ */
    m.tstCsv.c.1 = ''
    m.tstCsv.c.2 = eins
    m.tstCsv.c.3 = zwei
    m.tstCsv.c.0 = 3
    call tst t, "tstCSV"
    m.tstCsv.o      = 'value'
    m.tstCsv.o.eins = 'value eins'
    m.tstCsv.o.zwei = 'value zwei'
    call tstOut t, csv4Obj(tstCsv'.'o, tstCsv'.'c, 0)
    m.tstCsv.o.eins = 'value, , eins'
    call tstOut t, csv4Obj(tstCsv'.'o, tstCsv'.'c, 0)
    m.tstCsv.o.eins = ''
    m.tstCsv.o.zwei = 'value "zwei" oder?'
    call tstOut t, csv4Obj(tstCsv'.'o, tstCsv'.'c, 0)
    m.tstCsv.o.eins = '---'
    call tstOut t, csv4Obj(tstCsv'.'o, tstCsv'.'c, 1, '---')
    call tstEnd t
    return
endProcedure tstCSV

tstfUnits: procedure
/*
$=/tstfUnits/
    ### start tst tstfUnits ###########################################
    .             1 ==>    1  =->   -1  =+>    +1  =b>    1 .
    .             5 ==>    5  =->   -5  =+>    +5  =b>    5 .
    .            13 ==>   13  =->  -13  =+>   +13  =b>   13 .
    .           144 ==>  144  =-> -144  =+>  +144  =b>  144 .
    .          1234 ==> 1234  =->   -1k =+> +1234  =b> 1234 .
    .          7890 ==> 7890  =->   -8k =+> +7890  =b> 7890 .
    .             0 ==>    0  =->    0  =+>    +0  =b>    0 .
    .         234E3 ==>  234k =-> -234k =+>  +234k =b>  229k
    .          89E6 ==>   89M =->  -89M =+>   +89M =b>   85M
    .         123E9 ==>  123G =-> -123G =+>  +123G =b>  115G
    .     4567891E9 ==> 4568T =->   -5P =+> +4568T =b> 4154T
    .         0.123 ==>  123m =-> -123m =+>  +123m =b>    0 .
    .  0.0000456789 ==>   46u =->  -46u =+>   +46u =b>    0 .
    .   345.567E-12 ==>  346p =-> -346p =+>  +346p =b>    0 .
    .  123.4567E-15 ==>  123f =-> -123f =+>  +123f =b>    0 .
    .           ABC ==>   ABC =->  -ABC =+>    ABC =b>   ABC
    ABCDEFGHIJKLMN ==> JKLMN =-> JKLMN =+> IJKLMN =b> JKLMN
    .          1E77 ==> +++++ =-> -++++ =+> ++++++ =b> +++++.
    .         1E-77 ==>    0f =->   -0f =+>    +0f =b>    0 .
    .     18.543E18 ==>   19E =->  -19E =+>   +19E =b>   16E
    .     20.987E20 ==> 2099E =-> -++++ =+> +2099E =b> 1820E
    .             1 ==>  1.000  =-> -1.000  =+> +1.000  =b>  1.000 .
    .             5 ==>  5.000  =-> -5.000  =+> +5.000  =b>  5.000 .
    .            13 ==> 13.000  =-> -0.013k =+> +0.013k =b> 13.000 .
    .           144 ==>  0.144k =-> -0.144k =+> +0.144k =b>  0.141k
    .          1234 ==>  1.234k =-> -1.234k =+> +1.234k =b>  1.205k
    .          7890 ==>  7.890k =-> -7.890k =+> +7.890k =b>  7.705k
    .             0 ==>  0.000  =->  0.000  =+> +0.000  =b>  0.000 .
    .         234E3 ==>  0.234M =-> -0.234M =+> +0.234M =b>  0.223M
    .          89E6 ==> 89.000M =-> -0.089G =+> +0.089G =b> 84.877M
    .         123E9 ==>  0.123T =-> -0.123T =+> +0.123T =b>  0.112T
    .     4567891E9 ==>  4.568P =-> -4.568P =+> +4.568P =b>  4.057P
    .         0.123 ==>  0.123  =-> -0.123  =+> +0.123  =b>  0.123 .
    .  0.0000456789 ==> 45.679u =-> -0.046m =+> +0.046m =b>  0.000 .
    .   345.567E-12 ==>  0.346n =-> -0.346n =+> +0.346n =b>  0.000 .
    .  123.4567E-15 ==>  0.123p =-> -0.123p =+> +0.123p =b>  0.000 .
    .           ABC ==>     ABC =->    -ABC =+>     ABC =b>     ABC
    ABCDEFGHIJKLMN ==> HIJKLMN =-> HIJKLMN =+> HIJKLMN =b> HIJKLMN
    .          1E77 ==> +++++++ =-> -++++++ =+> +++++++ =b> +++++++.
    .         1E-77 ==>  0.000f =-> -0.000f =+> +0.000f =b>  0.000 .
    .     18.543E18 ==> 18.543E =->    -19E =+>    +19E =b> 16.083E
    .     20.987E20 ==>   2099E =->  -2099E =+>  +2099E =b>   1820E
$/tstfUnits/
$=/tstfUnitst/
    ### start tst tstfUnitst ##########################################
    .            .3 ==>  0s30 ++>   0s30 -+> -0s30 -->  -0s30
    .            .8 ==>  0s80 ++>   0s80 -+> -0s80 -->  -0s80
    .             1 ==>  1s00 ++>   1s00 -+> -1s00 -->  -1s00
    .           1.2 ==>  1s20 ++>   1s20 -+> -1s20 -->  -1s20
    .            59 ==> 59s00 ++>  59s00 -+> -0m59 --> -59s00
    .         59.07 ==> 59s07 ++>  59s07 -+> -0m59 --> -59s07
    .        59.997 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .            60 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .          60.1 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .           611 ==> 10m11 ++>  10m11 -+> -0h10 --> -10m11
    .        3599.4 ==> 59m59 ++>  59m59 -+> -1h00 --> -59m59
    .        3599.5 ==>  1h00 ++>   1h00 -+> -1h00 -->  -1h00
    .          3661 ==>  1h01 ++>   1h01 -+> -1h01 -->  -1h01
    .         83400 ==> 23h10 ++>  23h10 -+> -0d23 --> -23h10
    .         84700 ==> 23h32 ++>  23h32 -+> -1d00 --> -23h32
    .         86400 ==>  1d00 ++>   1d00 -+> -1d00 -->  -1d00
    .         89900 ==>  1d01 ++>   1d01 -+> -1d01 -->  -1d01
    .       8467200 ==> 98d00 ++>  98d00 -+>  -98d --> -98d00
    .    8595936.00 ==> 99d12 ++>  99d12 -+>  -99d --> -99d12
    .    8638704.00 ==>  100d ++>   100d -+> -100d -->  -100d
    .       8640000 ==>  100d ++>   100d -+> -100d -->  -100d
    .     863913600 ==> 9999d ++>  9999d -+> -++++ --> -9999d
    .     863965440 ==> +++++ ++>  +++++ -+> -++++ --> -+++++.
    .     8.6400E+9 ==> +++++ ++>  +++++ -+> -++++ --> -+++++.
$/tstfUnitst/ */
    call jIni
    call tst t, "tstfUnits"
    d = 86400
    lst = 1 5 13 144 1234 7890 0 234e3  89e6 123e9,
          4567891e9 0.123 0.0000456789 345.567e-12 123.4567e-15 ,
           abc abcdefghijklmn   1e77 1e-77 18.543e18 20.987e20
    do wx=1 to words(lst)
        call tstOut t, right(word(lst, wx), 14) ,
                 '==>' fUnits(    word(lst, wx), 'd') ,
                 '=->' fUnits( '-'word(lst, wx), 'd') ,
                 '=+>' fUnits(    word(lst, wx), 'd',  ,   , '+'),
                 '=b>' fUnits(    word(lst, wx), 'b')
        end
    do wx=1 to words(lst)
        call tstOut t, right(word(lst, wx), 14) ,
                 '==>' fUnits(    word(lst, wx), 'd', 7, 3) ,
                 '=->' fUnits( '-'word(lst, wx), 'd', 7, 3) ,
                 '=+>' fUnits(    word(lst, wx), 'd', 7, 3, '+'),
                 '=b>' fUnits(    word(lst, wx), 'b', 7, 3)
        end
    call tstEnd t
    call tst t, "tstfUnitst"
    d = 86400
    lst = .3 .8 1 1.2 59 59.07 59.997 60 60.1 611 ,
          3599.4 3599.5 3661 d-3000 d-1700 d d+3500 ,
          d * 98 d * 99.49 d * 99.985 d*100 d * 9999 d * 9999.6 ,
          d * 1e5
    do wx=1 to words(lst)
        call tstOut t, right(word(lst, wx), 14) ,
                 '==>' fUnits(    word(lst, wx), 't'   ) ,
                 '++>' fUnits(    word(lst, wx), 't', , , ' '),
                 '-+>' fUnits('-'word(lst, wx),  't' ),
                 '-->' fUnits('-'word(lst, wx), 't', , , ' ')
        end
    call tstEnd t
    return
endProcedure tstfUnits

tstSb: procedure expose m.
/*
$=/tstSb/
    ### start tst tstSb ###############################################
    end        : 0
    char  3    : 1 abc
    lit   d?   : 0 .
    lit   de   : 1 de
    lit   de ? fg fgh: 1 fg
    while HIJ  : 0 .
    end        : 0
    while Jih  : 1 hi
    while ? klj: 1 jklkl ?
    end        : 1
    while ? klj: 0 .
    char  3    : 0 .
    lit        : 0 .
    until cba  : 0 .
    until ?qd  : 1 abc
    until ?qr  : 1 defdef .
    until ?qr  : 0 .
    strEnd ?   : 1 ?
    strEnd ?   : 0 .
    strEnd ?   : 1 ab??cd????gh?
    strEnd ") ": 1 ab) .
    strEnd ") ": 1 cd) ) gh) .
    string     : 1 'eins?''' v=eins?'
    space      : 1  >
    string     : 1 "zwei""" v=zwei"
    string ?   : 1 ?drei??? v=drei?
    *** err: scanErr ending Apostroph(") missing
    .    e 1: last token  scanPosition "noEnd
    .    e 2: pos 27 in string 'eins?''' "zwei"""?drei???"noEnd
    string     : 0  v=drei?
$/tstSb/ */
    call pipeIni
    call scanIni
    call tst t, 'tstSb'
    call scanSrc s, 'abcdefghijklkl ?'
    call out 'end        :' scanSBEnd(s)
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit   d?   :' scanLit(s, 'd?') m.s.tok
    call out 'lit   de   :' scanLit(s, 'de') m.s.tok
    call out 'lit   de ? fg fgh:',
            scanLit(s, 'de', '?', 'fg', 'fgh') m.s.tok
    call out 'while HIJ  :' scanWhile(s, 'HIJ') m.s.tok
    call out 'end        :' scanSBEnd(s)
    call out 'while Jih  :' scanWhile(s, 'Jih') m.s.tok
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'end        :' scanSBEnd(s)
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit        :' scanLit(s) m.s.tok
    call scanSrc s, 'abcdefdef ?'
    call out 'until cba  :' scanUntil(s, 'cba') m.s.tok
    call out 'until ?qd  :' scanUntil(s, '?qd') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab??cd????gh?ijk'
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab) cd) ) gh) jk) )'
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call scanSrc s, "'eins?'''" '"zwei"""?drei???"noEnd'
    call out 'string     :' scanString(s) m.s.tok 'v='m.s.val
    call out 'space      :' scanWhile(s, ' ') m.s.tok'>'
    call out 'string     :' scanString(s) m.s.tok 'v='m.s.val
    call out 'string ?   :' scanString(s, '?') m.s.tok 'v='m.s.val
    call out 'string     :' scanString(s) m.s.tok 'v='m.s.val
    call tstEnd t
    return
endProcedure tstSb

tstSb2: procedure expose m.
/*
$=/tstSb2/
    ### start tst tstSb2 ##############################################
    end        : 0
    char  3    : 1 abc
    lit   d?   : 0 .
    lit   de   : 1 de
    lit   de ? fg fgh: 1 fg
    while HIJ  : 0 .
    end        : 0
    while Jih  : 1 hi
    while ? klj: 1 jklkl ?
    end        : 1
    while ? klj: 0 .
    char  3    : 0 .
    lit        : 0 .
    until cba  : 0 .
    until ?qd  : 1 abc
    until ?qr  : 1 defdef .
    until ?qr  : 0 .
    strEnd ?   : 1 ?
    strEnd ?   : 0 .
    strEnd ?   : 1 ab??cd????gh?
    strEnd ") ": 1 ab) .
    strEnd ") ": 1 cd) ) gh) .
$/tstSb2/ */
    call tst t, 'tstSb2'
    call scanIni
    call scanSrc s, 'abcdefghijklkl ?'
    call out 'end        :' scanEnd(s)
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit   d?   :' scanLit(s, 'd?') m.s.tok
    call out 'lit   de   :' scanLit(s, 'de') m.s.tok
    call out 'lit   de ? fg fgh:',
            scanLit(s, 'de', '?', 'fg', 'fgh') m.s.tok
    call out 'while HIJ  :' scanWhile(s, 'HIJ') m.s.tok
    call out 'end        :' scanEnd(s)
    call out 'while Jih  :' scanWhile(s, 'Jih') m.s.tok
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'end        :' scanEnd(s)
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit        :' scanLit(s) m.s.tok
    call scanSrc s, 'abcdefdef ?'
    call out 'until cba  :' scanUntil(s, 'cba') m.s.tok
    call out 'until ?qd  :' scanUntil(s, '?qd') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab??cd????gh?ijk'
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab) cd) ) gh) jk) )'
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call tstEnd t
    return
endProcedure tstSb2

tstScan: procedure expose m.
/*
$=/tstScan.1/
    ### start tst tstScan.1 ###########################################
    scan src a034,Und hr123sdfER"st1" 'str2''mit''apo''s' .
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    scan n tok 3: Und key  val .
    scan v tok 1:   key  val .
    scan n tok 10: hr123sdfER key  val .
    scan q tok 5: "st1" key  val st1
    scan v tok 1:   key  val st1
    scan a tok 19: 'str2''mit''apo''s' key  val str2'mit'apo's
    scan v tok 1:   key  val str2'mit'apo's
$/tstScan.1/ */
    call scanIni
    call tst t, 'tstScan.1'
    call tstScan1 'l"litEins"l"litZwei"ndv" "aq1' ,
        ,"a034,Und hr123sdfER""st1"" 'str2''mit''apo''s' "
    call tstEnd t

/*
$=/tstScan.2/
    ### start tst tstScan.2 ###########################################
    scan src a034,Und hr123sdfER"st1" 'str2''mit''apo''s' .
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    scan n tok 3: Und key  val .
    scan b tok 1:   key  val .
    scan n tok 10: hr123sdfER key  val .
    scan " tok 5: "st1" key  val st1
    scan b tok 1:   key  val st1
    scan ' tok 19: 'str2''mit''apo''s' key  val str2'mit'apo's
    scan b tok 1:   key  val str2'mit'apo's
$/tstScan.2/ */
    call tst t, 'tstScan.2'
    call tstScan1 'ndsb1' ,
        ,"a034,Und hr123sdfER""st1"" 'str2''mit''apo''s' "
    call tstEnd t

/*
$=/tstScan.3/
    ### start tst tstScan.3 ###########################################
    scan src a034,'wie 789abc
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    *** err: scanErr ending Apostroph(') missing
    .    e 1: last token  scanPosition 'wie 789abc
    .    e 2: pos 6 in string a034,'wie 789abc
    scan ' tok 1: ' key  val .
    scan n tok 3: wie key  val .
    scan s tok 1:   key  val .
    *** err: scanErr illegal number end after 789
    .    e 1: last token 789 scanPosition abc
    .    e 2: pos 14 in string a034,'wie 789abc
    scan d tok 3: 789 key  val .
    scan n tok 3: abc key  val .
$/tstScan.3/ */
    call tst t, 'tstScan.3'
    call tstScan1 'nds1' ,
        ,"a034,'wie 789abc"
    call tstEnd t

/*
$=/tstScan.4/
    ### start tst tstScan.4 ###########################################
    scan src litEinsefr 23 sdfER'str1'litZwei "str2""mit quo"s .
    scan l tok 7: litEins key  val .
    scan n tok 3: efr key  val .
    scan b tok 1:   key  val .
    scan d tok 2: 23 key  val .
    scan b tok 1:   key  val .
    scan n tok 5: sdfER key  val .
    scan a tok 6: 'str1' key  val str1
    scan l tok 7: litZwei key  val str1
    scan b tok 1:   key  val str1
    scan q tok 15: "str2""mit quo" key  val str2"mit quo
    scan n tok 1: s key  val str2"mit quo
    scan b tok 1:   key  val str2"mit quo
$/tstScan.4/ */
    call tst t, 'tstScan.4'
    call tstScan1 'l"litEins"l"litZwei"ndbaq1' ,
           ,"litEinsefr 23 sdfER'str1'litZwei ""str2""""mit quo""s "
    call tstEnd t

/*
$=/tstScan.5/
    ### start tst tstScan.5 ###########################################
    scan src  aha;+-=f ab=cdEf eF='strIng' .
    scan b tok 1:   key  val .
    scan k tok 4:  no= key aha val def
    scan ; tok 1: ; key aha val def
    scan + tok 1: + key aha val def
    scan - tok 1: - key aha val def
    scan = tok 1: = key aha val def
    scan k tok 4:  no= key f val def
    scan k tok 4: cdEf key ab val cdEf
    scan b tok 1:   key ab val cdEf
    scan k tok 8: 'strIng' key eF val strIng
    scan b tok 1:   key eF val strIng
$/tstScan.5/ */
    call tst t, 'tstScan.5'
    call tstScan1 'bk1'," aha;+-=f ab=cdEf eF='strIng' "
    call tstEnd t
    return
endProcedure tstScan

tstScanRead: procedure expose m.
/*
$=/tstScanRead/
    ### start tst tstScanRead #########################################
    name erste
    space
    name Zeile
    space
    nextLine
    nextLine
    space
    name dritte
    space
    name Zeile
    space
    name schluss
    space
$/tstScanRead/ */
    call scanReadIni
    call tst t, 'tstScanRead'
    b = jBuf('erste Zeile  ',,'  dritte Zeile  schluss  ')
    s = jOpen(jReset0(scanRead(b)), m.j.cRead)
    do while \scanEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanVerify(s, ' ') then call tstOut t, 'space'
        else if scanReadNL(s) then      call tstOut t, 'nextLine'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        end
    call jClose s
    call tstEnd t

/*
$=/tstScanReadMitSpaceLn/
    ### start tst tstScanReadMitSpaceLn ###############################
    name erste
    spaceLn
    name Zeile
    spaceLn
    name dritte
    spaceLn
    name Zeile
    spaceLn
    name schluss
    spaceLn
$/tstScanReadMitSpaceLn/ */
    call tst t, 'tstScanReadMitSpaceLn'
    s = scanReadOpen(scanRead(b))
    do forever
        if scanName(s) then         call out 'name' m.s.tok
        else if scanSpace(s) then call out 'spaceLn'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        else                        leave
        end
    call scanReadClose s
    call tstEnd t

/*
$=/tstScanJRead/
    ### start tst tstScanJRead ########################################
    1 jRead n tok erste val .
    2 jRead s tok   val .
    3 jRead n tok Zeile val .
    4 jRead s tok   val .
    5 jRead n tok dritte val .
    6 jRead s tok   val .
    7 jRead n tok Zeile val .
    8 jRead s tok   val .
    9 jRead n tok schluss val .
    10 jRead s tok   val .
    11 jRead 0 tok 1 val 1
    12 jRead s tok   val 1
    13 jRead + tok + val 1
    14 jRead s tok   val 1
    15 jRead 0 tok 2. val 2..
    16 jRead s tok   val 2..
    17 jRead + tok + val 2..
    18 jRead . tok . val 2..
    19 jRead s tok   val 2..
    20 jRead 0 tok +.3 val +.3
    21 jRead 0 tok -45e-3 val -45E-3
    22 jRead s tok   val -45E-3
    23 jRead " tok "a""b" val a"b
    24 jRead s tok   val a"b
    25 jRead ' tok 'c''d' val c'd
    className 1: ScanRes 18: ScanRes
$/tstScanJRead/ */

    call tst t, 'tstScanJRead'
    call jWrite jOpen(b,'>>'), '1  + 2. +. +.3-45e-3 "a""b"' "'c''d'"
    s = jOpen(jReset0(scanRead(jClose(b))), '<')
    do x=1 while jRead(s)
        v = m.s
        call out x 'jRead' m.v.type 'tok' m.v.tok 'val' m.v.val
        v.x = v
        end
    call jClose s
    call out 'className 1:' className(objClass(v.1)),
                    '18:' className(objClass(v.18))
    call tstEnd t
    return
endProcedure tstScanRead

tstScanUtilInto: procedure expose m.
/*
$=/tstScanUtilIntoL/
TEMPLATE P3
     DSN('DBAF.DA540769.A802A.P00003.BV5I3NRN.REC')
     DISP(OLD,KEEP,KEEP)
TEMPLATE P4
     DSN('DBAF.DA540769.A802A.P00004.BV5I3NTK.REC')
     DISP(OLD,KEEP,KEEP)
LOAD DATA        LOG NO RESUME NO REPLACE COPYDDN(TCOPYD)
           STATISTICS INDEX(ALL) REPORT NO UPDATE ALL
 EBCDIC  CCSID(00500,00000,00000)
 SORTKEYS
  -- ENFORCE NO
  SORTDEVT DISK
  SORTNUM 160
  WORKDDN(TSYUTD,TSOUTD)
  INTO TABLE OA1P.TWB981 PART 1 INDDN TREC134
 WORKDDN(TSYUTS,TSOUTS)
 INTO TABLE "A540769"
   ."TWK802A1"
 PART 00001 INDDN P0
 WHEN(00001:00002) = X'0041'
 ( "DE1"
  POSITION(  00003:00010) DECIMAL
 , "CH2"
  POSITION(  00011:00015) CHAR(00005)
 , "TS3"
  POSITION(  00016:00041) TIMESTAMP EXTERNAL
 , "TI4"
  POSITION(  00042:00049) TIME EXTERNAL
 , "DA5"
  POSITION(  00050:00059) DATE EXTERNAL
 , "IN6"
  POSITION(  00060:00063) INTEGER
 , "RE7"
  POSITION(  00064:00067) FLOAT(21)
 )
 INTO TABLE "A540769"."TWK802A1"
 PART 00002 INDDN P0
 WHEN(00001:00002) = X'0041'
 ( "DE1"
  POSITION(  00003:00010) DECIMAL
 , "CH2"
  POSITION(  00011:00015) CHAR(00005)
 )
 dobido
$/tstScanUtilIntoL/
$=/tstScanUtilInto/
  ### start tst tstScanUtilInto #####################################
  -- 1 scanUtilInto
  . ( "DE1"
  .  POSITION(  00003:00010) DECIMAL
  . , "CH2"
  .  POSITION(  00011:00015) CHAR(00005)
  . , "TS3"
  .  POSITION(  00016:00041) TIMESTAMP EXTERNAL
  . , "TI4"
  .  POSITION(  00042:00049) TIME EXTERNAL
  . , "DA5"
  .  POSITION(  00050:00059) DATE EXTERNAL
  . , "IN6"
  .  POSITION(  00060:00063) INTEGER
  . , "RE7"
  .  POSITION(  00064:00067) FLOAT(21)
  . ) .
  .  -- table OA1P.TWB981 part 00001
  -- 2 scanUtilInto
  . ( "DE1"
  .  POSITION(  00003:00010) DECIMAL
  . , "CH2"
  .  POSITION(  00011:00015) CHAR(00005)
  . ) .
  .  -- table A540769.TWK802A1 part 00002
  -- 3 scanUtilInto
$/tstScanUtilInto/ */

    call scanReadIni
    b = jBuf()
    call mAddst b'.BUF', mapInline('tstScanUtilIntoL')
    call tst t, 'tstScanUtilInto'
    s = jOpen(jReset0(scanUtilOpt(ScanRead(b))), '<')
    do ix=1
        call out '--' ix 'scanUtilInto'
        if \ scanUtilInto(s) then
            leave
        call out '  -- table' m.s.tb 'part' m.s.part
        end
    call tstEnd t
    return
endProcedure tstSCanUtilInto

tstScanWin: procedure expose m.
/*
$=/tstScanWin/
    ### start tst tstScanWin ##########################################
    info 0: last token  scanPosition erste     Zeile                 dr+
    itteZe\npos 1 in line 1: erste     Zeile
    name erste
    spaceNL
    name Zeile
    spaceNL
    name dritteZeeeile
    info 5: last token dritteZeeeile scanPosition    zeile4            +
    .    fuenfueberSechs\npos 1 in line 4:    zeile4
    spaceNL
    name zeile4
    spaceNL
    name fuenfueberSechsUnddSiebenUNDundUndUAcht
    spaceNL
    info 10: last token   scanPosition undZehnueberElfundNochWeiterZwoe+
    lfundim1\npos 9 in line 10:         undZehn
    name undZehnueberElfundNochWeiterZwoelfundim13
    spaceNL
    name Punkt
    infoE 14: last token Punkt scanPosition \natEnd after line 13: im13+
    .      Punkt
$/tstScanWin/ */
    call scanWinIni
    call tst t, 'tstScanWin'
    b = jBuf('?erste     Zeile?',,'?  dritteZeeeile?', '?   zeile4 ',
       ,'?          fuenf?', '?ueberSechsUnddS?', '?iebenUNDundUndU?',
       ,'?Acht           ?', '?               ?', '?        undZehn?',
       ,'?ueberElfundNoch?', '?WeiterZwoelfund?', '?im13      Punkt?')
    s = jOpen(jReset0(scanWin(b, '15@2')), m.j.cRead)
    call tstOut t, 'info 0:' scanInfo(s)
    do sx=1 while \scanEnd(s)
        if scanSpace(s) then call tstOut t, 'spaceNL'
        else if scanName(s) then        call tstOut t, 'name' m.s.tok
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        if sx // 5 = 0 then
            call tstOut t, 'info' sx':' scanInfo(s)
        end
    call tstOut t, 'infoE' sx':' scanInfo(s)
    call tstEnd t

/*
$=/tstScanWinRead/
    ### start tst tstScanWinRead ######################################
    info 0: last token  scanPosition erste     Zeile                z3 +
    com Ze\npos 1 in line 1: erste     Zeile
    name erste
    spaceNL
    name Zeile
    spaceNL
    name z3
    info 5: last token z3 scanPosition  com Zeeeile z4 come4          f+
    uenf\npos 4 in line 3:  z3 com Zeeeile
    spaceNL
    name z4
    spaceNL
    name fuenf
    spaceNL
    info 10: last token   scanPosition com    Sechs  com  sieben   comA+
    cht  com\npos 15 in line 5:     fuenf     c
    name com
    spaceNL
    name Sechs
    spaceNL
    name com
    info 15: last token com scanPosition   sieben   comAcht  com com   +
    . com\npos 2 in line 7: m  sieben   com
    spaceNL
    name sieben
    spaceNL
    name Acht
    spaceNL
    info 20: last token   scanPosition ueberElfundNochWeit com elfundim+
    13\npos 1 in line 11: ueberElfundNoch
    name ueberElfundNochWeit
    spaceNL
    name im13
    spaceNL
    name Punkt
    info 25: last token Punkt scanPosition \natEnd after line 13: im13 +
    .     Punkt
    infoE 26: last token Punkt scanPosition \natEnd after line 13: im13+
    .      Punkt
$/tstScanWinRead/ */
    call tst t, 'tstScanWinRead'
    b = jBuf('?erste     Zeile?',,'? z3 com Zeeeile?', '? z4 come4 ',
       ,'?    fuenf     c?', '?om    Sechs  co?', '?m  sieben   com?',
       ,'?Acht  com com  ?', '?  com          ?', '?  com   undZehn?',
       ,'?ueberElfundNoch?', '?Weit com elfund?', '?im13      Punkt?')
    s = jReset0(scanWin(b, '15@2'))
    call scanOpt s, , , 'com'
    call tstOut t, 'info 0:' scanInfo(jOpen(s, m.j.cRead))
    do sx=1 while \scanEnd(s)
        if scanName(s) then        call tstOut t, 'name' m.s.tok
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        if sx // 5 = 0 then
            call tstOut t, 'info' sx':' scanInfo(s)
        end
    call tstOut t, 'infoE' sx':' scanInfo(s)
    call tstEnd t
    return
endProcedure tstScanWin

tstScanSqlStmt: procedure expose m.
/*
$=/tstScanSqlStmt/
    ### start tst tstScanSqlStmt ######################################
    cmd1 select   current time                 stamp from s.1
    cmd2 update ";--""'/*"
    cmd3 delete '*/''"' / 3 - 1
    cmd4 terminator test
    cmd5 und so
    cmd6 term: ist
    cmd7 term>  in com nein >
    cmd8 .
$/tstScanSqlStmt/ */
    call pipeIni
    call scanWinIni
    call tst t, 'tstScanSqlStmt'
    b = jBuf('select -- /* c1', ' /* c1 */ current/* c2 " '' ',
       ,'c3"', '  c4   */time', 'stamp-- c5', 'from s.1; /* c6 */ ;' ,
       ,';update ";--""''/*";;       del123',
       , 'ete ''*/''''"'' / 3 - 1  -- c7', '/*c8 */   ' ,
       , ';terminator test; ','terminator|; und--  ', 'so| | |',
       , 'term: --#SET TERMINATOR : oder', 'ist: ',
       , 'term> /*--#SET TERMINATOR > oder', ' */ in com nein >:')
    call scanWinOpen scanSqlStmtOpt(scanWinReset(tstJcat, b, 30), ';')
    call scanSqlOpt tstJcat
    do sx=1 until nx = ''
        nx = scanSqlStmt(tstJCat)
        call tstOut t, 'cmd'sx nx
        end
    call scanReadCLose tstJCat
    call tstEnd t
/*
$=/tstScanSqlStmtRdr/
    ### start tst tstScanSqlStmtRdr ###################################
    cmd1 select   current time                 stamp from s.1
    cmd2 update ";--""'/*"
    cmd3 delete '*/''"' / 3 - 1
    cmd4 terminator test
    cmd5 und so
    cmd6 term: ist
    cmd7 term>  in com nein >
$/tstScanSqlStmtRdr/ */
    call tst t, 'tstScanSqlStmtRdr'
    r = jOpen(ScanSqlStmtRdr(b, 30), '<')
    do sx=1 while jRead(r)
        call tstOut t, 'cmd'sx m.r
        end
    call jClose r
    call tstEnd t
    return
endProcedure tstScanSqlStmt

tstScanSql: procedure expose m.
    call scanWinIni
/*
$=/tstScanSqlId/
    ### start tst tstScanSqlId ########################################
    sqlId ABC
    spaceNL
    sqlId AB__345EF
    spaceNL
$/tstScanSqlId/ */
    call tst t, 'tstScanSqlId'
    b = jBuf('abc  --  kommentar', right('ab_', 72), '_345ef-- kom')
    s = scanOpen(scanSqlReset(tstScn, b))
    do sx=1 while \scanEnd(s)
        if scanSqlID(s) then       call tstOut t, 'sqlId' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlDelimited/
    ### start tst tstScanSqlDelimited #################################
    sqlDeId ABC
    spaceNL
    sqlDeId AB_3F
    spaceNL
    sqlDeId abc
    spaceNL
    sqlDeId ab_Ef
    spaceNL
$/tstScanSqlDelimited/ */
    call tst t, 'tstScanSqlDelimited'
    b = jBuf('abc  --  kommentar',,'  -- ',,right('ab_', 72),'3F-- kom',
           , '"abc"  --  ko', ' -- ',,right('"ab_', 72),'Ef"-- kom')
    s = scanOpen(scanSqlReset(tstScn, b))
    do sx=1 while \scanEnd(s)
        if scanSqlDeID(s) then       call tstOut t, 'sqlDeId' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlQualified/
    ### start tst tstScanSqlQualified #################################
    sqlQuId ABC 1 ABC
    sqlQuId AB_3F 1 AB_3F
    sqlQuId abc 1 abc
    sqlQuId ab_Ef 1 ab_Ef
    sqlQuId EINS.Zwei.DREI 3 EINS
    sqlQuId vi er.fu  enf 2 vi er
$/tstScanSqlQualified/ */
    call tst t, 'tstScanSqlQualified'
    b = jBuf('abc  --  kommentar',,'  -- ',,right('ab_', 72),'3F-- kom',
           , '"abc"  --  ko', ' -- ',right('"ab_', 72),'Ef"-- kom',
           , 'eins."Zwei', '" -- com', ' . -- com', '  -- com',
           , 'drei -- ko', '"vi er"."fu  enf   " -- co')
    s = scanOpen(scanSqlReset(tstScn, b))
    do sx=1 while \scanEnd(s)
        if scanSqlQuID(s) then
            call tstOut t, 'sqlQuId' m.s.val m.s.val.0 m.s.val.1
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlNum/
    ### start tst tstScanSqlNum #######################################
    sqlNum 1
    spaceNL
    sqlNum 2..
    spaceNL
    sqlNum .3
    spaceNL
    sqlNum 4.5
    spaceNL
    sqlNum +6
    spaceNL
    sqlNum +7.03
    spaceNL
    sqlNum -8
    spaceNL
    sqlNum -.9
    spaceNL
    sqlNum 1E2
    spaceNL
    sqlNum -2.E-2
    spaceNL
    sqlNum +.3E+3
    spaceNL
$/tstScanSqlNum/ */
    call tst t, 'tstScanSqlNum'
    b = jBuf('1 2. .3 4.5 +6 + --kom', , ' -- com ', , ' 7.03 -8 -  .9',
             '1e2 - 2.e-2 + .3e+3')
    s = scanOpen(scanSqlReset(tstScn, b))
    do sx=1 while \scanEnd(s)
        if scanSqlNum(s) then
            call tstOut t, 'sqlNum' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlNumUnit/
    ### start tst tstScanSqlNumUnit ###################################
    sqlNumUnit 1 KB
    spaceNL
    sqlNumUnit .3 MB
    sqlNumUnit .5
    sqlNumUnit +6.E-5 B
    spaceNL
    sqlNumUnit -7
    char *
    spaceNL
    sqlNumUnit -.8
    char T
    char B
    spaceNL
    *** err: scanErr scanSqlNumUnit after +9. bad unit TB
    .    e 1: last token Tb scanPosition .
    .    e 2: pos 41 in line 1: 1 kb .3mB.5 +   6.e-5B -7* -.8 TB + 9.T+
    b
    sqlNumUnit +9..
    spaceNL
$/tstScanSqlNumUnit/ */
    call tst t, 'tstScanSqlNumUnit'
    b = jBuf('1 kb .3mB.5 +   6.e-5B -7* -.8 TB + 9.Tb')
    s = scanOpen(scanSqlReset(tstScn, b))
    do sx=1 while \scanEnd(s)
        if scanSqlNumUnit(s, 0, 'B KB MB') then
            call tstOut t, 'sqlNumUnit' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else if scanChar(s ,1) then call tstOut t, 'char' m.s.tok
        else call scanErr s, 'cannot scan'
        end
    call tstEnd t
    return
endProcedure tstScanSql

/*--- one single test scan with lines to scan in stem ln -------------*/
tstScan1:
parse arg classs, ln
    call tstOut t, 'scan src' ln
    call scanSbReset scanOpt(s), ln
    m.s.key = ''
    m.s.val = ''
    do forever
        x = tstScanType(s, classs)
        if x == '' then
           leave
        call tstOut t, 'scan' x 'tok' length(m.s.tok)':' m.s.tok ,
                  'key' m.s.key 'val' m.s.val
        end
    return
endProcedure tstScan1

tstScanType: procedure expose m.
parse arg s, opt
    cx = 1
    a2 = ''
    res = 0
    do while cx <= length(opt)
        f = substr(opt, cx, 1)
        cx = cx + 1
        if pos(substr(opt, cx, 1), "'""") > 0 then do
            m.tstScanType.src = opt
            m.tstScanType.pos = cx
            call scanString tstScanType
            a2 = m.tstScanType.val
            cx = m.tstScanType.pos
            end
        if      f == 'a' then
            res = scanString(s, "'")
        else if f == 'b' then
            res = scanSpace(s)
        else if f == 'c' then
            res = scanChar(s, a2)
        else if f == 'd' then
            res = scanNat(s, a2)
        else if f == 'k' then
            res = scanKeyValue(s, 'def')
        else if f == 'l' then
            res = scanLit(s, a2)
        else if f == 'q' then
            res = scanString(s, '"')
        else if f == 'v' then
            res = scanVerify(s, a2)
        else if f == 'w' then
            res = scanWord(s)
        else if f == 'y' then
            res = scanVerify(s, a2, 'm')
        if res then
            return f
        end
    return scanType(s)
endProcedure tstScanType


/****** tstData begin *************************************************/
tstData: procedure expose m.
parse arg ty 2 le 'n' nu, l, r, num
    call scanIni
    abs = abs(num)
    if nu \== '' then do
        if abs // 5 = 0 then
            return 'null' || (abs % 5 // nu + 1)
        end
    if ty = 'c' then do
        if le = '' then
            le = 8
        le = abs // le + 1
        if r = '' then
            r = '+'
        return left(l || num || r, le, right(r, 1))
        end
    if pos(ty, 'ief') < 1 then
        call err 'bad type' ty
    nn = abs
    if abbrev(num, '-') | abbrev(num, '+') then
        parse var num si 2 nn
    else
        si = ''
    if ty == 'e' then
        ex = 'e' || left('-', abs // 2) || (abs // 15)
    else
        ex = ''
    if le \== '' then
        nn = right(nn, abs // max(1, le - length(si||ex)) + 1, 1)
    if ty \== 'i' & (abs // 4) \= 0 & length(nn) > 1 then
            nn = overlay('.', nn, length(nn) - abs // length(nn))
    return si || nn || ex
endProcedure tstData

tstDataClassFo: procedure expose m.
parse arg flds
    ty = ''
    do fx=1 by 2 to words(flds)
        if word(flds, fx) = '.' then
            ty = ty', v'
        else
            ty = ty', f' word(flds, fx) 'v'
        end
    t = classNew('n* tstData u' substr(ty, 2))
    fo = oNew(m.t.name)
    ff = oFlds(fo)
    do fx=1 to m.ff.0
        f = fo || left('.', m.ff.fx \== '') || m.ff.fx
        m.f = word(flds, 2*fx)
        end
    return fo
endProcedure tstDataClassFo


tstDataClassOut: procedure expose m.
parse arg flds, f, t
    fo = tstDataClassFo(flds)
    ff = oFlds(fo)
    do x=f to t
        o = oCopy(fo)
        do fx=1 to m.ff.0
            f = o || left('.', m.ff.fx \== '') || m.ff.fx
            m.f = tstData(m.f, m.ff.fx, '+'m.ff.fx'+', x)
            end
        call out o
        end
    return
endProcedure tstDataClassOut
/****** tst **********************************************************
      test infrastructure
***********************************************************************/
/*--- migrate all compares to new ones:
        tstCI input compare
        tstCO ouput migrated compares
        tstCIO input and output -------------------------------------*/
tstUtTime: procedure expose m.
    say 'begin' utTime()  sysvar('sysnode')
    do 3000000
       end
    call sleep 1
    say 'end  ' utTime()
return

tstUtc2d: procedure expose m.
/*
$=/tstUtc2d/
    ### start tst tstUtc2d ############################################
    .             ff            255
    .           ffff          65535
    .          10000          65536          65536 = 1 * 16 ** 4
    .          10001          65537
    .         ffffff       16777215
    .        1000000       16777216       16777216 = 1 * 16 ** 6
    .        1000001       16777217
    .        20000FF       33554687
    .      100000000     4294967296     4294967296 = 1 * 16 ** 8
    .      300000000    12884901888    12884901888 = 3 * 16 ** 8
    .      3020000EF    12918456559
$/tstUtc2d/
*/
    numeric digits 33
    call tst t, 'tstUtc2d'
    all = 'ff ffff 10000 10001 ffffff 1000000 1000001 20000FF' ,
           '100000000 300000000 3020000EF'
    do ax = 1 to words(all)
        a = word(all, ax)
        if substr(a, 2) = 0 then
            b = right(left(a, 1) * 16 ** (length(a)-1), 15) ,
              '=' left(a, 1) '* 16 **' (length(a)-1)
        else
            b = ''
        call tstout t, right(a, 15)right(utc2d(x2c(a)), 15)b
        end
    call tstEnd t
    return
endProcedure tstUtc2d

tstCI: procedure expose m.
parse arg m, nm
    m.m.CIO = 0
    signal tstCIwork
tstCIO: procedure expose m.
parse arg m, nm
    m.m.CIO = 1
tstCIwork:
    m.m.name = nm
    m.m.cmp.1 = left('### start tst' nm '', 67, '#')

    do ix=2 to arg()-1
        m.m.cmp.ix = arg(ix+1)
        end
    m.m.cmp.0 = ix-1
    if m.m.CIO then
        call tstCO m
    return

tstCO: procedure expose m.
parse arg m
    call tst2dpSay m.m.name, m'.CMP', 68
    return
/*--- initialise m as tester with name nm
        use inline input nm as compare lines -----------------------*/
tstReset: procedure expose m.
parse arg m, nm
    call tstIni
    m.m.name = nm
    m.m.inIx  = 0
    m.m.out.0 = 0
    m.m.err   = 0
    m.err.count = 0
    m.tst.act = m
    if \ datatype(m.m.trans.0, 'n') then
        m.m.trans.0 = 0
    m.m.trans.old = m.m.trans.0
    return
endProcedure tstReset

tst: procedure expose m.
parse arg m, nm, cmpSt
    call tstReset m, nm
    m.tst.tests = m.tst.tests+1
    if cmpSt == '' then do
        cmpSt = mCut(t'.CMP', 0)
        call tst4dp cmpSt, mapInline(nm)
        end
    m.m.cmp = cmpSt
    m.m.moreOutOk = 0
    call mAdd mCut(m'.IN', 0), 'tst in line 1 eins ,'    ,
                     , 'tst in line 2 zwei ;   ' ,
                     , 'tst in line 3 drei .schluss.'
    call tstOut m, left('### start tst' nm '', 67, '#')
    call errReset 'h', 'return tstErrHandler(ggTxt)'
    m.m.errCleanup = m.err.cleanup
    if m.tst.ini.j \== 1 then do
        m.tst_m = m
/*      call err implement outDest 'i', 'call tstOut' quote(m)', msg'
*/      end
    else do
        drop m.tst_m
        call oMutatName m, 'Tst'
        call oMutatName m'.IN', 'Tst'
        m.m.jReading = 1
        m.m.jWriting = 1
        m.m.jUsers = 0
        m.m.in.jReading = 1
        m.m.in.jWriting = 1
        m.m.in.jUsers = 0
        if m.tst.ini.e \== 1 then do
            m.m.oldJin = m.j.in
            m.m.oldOut = m.j.out
            m.j.in = m'.IN'
            m.j.out = m
            end
        else do
            if m.pipe.0 <> 2 then
                call tstErr m, 'm.pipe.0' m.pipe.0 '<> 2'
            call pipe '+Ff', m , m'.IN'
            end
        end
    return m
endProcedure tst

tstEnd: procedure expose m.
parse arg m, opt opt2
    cmp = m.m.cmp
    m.m.jReading = 0
    m.m.jWriting = 0
    drop m.tst_m
    if m.tst.ini.j == 1 then do
        m.m.jReading = 0
        m.m.jWriting = 0
           if m.tst.ini.e \== 1 then do
            m.j.in = m.m.oldJin
            m.j.out = m.m.oldOut
            end
        else do
            if m.j.in \== m'.IN' | m.j.out \== m then
                call tstErr m, m.j.in '\==' m '|' m.j.out '\==' m
            call pipe '-'
            if m.pipe.0 <> 2 then
                call tstErr m, 'm.pipe.0' m.pipe.0 '<> 2'
            end
        end
    if m.m.err = 0 then
        if m.m.errCleanup \= m.err.cleanup then
            call tstErr m, 'err.Cleanup' m.err.cleanup '<> old',
                        m.m.errCleanup
    if m.m.out.0 \= m.cmp.0 & \ (m.m.moreOutOk,
            &  m.m.out.0 > m.cmp.0) then do
        call tstErr m, 'old' m.cmp.0 'lines \= new' m.m.out.0
        do nx = m.m.out.0 + 1 to min(m.m.out.0+10, m.cmp.0)
            say 'old -  ' m.cmp.nx
            end
        end
    call errReset 'h'
    m.tst.act = ''
    soll = 0
    if opt = 'err' then do
        soll = opt2
        if m.m.err \= soll then
            call err soll 'errors expected, but got' m.m.err
        end
    if m.m.err \= soll then do
        say 'new lines:' (m.m.out.0 - 1)
           call tst2dpSay m.m.name, m'.OUT', 68
        end
    say left('###' m.m.name 'end with' m.m.err 'errors ', 67, '#')

    if 1 & m.m.err \= soll then
        call err 'dying because of' m.m.err 'errors'
    m.m.trans.0 = m.m.trans.old
    return
endProcedure tstEnd

tst2dp: procedure expose m.
parse arg st, dp, ml
    dx = m.dp.0
    do sx=1 to m.st.0
        li = m.st.sx
        cx = 1
        do until cx > length(li)
            c = substr(li, cx, 1)
            ou = left('.', strip(c) == '' | pos(c, '.+') > 0)
            ex = min(length(li),cx+ml-length(ou)-2)
            ou = ou || substr(li, cx, ex+1-cx)
            dx = dx + 1
            c = right(ou, 1)
            if ex < length(li) then
                m.dp.dx = ou || '+'
            else if strip(c) == '' | pos(c, '.+') > 0 then
                m.dp.dx = ou || '.'
            else
                m.dp.dx = ou
            cx = ex +1
            end
        end
    m.dp.0 = dx
    return
endProcedure tst2dp

tst2dpSay: procedure expose m.
parse arg name, st, ml
    say '$=/'name'/'
    call tst2dp st, mCut('TST.TMP', 0), 68
    do nx=1 to m.tst.tmp.0
           say '   ' m.tst.tmp.nx
           end
     say '$/'name'/'
return tst2dpSay

tst4dp: procedure expose m.
parse arg st, dp
    sx = m.st.0
    inData = 0
    data = ''
    do dx=1 to m.dp.0
        li = strip(m.dp.dx)
        if pos(left(li, 1), '.+') > 0 then
         li = substr(li, 2)
        if right(li, 1) == '+' then do
            inData = 1
            data = data || left(li, length(li)-1)
            iterate
            end
        if right(li, 1) == '.' then
            li = left(li, length(li)-1)
        sx = sx + 1
        m.st.sx = repAll(data || li, '$ä', '/*', '$ö', '*/')
        inData = 0
        data = ''
        end
    m.st.0 = sx
    if inData then
        call err 'end inData'
    return
endProcedure tst4dp

/*--- write to test: say lines and compare them ----------------------*/
tstOut: procedure expose m.
parse arg m, arg
    do tx=m.m.trans.0 by -1 to 1
        arg = repAll(arg, word(m.m.trans.tx, 1),
            , subword(m.m.trans.tx, 2))
        end
    call mAdd m'.OUT', arg
    nx = m.m.out.0
    cmp = m.m.cmp
    c = m.cmp.nx
    if nx > m.cmp.0 then do
        if nx = m.cmp.0+1 & \ m.m.moreOutOK then
            call tstErr m, 'more new Lines' nx
        end
    else if c \== arg then do
        do cx=1 to min(length(c), length(arg)) ,
             while substr(c, cx, 1) == substr(arg, cx, 1)
             end
         msg = 'old line' nx '<> new overnext, firstDiff' cx',',
                 'len old' length(c)', new' length(arg)

        if cx > 10 then
            msg = overlay('|', msg, cx-10)
        call tstErr m, msg
        say c
        end
    say arg
    return 0
endProcedure tstOut

tstWrite: procedure expose m.
parse arg m, var
    cl = objClass(var, '')
    if cl == '' then do
        if var == '' then
            call tstOut t, 'tstR: @ obj null'
        else
            call tstOut m, var
  /*        call tstOut m, 'out:' var   */
  /*        call tstOut t, 'no class for' var 'in tstWriteO|'  */
        end
    else if abbrev(var, m.o_escW) then do
        call tstOut t, o2String(var)
        end
    else if cl == m.class_V then do
        call tstOut t, m.var
        end
    else if oKindOf(var, 'JRW') then do
        call tstOut t, 'tstWriteO kindOf JRW jWriteNow begin <<<'
        call jWriteNow m, var
        call tstOut t, 'tstWriteO kindOf JRW jWriteNow end   >>>'
        end
    else if oKindOf(var, 'ORun') then do
        call tstOut t, 'tstWriteO kindOf ORun oRun begin <<<'
        call oRun var
        call tstOut t, 'tstWriteO kindOf ORun oRun end   >>>'
        end
    else do
        do tx=m.m.trans.0 by -1 to 1 ,
                while word(m.m.trans.tx, 1) \== var
            end
        if tx < 1 then
            call mAdd M'.TRANS', var 'tstWriteoV' || (m.m.trans.0+1)
        call classOut , var, 'tstR: '
        end
    return
endProcedure tstWrite

tstRead: procedure expose m.
parse arg mP
    if right(mP, 3) == '.IN' then
       m = left(mP, length(mP)-3)
    else
        call err 'tstRead bad m' mP
    ix = m.m.inIx + 1
    m.m.inIx = ix
    if ix <= m.m.in.0 then do
        call tstOut m, '#jIn' ix'#' m.m.in.ix
        m.mP = m.m.in.ix
        return 1
        end
    call tstOut m, '#jIn eof' ix'#'
    return 0
endProcedure tstRead

tstFilename: procedure expose m.
parse arg suf, opt
    if m.err.os == 'TSO' then do
        dsn = dsn2jcl('~tmp.tst.'suf)
        if opt = 'r' then do
            if sysDsn("'"dsn"'") \== 'DATASET NOT FOUND' then
                call adrTso "delete '"dsn"'"
            call csiOpen 'TST.CSI', dsn'.**'
            do while csiNext('TST.CSI', 'TST.FINA')
                say 'deleting csiNext' m.tst.fina
                call adrTso "delete '"m.tst.fina"'"
                end
            end
        return dsn
        end
    else if m.err.os == 'LINUX' then do
        if abbrev(suf, '/') then
            fn = suf
        else
            fn = .Stream%%new('~/tmp/tst/'suf)%%qualify /* full path */
        cx = lastPos('/', fn)
        if cx > 0 then do
            dir = left(fn, cx-1)
            if \sysIsFileDirectory(dir) then
                call adrSh "mkdir -p" dir
            if \sysIsFileDirectory(dir) then
                call err 'tstFileName could not create dir' dir
            end
        if opt \= 'r' then
            nop
        else if sysIsFile(fn) then
            call sysFileDelete fn
        else if sysIsFileDirectory(fn) then
            call adrSh 'rm -r' fn
        return fn
        end
    else
        call err 'tstFilename does not implement os' m.err.os
endProcedure tstFilename

/*--- say total errors and fail if not zero --------------------------*/
tstTotal: procedure expose m.
    say '######'
    say '######'
    say '######' m.tst.tests 'tests with' ,
                 m.tst.err 'errors in' m.tst.errNames
    say '######'
    say '######'
    if m.tst.err \== 0 then
        call err m.tst.err 'errors total'
    return
endProcedure tstTotal

/*--- test err: message, count it and continue -----------------------*/
tstErr: procedure expose m.
parse arg m, msg
    say '### error' msg
    m.m.err = m.m.err + 1
    m.tst.err = m.tst.err + 1
    nm = m.m.name
    if wordPos(nm, m.tst.errNames) < 1 then
        m.tst.errNames = m.tst.errNames nm
    return 0
endProcedure tstErr

/*--- tstErrHandler: intercept errors --------------------------------*/
tstErrHandler: procedure expose m.
parse arg ggTxt
    m = m.tst.act
    if m == '' then
        call err ggTxt
    m.err.count = m.err.count + 1
    call splitNl err, errMsg(' }'ggTxt)
    call tstOut m.tst.act, '*** err:' m.err.1
    do x=2 to m.err.0
        call tstOut m, '    e' (x-1)':' m.err.x
        end
    return 0
endSubroutine tstErrHandler

tstTrc: procedure expose m.
parse arg msg
    m.tst.trc = m.tst.trc + 1
    say 'tstTrc' m.tst.trc msg
    return m.tst.trc
endProcedure tstTrc

/*--- tstIni: global initialization ----------------------------------*/
tstIni: procedure expose m.
    if m.tst.ini \== 1 then do
        m.tst.ini = 1
        call mapIni
        m.tst.err = 0
        m.tst.trc = 0
        m.tst.errNames = ''
        m.tst.tests = 0
        m.tst.act = ''
        end
    if m.tst.ini.j \== 1 & m.j.ini == 1 then do
        m.tst.ini.j = 1
          call classNew 'n Tst u JRW', 'm',
             , "jRead return tstRead(m)",
             , "jWrite call tstWrite m, line"
        end
    if m.tst.ini.e \== 1 & m.pipe.ini == 1 then do
        m.tst.ini.e = 1
        end
    return
endProcedure tstIni
/* copy tstAll end   **************************************************/
/* copy wshCopy begin ************************************************/
wshIni: procedure expose m.
    call compIni
    call sqlIni
    call scanWinIni
    return
endProcedure wshIni
/* copy wshCopy end   ************************************************/
/* copy db2Util begin ************************************************/
tstDb2Ut: procedure expose m.
/*
$=/tstDb2Ut/
$/tstDb2Ut/
*/
    call pipeIni
    call scanIni
    call tst t, 'tstDb2Ut'
    call mAdd mCut(t'.IN', 0), '   template old ,'    ,
                     , 'LOAD DATA INDDN oldDD ' ,
                     , '( cols  )'
call db2UtilPunch 'rep iDsn=DSN.INPUT.UNL'
call tstEnd t
return
endProcedure tstDb2Ut
/* ???????????? achtung nicht fertig |
          Idee: allgemein Punch Umformungs Utility
              aber man müsste wohl auf scan Util umstellen
                  und abstürzen wenn man etwas nicht versteht
          GrundGerüst von cadb2 umgebaut
????????????????? */

db2UtilPunch: procedure expose m.
parse upper arg args
    call scanSrc scanOpt(s), args
    a.rep = 1
    a.tb = ''
    a.trunc = 0
    a.iDD = ''
    a.iDSN = ''
    do while scanKeyValue(scanSkip(s), 1)
        ky = m.s.key
        say '????ky' ky m.s.val
        if wordPos(ky, 'REP TB TRUNC IDD IDSN') < 1 then
            call scanErr s, 'bad key' ky
        a.ky = m.s.val
        end
    if a.iDSN \== '' then do
        if a.iDD == '' then
            a.iDD = 'IDSN'
        call out '  TEMPLATE' a.iDD 'DSN('a.iDsn')'
        end
    do while in() & word(m.in, 1) <> 'LOAD'
        call out m.in
        end
    ll = space(m.in, 1)
    if \ abbrev(ll, 'LOAD DATA ') then
        call err 'bad load line:' m.in
    call out subword(m.in, 1, 2) 'LOG NO'
    if abbrev(ll, 'LOAD DATA INDDN ') then
        call db2UtilPunchInDDn word(ll, 4)
    else if \ abbrev(ll, 'LOAD DATA LOG ') then
        call err 'bad load line' ix':' m.i.ix
    if a.rep then
        call out '    STATISTICS INDEX(ALL) UPDATE ALL'
    call out '    DISCARDS 1'
    call out '    ERRDDN   TERRD'
    call out '    MAPDDN   TMAPD '
    call out '    WORKDDN  (TSYUTD,TSOUTD) '
    call mAdd o, '  SORTDEVT DISK '
    do ix=ix+1 to m.i.0
        if pos('CHAR(', m.i.ix) > 0 then
            call mAdd o, strip(m.i.ix, 't') 'TRUNCATE'
        else if word(m.i.ix, 1) word(m.i.ix, 3) == 'PART INDDN' then
            call mAdd o, m.i.ix,
                       , '  RESUME NO REPLACE COPYDDN(TCOPYD)' ,
                       , '  DISCARDDN TDISC '
        else
            call mAdd o, m.i.ix
        end
    call writeDsn oDsn ':~'iDsn, 'M.O.', , 1
    return
endProcedure db2UtilPunch

db2UtilPunchInDDn:
parse arg inDDn
     if a.iDD == '' then
         ll =  '    INDDN' inDDn
     else
         ll =  '    INDDN' a.iDD
     if a.rep then
         call out ll 'RESUME NO REPLACE COPYDDN(TCOPYD)'
     else
         call out ll 'RESUME YES'
     call out  '    DISCARDDN TDISC'
     return
endSubroutine db2UtilPunchInDDn
/* copy db2Util end   ************************************************/
/* copy time begin ****************************************************
 11.12.14 wk: added lrsn2uniq
 11.05.13 wk: numeric digits transparent: in jeder Procedure drin
              time2jul, tst externalisiert
**********************************************************************/
/*--- read timeZoneOffset and leapSeconds registers
        and set variables for uniq ----------------------------------*/
timeReadCvt: procedure expose m.
parse arg debug
    numeric digits 15
    /* offsets documented in z/OS Data Areas  Vol.1 */
    cvtOH      = '00000010'          /* cvt control block Address */
    cvtext2O   = x2d('00000560') /* offset  to extension 2    */
    cvtldtoO   = x2d('00000038') /* offset to timezone    */
    cvtlsoO    = x2d('00000050') /* offset to leapSeconds */

    /* CVT CB        address               + extention2   */
    cvtExt2A       = C2D(STORAGE(cvtOH,4)) + cvtext2O
    /* cvtLdto timeZone              address +offset      */
    m.time_Zone    = c2d(STORAGE(d2x(cvtext2A+cvtldtoO, 8), 8))
    m.time_StckUnit = 1e-6 / 256 / 16
    /* cvtLso LeapSecs               address +offset      */
    m.time_Leap    = c2d(STORAGE(d2x(cvtext2A+cvtlsoO, 8), 8))
    m.time_UQDigits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345678'
                         /* find lrsn of day 0 */
    m.time_UQZero = x2d(timeGmt2Lrsn('2004-12-31-00.00.22.000000')) ,
                   % 64 * 64    /* 0 out last 6 bits  */
    if debug == 1 then do
      say 'stckUnit          =' m.time_StckUnit
      say 'timeLeap          =' d2x(m.time_Leap,16) '=' m.time_Leap ,
                   '=' format(m.time_Leap * m.time_StckUnit,9,3) 'secs'
      say 'timeZone          =' d2x(m.time_Zone,16) '=' m.time_Zone,
                   '=' format(m.time_Zone * m.time_StckUnit,6,3) 'secs'
      say "cvtext2_adr       =" d2x(cvtExt2A, 8)
      say 'timeUQZero        =' m.time_UQZero
      say 'timeUQDigis       =' ,
                    length(m.time_UQDigits) 'digits' m.time_UQDigits
    end
    m.time_ReadCvt = 1
    return
endSubroutine timeReadCvt

timestampParse:
    parse arg yyyy'-'mo'-'dd'-'hh'.'mm'.'ss'.'ffffff
    return

/*--- GMT timestamp yyyy-mm.... -> stck value char(8)
         BLSUXTID is described in z/OS V1R7.0 MVS IPCS Customization
         BLSUXTID format : mo/dd/yyyy hh:mm:ss.ffffff ---------------*/
timeGmt2Stck: procedure expose m.
    parse arg tst
    call timestampParse tst
    tDate = mo'/'dd'/'yyyy' 'hh':'mm':'ss'.'ffffff
    ACC=left('', 8, '00'x)
    ADDRESS LINKPGM "BLSUXTID TDATE ACC"
    RETURN acc
endProcedure timeGmt2Stck

/*--- GMT timestamp yyyy-mm.... -> stck value in hex(16) ------------*/
timeGmt2LRSN: procedure expose m.
    return c2x(left(timeGmt2Stck(arg(1)), 6))
endProcedure timeGmt2LRSN

/*--- LZT (Locale Zurich Tst -> stck value in hex(16) ---------------*/
timeLZT2LRSN: procedure expose m.
parse arg tst
    numeric digits 23
    if m.time_ReadCvt \== 1 then
        call timeReadCvt
    return left(d2x(c2d(timeGmt2Stck(tst)) ,
                     - m.time_Zone + m.time_Leap, 16), 12)
endProcedure timeLZT2LRSN
/*--- conversion from Stck Clock Value to GMT Timestamp
        BLSUXTOD is described in z/OS V1R7.0 MVS IPCS Customization
        input -> + leapseconds -> output ----------------------------*/
timeStck2Gmt: PROCEDURE expose m.
parse arg stck
  stck = left(stck, 8, '00'x)
  TDATE = COPIES('0' , 26)
  ADDRESS LINKPGM "BLSUXTOD stck TDATE"
  /* return format   : mo/dd/yyyy hh:mm:ss.ffffff */
  /* Timestamp format: yyyy-mm-dd-hh.mm.ss.ffffff */
  parse var TDATE mo '/' dd '/' yyyy hh ':' mm ':' ss '.' ffffff
RETURN yyyy'-'mo'-'dd'-'hh'.'mm'.'ss'.'ffffff
endProcedure timeStck2Gmt

/*--- conversion from Lrsn Clock Value to GMT Timestamp -------------*/
timeLrsn2Gmt:
    return timeStck2Gmt(x2c(arg(1)))
endProcedure timeLrsn2Gmt

/*--- conversion from Lrsn Clock Value to LZT Local Zurich Time -----*/
timeLrsn2LZT: procedure expose m.
parse arg lrsn
    numeric digits 23
    if m.time_ReadCvt \== 1 then
        call timeReadCvt
    return timeStck2Gmt(d2c(x2d(left(lrsn, 16, 0)) ,
                           + m.time_Zone-m.time_Leap))
endProcedure timeLrsn2LZT

/*--- timestamp to julian --------------------------------------------*/
time2jul: procedure expose m.
parse arg yyyy '-' mm '-' dd '-'
        /* date function cannot convert to julian, only from julian
            ==> guess a julian <= the correct and
                try the next values
        */
    j = trunc((mm-1) * 29.5) + dd
    yy = right(yyyy, 2)
    do j=j by 1
        j = right(j, 3, 0)

        d = date('s', yy || j, 'j')
        if substr(d, 3) = yy || mm || dd then
            return yy || j
        end
    return
endProcedure time2jul
/* convert a lrsn to the uniq variable ********************************/
timeLrsn2uniq: procedure expose uniqZero uniqDigits debug
parse arg lrsn
    /* unique are bits 0:41 of the TodClock value
              minus 31.12.2004 represented
              by base 35 by 'ABC...YZ01..8'
    */
    if m.time_ReadCvt \== 1 then
        call timeReadCvt
    lrsn = left(lrsn, 12, 0)
    numeric digits 15
    diff = x2d(lrsn) - m.time_UQZero
    if diff < 0 then
        return'< 2005'
    return right(i2q(diff % 64, m.time_UQDigits), 8, 'A')
endProcedure timeLrsn2Uniq

/* convert a uniq variable to lrsn ************************************/
timeUniq2lrsn: procedure expose m.
parse arg uniq
    if m.time_ReadCvt \== 1 then
        call timeReadCvt
    numeric digits 15
    u1 = q2i(left(uniq, 8, 'A'), m.time_UQDigits) * 64
    lrsn = right(d2x(u1 + m.time_UQZero), 12, 0)
    return lrsn
endProcedure uniq2lrsn
/*--- translate a number in q-system to decimal
       arg digits givs the digits corresponding to 012.. in the q sysem
       q = length(digits) --------------------------------------------*/
q2i: procedure expose m.
parse arg v, digits
    b = length(digits)
    i = 0
    do x = 1 to length(v)
        q = substr(v, x, 1)
        r = pos(q, digits)
        if r < 1 then
            call err 'bad digit' q 'in' v 'valid digits' digits
        i = i * b + r - 1
        end
    return i
endProcedure q2i
/*--- translate a decimal number to q-system - inverse of q2i --------*/
i2q: procedure expose m.
parse arg i, digits
    if i = 0 then
        return left(digits, 1)
    b = length(digits)
    v = ''
    do while i > 0
        v = substr(digits, 1 + (i // b), 1) || v
        i = i % b
        end
    return v
endProcedure i2q
/* copy time end -----------------------------------------------------*/
/* copy sort begin ****************************************************/
sortComparator: procedure expose m.
parse arg cmp, le, ri
    if cmp == '' then
        cmp = '<<='
    if length(cmp) < 6 then
        m.sort_comparator = 'cmp =' le cmp ri
    else if pos(';', cmp) < 1 then
        m.sort_comparator = "aLe =" le"; aRi =" ri"; cmp =" cmp
    else
        m.sort_comparator = "aLe =" le "; aRi =" ri";" cmp
    return
endProcedure sort

sortWords: procedure expose m.
parse arg wrds, cmp
    if words(wrds) <= 1 then
        return strip(wrds)
    m.sort_ii.0 = words(wrds)
    do sx=1 to m.sort_ii.0
        m.sort_ii.sx = word(wrds, sx)
        end
    call sort sort_ii, sort_oo, cmp
    r = m.sort_oo.1
    do sx=2 to m.sort_oo.0
        r = r m.sort_oo.sx
        end
    return r
endProcedure sortWords

sortWordsQ: procedure expose m.
parse arg wrds, cmp
    call sortComparator cmp, 'word(le, lx)', 'word(ri, rx)'
    return strip(sortWord1(wrds))
endProcedure sortWordsQ

sortWord1: procedure expose m.
parse arg wrds
    if words(wrds) <= 1 then
        return wrds
    h = words(wrds) % 2
    le = sortWord1(subWord(wrds, 1, h))
    ri = sortWord1(subWord(wrds, h+1))
    lx = 1
    rx = 1
    res = ''
    do forever
        interpret m.sort_comparator
        if cmp then do
            res = res word(le, lx)
            if lx >= words(le) then
                return res subword(ri, rx)
            lx = lx + 1
            end
        else do
            res = res word(ri, rx)
            if rx >= words(ri) then
                return res subword(le, lx)
            rx = rx + 1
            end
        end
endProcedure sortWord1

sort: procedure expose m.
parse arg i, o, cmp
    call sortComparator cmp, 'm.l.l0', 'm.r.r0'
    call sort1 i, 1, m.i.0, o, 1, sort_work, 1
    m.o.0 = m.i.0
    return
endProcedure sort

sort1: procedure expose m.
parse arg i, i0, le, o, o0, w, w0
    if le <= 3 then do
        if le = 3 then do
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, w, w0
            call sortMerge i, i0+2, i0+3, w, w0, w0+2, o, o0
            end
        else if le = 2 then
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, o, o0
        else if le = 1 then
            m.o.o0 = m.i.i0
        return
        end
    h = (le + 1) % 2
    call sort1 i, i0,   h,    o, o0+le-h, w, w0
    call sort1 i, i0+h, le-h, w, w0,      o, o0
    call sortMerge o, o0+le-h, o0+le, w, w0, w0+le-h, o, o0
    return
endProcedure sort1

sortMerge: procedure expose m.
parse arg l, l0, le, r, r0, re, o, o0
    do while l0 < le & r0 < re
        interpret m.sort_comparator
        if cmp then do
            m.o.o0 = m.l.l0
            l0 = l0 + 1
            end
        else do
            m.o.o0 = m.r.r0
            r0 = r0 + 1
            end
        o0 = o0 + 1
        end
    do while l0 < le
        m.o.o0 = m.l.l0
        l0 = l0 + 1
        o0 = o0 + 1
        end
    do while r0 < re
        m.o.o0 = m.r.r0
        r0 = r0 + 1
        o0 = o0 + 1
        end
    return
endProcedure sortMerge
/* copy sort end   ****************************************************/
/* copy match begin ***************************************************/
/*--- 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
    if symbol('m.match_m.mask') == 'VAR' then
        interpret m.match_m.mask
    else
        interpret matchGen('MATCH_M.'mask, mask, 'm')
endProcedure match

matchGG: procedure expose m.
parse arg wert, cd, vars
    interpret cd
endProcedure matchGG

matchVars: procedure expose m.
parse arg wert, mask, vars
    if symbol('m.match_v.mask') == 'VAR' then
        interpret m.match_v.mask
    else
        interpret matchGen('MATCH_V.'mask, mask, 'v')
endProcedure match

matchRep: procedure expose m.
parse arg wert, mask, mOut
    vars = 'MATCH_VV'
    mm = mask'\>'mOut
    if symbol('m.match_r.mm') == 'VAR' then
        interpret m.match_r.mm
    else
        interpret matchGen('MATCH_R.'mm, mask, 'r', mOut)
endProcedure matchRep

matchGen: procedure expose m.
parse arg m, mask, opt, mOut
    a = matchScan(match_sM, mask)
    if symbol('m.match_g') \== 'VAR' then
        m.match_g = 0
    if opt \== 'r' then do
        r = matchgenMat(a, opt, 1, m.a.0, 0)
        end
    else do
        m.match_g = m.match_g + 1
        sub = 'MATCH_G'm.match_g
        m.sub = matchgenMat(a, 'v', 1, m.a.0, 0)
        o = matchScan(match_sO, mOut)
        r = matchGenRep(o, m.a.wildC)
        r = 'if matchGG(wert, m.'sub', vars) then return' r';' ,
            'else return "";'
        end
    m.m = r
    return r
endProcedure matchGen

matchScan: procedure expose m.
parse arg a, mask, opt
    s = match_scan
    call scanSrc s, mask
    ax = 0
    vx = 0
    m.a.wildC = ''
    do forever
        if scanUntil(s, '*?&\') then do
            if m.a.ax == 'c' then do
                m.a.ax.val = m.a.ax.val || m.s.tok
                end
            else do
                ax = ax + 1
                m.a.ax = 'c'
                m.a.ax.val = m.s.tok
                end
            end
        else if scanChar(s, 1) then do
            if pos(m.s.tok, '*?') > 0 then do
                ax = ax + 1
                vx = vx + 1
                m.a.ax = m.s.tok
                m.a.ax.ref = vx
                m.a.wildC = m.a.wildC || m.s.tok
                end
            else if m.s.tok == '\' then do
                call scanChar s, 1
                if pos(m.s.tok, '\*?&') < 1 then
                    return scanErr(s, 'bad char after \')
                if abbrev(m.a.ax, 'c') then
                    m.a.ax.val = m.a.ax.val || m.s.tok
                else do
                    ax = ax + 1
                    m.a.ax = 'c'
                    m.a.ax.val = m.s.tok
                    end
                end
            else if m.s.tok == '&' then do
                if opt \== 'r' then
                    call scanErr s, '& in input'
                if \ scanChar(s,1) | pos(m.s.tok, 'ms123456789')<1 then
                    call scanErr s, 'bad & name' m.s.tok
                ax = ax + 1
                m.a.ax = '&'
                m.a.ax.ref = m.s.tok
                end
            else
                call scanErr s, 'bad char 1 after until'
            end
        else
            leave
        end
    m.a.0 = ax
    if vx \== length(m.a.wildC) then
        call scanErr 'vars' m.a.wildC 'mismatches' vx
    return a
endProcedure matchScan

matchGenMat: procedure expose m.
parse arg a, opt, fx, tx, minLL
    ml = 0
    if fx == 1 then do
        do ax=1 to m.a.0
            if m.a.ax == '?' then
               ml = ml + 1
            else if m.a.ax == 'c' then
               ml = ml + length(m.a.ax.val)
            m.a.minLen.ax = ml
            end
        end
    r = ''
    ret1 = ''
    ret1After = ''
    lO = 0
    do fy=fx to tx
        if m.a.fy == 'c' then do
            r = r 'if substr(wert,' (1+lO)
            if fy < m.a.0 then
                r = r',' length(m.a.fy.val)
            r = r') \==' quote(m.a.fy.val, "'") 'then return 0;'
            lO = lO + length(m.a.fy.val)
            end
        else if m.a.fy == '?' then do
            lO = lO + 1
            if opt == 'v' then
                ret1 = ret1 'm.vars.'m.a.fy.ref ,
                        '= substr(wert,' lO', 1);'
            end
        else if m.a.fy == '*' then
            leave
        else
            call err 'bad match ast' a'.'fy m.a.fy
        end
    rO = 0
    do ty=tx by -1 to fy
        if m.a.ty == 'c' then do
            rO = rO + length(m.a.ty.val)
            r = r 'if substr(wert, length(wert) -' (rO - 1)',' ,
                  length(m.a.ty.val)')' ,
                  '\==' quote(m.a.ty.val, "'") 'then return 0;'
            end
        else if m.a.ty == '?' then do
            rO = rO + 1
            if opt == 'v' then
                ret1 = ret1 'm.vars.'m.a.fy.ref ,
                        '= substr(wert, length(wert) -' (rO-1)', 1);'
            end
        else if m.a.ty ==  '*' then
            leave
        else
            call err 'bad match ast' a'.'fy m.a.fy
        end
    if fy > ty then do /* every thing is handled with fix len */
        if fx = tx & abbrev(m.a.fx, 'c') then
            r = 'if wert \==' quote(m.a.fx.val, "'") ,
                               'then return 0;'
        else
            r = 'if length(wert) \==' (lO + rO) 'then return 0;' r
        end
    else do
        myMiLe = m.a.minLen.ty
        if fy > 1 then do
            fq = fy -1
            myMiLe = myMiLe - m.a.minLen.fq
            end
        if minLL < myMiLe then
            r = 'if length(wert) <' myMiLe 'then return 0;' r
        if fy = ty & m.a.fy == '*' then     /* single * */
            ret1  = ret1 'm.vars.'m.a.fy.ref ,
                 '= substr(wert,' (1+lO)', length(wert) -' (lO+rO)');'
        else if fy < ty & abbrev(m.a.fy, '*') ,
                        & abbrev(m.a.ty, '*') then do
                                /* several variable length parts */
            suMiLe = m.a.minLen.ty - m.a.minLen.fy
            m.match_g = m.match_g + 1
            sub = 'MATCH_G'm.match_g
            m.sub = matchGenMat(a, opt, fy+1, ty, suMiLe)
            if rO = 0 then
                subV = 'substr(wert, lx)'
            else do
                r = r 'wSub = left(wert, length(wert) -' rO');'
                subV = 'substr(wSub, lx)'
                end
            r = r 'do lx = length(wert) -' (suMiLe+rO-1) ,
                       'by -1 to' (lO+1)';' ,
                       'if \ matchGG('subV', m.'sub', vars) then' ,
                            'iterate;'
            ret1  = ret1 'm.vars.'m.a.fy.ref '= substr(wert,' (lO+1) ,
                     ||  ', lx -' (lO+1)');'
            ret1After = 'end; return 0;'
            end
        else
            call err 'matchGenMat bad case' a'.'fy m.a.fy a'.'ty m.a.ty
        end
    if opt == 'v' & fx == 1 then do
        if r <> '' then
           r = 'm.vars.0 = -9;' r
        ret1 = ret1 'm.vars.0 =' length(m.a.wildC)';'
        end
    r = r ret1 'return 1;' ret1After
    return r
endProcedure matchGenMat

matchGenRep: procedure expose m.
parse arg o, wildC
    xQ = 0
    xS = 0
    do ox=1 to m.o.0
        if m.o.ox == '?' then do
             xQ = pos('?', wildC, xQ+1)
             if xQ < 1 then
                 call err 'unmatchted ?' ox
             m.o.ox.re2 = xQ
             end
        else if m.o.ox == '*' then do
             xS = pos('*', wildC, xS+1)
             if xS < 1 then
                 call err 'unmatchted *' ox
             m.o.ox.re2 = xS
             end
        else if m.o.ox == '&' & m.o.ox.ref >> '0' then do
            if m.o.ox.ref > length(wildC) then
                 call err '&'m.o.ox.ref 'but wildcards' wildC
            xQ = m.o.ox.ref
            xS = xQ
            m.o.ox.re2 = xQ
            end
        end
    r = ''
    do ox=1 to m.o.0
        if abbrev(m.o.ox, 'c') then
            r = r '||' quote(m.o.ox.val, "'")
        else if m.o.ox == '&' & m.o.ox.re2 == 's' then
            r = r '|| wert'
        else if m.o.ox == '&' & m.o.ox.re2 == 'm' then
            r = r '||' quote(mask, "'")
        else if pos(m.o.ox, '*?&') > 0 then
            r = r '|| m.vars.'m.o.ox.re2
        end
    if r=='' then
        return "''"
    else
        return substr(r, 5)
endProcedure matchGenRep

/* copy match end *****************************************************/
/* copy comp begin *****************************************************
    the shell compiler
         syntax and semantics of the shell language see javaDoc
***********************************************************************/
/***** initialisation *************************************************/
/*--- module initialisation ------------------------------------------*/
compIni: procedure expose m.
    if m.compIni = 1 then
        return
    m.compIni = 1
    call pipeIni
    call scanReadIni
    cc = classNew('n Compiler u')
    call mNewArea 'COMP.AST', '='

    m.comp_chOp   = '.-<@|?%^'
    m.comp_chKind = '.-=#@:%^'
    m.comp_chKiNO = '=:#'
    m.comp_chKiNBOE = '=<#:' /* nonBLock only expression not Primary*/
    call mPut 'COMP.INFO..', "object"
    call mPut 'COMP.INFO.-', "string"
    call mPut 'COMP.INFO.=', "skeleton"
    call mPut 'COMP.INFO.#', "text"
    call mPut 'COMP.INFO.@', "rexxShell"
    call mPut 'COMP.INFO.:', "pureShell"

    m.comp_chDol = '$'
    m.comp_chSpa = ' 'x2c('09')
    call mPut 'COMP.EXTYPE.b', m.comp_chDol'{}'       /* braces */
    call mPut 'COMP.EXTYPE.d', m.comp_chDol            /* data */
    call mPut 'COMP.EXTYPE.s', m.comp_chDol            /* strip */
    call mPut 'COMP.EXTYPE.w', m.comp_chDol||m.comp_chSpa /* word */

    m.comp.idChars = m.ut_alfNum'@_'
    m.comp.wCatC = 'compile'
    m.comp.wCatO = 'out'
    m.comp.wCatS = 'do withNew with for forWith ct proc table'
    return
endProcedure compIni

compReset: procedure expose m.
parse arg m
    m.m.scan = m'.scan'
    return m
endProcedure compReset

/*--- constructor of Compiler ----------------------------------------*/
comp: procedure expose m.
parse arg src
    nn = oNew('Compiler')
    m.nn.cmpRdr = in2File(src)
    return nn
endProcedure comp

/**** user interface **************************************************/
/*--- compile and run ------------------------------------------------*/
compRun: procedure expose m.
parse arg spec, inO, ouO, infoA
    cmp = comp(inO)
    r = compile(cmp, spec)
    if infoA \== '' then
        m.infoA = 'run'
    if ouO \== '' then
        call pipe '+F', ouO
    call oRun r
    if ouO \== '' then
        call pipe '-'
    return 0
endProcedure compRun

/*--- compile the source and return an oRunner with the code ---------*/
compile: procedure expose m.
parse arg m, spec
    call compReset m
    s = m.m.scan
    if spec = '' then
        spec = '@'
    else
        spec = strip(spec, 'l')
    if m.m.cmpRdr == '' then do
        call scanSbReset scanOpt(scanSbReset(s, spec), , '0123456789')
        end
    else do
        call scanReadOpen scanReadReset(s, m.m.cmpRdr, , '0123456789'),
                          , 1, spec
        end
    m.m.out = ''
    m.m.end = ''
    res = compAst(m, '¢')
    eOld = m.err.count
    do forever
        one = ''
        if pos(scanLook(s, 1), m.comp_chKind'*') > 0 then do
            call scanChar s, 1
            ki = m.s.tok
            one = compileOne(m, ki)
            end
        else if \ scanName(s) then do
            call scanErr s, 'kind or hook expected after $#'
            end
        else if m.s.tok == 'out' then do
            m.m.out = scanPos(s)
            leave
            end
        else if m.s.tok == 'end' then do
            if m.m.end = '' then
                m.m.end = scanPos(s)
            one = compileOne(m)
            end
        else if m.s.tok == 'version' then do
            call scanSpace s
            if \ scanWord(s) | m.s.tok \== '2.0' then
                call scanErr s, 'only version 2.0 is supported'
            call scanNl s, 1
            end
        else do
            one = compileOne(m, , m.s.tok)
            end
        if one \== '' then
            call mAdd res, one
        if scanEnd(s) then
            leave
        if \ scanLit(s, '$#') then
            return scanErr(s, m.comp.info.ki "expected: compile",
                 ki "stopped before end of input")
        end
    if m.m.cmpRdr \== '' then
        call scanReadClose s
    if m.err.count <> eOld then
        return ''
    return oRunner(compAst2Rx(m, ';', res))
endProcedure compile

/*--- compile or use hook for one part from spec or input -----------*/
compileOne: procedure expose m.
parse arg m, ki, hook
    s = m.m.scan
    call compSpComment m
    if ki == '*' | m.m.end \== '' then do
        do until scanLook(s, 2) == '$#' | scanEnd(s)
            call scanNl s, 1
            end
        return ''
        end
    if ki \== '' then do
        call scanNl s
        return compUnit(m, ki, '$#')
        end
    say 'interpreting hook' hook':' strip(scanLook(s))
    interpret return 'wshHook_'hook'(m)'
endProcedure compileOne

/*--- parse the whole syntax of a unit ------------------------------*/
compUnit: procedure expose m.
parse arg m, ki, stopper
    s = m.m.scan
    if pos(ki, m.comp_chKind) < 1 then
        return scanErr(s, 'bad kind' ki 'in compUnit(...'stopper')')
    else if ki <> '#' then do
        a = compAst(m, '¢')
        do forever
            one = compPipe(m, ki)
            if one \== '' then
             call mAdd a, one
            if \ scanLit(m.m.scan, '$;', '$<>') then
                   return a
            end
        end
    else do
        res = compAST(m, '¢')
        call scanChar s
        if verify(m.s.tok, m.comp_chSpa) > 0 then
            call mAdd res, compAstAddOp(m,
                , compAst(m, '=', strip(m.s.tok, 't')), '$')
        do while scanNL(s, 1) & \ abbrev(m.s.src, stopper)
            call mAdd res, compAstAddOp(m,
                , compAst(m, '=', strip(m.s.src, 't')), '$')
            end
        return res
        end
endProcedure compUnit

/*--- compile an expression, type d=data, b=block w=word, s=strip ---*/
compExpr: procedure expose m.
parse arg m, type, ki
    s = m.m.scan
    if symbol('m.comp.exType.type') \== 'VAR' then
        call err s, 'bad type' type 'in compExpr'
    if ki == '#' then do
        call scanChar(s)
        if type == 's' then
            res = compAst(m, '=', strip(m.s.tok))
        else
            res = compAst(m, '=', , m.s.tok)
        res = compAST(m, '-', , res)
        m.res.containsC = 1
        m.res.containsD = 1
        return res
        end
    else if ki == '%' | ki == '^' then do
        call compSpComment m
        vr = compVar(m, left('c', ki == '^'))
        if vr == '' then
            return ''
        if m.vr.var == 'c' then
            res = compAst(m, 'M')
        else
            res = compAst(m, ki, , compASTAddOp(m, vr, '&'))
        call compSpComment m
        ex = compOpBE(m, '=')
        if ex \== '' then
            call mAdd res, ex
        call compSpComment m
        m.res.containsC = 1
        m.res.containsD = 1
        return res
        end

    if length(ki) \== 1 | pos(ki, '.-=@') < 1 then
        return scanErr(s, 'bad kind' ki 'in compExpr')
    res = compAST(m, translate(ki, '-', '='))
    m.res.containsC = 0
    astKi = translate(ki, 'os=c', '.-=@')
    laPrim = 0
    gotTxt = 0
    if pos(type, 'sb') > 0 then
        m.res.containsC = compSpComment(m) >= 2
    do forever
        if scanVerify(s, m.comp.exType.type, 'm') then do
            one = compAST(m, astKi, m.s.tok)
            if verify(m.s.tok, m.comp_chSpa) > 0 then
                gotTxt = 1
            end
        else do
            old = scanPos(s)
            if \ scanLit(s, m.comp_chDol) then
                leave

            if pos(scanLook(s, 1), '.-') > 0 then
                one = compCheckNN(m, compOpBE(m, , 1, 0),
                   , 'primary block or expression expected')
            else
                one = compPrimary(m)
            if one = '' then do
                call scanSetPos s, old
                leave
                end
            laPrim = m.res.0 + 1
            end
        call mAdd res, one
        if compComment(m) then
            m.res.containsC = 1
        end
    if pos(type, 'bs') > 0 then do
        do rx=m.res.0 by -1 to laPrim+1
            one = m.res.rx
            m.one.text = strip(m.one.text, 't')
            if length(m.one.text) <> 0 then
                leave
            call mFree one
            end
        m.res.0 = rx
        end
    m.res.containsD = laPrim > 0 | gotTxt
    return compAstFree0(res, '')
endProcedure compExpr

/*--- compile a primary and return code ------------------------------*/
compPrimary: procedure expose m.
parse arg m, ops
    s = m.m.scan
    if scanString(s) then
        return compASTAddOp(m, compAST(m, '=', m.s.val), ops)
    r = compVar(m, left('c', right(ops, 1) == '^'))
    if r == '' then
        return ''
    if m.r.var \== 'c' then
         return compASTAddOp(m, compAst(m, '&', m.r.var, r), ops)
    else
         return compASTAddOp(m, compAst(m, 'M'),
                              , left(ops, length(ops)-1))
endProcedure compPrimary

/*--- oPBE ops (primary or block or expression)
       oDef = default Kind, oPre = opPrefix,
       uniq=1 extract unique, uniq='<' prefix < ----------------------*/
compOpBE: procedure expose m.
parse arg m, oDef, uniq, withEx
    s = m.m.scan
    old = scanPos(s)
    if \ scanVerify(s, m.comp_chOp || m.comp_chKiNO) then
        op = oDef
    else
        op = left('<', uniq == '<') || m.s.tok
    x = verify(op, '%^', 'm')
    if x > 0 & x < length(op) then
        call scanErr s, 'old syntax? run not at end'
    kx = verify(op, m.comp_chKiNO, 'm')
    if kx = length(op) then
        nop
    else if kx \== 0 then
        call scanErr s, 'kind' substr(op, kx, 1) 'not after ops'
    else if pos(right(op, 1), m.comp_chKind) > 0 then
        nop
    else if right(op, 1) == '<' then
        op = op'='
    else
        call scanErr s, 'no kind after ops' op
    if pos(scanLook(s, 1), '/¢') > 0 then do
        if uniq == 1 & length(op) == 1 then
            if op == '.' then
                op = '|.'
            else if pos(op, '-@<') > 0 then
                op = op || op
        return compBlock(m, op)
        end
    if compSpComment(m) == 0 ,
        & pos(right(op, 1), m.comp_chKiNBOE) <= 0 then
        return compPrimary(m, op)
    if withEx \== 0 then do
        res = compExpr(m, 's', right(op, 1))
        if pos(right(op, 1), m.comp_chKind) < 1 then
            op = left(op, kx-1)
        if res \== '' then
            return compASTAddOp(m, res, op)
        end
    call scanSetPos s, old
    return ''
endProcedure compOPBE

/*--- compile a pipe and return code ---------------------------------*/
compPipe: procedure expose m.
parse arg m, ki
    s = m.m.scan
    res = compAST(m, 'P', ' ', '', '')
    do forever
        one = compExprStmts(m, ki)
        if one \== '' then do
            if m.res.0 > 2 then
                call scanErr s, '$| before statements needed'
            call mAdd res, one
            end
        pre = left(m.comp_chDol, scanLit(s, m.comp_chDol))
        if scanLook(s, 2) == '<>' then
            leave
        if scanLit(s, '<') then do
            if m.res.2 == '' then
                m.res.2 = compAst(m, '.')
            else
                call mAdd m.res.2, compAst(m, 'o', ', ')
            call mAdd m.res.2, compOpBE(m, '<', '<')
            m.res.text = m.res.text'f'
            end
        else if scanLit(s, '>>', '>') then do
            if m.res.1 <> '' then
                call scanErr s, 'duplicate output'
            m.res.text = if(m.s.tok == '>', 'F', 'A') ,
                ||substr(m.res.text, 2)
            m.res.1 = compOpBE(m, '<', '<')
            end
        else if scanLit(s, '|') then do
            if m.res.0 < 3 then
                call scanErr s, 'stmts expected before |'
            call compSpNlComment m
            call mAdd res, compCheckNE(m, compExprStmts(m, ki),
                , 'stmts or expressions after | expected')
            end
        else
            leave
        end
    call scanBack s, pre
    if m.res.0 > 3 | m.res.1 \== '' | m.res.2 \== '' then
        return res
    one = if(m.res.0 = 3, m.res.3)
    call mFree res
    return one
endProcedure compPipe

/*--- compile expressions and stmts ---------------------------------*/
compExprStmts: procedure expose m.
parse arg m, ki
    s = m.m.scan
    res = compAst(m, '¢')
       nlLe = 0 /* sophisticated logic using left and right NLs*/
    tb = ''
    do forever
        if tb \== '' then do
            call compSpComment m
            px = m.s.pos
            e1 = compExpr(m, 'w', '=')
            if e1 \== '' then do
                aa = compAst(m, '¢')
                fx = 0
                do until e1 == ''
                    do fx=fx+1 to m.tb.0 until px < m.ff.end
                        ff = m.tb.fx
                        end
                    if fx > m.tb.0 then
                        call scanErr s, 'right of all table fields'
                    if m.s.pos <= m.ff.pos then
                        call scanErr s, 'before table field' m.ff.name
                    call mAdd aa, compAst(m, 'A', ,
                        , compAst(m, '=', m.ff.name), e1)
                    call compSpComment m
                    px = m.s.pos
                    e1 = compExpr(m, 'w', '=')
                    end
                call mAdd res, compAst(m, 'F', 'with',
                    , compAst(m, 'o', "oNew('"m.tb.class"')"),
                    , aa, compAst(m, '*', '$.'))
                end
            nlRi = scanNL(s)
            end
        else if ki == ':' then do
            call compSpNlComment m, '*'
            nlRi = 0
            end
        else if ki == '@' then do
            call compSpNlComment m
            one = compExpr(m, 's', ki)
            if one == '' then
                nlRi = 0
            else if m.one.0 < 1 then
                call scanErr s, 'assert not empty' m.one.0
            else do
                do forever /* scan all continued rexx lines */
                    nlRi = 1
                    la = m.one.0
                    la = m.one.la
                    if m.la.kind \== 'c' then
                        leave
                    m.la.text = strip(m.la.text, 't')
                    if right(m.la.text, 1) \== ',' then
                        leave
                    m.la.text = strip(left(m.la.text,
                            , length(m.la.text)-1), 't')' '
                    call compSpNlComment m
                    cont = compExpr(m, 's', '@')
                    if cont == '' | m.cont.kind \== m.one.kind then
                        call scanErr s, 'bad rexx continuation'
                    call mAddSt one, cont
                    call mFree cont
                    end
                call mAdd res, one
                end
            end
        else if ki == '%' | ki == '^' then do
            do cc=0 while compSpNlComment(m)
                end
            one = compExpr(m, 's', ki)
            nlRi = one \== ''
            if nlRi then do
                if ki == '^' then
                    one = compAstAddOp(m, one, '$')
                call mAdd res, one
                end
            end
        else do
            do cc=0 while compComment(m)
                end
            one = compExpr(m, 'd', ki)
            nlRi = scanNL(s)
            if one == '' then do
                if nlLe & nlRi & cc < 1 then
                    call mAdd res,compAstAddOp(m,compAst(m,'='), '$')
                end
            else if m.one.containsD | (nlLe & nlRi,
                      & \ (cc > 0 | m.one.containsC)) then do
                call mAdd res, compAstAddOp(m, one, '$')
                end
            else do
                call mFree one
                end
            end
        nlLe = nlRi
        if \ nlRi then do
            one = compStmt(m)
            if one = '' then
                return compAstFree0(res)
            if m.one.kind == 'T' then
                tb = one
            else
                call mAdd res, one
            end
        end
endProcedure compExprStmts

/*--- compile a single statement -------------------------------------*/
compStmt: procedure expose m.
parse arg m
    s = m.m.scan
    res = compAss(m)
    if res \== '' then
        return res
    pre = ''
    old = scanPos(s)
    if scanLit(s,m.comp_chDol'$',m.comp_chDol'@',m.comp_chDol,'@') then
        pre = m.s.tok
    if pre == m.comp_chDol'$' then
        return  compAstAddOp(m, compCheckNN(m, compOpBE(m,'=', 1),
            , 'block or expression expected after $$'), '$')
    wCat = compName(m, 'sv')
    fu = m.s.tok

    if right(pre, 1) == '@' & wCat \== 's' then do
        if wCat == 'v' then
            return compAstAddOp(m, compAst(m, '=', fu), '%.&')
        if compSpNlComment(m) then
                return compAstAddOp(m,
                , compCheckNE(m, compExpr(m, 's','@'),
                , 'block/primary/expr after $@'), ';')

        one = compCheckNN(m, compOpBE(m, '@') ,
                 ,  'primary, block or expression')
        if one \== '' then
            return one
        call scanSetPos s, old
        return ''
        end

    if wordPos(fu, 'for forWith with withNew') > 0 then do
        res = compAst(m, 'F', fu)
        call compSpComment m
        if fu \== 'with' then do
            b = compVar(m, left('o', fu == 'withNew'))
            end
        else do
            b = compAss(m, 'o')
            if b == '' then
                b = compCheckNE(m, compExpr(m, 's', '.'),
                , "assignment or expression after with")
            end
        call compSpComment m
        st = compCheckNN(m, compStmt(m), "var? statement after" fu)
        if b = '' then do
            b = compBlockName(m, st)
            if b == '' then
                call scanErr s, "variable or named block after" fu
            b = compAst(m, '=', b)
            end
        call mAdd res, b, st
        if fu == 'withNew' then do
            vars = substr(compAstAssignedVars(m.res.2), 3)
            m.res.class = classNew('n* CompTable u' vars)
            m.res.1 = compAst(m, 'A', ,m.res.1, compAst(m, 'o',
                , "oNew('"m.res.class"')"))
            end
        if abbrev(fu, 'with') then do
            r1 = m.res.1
            if m.r1.kind == 'A' then do
                a1 = m.r1.1
                if m.a1.var == 'o' then do
                    call mAdd res, compAstAddOp(m, m.r1.2, '$.')

                    m.res.1 = m.r1.2
                    call mFree a1
                    end
                end
            end
        return res
        end

    if fu == 'do' then do
        call compSpComment m
        pre = compExpr(m, 's', '@')
        res = compAst(m, 'D', , pre)
        p1 = m.pre.1
        if pre \== '' then do
            txt = ''
            do px=1 to m.pre.0
                pC = m.pre.px
                if m.pC.kind \== 'c' then
                    leave
                txt = txt m.pC.text
                cx = pos('=', txt)
                if cx > 0 then do
                    m.res.text = strip(left(txt, cx-1))
                    leave
                    end
                end
            end
        call compSpComment m
        call mAdd res, compCheckNN(m, compStmt(m), 'stmt after do')
        return res
        end

    if fu == 'ct' then do
        call compSpComment m
        return compAst(m, 'C', , compCheckNN(m, compStmt(m),
            , 'ct statement'));
        end
    if fu == 'proc' then do
           call compSpComment m
        nm = ''
        if compName(m, 'v') == 'v' then do
            nm = m.s.tok
            call compSpComment m
            end
        st = compCheckNN(m, compStmt(m), 'proc statement')
        if nm == '' then do
            nm = compBlockName(m, st)
            if nm == '' then
                call scanErr s, 'var or namedBlock expected after proc'
            end
        return compAst(m, 'B', '', compAst(m, '=', nm), st)
        end
    if fu == 'table' then do
        call compSpComment m
        if scanNl(s) then
            call compSpComment m
        res = compAst(m, 'T', 'c')
        flds = ''
        do while compName(m, 'v') == 'v'
            f1 = compAst(m, 'T')
            m.f1.end = m.s.pos
            m.f1.pos = m.s.pos - length(m.s.tok)
            m.f1.name = m.s.tok
            m.f1.text = 'f' m.f1.name m.f1.pos m.f1.end
            call mAdd res, f1
            flds = flds', f' m.s.tok 'v'
            call compSpComment m
            end
        if \ scanNl(s) then
            call scanErr s, 'name or nl after table expected'
        if m.res.0 < 1 then
            call scanErr s, 'no names in table'
        m.res.class = classNew('n* CompTable u' substr(flds, 3))
        m.res.text = 'c' cl
        return res
        end
    call scanBack s, pre || fu
    return ''
endProcedure compStmt

compBlockName: procedure expose m.
parse arg m, a
    a1 = m.a.1
    if m.a.kind == '¢' then
         return m.a.text
    else if m.a.kind == '*' & m.a1.kind == '¢' then
        return m.a1.text
    return ''
endProcedure compBlockName

compVar: procedure expose m.
parse arg m, vk
    s = m.m.scan
    ty = compName(m, 'v' || vk)
    if ty \== '' then do
        r = compAst(m, '=', m.s.tok)
        m.r.var = ty
        return r
        end
    if \ scanLit(s, '{') then
        return ''
    call scanLit s, '?', '>'
    f = m.s.tok
    r = compCheckNE(m, compExpr(m, 'b', '='), 'var name')
    if \scanLit(s, '}') then
        call scanErr s, 'closing } missing after {'
    m.r.var = f
    return r
endProcedure compVar

compAss: procedure expose m.
parse arg m, vk
    s = m.m.scan
    call scanLit s, m.comp_chDol'=', '='
    pr = m.s.tok
    if pr \== '' then
        call compSpComment m
    v = compVar(m, vk)
    if v \== '' then do
        call compSpComment m
        if \ scanLit(s, '=') then
            call scanErr s, '= expected in assignment after' pr 'var'
        end
    else if pr == '' then
        return ''
    else
        oldInfo = scanInfo(s)
    eb = compCheckNE(m, compOpBE(m, '=', 1),
        , 'block or expression in assignment after' pr)
    if m.eb.kind == '¢' then
        eb = compAstAddOp(m, eb, '-')
    if v == '' then do
        v = compBlockName(m, eb)
        if v == '' then
            call scanErr s, 'var or namedBlock expected',
                    'in assignment after' pr, oldInfo
        else
            v = compAst(m, '=', v)
        end
    return compAst(m, 'A', , v, eb)
endProcedure compAss

/*--- block deals with the correct kind and operators
      the content is parsed by compUnit ------------------------------*/
compBlock: procedure expose m.
parse arg m, ops
    s = m.m.scan
    if \ scanLit(s, '¢', '/') then
        return ''
    start = m.s.tok
    if ops == '' | pos(right(ops, 1), m.comp_chKind) < 1 then
        return scanErr(s, 'bad kind' ops 'for block')
    ki = right(ops, 1)
    ops = left(ops, length(ops)-1)
    starter = start
    if start == '¢' then
        stopper = m.comp_chDol'!'
    else do
        call scanVerify s, '/', 'm'
        starter = '/'m.s.tok'/'
        stopper = m.comp_chDol || starter
        if \scanLit(s, '/') then
            call scanErr s, 'ending / after stopper' stopper 'expected'
        end
    res = compUnit(m, ki, stopper)
    if \ scanLit(s, stopper, substr(stopper, 2)) then
           call scanErr s, 'ending' stopper 'expected after' starter
    if abbrev(starter, '/') then
        m.res.text = substr(starter, 2, length(starter)-2)
    return compAstAddOp(m, res, ops)
endProcedure compBlock

/**** lexicals ********************************************************/
/*--- skip a comment. return 0 if there is none ----------------------*/
compComment: procedure expose m.
parse arg m
    s = m.m.scan
    got = 0
    do forever
        if scanLit(s, m.comp_chDol'**') then
            m.s.pos = 1 + length(m.s.src) /* before next nl */
        else if scanLit(s, m.comp_chDol'*+') then
            call scanNL s, 1
        else if scanLit(s, m.comp_chDol'*(') then do
            do forever
                if scanVerify(s, m.comp_chDol, 'm') then iterate
                if scanNL(s) then iterate
                if compComment(m) then iterate
                if \ scanLit(s, m.comp_chDol) then
                    call scanErr s, 'source end in comment'
                if scanLit(s, '*)') then
                    return 1
                if scanLit(s, m.comp_chDol) then iterate
                if scanString(s) then iterate
                end
            end
        else
            return got
        got = 1
        end
endProcedure compComment

/*--- skip spaces and comments ---------------------------------------*/
compSpComment: procedure expose m.
parse arg m, xtra
    s = m.m.scan
    got = 0
    do forever
        if scanVerify(s, m.comp_chSpa) then
            got = bitOr(got, 1)
        else if compComment(m) then
            got = bitOr(got, 2)
        else if xtra == '' then
            return got
        else if \ scanLit(s, xtra) then
            return got
        else do
            got = bitOr(got, 4)
            m.s.pos = 1+length(m.s.src)
            end
        end
endProcedure compSpComment

/*--- skip spaces, NLs and comments ----------------------------------*/
compSpNlComment: procedure expose m.
parse arg m, xtra
    found = 0
    do forever
        if compSpComment(m, xtra) < 1 then
            if \ scanNL(m.m.scan) then
             return found
        found = 1
        end
endProcedure compSpNlComment
/*--- scan a name in one of the categories
        v=var, c=compile, s=stmt ----------------------------------*/
compName: procedure expose m.
parse arg m, cats
    s = m.m.scan
    if \ scanName(s) then
        return ''
    if wordPos(m.s.tok, m.comp.wCatS) > 0 then do
        if pos('s', cats) > 0 then
            return 's'
        end
    else if wordPos(m.s.tok, m.comp.wCatC) > 0 then do
        if pos('c', cats) > 0 then
            return 'c'
        end
    else if wordPos(m.s.tok, m.comp.wCatO) > 0 then do
        if pos('o', cats) > 0 then
            return 'o'
        end
    else if pos('v', cats) > 0 then do
        return 'v'
        end
    call scanBack s, m.s.tok
    return ''
endProcedure compName


compSpNlComment: procedure expose m.
/**** small helper routines ******************************************/
/*--- if va == null then issue an error with msg --------------------*/
compCheckNN: procedure expose m.
parse arg m, va, msg
    if va == '' then
        call scanErr m.m.scan, msg 'expected'
    return va
endProcedure compCheckNN

/*--- return true iff expression is empty ---------------------------*/
compIsEmpty: procedure expose m.
parse arg m, a
    if a == '' then
        return 1
    do while a \== '' & m.a.kind == '*'
        a = m.a.1
        end
    return a == '' | (m.a.kind \== '¢' & m.a.0 < 1)
endProcedure compIsEmpty

/*--- if va == null or empty then issue an error with msg -----------*/
compCheckNE: procedure expose m.
parse arg m, ex, msg
    e1 = left(ex, 1)
    if compIsEmpty(m, ex) then
        call scanErr m.m.scan, msg 'expected'
    return ex
endProcedure compCheckNE

/**** AST = Astract Syntax Tree ****************************************

------- atoms, no children
  =  string constant
----------- rexx fragments
  c  rexxStatement
  s  string
  o  object
  r  run
  f  file

------- containers
  ¢  block          ==> * c-.
  @  code = 1 stmt  ==> *
  -  string
  .  object
  *  operand chain  ==> 1

------- molecules
  &  variable access==> 1 =-
  A  assignment     ==> 2
  B  proc           ==> 2
  C  ct             ==> 1
  D  do             ==> 2
  F  for + with     ==> 2
  P  Pipe           ==> * 1=input 2=output
  T  Table
  M  compile
  %  RunOut         ==> 1,2 (Run, arguments)
  ^  RunRet         ==> 1,2 (Run, arguments)
  I??  Input        ==> * .

***********************************************************************/
/*--- create a new AST -----------------------------------------------*/
compAST: procedure expose m.
parse arg m, ki, txt
    n = mNew('COMP.AST')
    if length(ki) <> 1 then
        call err 'compAST bad kind' ki / 0
    m.n.kind = ki
    m.n.text = txt
    if pos(ki, '¢@-.*&ABCDFPTM%^') > 0 then do
        do cx=1 to arg()-3
            m.n.cx = arg(cx+3)
            end
        m.n.0 = cx-1
        if txt \== '' & pos(ki, '&*FPT') < 1 then
            return err('kind' ki 'text='txt'|')/0
        end
    else if pos(ki, '=csorf') > 0  then do
        m.n.0 = 'kind'ki
        end
    else do
        call err "compAst kind '"ki"' not supported"
        1/0
        end
    return n
endProcedure compAST

/*--- free AST if empty ----------------------------------------------*/
compASTFree0: procedure expose m.
parse arg a, ret
    if m.a.0 > 0 then
        return a
    call mFree a
    return ret
endProcedure compAstFree0

/*--- add operandands to an AST -------------------------------------*/
compASTAddOp: procedure expose m.
parse arg m, a, ops
    do while right(ops, 1) == m.a.kind
        ops = left(ops, length(ops)-1)
        end
    if ops == '' then
        return a
    ops = translate(ops, '-', '=')
    if m.a.kind \== '*' then
        return compAst(m, '*', ops, a)
    m.a.text = ops || m.a.text
    return a
endProcedure compAstAddOp

/*--- return the kind of an AST --------------------------------------*/
compASTKind: procedure expose m.
parse arg m, a
    m.m.astOps = ''
    if \ abbrev(a, 'COMP.AST.') then
        return left(a, 1)
    c = a
    do while m.c.kind == 'ops'
        if m.c.text \== '' then
            return left(m.c.text, 1)
        c = m.c.1
        end
    if a == c then
        return '?'
    return compAstKind(m, c)
endProcedure compASTKind

compAstAssignedVars: procedure expose m.
parse arg a
    res = ''
    if m.a.kind == 'F' then
        return ''
    if m.a.kind == 'A' then do
        a1 = m.a.1
        if m.a1.kind == '=' & m.a1.var == 'v' then do
            if words(m.a1.text) \= 1 then
                call astErr a1 'bad var'
            a2 = m.a.2
            say m.a2.kind m.a2.text
            if pos(m.a2.kind, '-=s') > 0 ,
                | (m.a2.kind == '*' & abbrev(m.a2.text, '-')) then
                res = ', f' m.a1.text 'v'
            else if pos(m.a2.kind, '.<@o') > 0 ,
                | (m.a2.kind == '*' & abbrev(m.a2.text, '.<@')) then
                res = ', f' m.a1.text 'r'
            else
                call astErr a2, 'string or object????'
            end
        end
    if datatype(m.a.0, 'n') then
        do ax=1 to m.a.0
            res = res || compAstAssignedVars(m.a.ax)
            end
    return res
endProcedure compAstAssignedVars

compAstSay: procedure expose m.
parse arg a, lv
    if \ abbrev(a, 'COMP.AST.') then do
        if a \== '' then
            return err('bad ast' a)
        say left('', 19)': * empty ast'
        return
        end
    say lefPad(left('', lv) m.a.kind, 10) ,
        || rigPad(if(dataType(m.a.0, 'n'), m.a.0), 3),
        '@'rigPad(substr(a, 10), 4)':' m.a.text'|'
    if dataType(m.a.0, 'n') then do cx=1 to m.a.0
        call compAstSay m.a.cx, lv+1
        end
    return
endProcedure compAstSay

compAstErr: procedure expose m.
parse arg a, txt
    call errSay txt
    call compAstSay a, 0
    return err(txt)
endProcedure compAstErr

compAstOnlyOut: procedure expose m.
parse arg a, rec
    do ax=1 to m.a.0
        a1 = m.a.ax
        if m.a1.kind == '*' then
            if abbrev(m.a1.text, '$') then
                iterate
        if m.a1.kind = '¢' & rec \== 0 then
            if compAstOnlyOut(a1) then
                iterate
        return 0
        end
    return 1
endProcedure compAstOnlyOut

/*--- return the code for an AST with operand chain trg --------------*/
compCode2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if pos(o1, ';') > 0 then
        return compCode2rx(m, oR, strip(f))
    if pos(o1, '-.<|?@') > 0 then
        return compRun2rx(m, ops, quote(oRunner(f)))

    call err 'compCode2rx bad ops' ops
endProcedure compCode2rx

compCon2rx: procedure expose m.
parse arg m, ops, f, a
    do ox=length(ops) by -1 to 1 while pos(substr(ops,ox,1), '.-')>0
        end
    if substr(ops, ox+1, 1) == '.' then
        f = s2o(f)
    if length(f) < 20 then
        v = quote(f, "'")
    else if a \== '' & m.a.text == f then
        v = 'm.'a'.text'
    else
        v =    'm.'compAst(m, '=', f)'.text'
    if substr(ops, ox+1, 1) == '.' then
        return compObj2rx(m, left(ops, ox), v)
    else
        return compString2rx(m, left(ops, ox), v)
endProcedure compCon2rx

compString2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '-' then
        return compString2rx(m, oR, f)
    if o1 == '.' then
        return compObj2rx(m, oR, 's2o('f')')
    if pos(o1, '$') > 0 tOnlyOut then
        return compCode2rx(m, oR, 'call out' f';')
    if o1 == '&' then do
        o2 = substr('1'ops, length(ops), 1)
        if pos(o2,  '.<^%') < 1 then
            return compString2rx(m, oR, 'vGet('f')')
        else
            return compObj2rx(m, oR, 'vGet('f')')
        end
    if o1 == '<' then
        return compFile2rx(m, oR, 'file('f')')
    call err 'compString2rx bad ops' ops
endProcedure compString2rx

compObj2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '.' then
        return compObj2rx(m, oR, f)
    if o1 == '-' then
        return compString2rx(m, oR, 'o2string('f')')
    if o1 == '$' then
        return compCode2rx(m, oR, 'call out' f';')
    if o1 == '<' then
        return compFile2rx(m, oR, 'o2file('f')')
    if o1 == '@' then
        return compRun2Rx(m, oR, f)
    if pos(o1, ';%^') > 0 then
        return compRun2rx(m, ops, f)
    call err 'compObj2rx bad ops' ops
endProcedure compObj2rx

compRun2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '@' then
        return compRun2Rx(m, oR, f)
    if o1 == ';' | o1 == '%' then /*??? immer % verwenden ???*/
        return compCode2Rx(m, oR, 'call oRun' f';')
    if o1 == '^' then
        if pos(right(oR, 1),  '.<^%') < 1 then
            return compString2Rx(m, oR, 'oRun('f')')
        else
            return compObj2Rx(m, oR, 'oRun('f')')
    return compObj2rx(m, ops, f)
endProcedure compRun2rx

compFile2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if pos(o1, '<.@') > 0 then
        return compFile2rx(m, oR, f)
    if o1 == '|' | o1 == '?' then
        return compObj2Rx(m, oR, 'jSingle('f ||if(o1=='?', ", ''")')')
    return compRun2rx(m, ops, f)
endProcedure compFile2rx

compAst2rx: procedure expose m.
parse arg m, ops, a

    ki = m.a.kind

    if ki == 'c' then
        return compCode2Rx(m, ops, m.a.text)
    if ki == 's' then
        return compString2rx(m, ops, m.a.text)
    if ki == 'o' then
        return compObj2Rx(m, ops, m.a.text)
    if ki == '=' then
        return compCon2Rx(m, ops, m.a.text, a)
    if ki == '*' then
        return compAst2Rx(m, ops || m.a.text, m.a.1)
    o1 = right(ops, 1)
    oR = left(ops, max(0, length(ops)-1))
    if ki == '-' then
        if m.a.0 = 1 then
            return compAst2rx(m, ops, m.a.1)
        else
            return compString2rx(m, ops, compCatRexxAll(m, a,,, ' || '))
    if ki == '.' then
        return compObj2Rx(m, ops, compCatRexxAll(m, a,,, ' || '))
    if ki == '@' then
        return compCode2Rx(m, ops, compCatRexxAll(m, a,,,' || ')';')
    if ki == '¢' then do
        a1 = m.a.1
        if m.a.0 == 1 & m.a1.kind == '¢' then
            return compAst2Rx(m, ops, a1)
        if o1 == '-' then do
            if \ compAstOnlyOut(a) then
                return compAst2Rx(m, ops';', a)
            res = ''
            do ax=1 to m.a.0
                a1 = m.a.ax
                if m.a1.kind == '*' & abbrev(m.a1.text, '$') then
                    b = compAst2rx(m, overlay('-',m.a1.text,1), m.a1.1)
                else if m.a1.kind == '¢' then
                    b = compAst2rx(m, '-', a1)
                else
                    call compAstErr a, 'onlyOut but' ax'='a1 ,
                        'kind='m.a1.kind 'text='m.a1.text
                res = compCatRexx(res, b, ' ')
                end
                    /* ???? only necessary if part of expression ????*/
            return compString2rx(m, oR, '('strip(res)')')
            end
        if o1 == '<' then
            return compAst2Rx(m, ops'@', a)
        if o1 == '.' then
            return compAst2Rx(m, oR'|', a)
        if o1 == '|' | o1 == '?' then
            if m.a.0 = 1 & compAstOnlyOut(a, 0) then do
                a1 = m.a.1
                if m.a1.kind \== '*' | \ abbrev(m.a1.text, '$') then
                    call compAstErr a, 'onlyOut but',
                        '1='a1 'kind='m.a1.kind 'text='m.a1.text
                return compAst2Rx(m, oR'.'substr(m.a1.text, 2), m.a1.1)
                end
            else
                return compFile2Rx(m, ops, compAst2rx(m, '<;', a))
        if pos(o1, '@;') > 0 then do
            if m.a.0 = 1 then  /*???pipe und code können multi stmts */
               return compCode2Rx(m, ops, 'do;' ,
                    compAst2rx(m,';',m.a.1) 'end;')
            res = ''
            do ax=1 to m.a.0
                res = res compAst2rx(m, ';', m.a.ax)
                end
            if res = '' then
                return compCode2Rx(m, ops, 'nop;')
            return compCode2Rx(m, ops, 'do;'res 'end;')
            end
        end
    if ki == '&' then do
        nm = compAst2Rx(m, '-', m.a.1)
        if m.a.text=='' | m.a.text=='v' then
            return compString2rx(m, ops'&', nm)
        else if m.a.text == '?' then
            return compString2rx(m, ops, 'vIsDefined('nm')')
        else if m.a.text == '>' then
            return compString2rx(m, ops, 'vRead('nm')')
        else
            call compAstErr a, 'bad text' m.a.text 'in ast &'
        end
    if ki == '%' | ki == '^' then do
        c1 = compAst2Rx(m, '.', m.a.1)
        if m.a.0 > 1 then
            c1 =  c1',' compAst2Rx(m, '', m.a.2)
        return compRun2Rx(m, ops || ki, c1)
        end
    if ki == 'A' then do /* assignment */
        nm = compAst2Rx(m, '-', m.a.1)
        vl = m.a.2
        if m.vl.kind == '=' | m.vl.kind == '-' ,
            | (m.vl.kind == '*' & right(m.vl.text, 1) == '-') then
            return compCode2Rx(m, ops,
                , 'call vPut' nm',' compAst2Rx(m, '-', vl)';')
        else
            return compCode2Rx(m, ops,
                , 'call vPut' nm',' compAst2Rx(m, '.', vl)';')
        end
    if ki == 'B' then do /* proc */
        call vPut utInter('return' compAst2Rx(m, '-', m.a.1)),
            , oRunner(compAst2Rx(m ,';', m.a.2))
        return ''
        end
    if ki == 'C' then do /* ct */
     call utInter compAst2Rx(m, ';', m.a.1)
        return ''
        end
    if ki == 'D' then do /* do */
        res = 'do' compAst2rx(m, '', m.a.1)';'
        if m.a.text \== '' then
            res = res "call vPut '"m.a.text"'," m.a.text";"
        return compCode2Rx(m, ops, res compAst2Rx(m, ';', m.a.2),
             "end;")
        end
    if ki == 'F' then do /* for... */
        a1 = m.a.1
         st = compAst2Rx(m, ';', m.a.2)
        if abbrev(m.a.text, 'with') then do
            if m.a1.kind \== 'A' then do
                v = compAst2Rx(m, '.', a1)
                end
             else do
                v = compAst2Rx(m, ,a1)
                if \ abbrev(v, 'call vPut ') | right(v, 1) \==';' then
                    call scanErr s, 'bad vPut' v
                v = 'vPut('substr(v, 11, length(v)-11)')'
                end
            if m.a.0 <= 2 then
                return 'call vWith "+",' v';' st 'call vWith "-";'
            a3 = m.a.3
            if m.a3.kind == '*' then
                return 'call vWith "+",' v';' st,
                    compObj2Rx(m, m.a3.text, "vWith('-')")
            end
        v = compAst2Rx(m, '-', m.a.1)
        if m.a.text == 'forWith' then
            st = 'call vWith "=", vGet('v');' st
        if abbrev(m.a.text, 'for') then
            st = 'do while vRead('v');' st 'end;'
        if m.a.text == 'forWith' then
            st = 'call vWith "+";' st 'call vWith "-";'
        return compCode2Rx(m, ops, st)
        end
    if ki == 'P' then do /* pipe */
        if ((left(m.a.text, 1) == ' ') \== (m.a.1 == '')) ,
         | ((substr(m.a.text, 2) == '') \== (m.a.2 == '')) ,
         | (m.a.0 <= 3 & m.a.text == '') then
            call compAstErr a, 'bad/trivial astPipe'
        res = ''
        do ax=3 to m.a.0
            a1 = ''
            if ax < m.a.0 then /* handle output */
                t1 = 'N'
            else if m.a.1 == '' then
                t1 = 'P'
            else do
                t1 = left(m.a.text, 1)
                a1 = compAst2Rx(m, '.', m.a.1)
                end
            if ax == 3 then do /* handle input */
                t1 = '+'t1 || substr(m.a.text, 2)
                if m.a.2 \== '' then
                    a1 = a1',' compAst2Rx(m, '.', m.a.2)
                end
            else
                t1 = t1'|'
            res = res "call pipe '"t1"'," a1";" ,
                   compAst2Rx(m, ';', m.a.ax)
            end
        return compCode2Rx(m, ops, res "call pipe '-';")
        end
    if ki == 'R' then do /* Run with args */
        res = 'call oRun' compAst2Rx(m, , m.a.1)
        if m.a.0 > 1 then
            res = res',' compAst2Rx(m, , m.a.2)
        return compRun2rx(m, ops, res';')
        end
    if ki == 'M' then do
        if m.a.0 = 0 then
            args = ''
        else
            args = ',' compAst2Rx(m, , m.a.1)
        return compRun2rx(m, ops, 'compile(comp(in2Buf())' args')')
        end
    call compAstErr a, 'compAst2rx bad ops' ops 'kind' ki
endProcedure compAst2rx

compCatRexxAll: procedure expose m.
parse arg m, a, ops, mi, sep
    res = ''
    do ax=1 to m.a.0
        res = compCatRexx(res, compAst2rx(m, ops, m.a.ax), mi , sep)
        end
    return strip(res)
endProcedure compCatRexxAll

/*--- cat two rexx parts, avoid strange effects--------------------*/
compCatRexx: procedure expose m.
parse arg le, ri, mi, sep
    if mi \== '' then
        return le || mi || ri
    lr = right(le, 1)
    rl = left(ri, 1)
    if (lr == "'" | lr == '"') then do
        if rl == lr then                /* "a","b" -> "ab" */
            return left(le, length(le)-1) || substr(ri, 2)
        else if  rl == '(' then            /* "a",( -> "a" || ( */
            return le||sep||ri            /* avoid function call    */
        end
    else if pos(lr, m.comp.idChars) > 0 then
        if pos(rl, m.comp.idChars'(') > 0 then
            return le || sep || ri        /* a,b -> a || b */
    return le || ri
endProcedure compCatRexx
/* copy comp end ******************************************************/
/* copy scanSB begin ***************************************************
     Achtung: inc generiert SB aus scanSB, Aenderungen nur in scanSB|
ScanSB: basic scan
    scanLook(m,len) : returns next len chars, pos is not moved
    scanChar(m,len) : scans next len chars
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanWord(m,u)  : scan a space delimited word or a string,
                          if u=1 then uppercase non-strings
variable interface
    scanSrc(m, source) starts scanning a single line
    scanEnd(m)     : returns whether we reached end of input
    scanErr(m, txt): error with current scan location

    m is an address, to store our state
    if a scan function succeeds, the scan posititon is moved

returns: true if scanned, false otherwise
         m.m.tok  ==> last token
         m.m.val  ==> last value for scanString/Word
         m.m.pos ==> scan position
         m.m.src ==> scan source
***********************************************************************/
/*--- return the next len characters until end of src ----------------*/
scanLook: procedure expose m.
parse arg m, len
    if len == '' then
        return substr(m.m.src, m.m.pos)
    else
        return substr(m.m.src, m.m.pos,
                     , min(len, 1 + length(m.m.src) - m.m.pos))
endProcedure scanLook

/*--- scan len chararcters, atmost to end of src ---------------------*/
scanChar: procedure expose m.
parse arg m, len
    m.m.tok = scanLook(m, len)
    m.m.pos = m.m.pos + length(m.m.tok)
    return m.m.tok \== ''
endProcedure scanChar

/*--- scan the literal lit ------------------------------------------*/
scanLit: procedure expose m.
parse arg m
    do ax=2 to arg()
        if abbrev(substr(m.m.src, m.m.pos), arg(ax)) then do
            m.m.tok = arg(ax)
            m.m.pos = m.m.pos + length(arg(ax))
            return 1
            end
        end
    m.m.tok = ''
    return 0
endProcedure scanLit

/*--- scan with verify, vOpt is passed to verify ---------------------*/
scanVerify: procedure expose m.
parse arg m, alpha, vOpt, onlyIfMatch
    if arg() > 3 then
        call err 'deimplement onlyIfMatch???'
    if vOpt == '' then   /* empty string does not take default */
        nx = verify(m.m.src, alpha, , m.m.pos)
    else
        nx = verify(m.m.src, alpha, vOpt, m.m.pos)
    if nx = 0 then
        if onlyIfMatch == 1 then
            nx = m.m.pos
        else
            nx = length(m.m.src) + 1
    m.m.tok = substr(m.m.src, m.m.pos, nx - m.m.pos)
    m.m.pos = nx
    return m.m.tok \== ''
endProcedure scanVerify

/*--- scan while in charset ------------------------------------------*/
scanWhile: procedure expose m.
parse arg m, chSet
    return scanVerify(m, chSet, 'n')

/*--- scan until in charset ------------------------------------------*/
scanUntil: procedure expose m.
parse arg m, chSet
    return scanVerify(m, chSet, 'm')

/*--- scan until (and over) string End -------------------------------*/
scanStrEnd: procedure expose m.
parse arg m, sep
    px = m.m.pos
    do forever
        px = pos(sep, m.m.src, px)
        if px = 0 then do
            m.m.tok = ''
            return 0
            end
        px = px + length(sep)
        if \ abbrev(substr(m.m.src, px), sep) then do
            m.m.tok = substr(m.m.src, m.m.pos, px-m.m.pos)
            m.m.pos = px
            return 1
            end
        px = px + length(sep)
        end
endProcedure scanStrEnd

/*--- scan a string with quote char qu -------------------------------*/
scanString: procedure expose m.
parse arg m, prefs
    if prefs = '' then do
        call scanLit m, "'", '"'
        end
    else do
        do px=1 to words(prefs) until scanLit(m, word(prefs, px))
            end
        end
    if m.m.tok == '' then
        return 0
    t1 = m.m.tok
    qu = right(t1, 1)
    if \ scanStrEnd(m, qu) then do
        m.m.pos = m.m.pos - length(t1)
        return scanErr(m, 'ending Apostroph('qu') missing')
        end
    m.m.val = repAll(left(m.m.tok, length(m.m.tok)-1), qu||qu, qu)
    m.m.tok = t1 || m.m.tok
    return 1
endProcedure scanString

/*--- scan a word and put value into *.val
           a word is either delimited by space or stopper
                     or a string (with single or double quotes -------*/
scanWord: procedure expose m.
parse arg m, stopper, ucWord
    if scanString(m) then
        return 1
    if stopper == '' then
        stopper = ' '
    if \scanUntil(m, stopper) then
        return 0
    if ucWord == 1 then
        m.m.val = translate(m.m.val)
    else
        m.m.val = m.m.tok
    return 1
endProcedure scanWord

/*--- skip, scan and return next word --------------------------------*/
scanSkWord: procedure expose m.
parse arg m, stopper, ucWord, eMsg
    if scanWord(scanSkip(m), stopper, ucWord) then
        return m.m.val
    else if eMsg == '' then
        return ''
    else
        call scanErr m, eMsg 'expected'
endProcedure scanSkWord

/*--- go back the current token --------------------------------------*/
scanBack: procedure expose m.
parse arg m, tok
    if m.m.pos <= length(tok) then
        call scanErr sc, 'cannot back "'tok'" length'
    cx = m.m.pos - length(tok)
    if substr(m.m.src, cx, length(tok)) \== tok then
        return scanErr(m, 'cannot back "'tok'" value')
    m.m.pos = cx
    return
endProcedure scanBack

/*--- set new src - allow scanning without open ----------------------*/
scanSrc: procedure expose m.
parse arg m, m.m.src
    return scanSBStart(m)
endProcedure scanSrc

/*--- start scanning -------------------------------------------------*/
scanSBStart: procedure expose m.
parse arg m
    m.m.pos = 1
    m.m.tok = ''
    return m
endProcedure scanSBStart

scanSBSpace: procedure expose m.
parse arg m
    nx = verify(m.m.src, ' ', , m.m.pos)
    if nx = 0 then
        nx = length(m.m.src) + 1
    res = nx <> m.m.pos
    m.m.tok = left(' ', res)
    m.m.pos = nx
    return res
endProcedure scanSBSpace

/*--- skip over space, nl and comments and return m -----------------*/
scanSkip: procedure expose m.
parse arg m
    call scanSpace m
    return m
endProcedure scanSkip

scanErr: procedure expose m.
parse arg m, txt
    if arg() < 3 then
        return err('s}'txt'\n'scanInfo(m))
    else
        return err('scanErr' txt'\n'arg(3))
endProcedure scanErr

scanSBInfo: procedure expose m.
parse arg m
    return 'last token' m.m.tok 'scanPosition' ,
        strip(substr(m.m.src, m.m.pos, 40), 't') ,
        || '\npos' m.m.Pos 'in string' strip(m.m.src, 't')
endProcedure scanSBInfo

/*--- return position in simple format -------------------------------*/
scanSBPos: procedure expose m.
parse arg m
    return 'singleSrc' m.m.pos
    return if(m.m.pos > length(m.m.src), 'E', 'singleSrc' m.m.pos)

/*--- set position to position in arg to------------------------------*/
scanSetPos: procedure expose m.
parse arg m, to
    cur = scanPos(m)
    wc = words(cur)
    if wc <> words(to) ,
        | subWord(cur, 1, wc-1) <> subWord(to, 1, wc-1) then
        call scanErr m 'cannot back from' cur 'to' to
    m.m.pos = word(to, wc)
    return

/*--- return true if at end of src -----------------------------------*/
scanSBEnd: procedure expose m.
parse arg m
    return m.m.pos > length(m.m.src)

/*--- return true if at comment --------------------------------------*/
scanSBCom: procedure expose m.
parse arg m
    m.m.tok = ''
    if m.m.scanComment == '' then
        return 0
    if \ abbrev(substr(m.m.src, m.m.pos), m.m.scanComment) then
        return 0
    m.m.tok = substr(m.m.scr, m.m.pos)
    m.m.pos = 1 + length(m.m.src)
    return 1
endProcedure scanSBCom

/* copy scanSB end ****************************************************/
/* copy scan begin ****************************************************
Scan: scan an input: with multiple lines
    ==> all of scanSB
    scanEnd(m)     : returns whether we reached end of input
    scanLit(m,lit) : scan Literal lit if present or return 0
    scanChar(m,n)  : scan next n characters
    scanName(m)    : scan a name
    ScanNat(m)     : scan a natural number (without sign)
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanKeyValue(m): scan a key = value clause (with spaces)
    scanWord(m,u)  : scan a space delimited word or a string,
                          if u=1 then uppercase non-strings
    scanErr(m, txt): error with current scan location

    m is an address, to store our state
    if a scan function succeeds, the scan posititon is moved

returns: true if scanned, false otherwise
         m.m.tok  ==> last token
         m.m.val  ==> last value for scanString/Word/KeyValue
         m.key    ==> key for scanKeyValue
         m.m.pos ==> scan position
         m.m.src ==> scan source
***********************************************************************/
scanIni: procedure expose m.
    if m.scan.ini == 1 then
        return
    m.scan.ini = 1
    call jIni
    ts = classNew('n ScanRes u f TOK v, f VAL v, f KEY v, f TYPE v')
    call mAdd m.class_s ,
        , classNew('m scanEnd  return 1'),
        , classNew('m scanInfo return scanSBInfo(m)') ,
        , classNew('m scanPos return scanSBPos(m)')
    call classNew 'n ScanSB u', 'm',
        , 'scanEnd  return 1',
        , 'scanNL   m.m.tok = ""; return 0',
        , 'scanCom return scanSBCom(m)',
        , 'scanInfo return scanSBInfo(m)' ,
        , 'scanPos  return scanSBPos(m)',
        , 'jOpen    return scanSBStart(m)',
        , 'jClose   m.m.pos = "-- closed --"; return m',
        , 'isWindow 0',
        , "jRead if scanType(m) == '' then return 0;" ,
                    " m.m = oClaCopy('"ts"', m, ''); return 1"
    call classNew 'n ScanSqlStmtRdr u JRW', 'm',
        , "jReset   call scanSqlStmtRdrReset m, arg, arg2, arg3",
        , "jOpen    call scanOpen  m'.SCAN'" ,
        , "jClose   call scanClose m'.SCAN'" ,
        , "jRead    r = scanSqlStmt(m'.SCAN');if r==''then return 0" ,
                         "; m.m = r; return 1"
    return
endProcedure scanIni

scanOpen: procedure expose m.
parse arg m
    interpret objMet(m, 'jOpen')
    return m
endProcedure scanOpen

scanClose: procedure expose m.
parse arg m
    interpret objMet(m, 'jClose')
    return m
endProcedure scanOpen

scanSB: procedure expose m.
parse arg n1, np, co
     return oNew('ScanSB')
endProcedure scanSB

/*--- start scanning with a new single src ---------------------------*/
scanSBReset: procedure expose m.
parse arg m, m.m.src
    return scanSbStart(oMutatName(m, 'ScanSB'))
endProcedure scanSBReset

scanInfo: procedure expose m.
parse arg m
    interpret objMet(m, 'scanInfo')

/*--- return true if at end of src -----------------------------------*/
scanEnd: procedure expose m.
parse arg m
    if m.m.pos <= length(m.m.src) then
        return 0
    interpret objMet(m, 'scanEnd')

/*--- scan over white space, nl, comments ...-------------------------*/
scanSpace: procedure expose m.
parse arg m
    fnd = 0
    do while scanSBSpace(m) | scanCom(m) | scanNl(m)
        fnd = 1
        end
    m.m.tok = left(' ', fnd)
    return fnd
endProcedure scanSpace

/*--- scan next line -------------------------------------------------*/
scanNL: procedure expose m.
parse arg m, unCond
    interpret objMet(m, 'scanNL')

/*--- scan one comment -----------------------------------------------*/
scanCom: procedure expose m.
parse arg m
    interpret objMet(m, 'scanCom')

scanPos: procedure expose m.
parse arg m
    interpret objMet(m, 'scanPos')
endProcedure scanPos

scanOpt: procedure expose m.
parse arg m, m.m.scanName1, namePlus, m.m.scanComment
    if m.m.scanName1 == '' then
        m.m.scanName1 = m.ut_alfa
    if namePlus == '' then
        m.m.scanNameR = m.m.scanName1 || '0123456789'
    else
        m.m.scanNameR = m.m.scanName1 || namePlus
    m.m.scanNestCom = 0
    return m
endProcedure scanOpt

/*--- scan a Name, first char in *.scanName1, rest in *.scanNameR ----*/
scanName: procedure expose m.
parse arg m
    if pos(substr(m.m.src, m.m.pos, 1),
                 , m.m.scanName1) <= 0 then do
        m.m.tok = ''
        return 0
        end
    return scanVerify(m, m.m.scanNameR)
endProcedure scanName

/*--- scan a natural number (no sign, decpoint ...) ------------------*/
scanNat: procedure expose m.
parse arg m, chEn
    if \ scanVerify(m, '0123456789') then
        return 0
    if chEn \== 0 then
        if pos(scanLook(m , 1), m.m.scanNameR) > 0 then
            call scanErr m, 'illegal number end after' m.m.tok
    return 1
endProcedure ScanNat

/*--- scan an integer (optional sign, no decpoint ...) ---------------*/
scanInt: procedure expose m.
parse arg m, chEn
    call scanLit m, '+', '-'
    si = m.m.tok
    if \ scanNat(m, chEn) then do
        m.m.pos = m.m.pos - si
        return 0
        end
    m.m.tok = si || m.m.tok
    return 1
endProcedure scanInt

/*--- scan a number (optional sign, decpoint, exponent) ------------*/
scanNum: procedure expose m.
parse arg m, chEn
    sx = m.m.pos
    call scanLit m, '+', '-'
    po = scanLit(m, '.')
    if \ scanNat(m, 0) then do
        m.m.pos = sx
        return 0
        end
    if \ po then
        if scanLit(m, '.') then
            call scanNat m, 0
        if scanLit(m, 'e', 'E') then
            if \ scanInt(m, 0) then
                call scanErr m, 'exponent expected after' ,
                             substr(m.m.src, sx, m.m.pos-sx)
    m.m.tok = substr(m.m.src, sx, m.m.pos-sx)
    m.m.val = translate(m.m.tok)
    if chEn \== 0 then
        if pos(scanLook(m , 1), m.m.scanNameR) > 0 then
            call scanErr m, 'illegal number end after' m.m.tok
    return 1
endProcedure scanNum

scanType: procedure expose m.
parse arg m, opt
    m.m.tok = ''
    if scanName(m) then
        m.m.type = 'n'
    else if scanNum(m) then
        m.m.type = 0
    else if scanString(m) then
        m.m.type = left(m.m.tok, 1)
    else if scanSpace(m) then
        m.m.type = 's'
    else do
        call scanChar m, 1
        m.m.type = m.m.tok
        end
    return m.m.type
endProcedure scanType

/*--- scan a key = word phrase
          put key into m.key and word into m.m.val -------*/
scanKeyValue: procedure expose m.
parse arg m, def
    if \ scanName(m) then
        return 0
    m.m.key = m.m.tok
    if \ scanLit(scanSkip(m), '=') then do
        m.m.val = def
        m.m.tok = ' no='
        end
    else if \scanWord(scanSkip(m)) then
        return scanErr(m, 'word expected after' m.m.key '=')
    return 1
endProcedure scanKeyValue

/*--- emit an error with current scan pos ----------------------------*/
/* copy scan end   ****************************************************/
/* copy scanRead begin ************************************************/
scanReadIni: procedure expose m.
    if m.scanRead.ini = 1 then
        return
    m.scanRead.ini = 1
    call scanIni
 /* ts = classNew('n Scan u f TOK v, f VAL v, f KEY v, f TYPE v') */
    call classNew 'n ScanRead u ScanSB', 'm',
        , 'scanEnd return m.m.atEnd' ,
        , 'scanNL return scanReadNL(m, unCond)',
        , 'scanInfo return scanReadInfo(m)',
        , 'scanPos return scanReadPos(m)',
        , "jOpen   call scanReadOpen m, arg()>=3, arg(3)" ,
        , "jClose  call scanReadClose m"
    call classNew "n EditRead u JRW", "m" ,
        , "jRead  return editRead(m)",
        , "jOpen" ,
        , "jReset m.m.linex = arg - 1"
    return
endProcedure scanReadIni

/*--- begin scanning the lines of a reader ---------------------------*/
scanRead: procedure expose m.
parse arg rdr, n1, np, co
    return scanReadReset(oNew('ScanRead'), rdr, n1, np, co)

scanReadReset: procedure expose m.
parse arg m, m.m.rdr, n1, np, co
    return oMutatName(scanOpt(m, n1, np, co), 'ScanRead')
endProcedure scanReadReset

scanReadOpen: procedure expose m.
parse arg m, hasLine0, line0
    call scanReadO0 m
    if hasLine0 == 1 then
        m.m.src = line0
    else
        call scanNL m, 1
    return m
endProcedure scanReadOpen

scanReadO0: procedure expose m.
parse arg m
    call scanSrc m
    m.m.atEnd = 0
    m.m.lineX = 0
    m.m.val = ''
    m.m.key = ''
    call jOpen m.m.rdr, '<'
    return m
endProcedure scanReadO0

scanReadClose: procedure expose m.
parse arg m
    call jClose m.m.rdr
    m.m.atEnd = 'closed'
    return m
endProcedure scanReadClose

/*--- scan over next newLine
        if unCond \== 1 only if we are already at endOfLine
         return true if we scanned a NL ------------------------------*/
scanReadNL: procedure expose m.
parse arg m, unCond
    m.m.tok = ''
    if unCond \== 1 then
        if m.m.pos <= length(m.m.src) then
            return 0
    if m.m.atEnd then
        return 0
    m.m.tok = substr(m.m.src, m.m.pos)
    r = m.m.rdr
    if \ jRead(r) then do
        m.m.atEnd = 1
        m.m.pos = 1 + length(m.m.src)
        return 0
        end
    m.m.src = m.r
    m.m.pos = 1
    m.m.lineX = m.m.lineX + 1
    return 1
endProcedure scanReadNl

scanTextCom: procedure expose m.
parse arg m, untC, untWrds
    if m.m.scanComment \= '' then
        untC = untC || left(m.m.scanComment, 1)
    if \ m.m.scanNestCom then
        return scanText(m, untC, untWrds)
    else if wordPos('*/', untWrds) > 0 then
        return scanText(m, untC'*/', untWrds)
    res = scanText(m, untC'*/', untWrds '*/')
    if res then
        if scanLook(m, 2) == '*/' then
            call scanErr m, '*/ without preceeding comment start /*'
    return res
endProcedure scanTextCom

scanText: procedure expose m.
parse arg m, untC, untWrds
    res = ''
    do forever
        if scanUntil(m, untC) then do
            res = res || m.m.tok
            if m.m.pos > length(m.m.src) then do
                /* if windowing we need to move the window| */
                if scanNl(m, 0) then
                    if right(res, 1) \==' ' & scanLook(m, 1)\==' ' then
                        res = res' '
                iterate
                end
            end
        m.m.tok = res
        c9 = scanLook(m, 9)
        do sx=1 to words(untWrds)
            if abbrev(c9, word(untWrds, sx)) then
                return 1
            end
        if scanCom(m) | scanNl(m, 0) then do
            if right(res, 1) \== ' ' & scanLook(m, 1) \== ' ' then
                res = res' '
            end
        else if scanString(m) then
            res = res || m.m.tok
        else if scanChar(m, 1) then
            res = res || m.m.tok
        else if scanEnd(m) then do
            m.m.tok = res
            return res \== ''  /* erst hier NACH scanCom,  scanNl */
            end
        else
            call scanErr m, 'bad pos'
        end
endProcedure scanText

scanReadPos: procedure expose m.
parse arg m, msg
    return m.m.lineX m.m.pos
endProcedure scanReadPos

scanReadInfo: procedure expose m.
parse arg m
    msg = 'last token' m.m.tok 'scanPosition' ,
        strip(substr(m.m.src, m.m.pos, 40), 't')
    if scanEnd(m) then
        msg = msg'\natEnd after'
    else
        msg = msg'\npos' m.m.pos 'in'
    return msg 'line' m.m.lineX':' strip(m.m.src, 't')
endProcedure scanReadInfo

/*--- use scan sqlEdit macro --> temporarily here --------------------*/
/*--- read next line from edit data ----------------------------------*/
editRead: procedure expose m.
parse arg m
    m.m.lineX = m.m.lineX + 1
    if adrEdit('(ll) = line' m.m.lineX, 12) \= 0 then
        return 0
    m.m = ll
    return 1
endProcedure editRead
/*--- search loop in edit macro --------------------------------------*/
scanSqlSeekId: procedure expose m.
parse arg m, lx, cmd, opts
    if opts = '' then
        opts = word
                     /* line 1 col 0, otherwise first word is skipped*/
    if adrEdit("cursor =" max(trunc(lx), 1) 0, 12) = 12 then
        return -1
    do while adrEdit("seek" cmd opts, 4) = 0 /* find each command*/
        call adrEdit "(fx) = cursor"
        if m.debug then do
            call adrEdit "(LI) = LINE" fx
            call debug 'scanSqlSeekId after' lx 'found' cmd 'at' fx li
            end
        call jReset m.m.rdr, fx
        call jOpen m, '<'
        m.m.lineX = fx
        do while word(scanPos(m), 1) <= fx & scanSqlClass(m)
            if m.m.sqlClass = 'i' & m.m.val == cmd then
                return fx
            end
        call jClose m
        end
    return -1
endProcedure scanSqlSeekId
/* copy scanRead end **************************************************/
/* copy scanWin begin *************************************************
     scan the the concatenation of the lines of a reader
         any token my be split over several line
         except the end-of-line-comment-token
***********************************************************************/
scanWinIni: procedure expose m.
    if m.scanWin.ini = 1 then
        return
    m.scanWin.ini = 1
    call scanReadIni
    call classNew 'n ScanWin u ScanSB', 'm',
        , "jOpen call scanWinOpen m",
        , "jClose call scanReadClose m",
        , 'scanNL return scanWinNl(m, unCond)',
        , 'scanCom return scanWinCom(m)',
        , 'scanInfo return scanWinInfo(m)',
        , 'scanPos  return scanWinPos(m)',
        , 'isWindow 1',
        , 'jRead' classMet(class4Name('ScanSB'), 'jRead')
    return
endProcedure scanWinIni

/*--- instanciate a new window scanner, open rdr ---------------------*/
scanWin: procedure expose m.
parse arg rdr, wOpts
    return scanWinReset(scanSqlOpt(oNew('ScanWin')), rdr, wOpts)

/*--- set the reader and attributes of window scanner m -------------*/
scanWinReset: procedure expose m.
parse arg m, m.m.rdr, cuLe wiFo wiBa
    call oMutatName m, 'ScanWin'
    if pos('@', cuLe) > 0 then
        parse var cuLe cuLe '@' m.m.cutPos
    else
        m.m.cutPos = 1
    cuLe = word(cuLe 72, 1)
    m.m.cutLen = cuLe
    m.m.posMin = word(wiba 3, 1) * cuLe    /* room to go back */
    m.m.posLim = m.m.posMin + 3 * cuLe
    m.m.winTot = m.m.posLim + cuLe * (1 + word(wiFo 5, 1))
    m.m.cutLen = cuLe                      /* fix recLen */
    return m
endProcedure scanWinReset

/*--- open reader and start scanning --------------------------------*/
scanWinOpen: procedure expose m.
parse arg m, lx
    call scanReadO0 oMutatName(m, 'ScanWin')
    call scanWinRead m
    m.m.lineX = word(lx 1, 1)
    return m
endProcedure scanWinOpen

/*--- move the source window: cut left side and append at right side
      return number of characters cut at left ------------------------*/
scanWinRead: procedure expose m.
parse arg m
    dlt = 0
    if m.m.atEnd then
        return 0
    if m.m.pos >= m.m.posLim then do     /*  cut left side */
        dlt = m.m.pos - ((m.m.pos-1) // m.m.cutLen + 1 + m.m.posMin)
        call assert 'dlt >= 0 & dlt // m.m.cutLen = 0', 'dlt m.m.cutLen'
        m.m.src = substr(m.m.src, dlt+1)
        m.m.pos = m.m.pos - dlt
        m.m.lineX = m.m.lineX + dlt % m.m.cutLen
        end
    r = m.m.rdr
    do while length(m.m.src) < m.m.winTot /* read and fill to len */
        if \ jRead(r) then do
            m.m.atEnd = 1
            return dlt
            end
        m.m.src = m.m.src || substr(m.r, m.m.cutPos, m.m.cutLen)
        end
    call assert 'length(m.m.src) = m.m.winTot',
              , 'm.m.winTot length(m.m.src) m.m.src'
    return dlt
endProcedure scanWinRead

/*--- return position of next line start -----------------------------*/
scanWinNLPos: procedure expose m.
parse arg m
    return m.m.pos + m.m.cutLen - ((m.m.pos - 1) // m.m.cutLen)

/*--- scan comment ---------------------------------------------------*/
scanWinCom: procedure expose m.
parse arg m
    call scanWinRead m
    if m.m.scanComment \== '' then do
        cl = length(m.m.scanComment)
        if scanLook(m, cl) == m.m.scanComment then do
            np = scanWinNlPos(m)
            if np = m.m.pos then do
                m.m.pos = np +  m.m.cutLen
                return 1
                end
            if np >= m.m.pos + cl then do
                m.m.pos = np
                return 1
                end
            end
        end
    if m.m.scanNestCom then
        if scanLit(m, '/*') then do
            call scanTextCom m, , '*/'
            if \ scanLit(m, '*/') then
                 call scanErr m, 'nested comment after /* not finished'
            return 1
            end
    m.m.tok = ''
    return 0
endProcedure scanWinCom


/*--- scan nl --------------------------------------------------------*/
scanWinNL: procedure expose m.
parse arg m, unCond
    call scanWinRead m
    m.m.tok = ''
    if unCond \== 1 then
       return 0
    np = scanWinNLPos(m)
    if np = m.m.pos then
        return 0
    m.m.tok = substr(m.m.pos, np-m.m.pos)
    m.m.pos = np
    return 1
endProcedure scanWinNl

/*--- return current position in input ------------------------------*/
scanWinPos: procedure expose m.
parse arg m
    if scanEnd(m) then
        return 'E'
    ps = m.m.pos - 1
    return (m.m.lineX + (ps % m.m.cutLen)) (ps // m.m.cutLen + 1)
endProcedure scanWinPos

/*--- return a description of the current scan position --------------*/
scanWinInfo: procedure expose m.
parse arg m
    p = scanWinPos(m)
    if p == 'E' then do
        res = 'atEnd after'
        p = m.m.lineX - 1 + length(m.m.src) % m.m.cutLen
        end
    else do
        res = 'pos' word(p, 2) 'in'
        p = word(p, 1)
        end
    return 'last token' m.m.tok 'scanPosition' ,
          strip(left(substr(m.m.src, m.m.pos), 40), 't') ,
        || '\n'res 'line' p':' strip(substr(m.m.src,
          , 1 + (p - m.m.lineX) * m.m.cutLen, m.m.cutLen), 't')
endProcedure scanWinInfo
/* copy scanWin end   *************************************************/
/* copy scanSql begin *************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanSqlReset: procedure expose m.
parse arg m, r, scanWin, stmtOpt
    call scanSqlStmtOpt scanSqlOpt(m), strip(stmtOpt)
    if scanWin \== 0 then
        return scanWinReset(m, r, scanWin)
    else if r \== '' then
        return scanReadReset(m, r)
    else
        return scanSbReset(m, m.m.src)
endProcedure scanSqlReset

scanSqlOpt: procedure expose m.
parse arg m
    call scanOpt m, , '0123456789_' , '--'
    m.m.scanNestCom = 1
    return m
endProcedure scanSqlOpt

/*--- scan a sql token put class in m.sqlclass:
      'i': ordinary identifier   e.g. Name
      'd': delimited identifier  e.g. "Delimited"
      'q': qualified identifier  e.g. abc."efg"
      'u': integer units         e.g. 8G
      'n': number                e.g. -234 or .2e3
      's': string                e.g. 'abc''ef'
      '' : at end
         : any other character   e.g. ;
      ----------------------------------------------------------------*/
scanSqlClass: procedure expose m.
parse arg m, retSpace
    m.m.val = ''
    if scanSpace(m) & retSpace = 1 then do
        m.m.sqlClass = 'b'
        return 1
        end
    c2 = scanLook(m ,2)
    if scanString(m, "' x' X'") then do
        m.m.sqlClass = 's'
        if \abbrev(m.m.tok, "'") then
            m.m.val = x2c(m.m.val)
        end
    else if scanSqlQuId(m) then do
        if m.m.val.0 > 1 then
            m.m.sqlClass = 'q'
        else if abbrev(m.m.tok, '"') then
            m.m.sqlClass = 'd'
        else
            m.m.sqlClass = 'i'
        end
    else if scanSqlNum(m, 0)  then
        m.m.sqlClass = 'n'
    else if scanChar(m, 1) then
        m.m.sqlClass = m.m.tok
    else if scanEnd(m) then do
        m.m.sqlClass = ''
        return 0
        end
    else
        call scanErr m, 'cannot scan sql'
    return 1
endProcedure scanSqlClass

scanSqlSkipBrackets: procedure expose m.
parse arg m, br
    if br \== '' then
        nop
    else if scanLit(m, '(') then
        br = 1
    else
        return 0
    do while scanSqlClass(m) & m.m.sqlClass \== ';'
        if m.m.sqlClass = '('        then br = br + 1
        else if m.m.sqlClass \== ')' then iterate
        else if br > 1              then br = br - 1
        else                             return 1
        end
    call scanErr m, '; or eof, but' br 'closing ) expected'
endProcedure skipBrackets
/*--- scan an ordinary sql identifier e.g. abc, ef_12 ----------------*/
scanSqlId: procedure expose m.
parse arg m
    if \ scanName(m) then
        return 0
    m.m.val = translate(m.m.tok)
    return 1
endProcedure scanSqlId

/*--- scan a delimited or ordinay sql identifier ---------------------*/
scanSqlDeId: procedure expose m.
parse arg m
    if scanSqlId(m) then
        return 1
    if \ scanString(m, '"') then
        return 0
    m.m.val = strip(m.m.val, 't')
    return 1
endProcedure scanSqlDeId

/*--- scan a qualified sql identifier --------------------------------*/
scanSqlQuId: procedure expose m.
parse arg m
    res = ''
    rto = ''
    do qx=1
        if \ scanSqlDeId(m) then do
            if qx <> 1 then
                call scanErr m, 'id expected after .'
            return 0
            end
        m.m.val.qx = m.m.val
        res = res'.'m.m.val
        rto = rto'.'m.m.tok
        if \ scanLit(scanSkip(m), '.') then
            leave
        call scanSpace m
        end
    m.m.val.0 = qx
    m.m.val = substr(res, 2)
    m.m.tok = substr(rto, 2)
    return 1
endProcedure scanSqlQuId

/*--- scan a sql number ----------------------------------------------*/
scanSqlNum: procedure expose m.
parse arg m, checkEnd, noSp
    si = ''
    if noSp == 1 then
        call err 'deimplement noSp, use scanNum instead'
    if scanLit(m, '+', '-') then do
        si = m.m.tok
        call scanSpace m
        ch = scanLook(m, 2)
        if left(ch, 1) == '.' then
            ch = substr(ch, 2)
        if pos(left(ch, 1), '0123456789') < 1 then do
            call scanBack m, si
            m.m.val = ''
            return 0
            end
        end
    res = scanNum(m, checkEnd)
    m.m.val = si || m.m.val
    return res

endProcedure scanSqlNum

/*--- scan a sql number with a unit which may follow without space ---*/
scanSqlNumUnit: procedure expose m.
parse arg m, both, units
    if \ scanSqlNum(m, 0) then
        return 0
    nu = m.m.val
    sp = scanSpace(m)
    if scanSqlId(m) then do
        if units == '' | wordpos(m.m.val, units) > 0 then
            nu = nu m.m.val
        else if both | \ sp then
            call scanErr m, 'scanSqlNumUnit after' nu 'bad unit' m.m.val
        else
            call scanBack m, m.m.tok
        end
    else if both then
        call scanErr m, 'scanSqlNumUnit no unit after' nu
    else if \sp & pos(scanLook(m, 1), m.m.scanNameR) > 0 then
        call scanErr m, 'scanSqlNumUnit bad number end after' nu
    m.m.val = nu
    return 1
endProcedure scanSqlNumUnit

/*--- find next statement, after scanSqlStmtOpt -----------------------
       m.m.stop contains delimiter, will be changed by
          terminator?; or --#terminator               */
scanSqlStmtOpt: procedure expose m.
parse arg m, m.m.stop
    if m.m.stop == '' then
        m.m.stop = ';'
    return m
endProcedure scanSqlStmtOpt

scanSqlStmt: procedure expose m.
parse arg m
    loop = 0
    res = ''
    fuCo = copies(m.m.scanComment'#SET', m.m.scanComment \== '')
    do lx=1
        if lx > 100 then
            say '????iterating' scanLook(m)
        if m.m.stop == '' then
            scTx = scanTextCom(m, '''"' ,fuCo)
        else
            scTx = scanTextCom(m, '''"'left(m.m.stop,1), m.m.stop fuCo)
        if scTx then do
            res = res || m.m.tok
            if fuCo \== '' then
                if scanLook(m, length(fuCo)) == fuCo then do
                    tx = scanLook(m)
                    ok = word(tx, 2) == 'TERMINATOR' ,
                         & length(word(tx, 3)) == 1
                    if ok then
                       ok = scanCom(m)
                    if ok then do
                        m.m.stop = word(tx, 3)
                        if \ (right(res, 1) == ' ' ,
                                 | scanLook(m, 1) == ' ') then
                            res = res' '
                        end
                    else if scanChar(m, 1) then
                        res = res || m.m.tok
                    else
                        call scanErr m, 'no char, now what?'
                    iterate
                    end
            if m.m.stop \== '' then
                if \ scanLit(m, m.m.stop) then
                    iterate
            end
        res = strip(res)
        if length(res)=11 ,
            & abbrev(translate(res), 'TERMINATOR') then do
            m.m.stop = substr(res, 11, 1)
            res = ''
            end
        else if res \== '' | \ scTx then
            return res
        end
     call scanErr m, 'loop in scanSqlStmt'
endProcedure scanSqlStmt

/*-- return next sqlStmt from rdr ( or string or '' = j.in) ---------*/
scanSqlIn2Stmt: procedure expose m.
parse arg rdr, wOpt
    s = scanSqlIn2Scan(rdr, m'.SCAN_SqlIn2Stmt', wOpt)
    res = scanSqlStmt(scanOpen(s))
    call scanReadClose s
    return res
endProcedure scanSqlIn2Stmt

/*-- reset s as scanSql from rdr ( or string or '' = j.in) ----------*/
scanSqlIn2Scan: procedure expose m.
parse arg rdr, s, wOpt, sOpt
    interpret objMet(rdr, 'scanSqlIn2Scan')
endProcedure scanSqlIn2Scan

/*-- create a new scanSqlStmtRdr ------------------------------------*/
scanSqlStmtRdr: procedure expose m.
parse arg rdr, wOpt, sOpt
    return oNew('ScanSqlStmtRdr', rdr, wOpt, sOpt)

/*-- reset a new scanSqlStmtRdr
         must be called from jReset to allow jRead ------------------*/
scanSqlStmtRdrReset: procedure expose m.
parse arg m, rdr, wOpt, sOpt
    call scanSqlIn2Scan rdr, m'.SCAN', wOpt, sOpt
    return oMutatName(m, 'ScanSqlStmtRdr')
endProcedure scanSqlStmtRdrReset
/* copy scanSql end   *************************************************/
/* copy scanUtil begin *************************************************
    scan db2 utility input statements using scan and a reader
**********************************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanUtilOpt: procedure expose m.
parse arg m
    call scanSqlOpt m
    m.m.scanNestCom = 0
    m.m.utilBrackets = 0
    m.scanUtil =  'BACKUP CATENFM CATMAINT CHECK' ,
                  'COPY COPYTOCOPY DIAGNOSE EXEC LISTDEF LOAD' ,
                  'MERGECOPY MODIFY OPTIONS QUIESCE REBUILD' ,
                  'RECOVER REORG REPAIR REPORT RESTORE' ,
                  'RUNSTATS STOSPACE TEMPLATE UNLOAD'
    return m
endProcedure scanUtilOpt
/*--- scan next token and put its class in m.sc.utilClass:
      'u' a utility name
      'n' a name
      '"' a quoted name
      "'" an apostroph'd string
      '.' a .
      ',' a ,
      'v' a value
      ''  at end
      ---------------------------------------------------------------*/
scanUtil: procedure expose m.
parse arg sc
    m.sc.utilSpace = scanSpace(sc)
    ty = '?'
    if scanLit(sc, '(') then do
        m.sc.utilBrackets = m.sc.utilBrackets + 1
        end
    else if scanLIT(sc, ')') then do
        m.sc.utilBrackets = m.sc.utilBrackets - 1
        if m.sc.utilBrackets < 0 then
           call scanErr sc, 'unmatched closing bracket )'
        end
    else if scanLit(sc, ',') then do
        end
    else if scanLit(sc, '.') then do
        end
    else if scanString(sc, "'") then do
        end
    else if scanString(sc, '"') then do
        end
    else if scanName(sc) then do
        m.sc.val = translate(m.sc.tok)
        if m.sc.utilBrackets > 0 then
            ty = 'n'
        else if 0 < wordPos(m.sc.val, m.scanUtil) then
            ty = 'u'
        else
            ty = 'n'
        end
    else if scanVerify(sc, ' (),''"', 'm') then do
        ty = 'v'
        m.sc.val = translate(m.sc.tok)
        end
    else if \scanEnd(sc) then do
            call scanErr sc, 'scanUtil stopped before end'
        end
    else do
        ty = ''
        m.sc.val = ''
        end
    if ty == '?' then
        m.sc.utilClass = left(m.sc.tok, 1)
    else
        m.sc.utilClass = ty
    return m.sc.utilClass
endProcedure scanUtil

/*--- scan a value or a bracketed list of values ---------------------*/
scanUtilValue: procedure expose m.
parse arg sc, remApo, nl
    if remApo = '' | rempApo = 0 then
        remApo = "nv"
    else if rempApo = 1 then
        remApo = "nv'"
    if '(' \== scanUtil(sc) then
         return scanUtilValueOne(sc, remApo)
    v = ''
    brx = m.sc.utilBrackets
    oLine = word(scanPos(sc), 1)
    do forever
        call scanUtil sc
        one = scanUtilValueOne(sc, remApo)
        if one == '' then
           call scanErr sc, 'eof in brackets'
        else if brx > m.sc.utilBrackets then
           return v
        nLine = word(scanPos(sc), 1)
        if \ m.sc.utilSpace then
            v = v || one
        else if nl \== '' & oLine <> nLine then
            v = v || nl || one
        else
            v = v' 'one
        oLine = nLine
        end
endProcedure scanUtilValue

scanUtilValueOne: procedure expose m.
parse arg sc, valTy
    if m.sc.utilClass == '' then
        return ''
    else if m.sc.utilClass == 'u' then
        call scanErr sc, 'util in scanUtilValueOne'
    if pos(m.sc.utilClass, valTy) > 0 then
        return m.sc.val
    else
        return m.sc.tok
endProcedure scanUtilValueOne

/*--- skip over nested brackets --------------------------------------*/
scanUtilSkipBrackets: procedure expose m.
parse arg m, br, doCat
    if br \== '' then
        lim = m.m.utilBrackets - br
    else if scanLit(m, '(') then do
        lim = m.m.utilBrackets
        m.m.utilBrackets = lim + 1
        end
    else
        return 0
    doCat = doCat == 1
    res = ''
    do while scanUtil(m) \== ''
        if m.m.utilBrackets <= lim then do
            if doCat then
                m.m.val = res
            return 1
            end
        if doCat then
            res = res m.m.tok
        end
    return scanErr(m, 'eof with' m.m.utilBrackets 'open (')
endProcedure skipBrackets

/*--- analyze a punch file write intoField to stdOut -----------------*/
scanUtilInto: procedure expose m.
parse arg m
    if m.m.utilBrackets \== 0 then
        call scanErr m, 'scanUtilInto with brackets' m.m.utilBrackets
  /*sc = scanUtilReader(m.j.in)
    call jOpen sc, 'r'
 */ do forever
        cl = scanUtil(m)
        if cl == '' then
            return 0
        if cl = 'n' & m.m.tok == 'INTO' then
            leave
        end
    if scanUtil(m) \== 'n' | m.m.tok \== 'TABLE' then
        call scanErr m, 'bad into table '
    if \ scanSqlQuId(scanSkip(m)) then
        call scanErr m, 'table name expected'
    if m.m.utilBrackets \== 0 then
        call scanErr m, 'into table in brackets' m.m.utilBrackets
    m.m.tb = m.m.val
    m.m.part = ''
    m.m.when = ''
    do forever
        cl = scanUtil(m)
        if cl == '' then
            call scanErr m, 'eof after into'
        if cl == 'n' & m.m.tok == 'PART' then do
            if scanUtil(m) == 'v' then
                m.m.part = m.m.val
            else
                call scanErr m, 'bad part'
            end
        else if cl == 'n' & wordPos(m.m.val, 'WHEN WORKDDN') > 0 then do
            call scanUtilSkipBrackets m
            end
        else if cl == '(' then do
           leave
           end
        end
    oX =  m.m.lineX
    oL =  overlay('', m.m.src, 1, m.m.pos-2)
    do while m.m.utilBrackets > 0
        call scanUtil m
        if oX \== m.m.lineX then do
            call out strip(oL, 't')
            oX =  m.m.lineX
            oL =  m.m.src
            end
        end
    call out left(oL, m.m.pos)
 /* call jClose sc
 */ return 1
endProcedure scanUtilInto
/* copy scanUtil end **************************************************/
/* copy pipe begin *****************************************************
***********************************************************************/
pipeIni: procedure expose m.
    if m.pipe.ini == 1 then
        return
    m.pipe.ini = 1
    call catIni
    call mapReset v
    m.v_with.0 = 0
    m.v_withMap = ''
    m.v_with.0.map = ''
    m.pipe.0 = 1
    m.pipe.1.in  = m.j.in
    m.pipe.1.out = m.j.out
    call pipe '+'
    return
endProcedure pipeIni

/*-------------------------------
  +-       push pop frame
  PYNFA    ouput: Parent saY Newcat File, Appendtofile
  psf|     input: parent string file oldOut
  old          --> new
  pipeBegin    --> pipe '+N'
  pipeBeLa f   --> pipe '+F'
  pipeLast     --> pipe 'P|'
  pipeLast f   --> pipe 'F|', f
  pipeEnd      --> pipe '-'
--------------------------------*/
pipe: procedure expose m.
parse arg opts, aO, aI
    ox = 1; oc = substr(opts, ox, 1)
    ax = m.pipe.0
    px = ax -1
    if oc == '-' then do
        if px < 2 then
            call err 'pipe pop empty'
        call jClose m.pipe.ax.out
        call jClose m.pipe.ax.in
        ax = px
        m.pipe.0 = ax
        px = ax-1
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if oc == '+' then do
        px = ax
        ax = ax+ 1
        m.pipe.0 = ax
        m.pipe.ax.in  = jOpen(m.pipe.px.in, '<')
        m.pipe.ax.out = jOpen(m.pipe.px.out, '>')
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    oOut = m.pipe.ax.out
    if pos(oc, 'NYPFA') > 0 then do
        call jClose oOut
        if oc == 'Y' then
            m.pipe.ax.out = jOpen(m.pipe.1.out, '>')
        else if oc == 'P' then
            m.pipe.ax.out = jOpen(m.pipe.px.out, '>')
        else if oc == 'N' then
            m.pipe.ax.out = jOpen(Cat(), '>')
        else if oc == 'F' then
            m.pipe.ax.out = jOpen(o2file(aO), '>')
        else if oc == 'A' then
            m.pipe.ax.out = jOpen(o2file(aO), '>>')
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if pos(oc, 's|fp') > 0 then do
        call jClose m.pipe.ax.in
        if oc == 'p' then
            m.pipe.ax.in = jOpen(m.pipe.px.in, '<')
        else if oc == '|' then
            m.pipe.ax.in = jOpen(oOut, '<')
        else if oc == 'f' then do
            if arg() <= 3 then
                m.pipe.ax.in = jOpen(o2file(aI), '<')
            else do
                ct = jOpen(Cat(), '>')
                do lx = 3 to arg()
                    call jWriteAll ct, arg(lx)
                    end
                m.pipe.ax.in = jOpen(jclose(ct), '<')
                end
            end
        else if arg() <= 3 then
            m.pipe.ax.in = jOpen(jBuf(aI), '<')
        else do
            bu = jOpen(jBuf(), '>')
            do lx = 3 to arg()
                call jWrite bu, arg(lx)
                end
            m.pipe.ax.in = jOpen(jclose(bu), '<')
            end
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if oc \== ' ' then
        call err 'implement' substr(opts, ox) 'in pipe' opts
    m.j.in  = m.pipe.ax.in
    m.j.out = m.pipe.ax.out
    return
endProcedure pipe

/*--- write all from rdr (rsp in) to out, not lazy ----------------*/
pipeWriteNow: procedure expose m.
    parse arg rdr
    call jWriteNow m.j.out, if(rdr == '', m.j.in, rdr)
    return
endProcedure pipeWriteNow

/*--- write all from rdr (rsp in) to out, possibly lazy -----------*/
pipeWriteAll: procedure expose m.
parse arg rdr
    call jWriteAll m.j.out, if(rdr == '', m.j.in, rdr)
    return
endProcedure pipeWriteAll

pipePreSuf: procedure expose m.
parse arg le, ri
    do while in()
        call out le || m.in || ri
        end
    return
endProcedure pipePreSuf

vIsDefined: procedure expose m.
parse arg na
    return   '' \== vAdr(na, 'g')
endProcedure vIsDefined

vWith: procedure expose m.
parse arg fun, o
    if fun == '-' then do
        tBe = m.v_with.0
        tos = tBe - 1
        if tos < 0 then
            call err 'pop empty withStack'
        m.v_with.0 = tos
        m.v_withMap = m.v_with.tos.map
        return m.v_with.tBe.obj
        end
    tos = m.v_with.0
    if fun == '=' then do
        if o == '' & m.v_with.tos.obj == '' then
            return
        if o \== '' & m.v_with.tos.obj \== '' then
            if objClass(o) == objClass(m.v_with.tos.obj) then do
                m.v_with.tos.obj = o
                return
                end
        par = tos - 1
        end
    else if fun == '+' then do
        par = tos
        tos = tos + 1
        m.v_with.0 = tos
        end
    m.v_with.tos.obj = o
    if par > 0 then
        key = m.v_with.par.classes
    else
        key = ''
    if o \== '' then
        key = strip(key objClass(o))
    m.v_with.tos.classes = key
    if symbol('m.v_withManager.key') == 'VAR' then do
        m.v_with.tos.map = m.v_withManager.key
        m.v_withMap = m.v_withManager.key
        return
        end
    m = mapNew()
    m.v_with.tos.map = m
    m.v_withMap = m
    m.v_withManager.key = m
    do kx=1 to words(key)
        c1 = word(key, kx)
        call vWithAdd m, kx, classMet(c1, 'oFlds')
        call vWithAdd m, kx, classMet(c1, 'stms')
        end
    return
endProcedure vWith

vWithAdd: procedure expose m.
parse arg m, kx, ff
    do fx=1 to m.ff.0
        n1 = m.ff.fx
        dx = pos('.', n1)
        if dx > 1 then
            n1 = left(n1, dx-1)
        else if dx = 1 | n1 = '' then
            iterate
        call mPut m'.'n1, kx
        end
    return
endProcedure vWithAdd

vGet: procedure expose m.
parse arg na
    a = vAdr(na, 'g')
    if a = '' then
        call err 'undefined var' na
    return m.a
endProcedure vGet


vPut: procedure expose m.
parse arg na, val
    a = vAdr(na, 'p')
    m.a = val
    return val
endProcedure vPut

/*--- find the final address
      return f || a with address a and
             f = m -> mapGet(a), o -> obect m.a, s -> string m.a  ---*/
vAdr: procedure expose m.
parse arg na, f
    cx = 0
    cx = verify(na, '&>', 'm')
    if cx > 0 then
        a = left(na, cx-1)
    else do
        a = na
        cx = length(na)+1
        end
    nxt = 0
    do forever
        cy = verify(na, '&>', 'm', cx+1)
        if cy > 0 then
            fld = substr(na, cx+1, cy-cx-1)
        else
            fld = substr(na, cx+1)
        if substr(na, cx, 1) == '>' then do
            if nxt then
                a = vAdrByM(a)
            if fld \== '' then
               a = a'.'fld
            end
        else do
            if nxt then
                a = vAdrByM(a)
            mp = m.v_withMap
            aL = a
            if pos('.', a) > 0 then
                aL = left(a, pos('.', a)-1)
            if mp \== '' & symbol('m.mp.aL') == 'VAR' then do
                wx = m.mp.aL
                a = m.v_with.wx.obj'.'a
                end
            else if cx >= length(na) then
                return mapAdr(v, a, f)
            else
                a = mapAdr(v, a, 'g')
            if fld \== '' then
                a = vAdrByM(a)'.'fld
            end
        if cy < 1 then do
            if f == 'g' then
                if symbol('m.a') \== 'VAR' then
                    return ''
            return a
            end
        cx = cy
        nxt = 1
        end
endProcedure vAdr
vAdrByM:
parse arg axx
    if axx = '' then
        return err('null address at' substr(na, cx) 'in' na)
    if symbol('m.axx') \== 'VAR' then
        return err('undef address' axx 'at' substr(na, cx) 'in' na)
    ayy = m.axx
    if ayy == '' then
          return err('null address at' substr(na, cx) 'in' na)
    return ayy
endProcedure vAdrByM

vRead: procedure expose m.
parse arg na
    return inVar("V."na)

vReadO: procedure expose m.
parse arg na
    return vRead()
endProcedure vReadO

vHasKey: procedure expose m.
parse arg na
    return mapHasKey(v, na)

vRemove: procedure expose m.
parse arg na
    return mapRemove(v, na)
/* copy pipe end ******************************************************/
/* copy cat  begin ****************************************************
***********************************************************************/
/*--- create a new cat -----------------------------------------------*/
cat: procedure expose m.
    m = oNew('Cat') /* calls catReset */
    do ax=1 to arg()
        call catWriteAll m, arg(ax)
        end
    return m
endProcedure cat

catReset: procedure expose m.
parse arg m
    m.m.RWs.0 = 0
    m.m.catWr = ''
    m.m.catRd = ''
    m.m.catIx = -9e9
    m.m.catKeepOpen = ''
    return m
endProcedure catReset

catClose: procedure expose m.
parse arg m
    if m.m.catWr \== '' then do
        call jClose m.m.catWr
        call mAdd m'.RWS', m.m.catWr
        m.m.catWr = ''
        end
    if m.m.catRd \== '' then do
        call jClose m.m.catRd
        m.m.catRd = ''
        end
    m.m.catIx = -9e9
    return m
endProcedure catClose

catOpen: procedure expose m.
parse arg m, oo
    if oo == m.j.cRead then do
        m.m.catIx = 0
        call catNextRdr m
        m.m.jReading = 1
        end
    else if oo == m.j.cWri | oo == m.j.cApp then do
        if oo == m.j.cWri then
            m.m.RWs.0 = 0
        m.m.catIx = -9e9
        m.m.jWriting = 1
        end
    else do
        call err 'catOpen('m',' oo') bad opt'
        end
    return m
endProcedure catOpen

/*--- return and open next reader ------------------------------------*/
catNextRdr: procedure expose m.
parse arg m
    if m.m.catRd \== '' then
        call jClose m.m.catRd
    cx = m.m.catIx + 1
    m.m.catIx = cx
    if cx > m.m.RWs.0 then do
        m.m.catRd = ''
        return 0
        end
    m.m.catRd = m.m.RWs.cx
    if cx = word(m.m.catKeepOpen, 1) then
        m.m.catKeepOpen = subWord(m.catKeepOpen, 2)
    else
        call jOpen m.m.catRd , m.j.cRead
    return 1
endProcedure catNextRdr

catRead: procedure expose m.
parse arg m
    do while m.m.catRd \== ''
        cr = m.m.catRd
        if jRead(cr) then do
            m.m = m.cr
            return 1
            end
        call catNextRdr m
        end
    return 0
endProcedure catRead

catWrite: procedure expose m.
parse arg m, line
    if m.m.catWr == '' then
        m.m.catWr = jOpen(jBuf(), m.j.cWri)
    call jWrite m.m.catWr, line
    return
endProcedure catWrite

/*--- write contents of a reader to cat
          or keep it for later reading -------------------------------*/
catWriteAll: procedure expose m.
parse arg m
    if m.m.catWr \== '' then do
        call mAdd m'.RWS', jClose(m.m.catWr)
        m.m.catWr = ''
        end
    do ax=2 by 1 to arg()
        r = o2File(arg(ax))
        call mAdd m'.RWS', r
        if m.r.jReading then do
            m.m.catKeepOpen = m.m.rws.0 m.m.catKeepOpen
            call jOpen r, m.j.cRead
            end
        end
    return
endProcedure catWriteAll

/*--- create a reader/WriteO for an external file --------------------*/
file: procedure expose m.
parse arg str
    return oNew('File', str)
endProcedure file

fileChild: procedure expose m.
parse arg m, name, opt
    interpret objMet(m, 'fileChild')
endProcedure fileChild

fileRm: procedure expose m.
parse arg m
    interpret objMet(m, 'fileRm')
    return
endProcedure fileRm

filePath: procedure expose m.
parse arg m
    interpret objMet(m, 'filePath')
endProcedure filePath

fileIsFile: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsFile')
endProcedure fileIsFile

fileIsDir: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsDir')
endProcedure fileIsDir

fileMkDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileMkDir')
    return
endProcedure fileRm

fileRmDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileRmDir')
    return
endProcedure fileRm

/*--- create a reader/WriteO for the filelist of a directory----------*/
fileList: procedure expose m.
parse arg m, opt
    str = oIfStr(m, '')
    if str == '' then
        return oNew('FileList', filePath(m),  opt)
    else
        return oNew('FileList', dsn2Jcl(str),  opt)
endProcedure fileList

catIni: procedure expose m.
    if m.cat.ini == 1 then
        return
    m.cat.ini = 1
    call errIni
    call jIni
    call classNew "n Cat u JRW", "m",
        , "jOpen  call catOpen m, opt",
        , "jReset call catReset m, arg",
        , "jClose call catClose m",
        , "jRead return catRead(m)",
        , "jWrite call catWrite m, line; return",
        , "jWriteAll call catWriteAll m, rdr; return"

    if m.err.os == 'TSO' then
        call fileTsoIni
    else if m.err.os == 'LINUX' then
        call fileLinuxIni
    else
        call err 'file not implemented for os' m.err.os
    return
endProcedure catIni
/* copy cat  end   ****************************************************/
/* copy mail begin ***************************************************/
mailHead: procedure expose m.
parse arg m, subj, rec, snd
    m.m.1 = 'SUBJECT   ' subj
    m.m.2 = 'RECPLIST  ' rec
    m.m.3 = 'SENDERID  ' if(snd=='', userid(), snd)
    m.m.4 = 'SENDERIDCC N'
    m.m.5 = 'DOCTEXT'
    m.m.0 = 5
    return m
endProcedure mailHead

mailSend: procedure expose m.
parse arg m, dsn
    call mAdd m, 'DOCTEXTEND'
    if dsn == '' then
        call dsnAlloc 'dd(iemapi) new  ::v255'
    else
        call dsnAlloc 'dd(iemapi) shr' dsn
    call writeDD iemapi, 'M.'m'.'
    call tsoClose iemapi
    call dsnAlloc 'dd(iemlog) sysout(*)'
    call adrTso "call *(os3550)", '*'
    if rc \== 0 then
        say 'error os3550 rc='rc 'mail not sent|||||'
    call tsoFree iemlog iemapi
    return 0
endProcedure mailSend
/* copy mail end *****************************************************/
/* copy fiLinux begin *************************************************/
/*--- send ggShCmd to shell bash,
        fail if rc <> 0 or not listed in ggRet -----------------------*/
adrSh: procedure expose m.  /* really no need for variables???? */
    parse arg ggShCmd, ggRet
    address 'bash' ggShCmd
    if rc == 0                     then return 0
    else if ggRet == '*'           then return rc
    else if wordPos(rc, ggRet) > 0 then return rc
    else
        call err 'adrSh rc' rc 'for' ggShCmd
endProcedure adrSh

fileLinuxReset: procedure expose m.
parse arg m, nm
    m.m.spec = nm
    if abbrev(nm, '&') then do
        if nm == '&in' then do
            m.m.stream = .input
            m.m.jReading = 1
            end
        else if nm == '&out' then do
            m.m.stream = .output
            m.m.jWriting = 1
            end
        else do
            call err 'bad spec' nm
            end
        end
    else do
        m.m.stream = .Stream%%new(nm)
        m.m.stream%%init(m.m.stream%%qualify)
        end
    return m
endProcedure fileLinuxReset

fileLinuxOpen: procedure expose m.
parse arg m, opt
    if opt == m.j.cRead then do
        res = m.m.stream%%open(read shareread)
        m.m.jReading = 1
        end
    else do
        if opt == m.j.cApp then
            res = m.m.stream%%open(write append)
        else if opt == m.j.cWri then
            res = m.m.stream%%open(write replace)
        else
            call err 'fileLinuxOpen('m',' opt') with bad opt'
        m.m.jWriting = 1
        end
    if res \== 'READY:' then
        call err 'fileLinuxOpen fails' res':' opt ,
        "'"m.m.stream%%qualify"'"
    return m
endProcedure fileLinuxOpen

fileLinuxClose:
parse arg m
    res = m.m.stream%%close
    if res \== 'READY:' then
        call err 'fileLinuxClose' res':' m.m.stream%%qualify
    return m
endProcedure fileLinuxClose

fileLinuxRead: procedure expose m.
parse arg m, var
    res = m.m.stream%%lineIn
    if res == '' then
        if m.m.stream%%state \== 'READY' then
            return 0
    m.var = res
    m.o.o2c.var = m.class_V
    return 1
endProcedure fileLinuxRead

fileLinuxWrite: procedure expose m.
parse arg m, line
    if m.m.stream%%lineOut(line) then
        call err 'fileLinuxWrite'
    return
endProcedure fileLinuxWrite

fileLinuxRmDir: procedure expose m.
parse arg m, opt
    if opt == '' then
        return adrSh('rmdir' m.m.spec)
    else if opt == '-r' then
        return adrSh('rm -r' m.m.spec)
    else
        call err 'bad opt' opt 'in fileLinuxRmDir'
endProcedure fileLInuxRmDir

fileLinuxListReset: procedure expose m.
parse arg m, m.m.spec, o
    if o == 'r' then
        m.m.opt = 'S'
    else if o == '' then
        m.m.opt = ''
    else
        call err 'bad opt' o 'in fileLinuxListReset'
    m.m.rx = 'closed'
    return m
endProcedure fileLinuxListReset

fileLinuxListOpen: procedure expose m.
parse arg m
    if m \== translate(m) then
        call err 'bad m for fileLinuxList:' m
    if m.m.opt == '' then
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST')
    else
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST', m.m.opt)
    if rc \== 0 then
        call err 'rc' rc 'in sysFileTree('m.m.spec', m.'m'.LIST)'
    m.m.rx = 0
    m.m.jReading = 1
    return m
endProcedure fileLinuxListOpen

fileLinuxListRead: procedure expose m.
parse arg m, var
    x = m.m.rx + 1
    if x > m.m.list.0 then
        return 0
    m.var = substr(m.m.list.x, 43)
    m.m.rx = x
    call oMutate var, m.class_V
    return 1
endProcedure fileLinuxListRead

fileLinuxIni: procedure expose m.
    if m.fileLinux.ini == 1 then
        return
    m.fileLinux.ini = 1
    m.file.sep = '/'
    call jIni
    call classNew "n File u JRW", "m",
        , "jReset call fileLinuxReset m, arg",
        , "jOpen  call fileLinuxOpen m, opt",
        , "jClose call fileLinuxClose m",
        , "jRead return fileLinuxRead(m, var)",
        , "jWrite call fileLinuxWrite m, line",
        , "filePath return m.m.stream%%qualify",
        , "fileIsFile return sysIsFile(m.m.stream%%qualify)" ,
        , "fileIsDir return sysIsFileDirectory(m.m.stream%%qualify)" ,
        , "fileChild return file(m.m.stream%%qualify'/'name)",
        , "fileRm return adrSh(m.m.spec)",
        , "fileMkDir return adrSh('mkdir' m.m.stream%%qualify)" ,
        , "fileRmDir return fileLinuxRmDir(m, opt)"
    call classNew "n FileList u JRW", "m",
        , "jReset call fileLinuxListReset m, arg, arg2",
        , "jOpen  call fileLinuxListOpen m, opt",
        , "jClose m.m.rx = 'closed'",
        , "jRead return fileLinuxListRead(m, var)"
    return
endProcedure fileLinuxIni
/* copy fiLinux end   *************************************************/
/* copy fileTso begin *************************************************/
fileTsoReset: procedure expose m.
parse arg m, sp
    m.m.readIx = 'c'
    if symbol('m.m.defDD') \== 'VAR' then do
        m.m.defDD = 'CAT*'
        m.fileTso.buf = m.fileTso.buf + 1
        m.m.buf = 'FILETSO.BUF'm.fileTso.buf
        m.m.spec = sp
        end
    if sp \== '' then do
        m.m.spec = dsnSpec(sp)
        rr = translate(subword(m.m.spec, 4))
        m.m.stripT = \ (pos(':V', rr) > 0 | pos('RECFM(V', rr) > 0)
        end
    return m
endProcedure fileTsoReset

fileTsoOpen: procedure expose m.
parse arg m, opt
    buf = m.m.buf
    if opt == m.j.cRead then do
        aa = dsnAlloc(m.m.spec, 'SHR', m.m.defDD)
        aDD = word(aa, 1)
        aDsn = m.tso_dsn.aDD
        if aDsn <> '' then
            if pos('(', aDsn) > 0 & pos('/', aDsn) < 1 then
                if sysDsn("'"m.tso_dsn.aDD"'") <> 'OK' then
                    call err 'cannot read' m.tso_dsn.aDD':',
                               sysDsn("'"m.tso_dsn.aDD"'")
        call tsoOpen word(aa, 1), 'R'
        m.m.jReading = 1
        m.buf.0 = -1
        m.m.readIx = 0
        end
    else do
        if opt == m.j.cApp then
            aa = dsnAlloc(m.m.spec, 'MOD', m.m.defDD)
        else if opt == m.j.cWri then
            aa = dsnAlloc(m.m.spec, 'OLD', m.m.defDD)
        else
            call err 'fileTsoOpen('m',' opt') with bad opt'
        call tsoOpen word(aa, 1), 'W'
        m.m.jWriting = 1
        m.buf.0 = 0
        m.m.readIx = 'w'
        end
    parse var aa m.m.dd m.m.free
    m.m.dsn = mGet('TSO_DSN.'m.m.dd)
    return m
endProcedure fileTsoOpen

fileTsoClose: procedure expose m.
parse arg m
    buf = m.m.buf
    if m.m.readIx \== 'c' then do
        if m.m.readIx == 'w' & m.buf.0 > 0 then
            call writeDD m.m.dd, 'M.'BUF'.'
        call tsoClose m.m.dd
        call tsoFree  m.m.free
        end
    m.buf.0 = 'closed'
    m.m.readIx = 'c'
    m.m.free  = ''
    m.m.dd    = ''
    return m
endProcedure fileTsoClose

fileTsoRead: procedure expose m.
parse arg m
    ix = m.m.readIx + 1
    buf = m.m.buf
    if ix > m.buf.0 then do
        res = readDD(m.m.dd, 'M.'buf'.')
        if \ res then
            return 0
        ix = 1
        end
    m.m.readIx = ix
    m.m = m.buf.ix
 /* call oMutate var, m.class_V  ?????????? */
    return 1
endProcedure fileTsoRead

fileTsoWrite: procedure expose m.
parse arg m, var
    buf = m.m.buf
    ix = m.buf.0 + 1
    m.buf.0 = ix
    if m.m.stripT then
        m.buf.ix = strip(var, 't')
    else
        m.buf.ix = var
    if ix > 99 then do
        call writeDD m.m.dd, 'M.'buf'.'
        m.buf.0 = 0
        end
    return
endProcedure fileTsoWrite

fileTsoWriteO: procedure expose m.
parse arg m, var
    if objClass(var, m.class_V) == m.class_V then do
        call fileTsoWrite m, m.var
        return
        end
    call err 'fileTsoWriteO('m',' var') cannot write objects of class',
                              objClass(var)
endProcedure fileTsoWriteO

fSub: procedure expose m.
    return file('.sysout(T) writer(intRdr)')
endProcedure fSub

fEdit: procedure expose m.
parse arg spec, vw
    if spec == '' then
        spec = 'new ::f'
    else if abbrev(spec, '::') then
        spec = 'new' spec
    else if abbrev(spec, ':') then
        spec = 'new' ':'spec
    f  = oNew('FileEdit', spec)
    m.f.editType = if(abbrev(translate(vw), 'V'), 'view', 'edit')
    return f
endProcedure fEdit

fileTsoEditClose: procedure expose m.
parse arg m
    dsn = m.m.dsn
    if dsn \== '' then do
        call fileTsoClose m
        call adrIsp m.m.editType "dataset('"dsn"')", 4
        return
        end
    fr = m.m.free
    dd = m.m.dd
    m.m.free = ''
    call fileTsoClose m
    call adrIsp "LMINIT DATAID(lmmId) ddName("dd") ENQ(SHRW)"
    eRc = adrIsp(m.m.editType "dataid("lmmId")", '*')
    lRc = adrIsp("LMFree DATAID("lmmId")", '*')
    call tsoFree fr
    if (eRc \== 0 & eRc \== 4) | lRc \== 0 then
        call err m.m.editType 'rc' eRc', lmFree rc' lRc
    return
endProcedure fileTsoEditClose

fileTsoIni: procedure expose m.
    if m.fileTso.ini == 1 then
        return
    m.fileTso.ini = 1
    m.file.sep = '.'
    m.fileTso.buf = 0
    call jIni
    um = "call err 'for tso undefined method'"
    call classNew "n File u JRW", "m",
        , "jOpen  call fileTsoOpen m, opt",
        , "jReset call fileTsoReset m, arg",
        , "jClose call fileTsoClose m",
        , "jRead return fileTsoRead(m, var)",
        , "jWrite call fileTsoWrite m, line",
        , "filePath return word(m.m.spec, 1)"           ,
        , "fileIsFile" um "'fileIsFile'"      ,
        , "fileIsDir   return 1"              ,
        , "fileChild   return file(word(m.m.spec, 1)'.'name opt)",
        , "fileRm"     um "'fileRm'"          ,
        , "fileMkDir"  ,
        , "fileRmDir"  um "'fileRmDir'"
 /*     , "filePath return m.m.stream%%qualify",
        , "fileIsFile return sysIsFile(m.m.stream%%qualify)" ,
        , "fileIsDir return sysIsFileDirectory(m.m.stream%%qualify)" ,
        , "fileChild return file(m.m.stream%%qualify'/'name)",
        , "fileRm return adrSh(m.m.spec)",
        , "fileMkDir return adrSh('mkdir' m.m.stream%%qualify)" ,
        , "fileRmDir return fileLinuxRmDir(m, opt)" */
    call classNew "n FileList u JRW", "m",
        , "jReset if arg2 == 'r' then m.m.dsnMask=arg'.**';",
                                "else m.m.dsnMask=arg'.*';",
        , "jOpen  call csiOpen m, m.m.dsnMask",
        , "jClose" ,
        , "jRead return csiNext(m, m)"
    call classNew "n FileEdit u File", "m",
        , "jClose call fileTsoEditClose m"
    return
endProcedure fileTsoIni
/* copy fileTso end   *************************************************/
/* copy mat begin *****************************************************/
sqrt: procedure expose m.
parse arg n
    if n < 2 then
        return n
     k = 1
     g = n
     do while k+1 < g
         m = (g + k) % 2
         if m * m <= n then
             k = m
         else
             g = m
         end
     return k
endProcedure sqrt

isPrime: procedure expose m.
parse arg n
    if n < 2 then
        return 0
    if n // 2 = 0 then
        return n = 2
    do q=3 by 2 to sqrt(n)
        if n // q = 0 then
            return 0
        end
    return 1
endProcedure isPrime

nxPrime: procedure expose m.
parse arg n
    do i = n + (\ (n // 2)) by 2
        if isPrime(i) then
            return i
        end
endProcedure nxPrime

permut: procedure expose m.
parse arg m, p
    m.m.1 = 1
    do i=2 while p > 0
        j = i - (p // i)
        m.m.i = m.m.j
        m.m.j = i
        p = p % i
        end
    m.m.0 = i-1
    return i-1
endProcedure permut
/* copy mat end   *****************************************************/
/* copy sqlDiv begin **************************************************/
/*--- generate the format m for a sql cx as specified in sp
          use the information from the sqlDa -------------------------*/
sqlFTabReset: procedure expose m.
parse arg ff, c, maxCh, maxBlo, maxDe
    return sqlFTabOpts(fTabReset(ff, , , '-'), c, maxCh, maxBlo, maxDe)

sqlFTabOpts: procedure expose m.
parse arg ff, cx, m.ff.maxChar, m.ff.blobMax, m.ff.maxDec
    if m.ff.maxChar == '' then
        m.ff.maxChar = 32
    if m.ff.blobMax == '' then
        m.ff.blobMax = 200
    bf = '%-'max(m.ff.blobMax, 4)'C'
    m.ff.flds = ''
    m.ff.sqlX = cx
    m.ff.sqlOthers = 0
    m.ff.sql2fmt.384 = '%-10C' /* date    */
    m.ff.sql2fmt.388 = '%-8C'  /* time    */
    m.ff.sql2fmt.392 = '%-26C' /* timestamp */
    m.ff.sql2fmt.400 = 'c'     /* graphic string */
    m.ff.sql2fmt.404 = bf      /* BLOB           */
    m.ff.sql2fmt.408 = bf      /* CLOB           */
    m.ff.sql2fmt.412 = bf      /* DBCLOB         */
    m.ff.sql2fmt.448 = 'c'     /* varchar        */
    m.ff.sql2fmt.452 = 'c'     /* char           */
    m.ff.sql2fmt.452 = 'c'     /* long varchar   */
    m.ff.sql2fmt.460 = 'c'     /* null term. string */
    m.ff.sql2fmt.464 = 'c'     /* graphic varchar   */
    m.ff.sql2fmt.468 = 'c'     /* graphic char      */
    m.ff.sql2fmt.472 = 'c'     /* long graphic varchar   */
    m.ff.sql2fmt.480 = '%-7e'  /* float                  */
    m.ff.sql2fmt.484 = 'd'     /* packed decimal         */
    m.ff.sql2fmt.492 = '%20i'  /* bigInt                 */
    m.ff.sql2fmt.496 = '%11i'  /* int                    */
    m.ff.sql2fmt.500 = '%6i'   /* smallInt               */
    m.ff.sql2fmt.904 = '%-34H' /* rowID 17 Byte Binary   */
    return ff
endProcedure sqlFTabOpts
/*--- set a defaultFormat for type tx in fTab ff ---------------------*/
sqlFTabDef: procedure expose m.
parse arg ff, tx, m.ff.sql2fmt.tx
return ff

sqlFTabAdd: procedure expose m.
parse arg m, c1 aDone, f1, l1
    if symbol('m.m.set.c1') == 'VAR' then do
        sx = m.m.set.c1
        if word(m.m.set.sx, 1) == c1 & sx <= m.m.set.0 then do
            parse var m.m.set.sx c1 aDone
            f1 = m.m.set.sx.fmt
            l1 = m.m.set.sx.labelTi
            end
        end
    cx = m.m.sqlX
    f2x = classMet(sqlFetchClass(cx), 'f2x')
    if symbol('m.f2x.c1') \== 'VAR' then
        call err 'colName not found' c1
    kx = m.f2x.c1
    t1 = m.sql.cx.d.kx.sqlName
    if l1 == '' then
        l1 = t1
    if f1 == '' then do
        ty = m.sql.cx.d.kx.sqlType
        le = m.sql.cx.d.kx.sqlLen
        withNulls = ty // 2
        ty = ty - withNulls
        if symbol('m.m.sql2fmt.ty') <> 'VAR' then
            call err 'sqlType' ty 'col' c1 'not supported'
        f1 = m.m.sql2fmt.ty
        if f1 == 'c' then
            f1 = '%-'min(le, m.m.maxChar)'C'
        else if f1 == 'd' then do
            trace ?r
            pr =  le % 256
            de =  le // 256
            f1 = '%'pr'.'de'i'
            end
        if \ abbrev(f1, '%') then
            call err 'sqlType' ty 'col' c1 'bad format' f1
        end
    call fTabAddRCT m, c1 aDone, f1, t1, l1
    ox = m.m.0
    m.m.ox.tit.0 = max(arg()-3, 1)
    do tx=2 to m.m.ox.tit.0
        m.m.ox.tit.tx = arg(tx+3)
        end
    return m
endProcedure sqlFTabAdd

sqlFTabOthers: procedure expose m.
parse arg m, doNot
    cx = m.m.sqlX
    ff = m.sql.cx.fetchFlds
    m.m.sqlOthers = 1
    do kx=1 to m.sql.cx.d.sqlD
        c1 = word(ff, kx)
        wx = wordPos(c1, m.m.cols)
        if (wx < 1 | m.m.wx.done \== 1) & wordPos(c1, doNot) < 1 then
            call sqlFTabAdd m, c1
        end
    return m
endProcedure sqlFTabOthers

sqlFTab: procedure expose m.
parse arg m
    call fTabBegin m
    do while sqlRxFetch(m.m.sqlX, 'sqlFTab')
        call out f(m.m.fmt, 'sqlFTab')
        end
    return fTabEnd(m)
endProcedure sqlFTab

sqlFTabCol: procedure expose m.
parse arg m
    if pos('c', m.m.generated) < 1 then
        call fTabColGen m
    do rx=1 while sqlRxFetch(m.m.sqlX, 'sqlFTab')
        call out left('--- row' rx '',  80, '-')
        call fTabCol m, 'sqlFTab'
        end
    call out left('--- end of' (rx-1) 'rows ', 80, '-')
    return
endProcedure sqlFTabCol

sqlCatTb: procedure expose m.
parse arg ty gOnly, wh, ord, fTab, paPlus
    tb = tkrTable(, ty)
    if gOnly == 1 then
        edFun = ''
    else
        edFun = tkrTable(, ty, 'e')
    cx = 1
    ft = 'ft'm.tb.alias
    call sqlFTabOpts FTabReset(ft, 'c 1', '1 c', '-'),
                     ,cx , 12, if(fTab, , 2000)
    call sqlFTabDef      ft, 492, '%7e'
    call FTabSet         ft, 'CONTOKEN'  , '%-16H'
    call FTabSet         ft, 'DCONTOKEN'  , '%-16H'
    call FTabSet         ft, 'DBNAME'    , '%-8C', 'db'
    call FTabSet         ft, 'DSNAME'    , '%-44C'
    call FTabSet         ft, 'DSNUM'     , '%5i'
    call FTabSet         ft, 'PARTITION' ,'%5i' , 'part'
    call FTabSet         ft, 'PIT_RBA'   , '%-12H'
    call FTabSet         ft, 'RBA1'      , '%-12H'
    call FTabSet         ft, 'RBA2'      , '%-12H'
    call FTabSet         ft, 'START_RBA' ,'%-12H'
    call FTabSet         ft, 'TSNAME'    , '%-8C', 'ts'
    call FTabSet         ft, 'VERSION'   , '%-28C'
    if edFun \== '' then do
        interpret 'sq =' edFun'(ft, tb, wh, ord)'
        end
    else do
        cl = sqlColList(m.tb.table, m.ft.blobMax)
        sq = 'select' cl tkrTable( , tb, 'f') wh ,
             'order by' if(ord=='', m.tb.order, ord)
        call sqlQuery cx, sq
        call sqlFTabOthers ft
        call sqlCatTbVl ft, tb
        end
    if fTab then
        call sqlFTab ft
    else
        call sqlFTabCol ft
    call sqlClose cx
    call sqlCatTbTrailer space(m.TKR.path paPlus, 1), sq
    return 0
endProcedure sqlCatTb

sqlCatTbVlsep:
    return '+++'

sqlCatTbVl: procedure expose m.
parse arg ft, tb, sep
    if sep == '' then
        sep = sqlCatTbVLsep()
    if m.tb.vlKey == '' then
        return
    ky = m.tb.vlKey
    ff = ''
    tt = ''
    do kx=1 to m.ky.0
        tt = tt || sep || m.ky.kx.col
        ff = ff || sep'@'m.ky.kx.col'%S'
        end
    call fTabAddRCT ft, substr(tt,length(sep)+1) ,
          , substr(ff,length(sep)+1)
    return
endProcedure sqlCatTbVl

sqlCatTbTrailer: procedure expose m.
parse arg pa, sq
    ox = lastPos(' order by ', sq)
    if ox < 1 then
        call err 'order by not found in' sq
    ord = substr(sq, ox+10)
    sq = left(sq, ox-1)
    sqUp = translate(sq)
    call out ''
    call out 'dbSys:' m.sql_dbSys
    call out 'path:' pa
    int = ''
    iNx = '  '
    br = ''
    cx = 1
    stops = '(select from where'
    do while cx < length(sq)
        nx = -1
        do sx=1 to words(stops)
            n2 = pos(word(stops, sx), sq, cx+1)
            if n2 > cx & (nx < 1 | n2 < nx) then
                nx = n2
            end
        if nx < 0 then
            leave
        call out int || substr(sq, cx, nx-cx)
        int = iNx
        if substr(sq, nx, 3) = '(se' then do
            iNx = iNx'  '
            br = left(br, length(int))')'
            end
        cx = nx
        end
    ll =  strip(substr(sq, cx))
    bq = strip(br)
    do while bq <> ''
        if right(bq, 1) \== ')' | right(ll, 1) \== ')' then
           call err 'missing ) bq:' bq', ll:' ll
        ll = strip(left(ll, length(ll) - 1))
        bq = strip(left(bq, length(bq) - 1))
        end
    call out int || ll
    if br <> '' then
        call out br
    if ord <> '' then
        call out '  order by' ord
    return
endProcedure sqlCatTbTrailer

sqlCatIxKeys: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select ikK.colSeq, ikK.colName, ikK.ordering, ikK.period' ,
             ', ik.creator, ik.name, ik.tbCreator, ik.tbName, ikC.*'  ,
          tkrTable(, tb ,'f') wh,
          'order by' if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, CREATOR, '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME   , '%-16C','index'
    call sqlFTabAdd      ft, colSeq  , '%5i',  'coSeq'
    call sqlFTabAdd      ft, colName, '%-16C', 'column'
    call sqlFTabAdd      ft, ordering
    call sqlFTabAdd      ft, period
    call sqlFTabAdd      ft, COLNO
    call sqlFTabAdd      ft, COLTYPE
    call sqlFTabAdd      ft, LENGTH
    call sqlFTabAdd      ft, SCALE
    call sqlFTabAdd      ft, NULLS
    call sqlFTabOthers ft, 'COL9 COL10 COL11 COL47'
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatIxKeys

sqlCatIXStats: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select *' tkrTable( , tb, 'f') wh ,
         'order by' if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, CREATOR, '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME   ,       , 'index'
    call sqlFTabAdd      ft, INSTANCE   , '%1i' , 'i'
    call sqlFTabAdd      ft, PARTITION , , 'part'
    call sqlFTabOthers ft
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatIXStats

sqlCatTables: procedure expose m.
parse arg ft, tb, wh, ord
    al = m.tb.alias
    sq = 'select' al'.*, tsX.type tsType, tsX.partitions',
            ', tsX.pgSize, tsX.dsSize' ,
            ', timestamp(rba1 || x''0000'') rba1Tst' ,
            ', timestamp(rba2 || x''0000'') rba2Tst' ,
          'from' m.tb.table 'left join sysibm.sysTablespace tsX',
            'on' al'.dbName = tsx.dbName and' al'.tsName = tsX.name',
            'where' m.tb.cond wh ,
            'order by'  if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, creator   , '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME      , '%-24C', 'table'
    call sqlFTabAdd      ft, type
    call sqlFTabAdd      ft, dbNAME    , '%-8C', 'db'
    call sqlFTabAdd      ft, tsNAME    , '%-8C', 'ts'
    call sqlFTabAdd      ft, tsType
    call sqlFTabAdd      ft, partitions,       , 'parts'
    call sqlFTabAdd      ft, pgSize
    call sqlFTabAdd      ft, dsSize
    call sqlFTabOthers ft, 'RBA1 RBA1TST RBA2 RBA2TST'
    call sqlFTabAdd      ft, rba1      , '%-12H'
    call sqlFTabAdd      ft, rba1Tst   ,       , 'rba1Timestamp:GMT'
    call sqlFTabAdd      ft, rba2      , '%-12H'
    call sqlFTabAdd      ft, rba2Tst   ,       , 'rba2Timestamp:GMT'
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatTables

sqlCatTSStats: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select' m.tb.alias'.*' ,
           tkrTable( , tb, 'f') wh ,
           'order by' if(ord == '', m.tb.order , ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, DBNAME, '%-8C', 'db'
    call sqlFTabAdd      ft, NAME   , '%-8C', 'ts'
    call sqlFTabAdd      ft, INSTANCE   , '%1i' , 'i'
    call sqlFTabAdd      ft, PARTITION , , 'part'
    call sqlFTabAdd      ft, NACTIVE   , , 'nActive'
    call sqlFTabAdd      ft, NPAGES    , , 'nPages'
    call sqlFTabAdd      ft, SPACE       , , 'spaceKB'
    call sqlFTabAdd      ft, TOTALROWS   , , 'totRows'
    call sqlFTabAdd      ft, DATASIZE         , , 'dataSz'
    call sqlFTabAdd      ft, LOADRLASTTIME    , , 'loadRLasttime'
    call sqlFTabAdd      ft, REORGLASTTIME    , , 'reorgLasttime'
    call sqlFTabAdd      ft, REORGINSERTS     , , 'inserts'
    call sqlFTabAdd      ft, REORGDELETES     , , 'deletes'
    call sqlFTabAdd      ft, REORGUPDATES     , , 'updates'
    call sqlFTabAdd      ft, REORGUNCLUSTINS  , , 'unClIns'
    call sqlFTabAdd      ft, REORGDISORGLOB   , , 'disorgL'
    call sqlFTabAdd      ft, REORGMASSDELETE  , , 'massDel'
    call sqlFTabAdd      ft, REORGNEARINDREF  , , 'nearInd'
    call sqlFTabAdd      ft, REORGFARINDREF   , , 'farInd'
    call sqlFTabAdd      ft, REORGCLUSTERSENS , , 'cluSens'
    call sqlFTabAdd      ft, REORGSCANACCESS  , , 'scanAcc'
    call sqlFTabAdd      ft, REORGHASHACCESS  , , 'hashAcc'
    call sqlFTabAdd      ft, STATSLASTTIME    , , 'statsLasttime'
    call sqlFTabAdd      ft, STATSINSERTS     , , 'inserts'
    call sqlFTabAdd      ft, STATSDELETES     , , 'deletes'
    call sqlFTabAdd      ft, STATSUPDATES     , , 'updates'
    call sqlFTabAdd      ft, STATSMASSDELETE  , , 'massDel'
    call sqlFTabAdd      ft, COPYLASTTIME     , , 'copyLasttime'
    call sqlFTabAdd      ft, COPYUPDATETIME   , , 'copyUpdatetime'
    call sqlFTabAdd      ft, COPYUPDATELRSN   , '%-12H', 'updateLRSN'
    call sqlFTabAdd      ft, COPYUPDATEDPAGES , , 'updaPgs'
    call sqlFTabAdd      ft, COPYCHANGES      , , 'changes'
    call sqlFTabOthers ft
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatTSStats

sql4obj: procedure expose m.
parse arg m, tb
    call out 'insert into' tb '--' className(objClass(m))
    line = ''
    ff = oFlds(m)
    pr = '   ('
    do fx=1 to m.ff.0
        call sql4ObjOut m.ff.fx
        end
    call sql4ObjOut , 1
    call out '   ) values '
    pr = '   ('
    do fx=1 to m.ff.0
        f1 = m || left('.', m.ff.fx  \== '')m.ff.fx
        v = m.f1   /* no strip T, gives errors in RCM profile | */
        if dataType(v, n) then
            call sql4ObjOut v
        else do qx=1 until v == ''
            vx = verify(v, m.ut_alfPrint)
            if vx = 0 then do
                l1 = min(60, length(v))
                w = quote(left(v, l1), "'")
                end
            else if vx > 29 then do
                l1 = min(60, vx-1)
                w = quote(left(v, l1), "'")
                end
            else do
                l1 = min(29, length(v))
                w = 'x'quote(c2x(left(v, l1)), "'")
                end
            if qx == 1 then
                call sql4ObjOut w
            else do
                if qx = 2 then
                    call sql4ObjOut , 1
                call out '   ||' w
                end
            v = substr(v, l1+1)
            end
        end
    call sql4ObjOut , 1
    call out '   ) ; '
    return
endProcedure
sql4objOut:
parse arg t1, force
    if (force == 1 & line \== '') | length(line t1) > 65 then do
        call out pr  substr(line, 3)
        pr = '   ,'
        line = ''
        end
    if force \== 1 then
        line = line',' t1
    return
endProcedure sql4objOut
/*--- -dis db interface ---------------------------------------------*/
/*--- do one -dis db... and insert it into stem  --------------------*/
sqlDisDb: procedure expose m.
parse upper arg o, cc
    do cx=1
        mid = strip(left(m.cc.cx, 10))
        if words(mid) > 1 then
            call err 'bad msgId' mid 'line:' m.cc.cx
        if mid == '' | wordPos(mid, 'DSNT360I DSNT361I DSNT365I') ,
                > 0 then
            iterate
        if mid == 'DSN9022I' then
            if cx = m.cc.0 then
                return m.o.0
            else
                call err 'not at end' cx':' m.cc.cx
        if mid \== 'DSNT362I' then
            call err 'DSNT362I expected not line:' m.cc.cx
        dx = pos('DATABASE =', m.cc.cx)
        sx = pos('STATUS ='  , m.cc.cx)
        if dx < 1 | sx <= dx then
            call err 'bad DSNT362I line' cx':' m.cc.cx
        db = word(substr(m.cc.cx, dx+10), 1)
        sta = strip(substr(m.cc.cx, sx+8))
        call sqlDisDbAdd o, db, ,0, 0, 'DB', sta
        do cx=cx+1 while abbrev(m.cc.cx, '   ')
            end
        if abbrev(m.cc.cx, 'DSNT397I ') then do
            cx = cx + 1
            if \ abbrev(space(m.cc.cx, 1),
                 , 'NAME TYPE PART STATUS ') then
                call err 'NAME TYPE PART STATUS mismatch' cx m.cc.cx
            txNa = pos('NAME', m.cc.cx)
            txTy = pos('TYPE', m.cc.cx)
            txPa = pos('PART', m.cc.cx)
            txSt = pos('STAT', m.cc.cx)
            txEn = verify(m.cc.cx, ' ', 'n', txSt+6)
            if 0 then say 'title' txNa txTy txPa txSt txEn cx m.cc.cx
            cx=cx+1
            do forever
                do while abbrev(m.cc.cx, '----')
                    cx = cx + 1
                    end
                if abbrev(m.cc.cx, '*') then
                    leave
                parse var m.cc.cx sp =(txTy)  ty . =(txPa)  paFr . ,
                                       =(txSt) sta   =(txEn)
                sp = strip(sp)
                if words(sp) \= 1 | wordPos(ty, 'TS IX') < 0 then
                    call err 'bad name or type' cx':'m.cc.cx
                if paFr == '' | paFr == 'L*' then
                    paFr = 0
                else if abbrev(paFr, 'D') | abbrev(paFr, 'L') then
                    paFr = substr(paFr, 2)
                if \ datatype(paFr, 'n') then
                    call err 'part not numeric' cx':'m.cc.cx
                paTo = paFr
                cw = cx
                cx = cx + 1
                if abbrev(m.cc.cx, '    -THRU ') then do
                    parse var m.cc.cx =(txPa)  paTo . =(txSt)
                    if \ datatype(paTo, 'n') then
                        call err '-thru part not numeric' cx':'m.cc.cx
                    cx = cx + 1
                    end
                call sqlDisDbAdd o, db, sp, paFr, paTo, ty, sta
                end
            end
        if m.cc.cx = '******** NO SPACES FOUND' then
            cx = cx + 1
        if abbrev(m.cc.cx, '******* DISPLAY OF DATABASE ') ,
          & word(m.cc.cx,5) == db then
            if word(m.cc.cx,6) == 'ENDED' then
                iterate
            else if word(m.cc.cx,6) == 'TERMINATED' then
                call err 'db display overflow' cx':' m.cc.cx
        call err 'database' db 'ENDED mismatch' cx':' m.cc.cx
        end
endProcedure sqlDbDis

/*--- insert one tuple into tDbState ---------------------------------*/
sqlDisDbAdd: procedure expose m.
    if arg(7) == '' | arg(7) == 'RW' then
         return
parse arg o
    m.o.0 = m.o.0 + 1
    q = o'.'m.o.0
parse arg , m.q.db, m.q.sp, m.q.paFr, m.q.paTo, m.q.ty, m.q.sta
 /* say added q m.q.db'.'m.q.sp':'m.q.paFr'-'m.q.paTo m.q.ty':'m.q.sta*/
    ky = m.q.db'.'m.q.sp
    if symbol('m.o.ky') \== 'VAR' then
        m.o.ky = m.o.0
    return
endProceedure sqlDisDbAdd

/*--- get index in o for db sp part ----------------------------------*/
sqlDisDbIndex: procedure expose m.
parse arg st, d, s, pa
     if symbol('m.st.d.s') \== 'VAR' then
         return 0
     ix = m.st.d.s
     if ix > m.st.0 | d \== m.st.ix.db | s \== m.st.ix.sp then
         return 0
     if pa == '' then
         return ix
     do ix=ix to m.st.0 while d == m.st.ix.db & s == m.st.ix.sp
         if pa < m.st.ix.paFr then
             return 0
         else if pa <= m.st.ix.paTo then
             return ix
         end
     return 0
endProcedure sqlDisDbIndex

/*--- dsn Command, return true if continuation needed ----------------*/
sqlDsnCont: procedure expose m.
parse arg cc, ssid, cmd
    say '???dsnCont' cmd
    cont = sqlDsn(cc, ssid, cmd, 12) <> 0
    if cont then do
        cz = m.cc.0
        cy = cz - 1
        if \ abbrev(m.cc.cy, DSNT311I) ,
                | \ abbrev(m.cc.cz, 'DSN9023I') then
            call err 'sqlDsn rc=12 for' cmd 'out='cz ,
                     '\n'cy'='m.cc.cy'\n'cz'='m.cc.cz
        m.cc.0 = cz-2
        end
    return cont
endProcedure sqlDsnCont
/* copy sqlDiv end   **************************************************/
/* copy db2Cat begin **************************************************/
catTbLastCol: procedure expose m.
parse upper arg cr, tb
    return sql2one( ,
          "select strip(char(colcount)) || ' ' || strip(c.name) one"  ,
              "from sysibm.sysTables t left join sysibm.sysColumns c" ,
                  "on c.tbCreator = t.creator and c.tbName = t.name"  ,
                       "and c.colNo = t.colCount"                     ,
               "where t.creator = '"cr"' and t.name = '"tb"'",,,, 'r')
endProcedure catTbLastCol

catTbCols: procedure expose m.
parse upper arg cr, tb
    if sql2St("select strip(name) name "     ,
          "from sysibm.sysColumns " ,
          "where tbcreator = '"cr"' and tbname='"tb"'",
          "order by colNo", ggSt) < 1 then
        return ''
    res = m.ggst.1.name
    do cx=2 to m.ggst.0
        res = res m.ggst.cx.name
        end
    return res
endProcedure catTbCols

catTbColsTrunc: procedure expose m.
parse upper arg cr, tb, maxL
    if sql2St("select strip(name) name, colType, length, length2"     ,
          "from sysibm.sysColumns " ,
          "where tbcreator = '"cr"' and tbname='"tb"'",
          "order by colNo", ggSt) < 1 then
        return ''
    res = ''
    do cx=1 to m.ggst.0
        ty = m.ggSt.cx.colType
        if pos('LOB', ty) > 0 then
            res = res', substr('m.ggSt.cx.name', 1,' ,
                 min(maxL, m.ggSt.cx.length2)') 'm.ggSt.cx.name
        else if pos('CHAR', ty) > 0 & m.ggSt.cx.length > maxL then
            res = res', substr('m.ggSt.cx.name', 1,' maxL')',
                 m.ggSt.cx.name
        else
            res = res',' m.ggSt.cx.name
        end
    return substr(res, 3)
endProcedure catTbColsTrunc

catIxKeys: procedure expose m.
parse upper arg cr, ix
    sql = "select colSeq sq, colName col, ordering ord"       ,
              "from sysibm.sysKeys"                                   ,
               "where ixCreator = '"cr"' and ixName = '"ix"'" ,
               "order by colSeq"
    call sqlQuery 1, sql
    res = ''
    drop d
    do kx=1 while sqlFetch(1, d)
        if m.d.sq \= kx then
            call err 'expected' kx 'but got colSeq' m.d.sq ,
                     'in index' cr'.'ix'.'m.d.col
        res = res || strip(m.d.col) || translate(m.d.ord, '<>?', 'ADR')
        end
    call sqlClose 1
    return res
endProcedure catIxKeys

catColCom: procedure expose m.
parse upper arg fCr, fTb, tCr, tTb
    sql = "select t.name, t.colType, t.nulls, t.""DEFAULT"""        ,
                    ", coalesce(f.nulls, 'new')"                    ,
              "from sysibm.sysColumns t"                            ,
                "left join sysibm.sysColumns f"                     ,
                  "on f.tbCreator = '"fCr"' and f.tbName = '"fTb"'" ,
                    "and f.name = t.name"                           ,
              "where t.tbCreator = '"tCr"' and t.tbName = '"tTb"'"  ,
              "order by t.colNo"
    call sqlQuery 1, sql, 'na ty nu de nn'
    pr = ' '
    do kx=1 while sqlFetch(1)
        /* say kx m..na m..ty m..nu m..de 'nn' m..nn */
        if pos('CHAR', m..ty) > 0 then
            dv = "''"
        else if pos('INT' ,m..ty) > 0 ,
                | wordPos(m..ty, 'REAL FLOAT') > 0 then
            dv = 0
        else if m..ty == 'TIMESTMP' then
            dv = '0001-01-01-00.00.00'
        else if pos('LOB', m..ty) > 0 then
            dv = m..ty"('')"
        else
            dv = '???'
        if m..nu = 'Y' then
            dv = 'case when 1=0 then' dv 'else null end'
        r = '???'
        if m..ty = 'ROWID' then do
            r = '--'
            end
        else if m..nn == 'new' then do
            if m..de = 'Y' then
                r = '--'
            else if m..nu == 'N' then
                r = dv
            else
                r = 'case when 1=0 then' dv 'else null end'
            end
        else do
            if m..nu = 'Y' | (m..nu = m..nn) then
                r = ''
            else
                r = 'coalesce('m..na',' dv')'
            end
        if abbrev(r, '--') then do
            r = ' ' r
            end
        else do
            r = pr r
            pr = ','
            end
        if pos('???', r) > 0 then
            call err 'no default for type' m..ty 'in' tCr'.'tTb'.'m..na
        call out r m..na
        end
    call sqlClose 1
    return
endProcedure catColCom
/* copy db2Cat end   **************************************************/
/* copy sqlO   begin ***************************************************
    sql interface  mit  o und j Anbindung
***********************************************************************/
sqlIni: procedure expose m.
    if m.sql_ini == 1 then
        return
    m.sql_ini = 1
    call sqlRxIni
    call jIni
    call fTabIni
    call scanWinIni
    m.sqlO.cursors  = left('', 200)
    m.sql_rdrClass = classNew('n SqlRdr u JRW', 'm',
        , "jReset m.m.src = arg; m.m.type = arg2;",
        , "jOpen  call sqlRdrOpen m, opt",
        , "jClose call sqlRdrClose m",
        , "jRead return sqlRdrRead(m)")
    call classNew 'n SqlResRdr u JRW', 'm',
        , "jReset m.m.cursor = arg; m.m.type = arg2;",
        , "jOpen  call sqlRdrO2 m" ,
        , "jClose call sqlClose m.m.cursor" ,
        , "jRead return sqlRdrRead(m)"
    call classNew 'n SqlRxConnection u', 'm',
        , "sqlQuery  return sqlRxQuery(cx, src, feVa, 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, feVa, 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, feVa, retOK)",
        , "sqlFetch  return sqlCsmFetch(cx, dst, retOk)",
        , "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, feVa, retOk)",
        , "sqlFetch  return sqlCsmFetch(m.cx.cursor, dst, retOk)",
        , "sqlClose  return 0",
        , "sqlUpdate call err 'implement sqlCsmUpdate'"   ,
        , "sqlCall   call err 'implement sqlCsmCall'"
/*  call classNew 'n SqlExecuteRdr u JRW', 'm',
        , "jReset    call sqlExecuteRdrReset(m, arg, arg2)" ,
        , "jOpen     call sqlExecuteRdrOpen(m)" ,
        , "jClose    call sqlExecuteRdrClose(m)" ,
        , "jRead     call sqlExecuteRdrRead(m)"  ???????? */
    return 0
endProcedure sqlIni

/*--- connect to DB2 dsnRexx or csm ----------------------------------*/
sqlConnect: procedure expose m.
parse upper arg sys, retOk
    call sqlIni
    if sys == '' then
        sys = sqlDefaultSys()
    if pos('/', sys) <= 0 then do
        call  sqlRxConnect sys
        m.sql_connClass = class4Name('SqlRxConnection')
        end
    else do
        parse var sys m.sql_csmHost '/' m.sql_dbSys
        m.sql_connClass = class4Name('SqlCsmConnection')
        end
    return 0
endProcedure sqlConnect

/*--- disconnect from db2 --------------------------------------------*/
sqlDisconnect: procedure expose m.
parse arg retOk
    if m.sql_csmHost == '' then
        call sqlRxDisconnect
    else
        m.sql_csmHost = ''
    m.sql_dbSys = ''
    m.sql_connClass = 'sql not connected'
    return 0
endProcedure sqlDisonnect

/*--- execute a query from sql, with one resultset -------------------*/
sqlQuery: procedure expose m.
parse arg cx, src, feVa, retOk
    interpret classMet(m.sql_connClass, 'sqlQuery')
endProcedue sqlQuery
/*--- fetch next row to m.dst.* at end return false ------------------*/
sqlFetch: procedure expose m.
parse arg cx, dst, retOk
    interpret classMet(m.sql_connClass, 'sqlFetch')
endProcedue sqlFetch
/*--- close cursor 'c'cx ---------------------------------------------*/
sqlClose: procedure expose m.
parse arg cx, retOk
    interpret classMet(m.sql_connClass, 'sqlClose')
endProcedue sqlClose
/*-- execute an sql with no resultset, but possibly outParms ---------*/
sqlUpdate: procedure expose m.
parse arg cx, src, retOk
    interpret classMet(m.sql_connClass, 'sqlUpdate')
endProcedue sqlUpdate

/*-- execute an sql call with outParms and multiple resultSets -------*/
sqlCall: procedure expose m.
parse arg cx, src, retOk
    interpret classMet(m.sql_connClass, 'sqlCall')
endProcedure sqlCall

/*--- return a free cursor -------------------------------------------*/
sqlGetCursor: procedure expose m.
parse arg rng
    if rng == '' then
        return sqlGetCursorRng(rng, 10, 48)
    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

/*** execute sql's in a stream (separated by ;)
       opt: 'o' ==> write objects, otherwise fTabAuto
            'sql72' ==> spufi formatting (window 72) else linebreaks */
sqlStmts: procedure expose m.
parse arg sqlSrc, retOk, opt
   return sqlsOut(scanSqlStmtRdr(sqlSrc, opt), retOk, 'a')
   return
endProcedure sqlStmts

/*--- execute the given sql plus a commit
         until no more rows are updated -----------------------------*/
sqlUpdComLoop: procedure expose m.
parse arg src, retOk
    crs = sqlGetCursor()
    upds = 0
    if retOk == '' then
        retOk = 100
    do coms=0
        cd = sqlExecute(crs, src, retOk)
        if m.sql.crs.updateCount < 1 then do
            call sqlFreeCursor(crs)
            return sqlMsgLine( , upds, src, coms 'commits')
            end
        upds = upds + m.sql.crs.updateCount
        call sqlCommit
        if coms // 20 = 19 then
            say sqlMsgLine(time(), upds, src, (coms+1) 'commits')
        end
endProcedure sqlUpdComLoop

/*--- sql call statement ---------------------------------------------*/
sqlStmtCall: procedure expose m.
parse arg src, retOk, opt
    s = scanSqlReset(scanSrc(sqlstmtcall, 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.ut_alfDot) 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 fTabAuto 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 jRead(rdr)
         a = m.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

/*--- prepare and open cursor
      generate type and fetchList ------------------------------------*/
sqlRdr: procedure expose m.
parse arg src, type
     return oNew('SqlRdr', scanSqlIn2Stmt(src), type)
endProcedure sqlRdr

sqlRdrOpen: procedure expose m.
parse arg m, opt
    if opt\== m.j.cRead then
        call err 'opt not' m.j.cRead 'sqlRdrOpen('m',' opt')'
    cx = sqlGetCursor()
    m.m.cursor = cx
    if words(m.m.type) <> 1 | abbrev(m.m.type, ' ') then do
        m.sql.cx.fetchClass = ''
        call sqlQuery m.m.cursor, m.m.src, m.m.type
        m.m.type = sqlFetchClass(cx)
        end
    else do
        m.m.type = class4name(m.m.type)
        call sqlQuery m.m.cursor, m.m.src, mCat(classFlds(m.m.type),' ')
        m.sql.cx.fetchClass = m.m.type
        end
    call sqlRdrO2 m
    return
endProcedure sqlRdrOpen

sqlQuery2Rdr: procedure expose m.
parse arg cx
    r = jReset(oMutate('SQL_RDR.'cx, 'SqlResRdr'), cx)
    m.r.type = sqlFetchClass(cx)
    return r
endProcedure

sqlFetchClass: procedure expose m.
parse arg cx, force
     if m.sql.cx.fetchClass == '' | force == 1 then
          m.sql.cx.fetchClass = classNew('n* Sql u f%v' ,
              m.sql.cx.fetchFlds)
     return m.sql.cx.fetchClass
endProcedure sqlFetchClass

sqlRdrO2: procedure expose m.
parse arg m
    cx  = m.m.cursor
    if m.m.type \== m.sql.cx.fetchClass | m.m.Type == '' then
        call err 'type' m.m.type '\==' m.sql.cx.fetchClass 'fetchClass'
    m.m.rowCount = 0
    m.sql_lastRdr = m
    return
endProcedure sqlRdrO2

/*--- close sql Cursor -----------------------------------------------*/
sqlRdrClose: procedure expose m.
parse arg m, v
    call sqlClose m.m.cursor
    call sqlFreeCursor m.m.cursor
    m.m.cursor = ''
    return m
endProcedure sqlRdrClose

/*--- read next from cursor, return as object ------------------------*/
sqlRdrRead: procedure expose m.
parse arg m
    v = oNew(m.m.type)
    if \ sqlFetch(m.m.cursor, v) then do
        call mFree v
        return 0
        end
    m.m.rowCount = m.m.rowCount + 1
    m.m = v
    return 1
endProcedure sqlRdrRead

/*--- return sqlFTab for this (open) rdr -----------------------------*/
sqlRdrFtabReset: procedure expose m.
parse arg m, q, maxChar, blobMax, maxDec
    if m == '' then
        m = m.sql_lastRdr
    if \ dataType(m.m.cursor, 'n') then
        call err 'sqlRdrFTabReset('m') but cursor empty'
    return sqlFTabReset(q, m.m.cursor, maxChar, blobMax, maxDec)
endProcedure sqlRdrFTabReset

/*--- output sql as table --------------------------------------------*/
sql2tab: procedure expose m.
parse arg tBef, tAft, maxChar, blobMax, maxDec
    cx = sqlGetCursor()
    call sqlQuery cx, in2str(,' ')
    t = sqlFTabReset('SQL.'cx'.fTab', cx,
            , tBef, tAft, maxChar, blobMax, maxDec)
    call sqlFTab sqlFTabOthers(t)
    call sqlClose cx
    call sqlFreeCursor cx
    return
endProcedure sql2tab

/*--- select and write all to stdOut ---------------------------------*/
sqlSel: procedure expose m.
parse arg src, type
     s = sqlRdr(src, type)
     call pipeWriteAll s
     return m.s.rowCount
endProcedure sqlSel

/*--- result of each sql read from rdr to out
           oo='a' autoTab, 't'=sqlTab, 'o'=object, 'c'=csv ----------*/
sqlsOut: procedure expose m.
parse arg rdr, retOk, oo
    m.sql_errRet = 0
    if oo == '' then
        oo = 'a'
    cx = sqlGetCursor()
    r = jOpen(in2file(rdr), '<')

    do while jRead(r)
        sqlC = sqlExecute(cx, m.r, retOk)
        if m.sql_errRet then
            leave
        if m.sql.cx.resultSet == '' then do
             call outNl(m.sql_HaHi ,
                     || sqlMsgLine(sqlC, m.sql.cx.updateCount, m.r))
           end
        else if oo == 'o' then do
            call pipeWriteAll sqlQuery2Rdr(cx)
            end
        else if oo == 'a' | oo == 't' then do
            sqR = sqlQuery2Rdr(cx)
            ft = sqlfTabOpts(fTabReset('sqls2AutoFT', 'c 1'), cx)
            if oo == 't' then do
                call sqlFTabOthers(ft)
                end
            else do
                bf = in2Buf(sqR)
                if m.sql_errRet then
                    leave
                call sqlFTabDetect ft, bf'.BUF'
                call fTab ft, bf
                call out sqlMsgLine(m.sqR.rowCount 'rows fetched',
                                   , , m.r)
                end
            end
        else
            call err 'bad outputOption' oo
        end
    call jClose r
    if m.sql_errRet then do
   /*   call out 'sqlsOut terminating because of sql error' */
        call sqlClose cx, '*'
        say 'sqlsOut terminating because of sql error'
        end
    call sqlFreeCursor cx
    return \ m.sql_errRet
endProcedure sqlsOut

/*-- execute and put result to m -------------------------------------*/
sqlExecuteRes: procedure expose m.
parse arg m, cx, m.m.sql, retOk  ?????
    m.m.sqlCode = sqlExecute(cx, m.m.sql, retOk) + deimplement
    m.m.sqlMsg = m.sql_HaHi || sqlMsgLine(m.m.sqlCode,
                             , m.sql.cx.updateCount, m.m.sql)
endProcedure sqlExecuteRes
/*--- execute stmts with options -------------------------------------*/
sqlExecuteRdrReset: procedure expose m.
parse arg rdr, wOpt, m.m.retOk
    if abbrev(wOpt, '-sql') then  + deimplement  ??????????????????
        wOpt = substr(wOpt, 5)
    call scanSqlReset m'.SCAN', rdr, wOpt, ';'
    return m
endProcedure sqlExecuteRdrReset

sqlExecuteRdrOpen: procedure expose m.
parse arg m
    call scanOpt m'.SCAN'  + deimplement  ??????????????????
    m.m.cursor = sqlGetCursor()
    return m
endProcedure sqlExecuteRdrOpen

sqlExecuteRdrClose: procedure expose m.
parse arg m
    call scanOpt m'.SCAN'    + deimplement  ??????????????????
    call sqlFreeCursor m.m.cursor
    drop m.m.cursor
    return m
endProcedure sqlExecuteRdrClose

sqlExecuteRdrRead: procedure expose m.
parse arg m, var
    src = scanSqlStmt(m'.SCAN') + deimplement  ??????????????????
    if src == '' then
        return 0
    call sqlExecuteRes m, m.m.cursor, src, m.m.retOk
    m.var = m.m.cursor
    return 1
endProcedure sqlExecuteRdrRead

/* copy sqlO   end   **************************************************/
/* copy sqlCsm begin **************************************************/
/*--- send an sql to csm and handle sqlCode --------------------------*/
sqlCsmExe:
parse arg ggSqlStmt, ggRetOk
    sql_HOST =  m.sql_csmhost
    SQL_DB2SSID = m.sql_dbSys
    sql_query = ggSqlStmt
    address tso "CSMAPPC START PGM(CSMASQL)"
    if \ (rc = 0 |  rc = 4) then
        return 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\nsql =' ggSqlStmt
        else
            call errSay 'w}'sqlMsg(sqlCA2rx(sqlCa))
    return sqlCode
endProcedure sqlCsmExe

/*--- execute a query from sql, with one resultset -------------------*/
sqlCsmQuery: procedure expose m.
parse arg cx, sqlSrc, feVa, retOk, dst
    res = sqlCsmExe(sqlSrc, 100 retOk)
    if res < 0 then
        return res
    if dst == '' then
        dst = 'SQL.'cx'.CSMDATA'
    m.dst.0 = 0
    m.dst.laIx = 0
    st = 'SQL.'cx'.COL'
    if abbrev(feVa, '?') | abbrev(feVa, ':') then do
        return err('implement sqlCmsQuery fetchVars ? or :' feVa)
        end
    else if feVa <> '' then do
        vv = feVa
        end
    else do
        vv = ''
        do kx=1 to sqlD
            vv = sqlNiceVarsAdd(vv, SQLDA_REXXNAME.kx)
            end
        end
    m.sql.cx.fetchFlds = vv
    if sqlD <> words(vv) then
        return err('sqlCsmQuery sqlD' sqlD '<>' words(vv) 'for' vv)
    do kx=1 to sqlD
        rxNa = SQLDA_REXXNAME.kx
        cn = word(vv, kx)
        do rx=1 to sqlRow#
            if substr(sqlIndicator.rx, kx ,1) == 'ff'x then
                m.dst.rx.cn = m.sqlNull
            else
                m.dst.rx.cn = value(rxNa'.'rx)
            end
        end
    m.dst.0 = sqlRow#
    m.sql_lastRdr  = 'cms' cx
    return 0
endProcedure sqlCsmQuery

sqlCsmFetch: procedure expose m.
parse arg cx, dst
    src = 'SQL.'cx'.CSMDATA'
    rx = m.src.laIx + 1
    if rx > m.src.0 then
        return 0
    m.src.laIx = rx
    ff = m.sql.cx.fetchFlds
    do kx = 1 to words(ff)
        c = word(ff, kx)
        m.dst.c = m.src.rx.c
        end
    return 1
endProcedure sqlCsmFetch
/* copy sqlCsm end   **************************************************/
/* copy sqlRx  begin ***************************************************
       Achtung: inc generiert sql aus sqlRx, Aenderungen nur in sqlRx|
    sql interface
***********************************************************************/
/*--- initialize sqlRx -----------------------------------------------*/
sqlRxIni: procedure expose m.
    if m.sqlRx_ini == 1 then
        return
    m.sqlRx_ini = 1
    call utIni
    m.sqlNull = '---'
    m.sqlInd = 'sqlInd'
    m.sql_defCurs= 49
    m.sqlCAMsg = 0
    m.sqlSuMsg = 2
    m.sql_dbSys = ''
    m.sql_csmhost = ''
    isInProd = wordPos(sysvar(sysNode), 'RZ2') > 0
    m.sql_retOk   = 'dne' copies('rod', \ isInProd)
    return 0
endProcedure sqlRxIni

/*--- get default db2System ------------------------------------------*/
sqlDefaultSys: procedure expose m.
parse arg
    if sysvar(sysnode) == 'RZ1' then
        return 'DBAF'
    else if sysvar(sysnode) == 'RZ4' then
        return 'DP4G'
    else
        call err 'no default subsys for' sysvar(sysnode)
endProcedure sqlDefaultSys

/*--- 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
    if sys == '' then
        sys = sqlDefaultSys()
    m.sql_dbSys = sys
    ggSqlStmt =  'connect' sys
    address dsnRexx ggSqlStmt
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, ggRetOk, ggSqlStmt)
    return sqlCode
endProcedure sqlRxConnect

/*--- diconnect from db2 ---------------------------------------------*/
sqlRxDisconnect: procedure expose m.
parse arg retOk
    if m.sql_dbSys == '' then
        return 0
    ggSqlStmt =  'disconnect'
    m.sql_dbSys = ''
    m.sql_csmHost = ''
    address dsnRexx ggSqlStmt
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, retOk, ggSqlStmt)
    return sqlCode
endProcedure sqlRxDisconnect

/*--- reset sql cursor 'c'cx fields ----------------------------------*/
sqlReset: procedure expose m.
parse arg cx
     m.sql.cx.updateCount = ''
     m.sql.cx.resultSet   = ''
     m.sql.cx.d.sqlD = 'noSqlDA'
     m.sql.cx.i.sqlD = 'noDescInp'
     m.sql.cx.fetchVars = ''
     m.sql.cx.fetchFlds = ''
     m.sql.cx.fetchClass = ''
     m.sql.cx.type  = ''
     return
endProcedue sqlReset

/*--- execute a query from sql, with one resultset -------------------*/
sqlRxQuery: procedure expose m.
parse arg cx, src, feVa, retOk
     call sqlReset cx
     s1 = ''
     if pos(left(feVa, 1), '?:') < 1 then
         s1 = 'into :M.SQL.'cx'.D'
     res = sqlExec('prepare s'cx s1 'from :src', retOk)
     if res < 0 then
         return res
     call sqlRxFetchVars cx, feVa
     call sqlExec 'declare c'cx 'cursor for s'cx
     res = sqlExec('open c'cx, retOk)
     if res < 0 then
         return res
     m.sql.cx.updateCount = sqlErrd.3
     m.sql.cx.resultSet = cx
     return res
endProcedure sqlRxQuery

/*--- prepare a query from sql, with one resultset -------------------*/
sqlQueryPrepare: procedure expose m.
parse arg cx, src, feVa, retOk
     call sqlReset cx
     s1 = ''
     if feVa == '' | feVa = 'd' then
         s1 = 'into :M.SQL.'cx'.D'
     res = sqlExec('prepare s'cx s1 'from :src', retOk)
     if res < 0 then
         return res
     call sqlRxFetchVars cx, feVa
     call sqlExec 'declare c'cx 'cursor for s'cx
     return sqlExec('describe input s'cx 'into :M.SQL.'cx'.I')
endProcedure sqlQueryPrepare

sqlQueryExecute: procedure expose m.
parse arg cx retOk
    do ix=1 to arg()-1
        call sqlDASet cx , 'I', ix, arg(ix+1)
        end
     res = sqlExec('open c'cx 'using descriptor :M.SQL.'cx'.I', retOk)
     if res < 0 then
         return res
     m.sql.cx.updateCount = sqlErrd.3
     m.sql.cx.resultSet = cx
     return res
endProcedure sqlQueryExecute

/*--- fetch next row to m.dst.* at end return false ------------------*/
sqlRxFetch: procedure expose m.
parse arg cx, dst, retOk
    fetCode = sqlExec('fetch c'cx 'into' m.sql.cx.fetchVars, 100 retOk)
    if fetCode == 100 then
        return 0
    if fetCode < 0 then
        return fetCode
    interpret m.sql.cx.fetchCode
    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 sqlExec('execute immediate :src', retOk)
        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, retOk)
        end
    if fun == 'DECLARE'  then do
        if 'GLOBAL' == translate(word(substr(src, bx), 2)) then
            return sqlExec('execute immediate :src', retOk)
        end
    res = sqlExec(src, retOk)
    if wordPos(fun, 'DELETE INSERT UPDATE') > 0 then
        m.sql.cx.updateCount = sqlErrd.3
    return res
endProcedure sqlRxUpdate

/*-- prepare an update -----------------------------------------------*/
salUpdatePrepare: procedure expose m.
parse arg cx, src, retOk
    res = sqlExec('prepare s'cx 'from :src', retOk)
    return sqlExec('describe input s'cx 'into :M.SQL.'cx'.I')
endProcedure salUpdatePrepare

/*-- execute a prepared update with the given arguments --------------*/
sqlUpdExecute: procedure expose m.
parse arg cx retOk
    do ix=1 to arg()-1
        call sqlDASet cx , 'I', ix, arg(ix+1)
        end
    res = sqlExec('execute s'cx 'using descriptor :M.SQL.'cx'.I',
                  , retOk)
    m.sql.cx.updateCount = sqlErrd.3
    return res
endProcedure sqlUpdExecute

/*-- execute a query, update or call ---------------------------------*/
sqlExecute: procedure expose m.
parse arg cx, src, retOk
    f = translate(word(src, 1))
    bx = pos('(', f)
    if bx > 0 then
        f = left(f, max(1, bx-1))
    m.sql.cx.fun = f
    if f == 'SELECT' | f == 'WITH' | f == '(' 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

/*-- fetch all rows to stem and close --------------------------------*/
sqlFetch2St: procedure expose m.
parse arg cx, dst, retOK
    do sx=1 while sqlFetch(cx, dst'.'sx)
       end
    res = sx-1
    m.dst.0 = sx-1
    call sqlClose cx
    return m.dst.0
endProcedure sqlFetch2St

/*-- execute a query, copy result to stem ----------------------------*/
sql2St: procedure expose m.
parse arg src, dst, feVa, retOK
    cx = m.sql_defCurs
    res = sqlQuery(cx, src, feVa, retOk)
    return sqlFetch2St(cx, dst, retOk)
endProcedure sql2St

/*-- return first column of the only row and close -------------------*/
sqlFetch2One: procedure expose m.
parse arg cx, dst, retNone
    f1 = sqlFetch(cx, dst)
    if f1 then
        f2 = sqlFetch(cx, dst'.2')
    call sqlClose cx
    if \ f1 then
        if retNone \== '' then
            return substr(retNone, 2)
        else
            call err 'sqlFetch2One: no row returned'
    else if f2 then
        call err 'sqlFetch2One: more than 1 row'
    if m.sql.cx.fetchFlds == '' then do
        c1 = substr(word(m.sql.cx.fetchVars, 1), 2)
        res = value(c1)
        return res
        end
    c1 = word(m.sql.cx.fetchFlds, 1)
    return m.dst.c1
endProcedure sqlFetch2One

/*-- execute a query and return first column of the only row
           if > 1 row fail, if 0 rows return arg(3) or fail ----------*/
sql2One: procedure expose m.
parse arg src, dst, feVa, retOk, retNone
    cx = m.sql_defCurs
    call sqlQuery cx, src, feVa, retOk
    return sqlFetch2One(cx, dst, retNone)
endProcedure sql2One

/*--- describe table and return sqlDA --------------------------------*/
sqlDescribeTable: procedure expose m.
parse upper arg tb, force
    if force == 1 | \ datatype(m.sql.table.tb.sqlD, 'n') then
         call sqlExec 'describe table :tb into :M.SQL.TABLE.'tb
    return 'SQL.TABLE.'tb
endProcedure sqlDescribeTable

/*--- return select column list for table tb
      omit Blobs (blobMax < 0) or substr(blob, 1, blobMax) ----------*/
sqlColList: procedure expose m.
parse arg tb al, blobMax
    sd = sqlDescribeTable(tb)
    bs = ''
    lst = ''
    if al \== '' & right(al, 1) \== '.' then
        al = al'.'
    do sx=1 to m.sd.sqld
        if wordPos(m.sd.sx.sqlType, '404 405 408 409 412 413') < 1 then
            lst = lst',' al || m.sd.sx.sqlName
        else do
            bs = bs m.sd.sx.sqlName
            if blobMax >= 0 then
                lst = lst', length('al || m.sd.sx.sqlName')' ,
                                          m.sd.sx.sqlName'Len' ,
                     || ', substr('al  || m.sd.sx.sqlName ,
                     || ', 1,' blobMax')' m.sd.sx.sqlName
            end
        end
    m.sd.colList = substr(lst, 3)
    m.sd.blobs = strip(bs)
    return substr(lst, 3)
endProcedure sqlColList

/*--- use describe output to generate column names,
        ''         use names from ca (rexxified)
        nms+       use names, check ca for null values
        ?('?'?nm)+ use names, check for null if preceeded by ?
        :...       use string as is
                fetchVariables and sqlNull names ---------------------*/
sqlRxFetchVars: procedure expose m.
parse arg cx, src, cd
    st = 'SQL.'cx'.COL'
    if abbrev(src, ':') then do
        m.sql.cx.fetchVars = src
        m.sql.cx.fetchCode = cd
        m.sql.cx.fetchFlds = ''
        return
        end
    m.sql.cx.fetchVars = ''
    if abbrev(src, '?') then do
        call err  implement + rxFetchVars ?
        r = substr(src, 2)
        do wx=1 to words(src)
            cn = word(src, wx)
            if abbrev(cn, '?') then
                call sqlRexxAddVar substr(cn, 2), 0, 1
            else
                call sqlRexxAddVar cn, 0, 0
            end                              ????????????? */
        end
    else if src <> '' then do
        ff = src
        end
    else do
        ff = ''
        do kx=1 to m.sql.cx.d.sqlD
             ff = sqlNiceVarsAdd(ff, m.sql.cx.d.kx.sqlName)
             end
        end
    m.sql.cx.fetchFlds = ff
    if m.sql.cx.d.sqlD <> words(ff) then
        call err 'sqlRxFetchVars sqlD='m.sql.cx.d.sqlD ,
                '<>' words(ff) 'fields of' ff
    sNu = ''
    sFe = ''
    do kx=1 to m.sql.cx.d.sqlD
        nm = word(ff, kx)
        sFe = sFe', :m.dst.'nm
        if m.sql.cx.d.kx.sqlType // 2 then do
            sFe = sFe' :m.dst.'nm'.sqlInd'
            sNu = sNu 'if m.dst.'nm'.sqlInd < 0 then' ,
                   'm.dst.'nm '= m.sqlNull;'
            end
        end
    m.sql.cx.fetchVars = substr(sFe, 3)
    m.sql.cx.fetchCode = sNu cd
    return
endProcedure sqlRxFetchVars
/* ????????????
sqlRexxAddVar:
parse arg nm, nicify, hasNulls
     nm = sqlAddVar(st, nm, nicify)
     if \ hasNulls then
          vrs = vrs', :m.dst.'nm
     else do
         vrs = vrs', :m.dst.'nm ':m.dst.'nm'.sqlInd'
         sNu = sNu 'if m.dst.'nm'.sqlInd < 0 then' ,
                    'm.dst.'nm '= m.sqlNull;'
         end
    return
endSubroutine sqlRexxAddVar   ?????? */

sqlNiceVarsAdd: procedure expose m.
parse arg old, nx rest
    upper nx
    cx = verifId(nx)
    if cx > 0 then /* avoid bad characters for classNew| */
        nx = left(nx, cx-1)
    if nx <> '' & wordPos(nx, old) < 1 0 then
        return old nx
    else
        return old  'COL' || (words(old) + 1)
endProcedure sqlNiceVarsAdd

/*--- 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

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

/*--- execute sql thru the dsnRexx interface -------------------------*/
sqlExec: /* no procedure, to keep variables sql... */
    parse arg ggSqlStmt, ggRetOk
    m.sql_HaHi = ''
    address dsnRexx 'execSql' ggSqlStmt
    /* say 'sqlCode' sqlCode 'rc' rc 'for' ggSqlStmt ggNo */
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, ggRetOk, ggSqlStmt)
    return sqlCode
endProcedure sqlExec

sqlExecMsg: procedure expose m.
parse arg sql
    sc = sqlExec(sql, '*')
    return sqlMsgLine(sc, , sql)

sqlErrorHandler: procedure expose m. ,
                   sqlCode sqlErrMc sqlErrP sqlErrD. sqlWarn. sqlState
parse arg drC, retOk, verb rest
    if drC == 0 then
        return 'return 0'
    if pos('-', retOK) < 1 then
        retOK = retOk m.sql_retOk
    if wordPos(drC, '1 -1') < 1 then do
        eMsg = "'dsnRexx rc="drC"' sqlmsg()"
        end
    else if pos('*', retOK) > 0 | wordPos(sqlCode, retOK) > 0 then do
        if sqlCode < 0 & pos('say', retOK) > 0 then
            return "call outNl errMsg(' }'sqlMsg())"
        else
            return ''
        end
    else do
        upper verb
        if verb == 'DROP' then do
            if (sqlCode == -204 | sqlCode == -458) ,
                           & wordPos('dne', retok) > 0 then
                return 'return' sqlCode
            if sqlCode = -672 & wordPos('rod', retok) > 0 then do
                hahi = m.sql_HaHi || sqlMsgLine(sqlCode,
                                   , 'tb='sqlErrMc ,verb rest)'\n'
                haHi = haHi || sqlExecMsg('alter table' SqlErrMc ,
                        'drop restrict on drop')'\n'
                call sqlExec verb rest
                m.sql_HaHi = hahi
                return ''
                end
            end
        if drC < 0 then
            eMsg = "sqlmsg()"
        else if (sqlCode<>0 | sqlWarn.0 ^==' ') & pos('w',retOK)<1 then
            return "call outNl errMsg(' }'sqlMsg()); return" sqlCode
        else
            return ''
        end
    if wordPos('rb', retok) > 0 then
        eMsg = eMsg " || '\n"sqlExecMsg('rollback')"'"
    if wordPos('ret', retok) < 1 then
        return "call err" eMsg
    m.sql_errRet = 1
    return 'call outNl' eMsg
endProcedure sqlErrorHandler

sqlMsgLine: procedure expose m. sqlErrD.
parse arg res, cnt, src, plus
    verb = translate(word(src, 1))
    if datatype(res, 'n') then
        res = 'sqlCode' res
    if cnt \== '' then do
        res = res',' cnt
        vx = wordPos(translate(word(src,1)), 'DELETE INSERT UPDATE')
        if datatype(cnt, 'n') then
            if vx > 0 then
               res = res 'rows' word('deleted inserted updated', vx)
            else if cnt <> 0 then
                res = res 'rows updated'
        end
    if plus \== '' then
        res = res',' plus
    if abbrev(res, ', ') then
        res = substr(res, 3)
    if src \== '' then do
        ll = 75 - length(res)
        aa = strip(src)
        if length(aa) > ll then
            aa = space(aa, 1)
        if length(aa) > ll then
           aa = left(aa,  ll-3)'...'
        res = res':' aa
        end
    return res
endProcedure sqlMsgLine

/*--- 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

/*--- 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
    ggFrom = 'ggSqlStmt'
    ggW1 = translate(word(ggSqlStmt, 1))
    ggW2 = translate(word(ggSqlStmt, 2))
    if ggW1 == 'PREPARE' then
        ggFrom = sqlHostVarFind(ggSt, 'FROM')
    else if ggW1 ggW2 == 'EXECUTE IMMEDIATE' then
        ggFrom = sqlHostVarFind(ggSt, 1)
    ggPos = 0
    if datatype(sqlErrd.5, 'n') & sqlErrd.5 > 0 then do
        ggPos = sqlErrd.5
        ggRes = ggRes || sqlMsgSrcPos(value(ggFrom), sqlErrd.5)
        end
    if ggFrom == 'ggSqlStmt' then do
        ggRes = ggRes'\nsql =' sqlShorten(ggSqlStmt, 2000, ggPos)
        end
    else do
        ggRes = ggRes'\nsql =' sqlShorten(value(ggFrom), 2000, ggPos)
        ggRes = ggRes'\nstmt =' sqlShorten(ggSqlStmt, 2000)
        end
    ggPref = '\nwith'
    do ggXX=1 to m.ggSt.0
        if ggFrom = m.ggVa.ggXX then
            iterate
        ggRes = ggRes || ggPref m.ggBe.ggXX ':'m.ggVa.ggXX ,
                      '=' sqlShorten(value(m.ggVa.ggXX), 210)
        ggPref = '\n    '
        end
    if m.sqlSuMsg == 1 | (m.sqlSuMsg == 2 & m.sql_csmhost \== '') then
        ggRes = ggRes'\nsubsys =' m.sql_dbSys ,
             || ', host =' m.sql_csmhost
    return  ggRes
endSubroutine sqlMsg

sqlShorten: procedure expose m.
parse arg txt, maxL, pos
    if length(txt) <= maxL then
        return txt
    if \ datatype(pos, 'n') | pos < 1 then
        pos = 1
    ex = pos + min(60, maxL%7)
    if ex <= maxL - 4 then
        return left(txt, maxL-4) '...'
    if ex >= length(txt) then
        return left(txt, 67) '...\n'substr(txt, length(txt)-maxL+72)
    else
        return left(txt, 67) '...\n'substr(txt, ex-maxL+76, maxL-75) ,
                       '...'
endProcedure sqlShorten
/*--- 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 nop
    else if rc = 4 then say 'warn linkPgm dsnTiar rc' rc 'sqlCa' ca
    else                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: procedure expose m. ,
                   sqlCode sqlErrMc sqlErrP sqlErrD. sqlWarn. sqlState
    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 = 1
    do cnt
        cx = pos(':', src, cx) + 1
        if cx < 2 then
           leave
        if pos(substr(src, cx, 1), m.ut_rxN1) > 0 then
            iterate
        ex = verify(src, m.ut_rxDot, 'n', cx)
        if ex < 1 then
            m.st.var.sx = substr(src, cx)
        else
            m.st.var.sx = substr(src, cx, ex - cx)
        if m.st.var.sx == '' | length(m.st.var.sx) > 100 then
            iterate
                       /* 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.ut_alfa) > 0
            end
        if bB < bE & bB >= 0 then
            m.st.bef.sx = substr(src, bB+1, bE-bB)
        else
            m.st.bef.sx = ''
        sx = sx + 1
        end
    m.st.0 = sx-1
    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 sqlRx  end   **************************************************/
/* copy csi begin    ***************************************************
     csi interface: see dfs managing catalogs chapt. 11
         returncode/reason see message IDC3009I
**********************************************************************/
/*--- specify dsn mask and fields to start a csi catalog search --------
      arguments:
          m       objectPointer
          dsnMask specifies the dsns with wildcards:
              %  1 character
              *  0 - n character in one level
              ** 0 - n levels
          fields a (space separated) list of field names -------------*/
csiOpen: procedure expose m.
parse arg m, dsnMask, fields
    m.m.fld.0 = words(fields)
    ffix = d2c(m.m.fld.0, 2)
    do x=1 to m.m.fld.0
        m.m.fld.x = translate(word(fields, x))
        ffix = ffix || left(m.m.fld.x, 8)
        end
    if dsnMask \== '' & right(dsnMask, 1) \== ' ' ,
          & pos('*', dsnMask) < 1 & length(dsnMask) < 42 then
        dsnMask = dsnMask'.**'
    m.m.filt = left(dsnMask, 149) ,
             || left('Y', 3) ,        /* resume offset 149      */
             || ffix                  /* csiNumEn offset 152    */

    WORKLEN = 1024 * 64
    m.m.work = D2C(WORKLEN,4) || COPIES('00'X,WORKLEN-4)
    m.m.pos = workLen + 1
    return
endProcedure csiOpen

/*--- put the next dsn into m.o and m.o.* (for other fields)
      return 1 if next dsn exists 0 otherwise ------------------------*/
csiNext: procedure expose m.
parse arg m, o
    usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET AMOUNT OF WORK AREA USED */
    px = m.m.pos
    do forever
        if px > usedL then do
            if substr(m.m.filt, 150, 1) \== 'Y' then do
                m.m.pos = px
                m.o = ''
                return 0
                end
            reason = left('', 4)
            ADDRESS LINKPGM 'IGGCSI00  reason m.'m'.filt  m.'m'.work'
            if rc == 0 & substr(reason, 3, 2) == '0000'x then
                nop
            else if rc == 4 & substr(reason, 3, 2) == '0464'x then
                say 'data set entry with error'
            else
                call err 'call csi returns' rc,
                             'rc' c2d(substr(reason, 4,1)),
                             'reason' c2d(substr(reason, 3,1)),
                             'module' substr(reason, 1,2)
            usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET WORK AREA USED */
            numFD = C2D(SUBSTR(m.m.work,13,2)) /* no flds + 1 */
            if numFd <> m.m.fld.0 + 1 then
                call err 'csiNumFd' numFd 'not' m.m.fld.0 '+' 1
            px = 15
            iterate
            end
        eType =  substr(m.m.work, px+1, 1)
        m.o = strip(substr(m.m.work, px+2, 44), 't')
        flag = substr(m.m.work, px, 1)
        /* say 'eType' eType m.o 'flag' c2x(flag) */
        if eType == '0' then do
            if flag \== '00'x & flag \== '40'x then
                call err 'flag' c2x(flag) 'for catalog' m.o
            px = px + 50    /* length of catalog entry  */
            iterate
            end
        else do
            if \ abbrev(x2b(c2x(flag)), '101') then
                call err 'call csi entry flag' x2b(c2x(flag)),
                             'rc' c2d(substr(m.m.work, px+49,1)),
                             'reason' c2d(substr(m.m.work, px+48,1)),
                             'module' substr(m.m.work, px+46, 2),
                             'for entry' m.o,
                             'see qw IDC3009I'
            py = px + 46
            tl = c2d(substr(m.m.work, py, 2))
            pl = py + 4
            pf = py + m.m.fld.0 * 2 + 4
            do fx = 1 to m.m.fld.0
                fi = m.m.fld.fx
                fl = c2d(substr(m.m.work, pl, 2))
                m.o.fi = substr(m.m.work, pf, fl)
                if fi = 'MGMTCLAS' then
                    m.o.fi = substr(m.o.fi, 3, c2d(left(m.o.fi ,2)))
                else if wordPos(fi, 'COMUDSIZ NOBYTTRK') > 0 then
                    m.o.fi = utc2d(m.o.fi)
                pf = pf + fl
                pl = pl + 2
                end
            if py + tl <> pf then
                call err 'length mismatch for entry' m.o
            m.m.pos = pf
            return 1
            end
        end
endProcedure csiNext
/*--- if dsn is arcived return 'arcive'
      if dsn is tape return 'tape'
      otherwise return managment class ------------------------------*/
csiArcTape: procedure expose m.
parse arg vo, cl, dt, dsn
        if vo = '' then
            say err '||| no volume for dsn' dsn
        else if vo = 'ARCIVE' then
            res = 'arcive'
        else if cl <> '' then
            res = cl
        else if abbrev(vo, 'SHR') then
            res = 'SHR'
        else
            res = 'tape'
        if   res = 'arcive' then
            return res
      /*if   abbrev(res, 'ar') \= abbrev(dt, '00'x) , */
        if   abbrev(res, 'ta') \= abbrev(c2x(left(dt, 1)), '7') ,
           | (left(res, 1) >= 'A') \= abbrev(dt, '30'x) then
           say '||| mismatch cl' cl 'vo' vo 'dt' c2x(dt) 'dsn' dsn
        return res
endProcedure csiArcTape
/* copy csi end ******************************************************/
/* copy mbrList begin ************************************************/
mbrList: procedure expose m.
parse arg m, pds
    msk = strip(dsnGetMbr(pds))
    if msk == '*' then
        msk = ''
    parse value csmSysDsn(dsnSetMbr(pds)) with sys '/' dsn
    if sys == '*' then do
        call adrTso listDS "'"dsn"'" members
        oy = m.tso_trap.0 + 99
        mFound = 0
        mx = 0
        do ox=1 to m.tso_trap.0
            if mFound then do
                if msk \== '' then
                    if \ match(strip(m.tso_trap.ox), msk) then
                        iterate
                mx = mx +1
                m.m.mx = strip(m.tso_trap.ox)
                end
            else
                mFound = m.tso_trap.ox == '--MEMBERS--'
            end
        end
    else do
        if msk <> '' then
            msk = 'member('translate(msk, '%', '?')')'
        mbr_name.0 = -99
        call adrCsm "mbrList system("sys") dataset('"dsn"')" msk ,
                "index(' ') short"
        do mx=1 to mbr_name.0
            m.m.mx = strip(mbr_name.mx)
            end
        mx = mbr_name.0
        end
    m.m.0 = mx
    return mx
endProcedure mbrList
tstMbrList: procedure expose m.
/*
$=/tstMbrList/
    ### start tst tstMbrList ##########################################
    *** err: adrTso rc= 8 stmt=LISTDS 'A540769.TMP.TST.MBRLIST' MEMBERS+
    . .
    .    e 1: A540769.TMP.TST.MBRLIST
    .    e 2: IKJ58503I DATA SET 'A540769.TMP.TST.MBRLIST' NOT IN CATAL+
    OG
    #noPds: 0 mbrs in A540769.TMP.TST.MBRLIST
    #1: 1 mbrs in A540769.TMP.TST.MBRLIST
    1 EINS
    #0: 0 mbrs in A540769.TMP.TST.MBRLIST
    #4: 4 mbrs in A540769.TMP.TST.MBRLIST
    1 DREI
    2 FUENF
    3 VIER
    4 ZWEI
    #*IE*: 3 mbrs in A540769.TMP.TST.MBRLIST( *IE* )
    1 IE
    2 NNNIE
    3 VIER
    #*_IE*: 2 mbrs in A540769.TMP.TST.MBRLIST( *?IE* )
    1 NNNIE
    2 VIER
$/tstMbrList/
*/
    call tst t, 'tstMbrList'
 /* call tstMbrList1 "RZ2/A540769.WK.REXX(*DA?*)"  */
    pds = tstFileName('MbrList', 'r')
    da.1 = '2ine eins'
    call tstMbrList1 pds, '#noPds'
    call writeDsn pds'(eins) ::f', da., 1
    call tstMbrList1 pds, '#1'
    call adrTso "delete '"pds"(eins)'"
    call tstMbrList1 pds, '#0'
    call writeDsn pds'(zwei) ::f', da., 1
    call writeDsn pds'(drei) ::f', da., 1
    call writeDsn pds'(vier) ::f', da., 1
    call writeDsn pds'(fuenf) ::f', da., 1
    call tstMbrList1 pds, '#4'
    call writeDsn pds'(ie) ::f', da., 1
    call writeDsn pds'(nnnie) ::f', da., 1
    call tstMbrList1 pds"( *IE* )", '#*IE*'
    call tstMbrList1 pds"( *?IE* )", '#*_IE*'
    call adrTso "delete '"pds"'"
    call tstEnd t
    return
endProcedure tstMbrList
tstMbrList1: procedure expose m.
parse arg pds, txt
    call tstOut t, txt':' mbrList(tstMbrList, pds) 'mbrs in' pds
    do mx=1 to m.tstMbrList.0
        call tstOut t, mx m.tstMbrList.mx
        end
    return
endProdecure tstMbrList1
/* copy mbrList end   ************************************************/
/* copy csm begin ******************************************************
    interface to csm,
        it is integrate with adrTso, eg. dsnAlloc , 'RZ3/..' uses csm
***********************************************************************/
/*--- execute a single csmExec command ------------------------------*/
adrCsm:
    if wordPos(translate(word(arg(1), 1)), 'COPY MBRLIST') > 0 then
       ggTO = ''
    else if symbol('m.csm_timeOut') == 'VAR' then
        ggTO = 'timeout('m.csm_timeOut')'
    else
        ggTO = 'timeout(30)'
    ggStart = time()
    if adrTso('csmExec' arg(1) ggTO, '*') == 0 then
        return 0
    if pos('CSMXC43E NO CONNECTION TO S', m.tso_trap) > 0 ,
         | pos('CSMXC36E TIMEOUT LIMIT EXCEEDED DURING LOGON TO' ,
            , m.tso_trap) > 0 then
               /* CSMXC43E NO CONNECTION TO SYSPLEX:RR2 AVAILABLE
                  CSMXC36E TIMEOUT LIMIT EXCEEDED DURING LOGON TO SYS */
        m.csm_err = 'noConn'
    else if pos('IKJ56225I', m.tso_trap) > 0             ,
               & ( pos('ALREADY IN USE', m.tso_trap) > 0 ,
                 | pos('CATED TO ANOTH', m.tso_trap) > 0) then
               /* 5 IKJ56225I DATA SET A540769.TMP.TEXT ALREADY IN USE,
                  6 IKJ56225I DATA SET IS ALLOCATED TO ANOTHER JOB */
        m.csm_err = 'inUse'
    else
        m.csm_err = ''
    if pos('*', arg(2)) <= 0 & wordPos(m.tso_rc, arg(2)) <= 0 then
        call err strip('csmExec' m.csm_err) 'rc='m.tso_rc ,
            '\nstmt='subWord(m.tso_stmt, 2) m.tso_trap ,
            '\nend of csmExec, time='ggStart '-' time()
    return m.tso_rc
endProcedure adrCsm

csmDel: procedure expose m.
parse arg rz, dsn
    if dsnGetMbr(dsn) == '' then do
        call adrCsm "allocate system("rz") dataset('"dsn"')" ,
                         "disp(del) ddname(del1)"
        call adrTso 'free dd(del1)'
        end
    else do
        rr = adrCsm("mDelete system("rz") dataset('"dsnSetMbr(dsn)"')",
                          "member("dsnGetMbr(dsn)")", 8)
        if rr <> 0 then
            if pos('CSMEX77E Member:', m.tso_trap) < 1 ,
              | pos(' not found', m.tso_trap) < 1 then
                  call err 'rc='rr 'csm mDelete' rz'/'dsn':'m.tso_trap
        end
    return
endProcedure csmDel
/*--- copy members / datasets
      Vorlage: csrxUtil ----------------------------------------------*/
csmCopy: procedure expose m.
parse arg aFr, aTo, retOk
    frDD = tsoDD('csmFr*', 'a')
    frMbr = dsnGetMbr(aFr)
    if frMbr == '*' then
        fr = dsnSetMbr(aFr)
    else
        fr = aFr
    call csmAlloc fr frDD 'shr'
    toDD = tsoDD('csmTo*', 'a')
    toMbr = dsnGetMbr(aTo)
    if toMbr\== '=' then
        to = aTo
    else
        to = dsnSteMbr(aTo, frMbr)
    call csmAlloc to toDD 'shr ::D'frDD
    if frMbr \== '' & toMbr == '' & m.tso_dsOrg.toDD == 'PO' then do
        call adrTso 'free dd('toDD')'
        to = dsnSetMbr(aTo, frMbr)
        call csmAlloc to toDD 'shr'
        end
    inDD = tsoDD('csmIn*', 'a')
    if frMbr == '' & m.tso_dsOrg.frDD == 'PO' then do
        call tsoAlloc '-' inDD 'NEW ::F'
        call adrCsm "mbrList ddName("frDD") index(' ') short"
        do ix=1 to mbr_mem#
            i.ix = ' S M='mbr_name.ix
            end
        call writeDD inDD, 'I.', mbr_mem#
        call tsoCLose inDD
        end
    else do
        call adrTso 'alloc dd('inDD') dummy'
        end
    outDD = word(dsnAlloc('dd(csmO*) new ::V137'), 1)
    cmdu = 'CSMUTIL CSM,COPYREPLACE,DD(,,,,'inDD','outDD ,
               ||  ',,'frDD','toDD'),MARC(0)'
    cRc = adrTso(cmdU, '*')
    if cRc <> 0 then do
        call readDD outDD, o.
        call tsoClose outDD
        say 'rc='cRc',' o.0 'outputlines for' cmdU
        do ox=1 to o.0
            say o.ox
            end
        end
    call tsoFree frDD toDD inDD outDD
    if cRc <> 0 then
        call err 'csmCopy rc='cRc
    return cRc
endProcedure csmCopy

csmCopLOld: procedure expose m.
       /* csrxUtil geht nicht mit to(mbr), copy geht nicht fuer Load */
parse arg aFr, aTo, retOk
    if \ dsnExists(aTo) then do
            /* alloc ourself, because csrxutil forget mgmtclas */
        call dsnAlloc 'dd(csmCopy) catalog' aTo ,
            dsnLikeAtts(dsnSetMbr(aFr), 0)
        call tsoFree csmCopy
        end
    parse value csmSysDsn(aFr) with fSys '/' fDsn
    parse value csmSysDsn(aTo) with tSys '/' tDsn
    return adrTso("exec 'CSM.DIV.P0.EXEC(CSRXUTIL)'" ,
        "'COPY" fSys"/''"fDsn"'' TO" tSys"/''"tDsn"'' REPLACE'", retOk)
endProcedure csmCopLOld

csmCopyOld: procedure expose m.
parse arg aFr, aTo, retOk
    if dsnGetMbr(csnTo) \= '' & dsnGetMbr(csnTo) \= '' then do
        if dsnGetMbr(csnFr) <> dsnGetMbr(csnTo) then
            call err 'member rename' csnFr 'to' csnTo
        csnTo = dsnSetMbr(csnTo)
        end
    fr = csmSysDsn(aFr)
    frMbr = dsnGetMbr(fr)
    frDD = tsoDD('csmFr*', 'a')
    to = csmSysDsn(aTo)
    toMbr = dsnGetMbr(to, '=')
    toDD = tsoDD('csmTo*', 'a')
    call csmAlloc dsnSetMbr(fr) frDD 'shr'
    if frMbr == '' & m.tso_dsorg.frDD == 'PO' then
        if toMbr \== '=' then
            call err 'csmCopy from' fr'(*) to ps' to
        else
            frMbr = '*'
    if frMbr == '' & (toMbr \== '' & toMbr \== '=') then
        psOrLib = 'dsorg(po) dsntype(library)'
    else if frMbr \== '' & toMbr == '' then
        psOrLib = 'dsorg(ps)'
    else
        psOrLib = ''
    call csmAlloc dsnSetMbr(to) toDD 'shr ::D'frDD psOrLib
    c = 'indd('frDD') outDD('toDD')'
    if frMbr \== '*' then do
        if frMbr \== '' then
            c = c 'member('frMbr')'
        if toMbr \== '' & toMbr \== '=' then
            c = c 'newName('toMbr')'
        call adrCsm 'copy' c
        end
    else do
        call adrCsm "mbrList ddName("frDD") index(' ') short"
        do mx=1 to mbr_mem#
            call adrCsm 'copy' c 'member('mbr_name.mx')'
            end
        end
    call tsoFree frDD toDD
    return
endProcedure csmCopyOld

csmAlloc: procedure expose m.
parse arg dsn dd disp rest ':' nn, retRc
    sys = ''
    a2 = ''
    upper dd disp
    m.tso_dsn.dd = csmSysDsn(dsn)
    parse var m.tso_dsn.dd sys '/' dsn
    if disp = '' then
        disp = 'shr'
    else if words(disp) = 2 then
        disp = word(disp, 2)
    a1 = "SYSTEM("sys") DDNAME("dd")"
    if dsn == 'INTRDR' then do
        a1 = a1 'sysout(T) writer(intRdr)'
        end
    else do
        if dsn <> '' then do
            a1 = a1 "DATASET('"dsnSetMbr(dsn)"')"
            mbr = dsnGetMbr(dsn)
            if mbr <> '' then
                a1 = a1 'MEMBER('mbr')'
            end
        if abbrev(disp, 'SYSOUT(') then
            a1 = a1 disp
        else
            a1 = a1 "DISP("disp")"
        end
    nAtts = wordPos(disp, 'NEW MOD CAT') > 0 & nn \== ''
    if nAtts then
        rest = dsnCreateAtts('-'dsn , nn) rest
    cx = pos(' UCOUNT(', ' 'translate(rest))
    if cx > 0 then do
         rest = left(rest, cx) || 'nitCnt(' || substr(rest, cx+7)
         end
    cx = pos(' RECFM(', ' 'translate(rest))
    if cx > 0 then do
        cy = pos(')', rest, cx)
        rest = left(rest, cx+5) || space(substr(rest,cx+6,cy-cx-6), 0) ,
                                || substr(rest,cy)
        end
    cx = pos(' CYLINDERS ', ' 'translate(rest)' ')
    if cx > 0 then do
        rest = delStr(rest, cx+8, 1)
        end
    cx = pos(' CYL ', ' 'translate(rest)' ')
    if cx > 0 then do
        rest = insert('inder', rest, cx+2)
        end
    if retRc <> '' | nAtts | nn == '' then do
        alRc = adrCsm('allocate' a1 rest, retRc)
        m.tso_dsorg.dd = subsys_dsOrg
        return alRc
        end
    alRc = adrCsm('allocate' a1 rest, '*')
    m.tso_dsorg.dd = subsys_dsOrg
    if alRc = 0 then
        return 0
    say 'csmAlloc rc' alRc 'for' a1 rest '...trying to create'
    call csmAlloc m.tso_dsn.dd dd 'CAT' rest ':'nn
    call adrTso 'free  dd('dd')'
    return adrCsm('allocate' a1 rest)
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

csmLikeAtts: procedure expose m.
parse upper arg rz '/' dsn
    lc = adrCsm('dslist system('rz') dsnMask('dsnSetMbr(dsn)')')
    if stemsize <> 1 then
        call err 'csmLikeAtts stemSize='stemsize 'for dsn='dsn
    if abbrev(dsOrg.1, 'PO') then
        r = 'dsorg(po) dsnType(library)'
    else if abbrev(dsOrg.1, 'PS-') then
        r = 'dsorg(PS)'
    else
        r = 'dsorg('dsOrg.1')'
    r = r 'mgmtClas('mgmtClas.1')'                       ,
       /* 'dataClas('dataClas.1')'   */                  ,
          'recFM('strip(translate('1 2 3', recFm.1, '123'))')'  ,
          'lRecl('lRecl.1')'                         ,
          'space('tracksused.1','  tracks.1') tracks'
    /*    if \ datatype(tracksused.1, 'n') then do
              if \ datatype(tracks.1, 'n') then
                  r = r 'space('tracks.1',' tracks.1')'
              if \ datatype(tracks.1, 'n') then
                  tracks.1 = tracksUsed.1   */

    return r
endProcedure csmLikeAtts
/*--- execute a rexx (under tso) in another rz
           here we use rexx TPSYSIKJ which was written for
           jcl procedure RM@IKJ01
arguments
rz   which rz to run rexx
proc the (remote) procedure library to use
opt  options
cmd  the tso command to execute
----------------------------------------------------------------------*/
/*--- execute a rexx (under tso) in another rz
          directly (without TPSYSIKJ) --------------------------------*/
csmExRx: procedure expose m.
parse arg rz, proc, opt, cmd
    pStem = opt
    if pStem = '' then
        pStem ='CSMEXRX'               /* split tso cmd in linews */

    do cx=1 to (length(cmd)-1) % 68
        cmd.cx = substr(cmd, 68*cx-67,68)'-'
        end
    cmd.cx = substr(cmd, 68*cx-67)
    cmd.0 = cx
    timeout = 11
    if 0 then do
        call adrTso  'free ed(rmtSys)'  ,'*'
        call tsoFree tsoDD(rmtsPrt, 'a')
        call adrTso  'free dd(rmtsIn)','*'
        call adrTso  'free dd(sysproc)' ,'*'
        end
                                       /* alloc necessary dd */
    call dsnAlloc rz"/"proc "dd(rmSyPro) rmtDDn(sysProc)"
    call dsnAlloc rz"/tmp.tsin new dd(rmTsIn) rmtDdn(sysTsIn) ::f "
    call tsoOpen rmTsIn, 'w'           /* write tso cmd */
    call writeDD rmTsIn, cmd.
    call tsoClose rmtsin
    call dsnAlloc rz"/tmp.prt new dd(rmtsprt) rmtDdn(sysTsPrt)",
                    "::f133"
    call dsnAlloc rz"/tmp.rmt new dd(rmtSys) timeout("timeout")"
                                       /* now, run tso remote */
    call adrtso "csmappc start pgm(csmexec)" ,
           "parm('select tsocmd(''csmappc allocate plu(*.rmtSys)" ,
                 "tpname(sysikj) dealloc '')')",
           "timeout("timeOut")", '*'
    if rc <> 0 | appc_rc <> 0 then do  /* handle csm error */
        ee = 'csm tso exec rc='rc 'appc_rc='appc_rc
        say ee
        say '  rexx rz='rz 'proc='proc 'opt='opt
        say '  cmd='cmd
        call csmappcRcSay ggTsoCmd
        call readDD 'rmTsPrt', 'M.'pStem'.'
        call tsoClose rmtsPrt
        say m.pStem.0 'tso output lines'
        do px=1 to m.pStem.0
            say ' ' strip(m.pStem.px, 't')
            end
        call err ee
        end
    if opt <> '' then do               /* copy output to stem */
        call readDD 'rmTsPrt', 'M.'pStem'.'
        call tsoClose rmtsPrt
        end
    call tsoFree rmSyPro rmtsPrt rmtSys rmtsIn
    return
 endProcedure csmExRx

/*--- sys the re and result variables from csmAppcRc -----------------*/
csmappcRcSay: procedure expose appc_rc appc_reason appc_msg. ,
           appc_state_c appc_state_f
 parse arg cmd
     say 'rc='appc_rc 'reason='appc_reason ,
         'state_c='appc_state_c appc_state_f
     say '  for' cmd
     do ix=1 to appc_msg.0
         say ' ' appc_msg.ix
         end
     return appc_rc
 endProcedure csmappcRcSay

/*--- execute a single csmAppc command ------------------------------*/
csmAppc:
    return adrTso('csmAppc' arg(1), arg(2))
endProcedure csmAppc
/* copy csm end *******************************************************/
/* copy timing begin *************************************************/
timing: procedure expose m.
parse arg typ, c2, txt
    e1 = time('E')
    c1 = strip(sysvar('syscpu'))
    s1 = sysvar('syssrv')
    if typ == '' then
        return strip(f('%c ela=%5i cpu=%8.3i su=%9i' ,
            , time(), e1, c1, s1) txt)
    if symbol('m.timing_ela') \== 'VAR' then
        call err 'timing('typ',' c2',' txt') ohne ini'
    if symbol('m.timing.typ.ela') \== 'VAR' then do
        m.timing.typ.ela = 0
        m.timing.typ.cpu = 0
        m.timing.typ.su  = 0
        m.timing.typ.cnt = 0
        m.timing.typ.cn2 = 0
        if symbol('m.timing_types') == 'VAR' then
            m.timing_types = m.timing_types typ
        else
            m.timing_types = typ
        if symbol('m.timing_say') \== 'VAR' then
            m.timing_say = 0
        end
    m.timing.typ.ela = m.timing.typ.ela + e1 - m.timing_ela
    m.timing.typ.cpu = m.timing.typ.cpu + c1 - m.timing_cpu
    m.timing.typ.su  = m.timing.typ.su  + s1 - m.timing_su
    m.timing.typ.cnt = m.timing.typ.cnt + 1
    if c2 \== '' then
       m.timing.typ.cn2 = m.timing.typ.cn2 + c2
    m.timing_ela = e1
    m.timing_cpu = c1
    m.timing_su  = s1
    if m.timing_say then
            say left(typ, 10)right(m.timing.typ.cn2, 10) ,
                'ela='m.timing.typ.ela ,
                'cpu='m.timing.typ.cpu 'su='m.timing.typ.su txt
    return
endProcedure timing

timingSummary: procedure expose m.
    say 'timing summary' time()
    do tx = 1 to words(m.timing_types)
        typ = word(m.timing_types, tx)
        say left(typ, 10)right(m.timing.typ.cnt,  7)       ,
                      || right(m.timing.typ.cn2,  7)       ,
                         'cpu='right(m.timing.typ.cpu, 10) ,
                         'su='right(m.timing.typ.su, 10)
        end
    return
endProcedure timingSummary
/* copy timing end   *************************************************/
/* copy ii begin ********* Installation Info *************************/
iiini: procedure expose m.
    if m.ii_ini == 1 then
        return
    m.ii_ini = 1
    m.ii_rz = ''
    i = 'RZ0 0 T S0 RZ1 1 1 S1'  ,
        'RZX X X X2 RZY Y Y Y2 RZZ Z Z Z2'  ,
        'RQ2 Q Q Q2 RR2 R R R2 RZ2 2 B S2 RZ4 4 D S4'
    do while i <> ''
        parse var i rz m.ii_rz2c.rz m.ii_rz2plex.rz sys i
        if rz <> RZ0 & rz <> RZ1 then
            m.ii_rz = strip(m.ii_rz rz)
        m.ii_rz2Sys.rz  = sys
        m.ii_sys2rz.sys = rz
        end
    i = 'DBTF T DTF DVTB V DTB DBOC C DOC DBTC B DBT DBIA A DIA' ,
        'DE0G E DE0 DEVG M DEV DPXG I DPX DX0G X DX0' ,
        'DPYG Y DPY DPZG N DPZ' ,
        'DBOF F DOF DVBP P DBP DP2G Q DP2 DBOL O DOL DP4G U DP4'
    do while i <> ''
        parse var i db m.ii_db2c.db mbr i
        m.ii_mbr2db.mbr = db
        m.ii_db2Elar.db = wordPos(db, 'DVTB DVBP DEVG') > 0
        end
    m.ii_rz2db.rz0 = 'DBTC DBIA'
    m.ii_rz2db.rz1 = 'DBTF DVTB DBOC'
    m.ii_rz2db.rzx = 'DE0G DEVG DX0G DPXG'
    m.ii_rz2db.rzy = 'DE0G DEVG DPYG'
    m.ii_rz2db.rzz = 'DE0G DEVG DPZG'
    m.ii_rz2db.rq2 = 'DBOF DVBP DP2G'
    m.ii_rz2db.rr2 = 'DBOF DVBP DP2G'
    m.ii_rz2db.rz2 = 'DBOF DVBP DP2G'
    m.ii_rz2db.rz4 = 'DBOL DP4G'
    i = ''
    do rx=1 to words(m.ii_rz)
        rz = word(m.ii_rz, rx)
        i = i repAll(' 'space(m.ii_rz2db.rz, 1), ' ', ' 'rz'/')
        end
    m.ii_rzDb = space(i, 1)
    return
endProcedure iiIni

iiMbr2DbSys: procedure expose m.
parse upper arg mbr
    return iiLazy(ii_mbr2db, left(mbr, 3), 'member')

iiRz2C: procedure expose m.
parse upper arg rz
    return iiLazy(ii_rz2c, rz, 'rz')

iiRz2P: procedure expose m.
parse upper arg rz
    return iiLazy(ii_rz2plex, rz, 'rz')

iiRz2Dsn: procedure expose m.
parse upper arg rz
    return overlay('Z', rz, 2)

iiDBSys2C: procedure expose m.
parse upper arg db
    return iiLazy(ii_db2c, db, 'dbSys')

iiSys2RZ: procedure expose m.
parse upper arg sys
    return iiLazy(ii_sys2rz, left(sys, 2), 'sys')

iiLazy: procedure expose m.
parse arg st, key, txt
    if symbol('m.st.key') == 'VAR' then
        return m.st.key
    if m.ii_ini == 1 then
       return err('no' txt'='key 'in ii' st)
    call iiIni
    return iiLazy(st, key, txt)
endProcedure iiLazy

iiVPut:procedure expose m.
parse upper arg rz '/' db .
    call vPut 'rz', rz
    call vPut 'rzC', iiRz2C(rz)
    call vPut 'rzP', iiRz2P(rz)
    call vPut 'rzD', iiRz2Dsn(rz)
    call vPut 'dbSys', db
    call vPut 'dbSysC', iidbSys2C(db)
    call vPut 'dbSysElar', iiLazy(ii_db2Elar, db)
    return 1
endProcedure iiVPut

iiIxVPut:procedure expose m.
parse arg ix
    if ix > words(m.ii_rzDb) then
        return 0
    else
        return iiVPut(word(m.ii_rzDb, ix))
endProcedure iiIxVPut
/* copy ii end   ********* Installation Info *************************/
/* 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 tsoOpen grp, 'R'
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 tsoClose 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 m.tso_stmt, ggRet
    call outtrap m.tso_trap.
    address tso m.tso_stmt
    m.tso_rc = rc
    call outtrap off
    if m.tso_rc == 0 then
        return 0
    m.tso_trap = ''
    do ggXx=1 to min(7, m.tso_trap.0)
        m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
        end
    if m.tso_trap.0 > 7 then do
        if m.tso_trap.0 > 14 then
            m.tso_trap = m.tso_trap'\n............'
        do ggXx=max(8, m.tso_trap.0-6) to m.tso_trap.0
            m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
            end
        end
    if ggRet <> '*' & wordPos(m.tso_rc, ggRet) <= 0 then
        call err 'adrTso rc=' m.tso_rc 'stmt='m.tso_stmt m.tso_trap
    return m.tso_rc
endSubroutine 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 arg(2)
     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:  tsoOpen...'R', readDD*,  tsoClose
        write: tsoOpen...'W', writeDD*, tsoClose

        readDD returns true if data read, false at eof
        do not forget that open is mandatory to write empty file|
***********************************************************************/

/*--- open dd for read (rw='R') or write (rw='W') --------------------*/
tsoOpen: procedure expose m.
parse upper arg dd, rw
    return adrTso('execio' 0 'disk'RW dd '(open)')
return /* end tsoOpen */

/*--- close dd -----------------------------------------------------*/
tsoClose: procedure expose m.
    parse upper arg dd
    return adrTso('execio 0 diskR' dd '(finis)')
endProcedure tsoClose

/*--- 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 */

/*--- 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

/*--- 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
    parse value dsnAlloc('dd('m.m.dd')' m.m.dsn) with m.m.dd m.m.free
    call tsoOpen m.m.dd, 'R'
    return m
endProcedure readDDNxBegin

/*--- return the stem of the next line, or '' at end -----------------*/
readNx: procedure expose m.
parse arg m
    m.m.cx = m.m.cx + 1
    if m.m.cx <= m.m.0 then
        return m'.'m.m.cx
    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 stem of the curr line, '' at end --------------------*/
readNxCur: procedure expose m.
parse arg m
    if m.m.cx <= m.m.0 then
        return m'.'m.m.cx
    else
        return ''
endProcedure readNxCur

/*--- 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 tsoClose m.m.dd
    call tsoFree m.m.free
    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' | w == 'CAT' then
            di = di 'CAT'
        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 pos('(', w) > 0 then
            leave
        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.tso_trap.1 = ''
        m.tso_trap.2 = ''
        m.tso_trap.3 = ''
        res = dsnAlloc(spec, pDi, pDD, '*')
        if \ datatype(res, 'n') then
            return res
        msg = m.tso_trap.1'\n'm.tso_trap.2'\n'm.tso_trap.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 dd == '-' & pDD \== '' then
        dd = pDD
    if dd == '-' then
        dd = 'DD*'
    dd = tsoDD(dd, 'a')
    m.tso_dsn.dd = ''
    if na == '-' & di == '-' & rest = '' then
        return dd
    if di = '-' then
        if pDi == '' then
            di = 'SHR'
        else
            di = pDi
    if pos('(', na) < 1 then
        nop
    else if di = 'MOD' then
        call err 'disp mod for' na
    else
        di = 'SHR'
    if lastPos('/', na, 6) > 0 then
        rx = csmAlloc(na dd di rest, retRc)
    else
        rx = tsoAlloc(na dd di rest, retRc)
    if rx = 0 then
        return dd dd
    else
        return rx
endProcedure dsnAlloc

/*--- find a free dd with prefix dd ----------------------------------*/
tsoDD: procedure expose m.
parse upper arg dd, f, noErr
    if symbol('m.tso_ddAll') \== 'VAR' then do
        call errIni
        m.tso_ddAll = ''
        end
    if f == '-' then do
        ax = wordPos(dd, m.tso_ddAll)
        if ax > 0 then
            m.tso_ddAll = delWord(m.tso_ddAll, ax, 1)
        else if noErr \== 1 then
            call err 'tsoDD dd' dd 'not used' m.tso_ddAll
        end
    else if f <> 'A' then
        call err 'tsoDD bad fun' f
    else do
        if right(dd, 1) = '*' then do
            d0 = left(dd, length(dd)-1) || m.err.screen
            dd = d0
            do dx=1 while wordPos(dd, m.tso_ddAll) > 0
                dd = d0 || dx
                end
            end
        else if pos('?', dd) > 0 then
            dd = repAll(dd, '?', m.err.screen)
        if wordPos(dd, m.tso_ddAll) < 1 then
            m.tso_ddAll = strip(m.tso_ddAll dd)
        m.tso_dsn.dd = ''
        m.tso_dsOrg.dd = ''
        end
    return dd
endProcedure tsoDD

tsoAlloc: procedure expose m.
parse arg na dd disp rest ':' nn, retRc
    dd = translate(dd)
    c = 'alloc dd('dd')' disp
    if na == '-' then
        m.tso_dsn.dd = ''
    else if na \== 'INTRDR' then do
        c = c "DSN('"na"')"
        m.tso_dsn.dd = na
        end
    else do
        c = c "sysout(*) writer(intRdr)"
        m.tso_dsn.dd = '*intRdr'
        end
    if wordPos(disp, 'NEW MOD') > 0 & nn \== '' then
        c = c dsnCreateAtts(,nn)
    if adrTso(c rest, '*') = 0 then
        return 0
    if pos('IKJ56246I', m.tso_trap) > 0 then
        if pos('TED, FILE IN USE', m.tso_trap) > 0 then do
     /* IKJ56246I DATA SET A540769.WK.REXX NOT ALLOCATED, FILE IN USE */
        say 'tsoAlloc dd' dd 'already in use:' substr(m.tso_trap, 3)
        say '.... trying to free'
        call tsoFree dd, 1
        say '.... retrying to allocate' c rest
        if adrTso(c rest, '*') = 0 then
            return 0
        end
    if nn \= '' & wordPos(disp, 'OLD SHR') > 0 ,
          & pos('IKJ56228I', m.tso_trap) > 0 ,
          & pos(' NOT IN CATALOG ', m.tso_trap) > 0 then do
       /* IKJ56228I DATA SET A540769.XY.REXX NOT IN CATALOG OR CATAL*/
        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
    if pos('*', retRc) < 1 & wordPos(m.tso_rc, retRc) < 1 then
        call err 'tsoAlloc rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
    call sayNl 'tsoAlloc rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
    return m.tso_rc
endProcedure tsoAlloc

dsnExists: procedure expose m.
parse upper arg aDsn
    parse value csmSysDsn(aDsn) with rz '/' dsn
    if rz == '*' then
        return sysDsn("'"dsn"'") == 'OK'
    else if dsnGetMbr(dsn) == '' then do
        lc = adrCsm('dslist system('rz') dsnMask('dsn') short', 4)
        if stemsize = 0 | stemSize = 1 then
            return stemSize
        call err 'csmExists stemSize='stemsize 'for dsn='aDsn
        end
    else do
        cc = adrCsm("mbrList system("rz") dataset('"dsnSetMbr(dsn)"')",
                    "member("dsnGetMbr(dsn)") index(' ') short", 8)
        if cc <> 0 then do
            if pos(' NOT IN CATALOG\', m.tso_trap) > 0 ,
              & pos('CSMSV29E DATA SET ', m.tso_trap) > 0 then
                return 0
            return err('error in csm mbrList' aDsn m.tso_trap)
            end
        if mbr_name.0 == 0 | mbr_name.0 == 1 then
            return mbr_name.0
        call err 'csmExists mbr_mem#='mbr_name.0 'for dsn='aDsn
        end
endProcedure dsnExists

dsnLikeAtts: procedure expose m.
parse upper arg dsn, isFile
     if isFile then do
         ddDsn = m.tso_dsn.dsn
         if lastPos('/', m.tso_dsn.dsn, 4) < 1 then
              return tsoLikeAtts(dsn, 1)
         dsn = m.tso_dsn.dsn
         end
     sx = lastPos('/', dsn, 4)
     if sx < 1 then
         return tsoLikeAtts(dsn, 0)
     else if abbrev(dsn, '*/') | abbrev(dsn, sysVar('SYSNODE')'/') then
         return tsoLikeAtts(substr(dsn, sx+1), 0)
     else
         return csmLikeAtts(dsn)
endProcedure dsnLikeAtts

tsoLikeAtts: procedure expose m.
parse arg dsn, isFile
    rc = listDsi("'"dsn"'" copies('FILE', isFile) "SMSINFO")
    if rc = 0 then
        r = ''
    else if rc = 4 & sysReason = 19 then do
        r = 'UCOUNT(30)' /* 7.8.13 corrected attribute */
        say 'creating' dsn 'with multi volume' mv
        end
    else if rc ^= 0 then
        call err 'listDsi rc' rc 'reason' sysReason,
                             sysMsgLvl1 sysMsgLvl2
    if right(sysDsSms, 7) == 'LIBRARY' | abbrev(sysDsSms, 'PDS') ,
                      | sysDsOrg = 'PO' then
         r = 'dsOrg(PO) DSNTYPE(LIBRARY)' r
    else
         r = "dsOrg("sysDSorg")" r
    if sysUnits = 'TRACK' then
        sysUnits = 'TRACKS'
    return r "MGMTCLAS("sysMgmtClass")",
        "DATACLAS("sysDataClass")" ,
        "RECFM("strip(translate('1 2 3', ' 'sysREcFM, '123'))")",
        "LRECL("SYSLRECL")",
        "SPACE("sysPrimary"," sysSeconds")" ,
            sysUnits || left('S', sysUnits == 'TRACK')
    /*  "blksize("sysBLkSIZE")" removed 3.4.13: let sms do the magic */
endProcedure tsoLikeAtts

tsoFree: procedure expose m.
parse arg ddList, tryClose
    do dx=1 to words(ddList)
        dd = word(ddList, dx)
        if adrTso('free dd('dd')', '*') <> 0 then do
            if pos('IKJ56861I', m.tso_trap) > 0 & tryClose == 1 then
              if pos('NOT FREED, DATA SET IS OPEN', m.tso_trap),
                    > 0 then do
                  /* IKJ56861I  FILE A1 NOT FREED, DATA SET IS OPEN */
                say 'dataset open:' substr(m.tso_trap, 3)
                say '.... trying to close'
                if adrTso('execio 0 diskR' dd '(finis)', '*') = 0 then
                   call adrTso 'free dd('dd')', '*'
                end
            if m.tso_rc \== 0 then
                call sayNl 'rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
            end
        call tsoDD dd, '-', 1
        end
    return
endProcedure tsoFree

dsnCreateAtts: procedure expose m.
parse arg dsn, atts
    res = ''
    if dsn \== '' & \ abbrev(dsn, '-') then
        res = "dataset('"dsnSetMbr(dsn)"')"
    if abbrev(atts, ':') then do
        parse var atts a1 atts
        rl = substr(a1, 3)
        if abbrev(a1, ':F') then do
            if rl = '' then
                rl = 80
            res =  res "recfm("space(f b)") lrecl("rl")"
            end
        else if abbrev(a1, ':V') then do
            if rl = '' then
                rl = 32756
            res =  res "recfm("space(v b)") lrecl("rl")"
            end
        else if abbrev(a1, ':L') then
            res = res dsnLikeAtts(rl, 0)
        else if abbrev(a1, ':D') then
            res = res dsnLikeAtts(rl, 1)
        else
            call err 'csnCreateAtt bad :' a1
        end
    aU = ' 'translate(atts)
    hasOrg = pos(' DSORG(', aU) > 0 | pos(' DSNTYPE(', aU) > 0
    hasMbr = pos('(', dsn) > 0
    if hasMbr & \ hasOrg then
        atts = atts 'dsorg(po) dsntype(library)'
    if hasOrg | hasMbr then do
        ww = DSORG DSNTYPE
        do wx=1 to words(ww)
            do forever
                cx = pos(' 'word(ww, wx)'(', ' 'translate(res))
                if cx == 0 then
                    leave
                cy = pos(')', res, cx)
                res = delstr(res, cx, cy+1-cx)
                end
            end
        end
    res = res atts
    aU = ' 'translate(res)
    if pos(' MGMTCLAS(', aU) < 1 then
        res = res 'mgmtclas(COM#A091)'
    if pos(' SPACE(', aU) < 1 then
        res = res 'space(1, 50) cylinders'
    return res
endProcedure dsnCreateAtts
/*--- check if a dataset is archive ------------------------------------
          returns 'ok'                    if dataset on disk
                  'not'                   if dataset is not catalogued
                  'arc'                   if dataset archived
                  listDsi errorMsg        otherwise ------------------*/
dsnArc: procedure expose m.
parse upper arg dsn
    lc = listDsi("'"strip(dsn)"' noRecall")
    if lc = 0 then
        return 'ok'
    else if lc=4 & sysReason = 19 then  /* multiple volumes */
        return 'ok'
    else if lc=16 & sysReason = 5 then
        return 'notCat'
    else if lc=16 & sysReason = 9 then
        return 'arc'
    else
        return 'listDsi cc='lc', sysReason='sysReason ,
                          'm2='sysMsgLvl2', m1='sysMsgLvl1
endProcedure dsnArc
/*--- 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)'
    call tsoFree word(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)'
    call tsoFree word(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
    parse value dsnAlloc(frSpec, 'SHR', 'FRDD') with frDD frFr
    parse value dsnAlloc(toSpec, 'OLD', 'TODD') with toDD toFr
    call tsoOpen frDD, 'R'
    call tsoOpen toDD, 'W'
    cnt = 0
    do while readDD(frDD, r.)
        call writeDD toDD, r.
        cnt = cnt + r.0
        end
    call tsoClose frDD
    call tsoClose toDD
    call tsoFree frFr toFr
    if ggSay == 1 | m.debug == 1 then
       say cnt 'records copied from' frSpec 'to' to toSpec
    return
endSubroutine writeDsn
/* copy adrTso end ****************************************************/
/* copy csv begin *****************************************************/
csvIni: procedure expose m.
    if m.csv.ini == 1 then
        return
    m.csv.ini = 1
    call jIni
    call classNew "n CsvRdr u JRW, f RDR r", "m",
        , "jReset m.m.rdr = arg",
        , "jOpen call csvRdrOpen m, opt",
        , "jClose call jClose m.m.rdr; call oMutatName m, 'CsvRdr'"
    call classNew "n CsvRdrR u CsvRdr", "m",
        , "jRead return csvRdrRead(m)"
    call classNew "n CsvWrt u JRW, f RDR r", "m",
        , "jReset m.m.rdr = arg",
        , "jOpen call csvWrtOpen m, opt",
        , "jClose call jClose m.m.rdr; call oMutatName m, 'CsvWrt'"
    call classNew "n CsvWrtR u CsvWrt", "m",
        , "jRead return csvWrtRead(m)"
    return
endProcedure csvIni

/*--- create a new csvRdr --------------------------------------------*/
csvRdr: procedure expose m.
parse arg rdr
    return jReset(oNew('CsvRdr'), rdr)
endProcedure csvRdr

/*--- open csvRdr: read first line and create dataClass --------------*/
csvRdrOpen: procedure expose m.
parse arg m
    call jOpen m.m.rdr, '<'
    mr = m.m.rdr
    if jRead(mr) then do
        ff = 'f' repAll(m.mr, ',', ' v, f ') 'v'
        m.m.class = classNew("n* CsvF u" ff)
        end
    call oMutatName m, 'CsvRdrR'
    return
endProcedure csvRdrOpen

/*--- read next line and return derived object -----------------------*/
csvRdrRead: procedure expose m.
parse arg m
    mr = m.m.rdr
    do until m.mr <> ''
        if \ jRead(mr) then
            return 0
        end
    var = oNew(m.m.class)
    ff = classMet(m.m.class, 'oFlds')
    s = m'.SCAN'
    call scanSrc s, m.mr
    do fx=1
        f1 = m.ff.fx
        if scanString(s, '"') then
            m.var.f1 = m.s.val
        else do
            call scanUntil s, ','
            m.var.f1 = m.s.tok
            end
        if scanEnd(s) then
            leave
        if \ scanLit(s, ',') then
            call scanErr s, ',' expected
        end
    if fx <> m.ff.0 then
        call scanerr s, 'csv cla' m.ff.0 'fields but' cx 'in line'
    m.m = var
    return 1
endProcedure csvRdrRead

/*--- create a new csvRdr --------------------------------------------*/
csvWrt: procedure expose m.
parse arg rdr
    return jReset(oNew('CsvWrt'), rdr)
endProcedure csvWrt

/*--- open csvRdr: read first line and create dataClass --------------*/
csvWrtOpen: procedure expose m.
parse arg m
    call jOpen m.m.rdr, '<'
    m.m.class = ''
    m.m.o1    = ''
    call oMutatName m, 'CsvWrtR'
    return
endProcedure csvWrtOpen

/*--- read next line and return derived object -----------------------*/
csvWrtRead: procedure expose m.
parse arg m
    mr = m.m.rdr
    if m.m.o1 \== '' then do
        i1 = m.m.o1
        m.m.o1 = ''
        end
    else if jRead(mr) then
         i1 = m.mr
    else
        return 0
    if m.m.class == '' then do
        m.m.class = objClass(i1)
        m.m.o1 = i1
        t = ''
        ff = oFlds(i1)
        do fx=1 to m.ff.0
            t = t','m.ff.fx
            end
        m.m = substr(t, 2)
        return 1
        end
    else do
        m.m = csv4Obj(i1, oFlds(i1), 0)
        return 1
        end
endProcedure csvWrtRead

csv4obj: procedure expose m.
parse arg o, ff, hasNull, oNull
    res = ''
    do fx=1 to m.ff.0
        of1 = o || left('.', m.ff.fx \== '') || m.ff.fx
        v1 = m.of1
        if hasNull & v1 = oNull then
            res = res','
        else if v1 = '' then
            res = res',""'
        else if pos(',', v1) > 0 | pos('"', v1) > 0 then
            res = res','quote(v1, '"')
        else
            res = res','v1
        end
    return substr(res, 2)
endProcedure csv4obj
/* copy csv end   *****************************************************/
/* copy j begin *******************************************************
    the j framework
         jReset
         jOpen
         jClose
         jRead
         jWrite
***********************************************************************/
jRead: procedure expose m.
parse arg m
if arg() > 1 then call err '???  old interface'  / 0
    met = objMet(m, 'jRead')
    if m.m.jReading then
        interpret met
    else
        return err('jRead('m') but not opened r')
endProcedure jRead

jReadVar: procedure expose m.
parse arg m, var
if arg() > 2 then call err '???  old interface' / 0
    if \ jRead(m) then
        return 0
    m.var = m.m
    return 1
endProcedure jReadVar

jReadObRe: procedure expose m.
parse arg m
if arg() > 1 then call err '???  old interface' / 0
    if jRead(m) then
        return m.m
    else
        return ''
endProcedure jReadObRe

jReadO: procedure expose m.
parse arg m
if arg() > 1 then call err '???  old interface'  /0
    return jRead(m)
endProcedure jReadO

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

jWriteO: procedure expose m.
parse arg m, var
    call jWrite m, var
    return
endProcedure jWriteO

jWriteAll: procedure expose m.
parse arg m, rdr
    rdr = o2file(rdr)
    met = objMet(m, 'jWriteAll')
    if \ m.m.jWriting then
        return err('jWriteAll('m',' rdr') but not opened w')
    interpret met
    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)
        call jWrite m, m.rdr
        end
    call jClose rdr
    return
endProcedure jWriteNow

jWriteNowImplO: procedure expose m.
parse arg m, rdr
    call jOpen rdr, m.j.cRead
    do while jRead(rdr)
        call jWrite m, m.rdr
        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')')
    m.m.jReading = 0
    m.m.jWriting = 0
    m.m.jUsers = 0
    interpret objMet(m, 'jReset')
    return m
endProcedure jReset

jReset0: 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')')
    m.m.jReading = 0
    m.m.jWriting = 0
    m.m.jUsers = 0
    return m
endProcedure jReset

jOpen: procedure expose m.
parse arg m, opt
    met = objMet(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 met
            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 met
            m.m.jWriting = 1
            end
        end
    m.m.jUsers = oUsers + 1
    return m
endProcedure jOpen

jClose: procedure expose m.
parse arg m
    met = objMet(m, 'jClose')
    oUsers = m.m.jUsers
    if oUsers = 1 then do
        interpret met
        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 do
        call err '-sql in jCatLines'
        end
    f2 = fmt'%#?a%c%#? %#?e%# %& %&a'
    call jOpen m, m.j.cRead
    if \ jRead(m) then do
        call jClose m
        return f(f2'%##e')
        end
    res = f(f2'%##a', m.m)
    do while jRead(m)
        res = res || f(f2, m.m)
        end
    call jClose m
    return res
endProcedure jCatLines


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"
    c1 = classNew('n JRW u ORun, f JREADING v, f JWRITING v', 'm',
        , "new ?r m = jReset0(?new2); ?jReset; return m" ,
        , "jRead" am "jRead('m')'" ,
        , "jWrite" am "jWrite('m',' line')'" ,
        , "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")
    m.class.forceDown.c1 = c1'#new'
    c2 = classNew('n JRWDeleg u JRW', 'm',
        , "new ?r return jReset(?new1, arg)",
        , "jRead md = m.m.deleg; if \ jRead(md) then return 0;" ,
                         "m.m = m.md; return 1",
        , "jWrite  call jWrite(m.m.deleg, line)" ,
        , "jWriteAll call jWriteAll m.m.deleg, rdr",
        , "jWriteNow call jWriteNow m.m.deleg, rdr",
        , "jReset    if arg \== '' then m.m.deleg = arg;",
                                   "else call jReset m.m.deleg;",
        , "jOpen     call jOpen m.m.deleg,' opt; return m" ,
        , "jClose    call jClose m.m.deleg; return m" )
    m.class.forceDown.c2 = c2'#new'
    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  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)
    m.j.out = jOpen(oNew('JSay'), '>')
    m.j.errRead  = "return err('jRead('m') but not opened r')"
    m.j.errWrite = "return err('jWrite('m',' line') but not opened w')"
    call classNew "n JBuf u JRW, f BUF s r", "m",
        , "jOpen call jBufOpen m, opt",
        , "jClose call oMutatName m, 'JBuf'",
        , "jReset call jBufReset m, arg",
        , "jRead" m.j.errRead ,
        , "jWrite" m.j.errWrite
    call classNew "n jBufR u JBuf", "m",
        , "jRead return jBufRead(m)"
    call classNew "n JBufW u JBuf", "m",
        , "jWrite call jBufWrite m, line"
    call classNew "n JBufTxt u JBuf, f MAXL v ", "m",
        , "jReset call jBufReset m, arg; m.m.maxl = 80",
        , "jWrite call jBufWrite m, o2Text(var, m.m.maxl)"
    return
endProcedure jIni

/*--- return a JRW from rdr or in ------------------------------------*/
in2File: procedure expose m.
    parse arg m
    interpret objMet(m, 'in2File')
    return err('in2File did not return')
endProcedure in2File
      /* jstr is part of out interface --> in2Str */
in2Str: procedure expose m.
    parse arg m, fmt
    interpret objMet(m, 'in2Str')
    return err('in2Str did not return')
endProcedure in2Str

in2Buf: procedure expose m.
parse arg m
    interpret objMet(m, 'in2Buf')
    return err('in2Buf did not return')
endProcedure in2Buf

in: procedure expose m.
    if arg() > 0 then call err '??? old interface'
    r = m.j.in
    m.in_ret = jRead(r)
    m.in = m.r
    return m.in_ret
endProcedure in

inVar: procedure expose m.
parse arg var
    return jReadVar(m.j.in, var)
endProcedure inVar

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

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

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

/*--- out (part of) the lines of a stem ----------------------------*/
outSt: procedure expose m.
parse arg st, fx, tx
    do lx=word(fx 1, 1) to word(tx m.st.0, 1)
        call out m.st.lx
        end
    return st
endProcedure outSt

outX: procedure expose m.
parse arg line
    if symbol('m.tst_m') \== 'VAR' then
        call jWrite m.j.out, line
    else
        call tstOut m.tst_m, line
    return 0
endProcedure out

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

JRWDeleg: procedure expose m.
parse arg arg
    return oNew('JRWDeleg', arg)
endProcedure JRWDeleg

/*--- 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
    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
    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
    return m
endProcedure jBufReset

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

jBufWrite: procedure expose m.
parse arg m, line
    call mAdd m'.BUF', line
    return
endProcedure jBufWrite

jBufWriteStem: procedure expose m.
parse arg m, st
    ax = m.m.buf.0
    do sx=1 to m.st.0
        ax = ax + 1
        m.m.buf.ax = m.st.sx
        end
    m.m.buf.0 = ax
    return m
endProcedure jBufWrite

jBufRead: procedure expose m.
parse arg m
    nx = m.m.readIx + 1
    if nx > m.m.buf.0 then
        return 0
    m.m.readIx = nx
    m.m = m.m.BUF.nx
    return 1
endProcedure jBufRead

jBufCopy:
parse arg rdr
    b = jOpen(jBuf(), m.j.cWri)
    call jWriteNow b, rdr
    return jClose(b)
endProcedure jBufCopy

jSingle: procedure expose m.
parse arg m
    call jOpen m, '<'
    one = jRead(m)
    two = jRead(m)
    call jClose m
    if \ one then
        if arg() < 2 then
            call err 'empty file in jSingle('m')'
        else
            return arg(2)
    if two then
        call err '2 or more recs in jSingle('m')'
    return m.m
endProcedure jSingle
/* copy j end *********************************************************/
/* copy o begin ******************************************************
    an object is register for a class in o2c
    a class has a list of parents in cParent
    a class has a methodTable cMet with lazy initialization
             a method generator
        otherwise an existing method is simply copied
***********************************************************************/
oIni: procedure expose m.
    if m.o_ini == 1 then
        return
    m.o_ini = 1
    call classIni
    call classNew 'n= root u', 'm',
           , "new    ?l" ,
           , "new1   ?l" ,
           , "new2   ?l" ,
           , "oClear ?l" ,
           , "oCopy  ?l"
    return
endProcedure oIni

oMetLazy: procedure expose m.
parse arg cl, met, rest
    if met = 'new1' then do
        call mNewArea cl, 'O.'substr(cl,7)
        return  "oMutate(mNew('"cl"'), '"cl"')"
        end
    call classMet cl, 'oFlds'
    if met == 'new' then do
        return 'return' classMet(cl, 'new2')
        end
    else if met == 'new2' then do
        call classMet cl, 'oClear'
        return "classClear('"cl"'," classMet(cl, 'new1')")"
        end
    else if met == 'oClear' then do
        do fx=1 to m.cl.flds.0
            f1 = m.cl.flds.fx
            m.cl.flds_null.fx = if(m.cl.f2c.f1==m.class_W,
                            , m.o_escW, '')
            end
        m.cl.flds_null.0 = m.cl.flds.0
        return "return classClear('"cl"', m)"
        end
    else if met == 'oCopy' then do
        do sx=1 to m.cl.stms.0
            s1 = m.cl.stms.sx
            s2 = m.cl.s2c.s1
            call classMet m.cl.s2c.s1, 'oCopy'
            call classMet m.s2.1, 'oCopy'
            end
        call classMet cl, 'new1'
        return "return classCopy('"cl"', m, t)"
        end
    else
        call err 'bad method in oMetLazy('cl',' met')'
endProcedure oMetLazy

oMetRec: procedure expose m.
parse arg cl, met, rest
    gen = ''
    rx = 1
    do forever
        ry = pos('?', rest, rx)
        if ry == 0 then
            return gen || substr(rest, rx)
        gen = gen || substr(rest, rx, ry-rx)
        rx = verify(rest, m.ut_alfid, 'n', ry+1)
        if rx = 0 then
            rx = length(rest)+1
        rr = classMet(cl, substr(rest, ry+1, rx-ry-1))
        if word(rr, 1) = 'return' then
            rr = subword(rr,2)
        gen = gen  || rr
        end
endProcedure oMetRec

classInheritsOf: procedure expose m.
parse arg cl, sup
    cl = class4name(cl)
    sup = class4name(sup)
    if m.cl.inheritsOf \== 1 then do
        m.cl.inheritsOf = 1
        call classInheritsOfAdd cl, cl'.INHERITSOF'
        end
    return m.cl.inheritsOf.sup == 1
endProcedure classInheritsOf

classInheritsOfAdd: procedure expose m.
parse arg cl, trg, pa
    pa = classCycle(cl, pa)
    m.trg.cl = 1
    call assert "m.cl == 'u'"
    do cx=1 to m.cl.0
        c1 = m.cl.cx
        if m.c1 == 'u' then
            call classInheritsOfAdd c1, trg, pa
        end
    return
endProcedure classInheritsOf

classClear: procedure expose m.
parse arg cl, m
    if m.cl.flds_self then
        m.m = m.cl.flds_null.1
    do fx=1+m.cl.flds_self to m.cl.flds.0
        f1 = m.cl.flds.fx
        m.m.f1 = m.cl.flds_null.fx
        end
    if m.cl.stms_self then
        m.m.0 = 0
    do sx=1+m.cl.stms_self to m.cl.stms.0
        s1 = m.cl.stms.sx
        m.m.s1.0 = 0
        end
    return m
endProcedure classClear

classCopy: procedure expose m.
parse arg cl, m, t
    if t == '' then
        t = oMutate(mNew(cl), cl)
    else
        call oMutate t, cl
    if m.cl.flds_self then
        m.t = m.m
    do fx=1+m.cl.flds_self to m.cl.flds.0
        f1 = m.cl.flds.fx
        m.t.f1 = m.m.f1
        end
    if m.cl.stms_self then
        call classCopyStem m.cl.stms., m, t
    do sx=1+m.cl.stms_self to m.cl.stms.0
        s1 = m.cl.stms.sx
        call classCopyStem m.cl.s2c.s1, m'.'s1, t'.'s1
        end
    return t
endProcedure classCopy

classCopyStem: procedure expose m.
parse arg cl, m, t
    m.t.0 = m.m.0
    do sx=1 to m.t.0
        call classCopy cl, m'.'sx, t'.'sx
        end
    return 0
endProcedure classCopyStem

/*--- return true if src is a rexxVariable a, m.a.c etc. -------------*/
rxIsVar: procedure expose m.
parse arg src
    if pos(left(src, 1), m.ut_rxN1) > 0 then
        return 0
    else
        return verify(src, m.ut_rxId) = 0
endProcedure rxIsVar

/*--- return true if src is a rexxConstant rerpresenting its value ---*/
rxIsConst: procedure expose m.
parse arg src, vars                                                   c
    if \ rxIsVar(src) then
        return 0
    srU = translate(src)
    if srU \== src then
        return 0
    srU = '.'srU'.'
    if pos('.GG', srU) > 0 then
        return 0
    if vars == '' then
        return 1
    upper vars
    do vx=1 to words(vars)
        if pos('.'word(vars, vx)'.', vars) > 0 then
            return 0
        end
    return 1
endProcedure rxIsConst

/*--- return rexx code m.cc or mGet('cc') ----------------------------*/
rxMGet: procedure expose m.
parse arg v1, cc, vars
    if cc == '' then
        return 'm.'v1
    else if rxIsConst(cc, vars) then
        return 'm.'v1'.'cc
    else
        return 'mGet('v1 || quote('.'cc, "'")')'
endProcedure rxMGet

/*--- oCopy for classW ----------------------------------------------*/
oCopyW: procedure expose m.
trace ?r
parse arg m, t
    if t == '' then
        return m
    m.t = o2String(m)
    return oMutate(t, m.class_V)
endProcedure oCopyW

/*--- 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_V then
       call out p1 || m.m
   else if c == m.class_W then
       call out p1 || o2String(m)
   else
       call classOutDone c, m, pr, p1
   return
endProcedure objOut

/*--- recursively output (with outX:) object a with class t ---------*/
classOut: procedure expose m.
parse arg t, a, pr, p1
    return classOutDone(if(t=='',m.class_O, 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 outX(p1'done :'className(t) '@'a)
    done.t.a = 1
    if t = m.class_O then do
        if a == '' then
            return outX(p1'obj null')
        t = objClass(a, '')
        if t = '' then
            return outX(p1'obj has no class @'m.a)
        else
            return classOutDone(t, a, pr, p1'isA')
        end

    if t == m.class_V then
        return outX(p1'=' m.a)
    if t == m.class_W == 'w' then
        return outX(p1'}' substr(a, 2))
    if m.t == 'f' then
        return classOutDone(m.t.1, a'.'m.t.name, pr, p1'.'m.t.name)
    if m.t == 'r' then do
        if m.a == '' then
            return outX(p1'refTo :'className(m.t.1) '@null@')
        else
            return classOutDone(m.t.1, 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_V
        call outX 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 outX p1'stem' m.a.0
        do ux=1 to m.a.0
            call classOutDone m.t.1, 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.1, 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

/*--- create an an object of the class className
        and call it's new method ------------------------------------*/
oNew: procedure expose m.
parse arg cl, arg, arg2, arg3
    interpret classMet(class4name(cl), 'new')
endProcedure oNew

/*--- return the class of object obj --------------------------------*/
objClass: procedure expose m.
parse arg m
    if symbol('m.o.o2c.m') == 'VAR' then
         return m.o.o2c.m
    else if abbrev(m, m.o_escW) then
         return m.class_W
    else if arg() >= 2 then
        return arg(2)
    else
        return err('no class found for object' m)
endProcedure objClass

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

/*--- return the code of method me of object m or String or Null
         set m to the address and ggClass to the class ---------------*/
objMet: procedure expose m. m ggClass
parse arg m, me
    if symbol('m.o.o2c.m') == 'VAR' then
        ggClass = m.o.o2c.m
    else if abbrev(m, m.o_escW) then
        ggClass = m.class_w
    else if m \== '' then
        ggClass = m.class_S
    else
        ggClass = m.class_N
    if symbol('m.ggClass.method.me') == 'VAR' then
        return m.ggClass.method.me
    else
        return classMet(ggClass, me)
endProcedure objMet

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

oClear: procedure expose m.
parse arg m
    interpret objMet(m, 'oClear')
    return m
endProcedure oClear

/*--- copy object m of class cl to t --------------------------------*/
oClaCopy: procedure expose m.
parse arg cl, m, t
    interpret "drop cl;" classMet(cl, 'oCopy')
endProcedure oClaCopy

/*--- copy object m to t / create a new object if t=='' -------------*/
oCopy: procedure expose m.
parse arg m, t
    interpret objMet(m, 'oCopy')
endProcedure oCopy

/*--- return a new instance of a subclass of Run
        with code code in method oRun -------------------------------*/
oRunner: procedure expose m.
parse arg code
    return oNew(classNew('n* ORun u ORun, m oRun' code))
endProcedure oRunner

/*--- 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 of object m No Procedure:
        ??? optimize: class only run ???
         use from caller unprotected---------------------------------*/
oRunNP: procedure expose m.
    interpret objMet(arg(1), 'oRun')
    return
endProcedure oRunNP

/*--- run method oRun and return output in new JBuf ------------------*/
oRun2File: procedure expose m.
parse arg rn
    b = jBuf()
    call pipe '+F' , b
    call oRun rn
    call pipe '-'
    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 arg() = 1 then
        fmt = ' '
    interpret objMet(m, 'o2String')
    return err('o2String did not return')
endProcedure o2String

/*--- 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_V then
        l = m.m
    else if cl == m.class_W then
        l = substr(m, 2)
    else if cl == '?' then
        l = '@'m 'class=???'
    else do
        l = '@'m 'class='className(cl)
        ff = oFlds(m)
        do fx=1 to m.ff.0 while length(l) < maxL + 3
            if m.ff.fx == '' then
                 l = l', .='m.m
            else do
                 f1 = m.ff.fx
                 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.o_escW || str
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_V then
        return = m.m
    else if cl == m.class_W 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 address (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' (cu (',' cu)*)?
    cu = ce | c1* '%' c1* '%'? name+      (same type for each name)

    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
    m.o_escW = '!'
    call mapIni
    m.class.0 = 0
    call mapReset class_n2c  /* name to class */
    m.class_V = classNew('n v u', 'm',
                , "oCopy return oCopyV(m, t)" ,
                , "o2String return m.m",
                , "o2File return file(m.m)")
    m.class_W = classNew('n w u', 'm' ,
                , "o2String return substr(m, 2)" ,
                , "o2File return file(substr(m,2))")
    m.class_O = classNew('n o u')
    m.class_R = classNew('r')

    m.class_C = classNew('n class u')
    call classNew 'n= class u v' ,
            , 'c u u f NAME v',           /* union or class */
            , 'c f u f NAME v',           /* field          */
            , 'c s u' ,                   /* stem           */
            , 'c c u f NAME v',           /* choice         */
            , 'c r u' ,                   /* reference      */
            , 'c m u f NAME v, f MET  v'  /* method         */
    call mAdd m.class_C, classNew('s r class')
    m.class_root = classNew('n root u', 'm',
           , "f2c    ?l" ,
           , "f2x    ?l" ,
           , "oFlds  ?l" ,
           , "s2c    ?l" ,
           , "stms   ?l" ,
           , "in2Str ?r return ?o2String" ,
           , "in2File ?r return ?o2File" ,
           , "in2Buf ?r return jBufCopy(?o2File)" ,
           , "scanSqlIn2Scan ?r" ,
                   "return scanSqlReset(s, ?in2File, wOpt, sOpt)")
    m.class_S = classNew('n String u', 'm',
           , 'in2Str return m' ,
           , 'in2File return jBuf(m)',
           , 'in2Buf return jBuf(m)',
           , "scanSqlIn2Scan ?r if wOpt == '' then wOpt = 0;" ,
                   "return scanSqlReset(s, ?in2File, wOpt, sOpt)")
    m.class_N = classNew('n Null u', 'm',
           , 'in2Str return o2String(m.j.in, fmt)',
           , 'in2File return m.j.in',
           , 'in2Buf return jBufCopy(m.j.in)')
    call 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)'
    return
endProcedure classIni

classNe1: procedure expose m.
parse arg n, ty, nm, refs, io
    ky = ty','nm','space(refs, 1)','strip(io)
    if ty == 'f' & abbrev('=', nm) then do
        if words(refs) = 1 & io == '' then
            return strip(refs)
        else
            call err 'bad field name:' ky
        end
    if n then
        if symbol('m.class_k2c.ky') == 'VAR' then
            return m.class_k2c.ky
    m.class.0 = m.class.0 + 1
    n = 'CLASS.'m.class.0
    call mapAdd class_n2c, n, n
    m.n = ty
    m.n.name = nm
    m.n.met = strip(io)
    m.n.0 = words(refs)
    do rx=1 to m.n.0
        m.n.rx = mapGet(class_n2c, word(refs, rx))
        end
    if right(nm, 1) == '*' then
        nm = left(nm, length(nm)-1)substr(n, 7)
    if length(ty) \== 1 | pos(ty, 'ufscrm') < 1 then
        call err 'bad type' ty': classNe1('ky')' /0
    else if nm == '' & pos(ty, 'm') > 0 then
        call err 'empty name: classNe1('ky')'
    else if nm \== '' & ty \== 'c' & verifId(nm) > 0 then
        call err 'bad name' nm': classNe1('ky')'
    else if nm \= '' & pos(ty, 'rs') > 0 then
        call err 'name for type' ty': classNe1('ky')'
    else if (pos(ty, 'fcsr') > 0 & m.n.0 \== 1) ,
          | (    ty == 'm' & m.n.0 \== 0) then
        call err m.n.0 'bad ref count in classNe1('ky')'
    return n
endProcedure classNe1

classNew: procedure expose m.
parse arg clEx 1 ty rest
    n = ''
    nm = ''
    io = ''
    refs = ''
    if wordPos(ty, 'n n? n* n=') > 0 then do
        nmTy = right(ty, 1)
        parse var rest nm ty rest
        if nmTy = '=' then do
            if \ mapHasKey(class_n2c, nm) then
                call err 'class' nm 'not defined: classNew('clEx')'
            n = mapGet(class_n2c, nm)
            end
        if ty \== 'u' then
            call err 'class name' nm 'without u: classNew('clEx')'
        if nmTy == '?' then do
            if mapHasKey(class_n2c, nm) then
                return mapGet(class_n2c, nm)
            end
        else if nmTy == '*' & arg() == 1 then do
            if mapHasKey(class_n2c, clEx) then
                return mapGet(class_n2c, clEx)
            end
        end
    else do
        nmTy = ''
        if arg() == 1 then
            if mapHasKey(class_n2c, clEx) then
                return mapGet(class_n2c, clEx)
        if length(ty) <> 1 | pos(ty, 'ufscmr') < 1 then
            return err('bad type' ty': classNew('clEx')')
        if pos(ty, 'fcm') > 0 then
            parse var rest nm rest
        if ty == 'm' then
            io = rest
        else if pos(ty, 'fsc') > 0 | (ty == 'r' & rest \== '') then
            refs = classNew(strip(rest))
        else if ty == 'r' then
            refs = m.class_O
        end
    if ty == 'u' then do
        lx = 0
        do while lx < length(rest)
            t1 = word(substr(rest, lx+1), 1)
            cx = pos(',', rest, lx+1)
            if cx <= lx | t1 == 'm' then
                cx = length(rest)+1
            one = strip(substr(rest, lx+1, cx-lx-1))
            lx=cx
            if pos('%', word(one, 1)) < 1 then
                refs = refs classNew(one)
            else do
                parse value translate(word(one, 1), ' ', '-') ,
                      with wBe '%' wAf '%' ww
                do wx=2 to words(one)
                    refs = refs classNew(wBe word(one, wx) wAf)
                    end
                end
            end
        pref = ''
        do ax=2 to arg()
            if length(arg(ax)) == 1 & arg(ax) \== ' ' then
                pref = arg(ax)' '
            else
                refs = refs classNew(pref || arg(ax))
            end
        end
    if nmTy == '=' then do
        if m.n \== ty | ty \== 'u' then
            call err 'n= mismatch'
        do ux=1 to words(refs)
            call mAdd n, word(refs, ux)
            end
        end
    else if nmTy == '*' then
        n = classNe1(0, ty, nm'*', refs, io)
    else
        n = classNe1(nmTy == '', ty, nm, refs, io)
    if arg() == 1 then
        call mapAdd class_n2c, clEx, n
    if nmTy == '*' & m.n.name == nm'*' then
        m.n.name = nm || substr(n, 6)
    if nmTy \== '' & nmTy \== '=' then
       call mapAdd class_n2c, m.n.name, n
    return n
endProcedure classNew

/*--- return the name of a class if it exists otherwise the class ---*/
className: procedure expose m.
parse arg cl
    if \ mapHasKey(class_n2c, cl) then
        return 'notAClass:' cl
    c2 = mapGet(class_n2c, cl)
    if m.c2 = 'u' & m.c2.name \= '' then
        return m.c2.name
    else
        return cl
endProcedure className

/*--- 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

/*--- mutate object m to the class cl -------------------------------*/
oMutate: procedure expose m.
parse arg m, cl
    m.o.o2c.m = cl
    return m
endProcedure oMutate

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

/*--- find the code for a met in a class ---------------------------*/
classMet: procedure expose m.
parse arg cl, met
    if symbol('m.cl.method.met') == 'VAR' then
        return m.cl.method.met
    if symbol('m.class_n2c.cl') \== 'VAR' then
        call err 'no class classMet('cl',' met')'
    if cl \== m.class_n2c.cl then
        return classMet(m.class_n2c.cl, met)
    if symbol('m.cl.metLazy.met') == 'VAR' then do
         parse var m.cl.metLazy.met l1 lRest
         m.cl.method.met = "call err 'building lazy method" cl"#"me"'"
         if l1 == '?r' then
             m.cl.method.met = oMetRec(cl, met, lRest)
         else if l1 \== '?l' then
             call err 'implement' l1 'for lazy' className(cl)'#'met
         else if met = 'oFlds' then do
             m.cl.flds.0 = 0
             m.cl.flds_self = 0
             m.cl.stms.0 = 0
             m.cl.stms_self = 0
             call classFldAdd cl, cl
             m.cl.method.met = cl'.FLDS'
             end
         else if wordPos(met, 'f2c f2x stms s2c') > 0 then do
             call classMet cl, 'oFlds'
             if met == 'f2x' then
                 call mInverse cl'.FLDS', cl'.F2X'
             m.cl.method.met = cl'.'translate(met)
             end
         else
             m.cl.method.met = oMetLazy(cl, met, lRest)
         return m.cl.method.met
         end
    if m.cl.methods == 1 then
        return err('no method' met 'in class' className(cl))
    m.cl.methods = 1
    call classMetGen m.class_root, cl
    call classMetGen cl, cl
    return classMet(cl, met)
endProcedure classMet

classCycle: procedure expose m.
parse arg cl, pa
    if wordPos(cl, pa) < 1 then
        return pa cl
    call err implement cycle
endProcedure classCycle

classMetGen: procedure expose m.
parse arg cl, trg, pa
    pa = classCycle(cl, pa)
    if m.cl == 'm' then do
        mthd = m.cl.name
        drop m.trg.method.mthd m.trg.metLazy.mthd
        if \ abbrev(m.cl.met, '?') then
            m.trg.method.mthd = m.cl.met
        else
            m.trg.metLazy.mthd = m.cl.met
        end
    else if m.cl == 'u' then do
        do cx=1 to m.cl.0
            if wordPos(m.cl.cx,
                , m.class_V m.class_W m.class_O m.class_R) < 1 then
                call classMetGen m.cl.cx, trg, pa
            end
        end
    else if pos(m.cl, 'fscr') < 1 then
        call err 'bad cla' cl m.cl
    return
endProcedure classMetGen

classFlds: procedure expose m.
parse arg cl
    return classMet(cl, 'oFlds')
endProcedure classFlds

/*--- add the the fields of class cl to stem f ----------------------*/
classFldAdd: procedure expose m.
parse arg f, cl, nm, pa
    pa = classCycle(cl, pa)
    if cl == m.class_V | cl == m.class_W | cl == m.class_O ,
             | m.cl == 'r' then
             return classFldAdd1(f'.FLDS', f'.F2C', cl, nm,
                  , if(cl=m.class_W, m.o_escW, ''))
    if m.cl = 's' then do
        if m.cl.1 == '' then
            call err 'stem null class'
        return classFldAdd1(f'.STMS', f'.S2C', m.cl.1, nm, 0)
        end
    if m.cl = 'f' then
        return classFldAdd(f, m.cl.1, nm ,
          || left('.', m.cl.name \== '' & nm \== '') || m.cl.name, pa)
    do tx=1 to m.cl.0
        call classFldAdd f, m.cl.tx, nm, pa
        end
    return 0
endProcedure classFldAdd

classFldAdd1: procedure expose m.
parse arg fa, f2, cl, nm, null
    if symbol('m.f2.nm') == 'VAR' then
        if m.f2.nm == cl then
            return 0
        else
            return err('f2 mismatch f2='f2 Nm 'cl='cl 'old='m.f2.nm)
    m.f2.nm = cl
    if nm == '' then do
        call mMove fa, 1, 2
        m.fa.1 = ''
        call mPut fa'_SELF', 1
        end
    else do
        call mAdd fa, nm
        end
    return 0
endProcedure classFldAdd1
/* copy class 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.ut_alfDot, '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 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 = mapAdr(a, ky, 'a')
    if vv == '' then
        return err('duplicate in mapAdd('a',' ky',' val')')
    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 = mapAdr(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 mapAdr(a, ky, 'g') \== ''
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 =  mapAdr(a, ky, 'g')
    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 = mapAdr(a, ky, 'g')
    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 = 247 - 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 = mapAdr(a, ky, 'g')
            if adr \== '' then do
                ha = left(adr, length(adr) - 2)
                do i = 1 to m.ha.0
                     vv = ha'v'i
                     drop m.ha.i m.vv
                     end
                 drop m.ha.0
                 end
            end
        end
    m.st.0 = 0
    return a
endProcedure mapClear

/*--- return the value pointer for a key, '' if non existing
    f = 'g' return address if exists otherwise ''
        'p' return address if exists otherwise newly added address
        'a' return ''      if exists otherwise newly added address ---*/
mapAdr: procedure expose m.
parse arg a, ky, f
    if length(ky) + length(a) < 247 then do
        res = a'.'ky
        if symbol('m.res') == 'VAR' then
            return copies(res, f \== 'a')
        else if f == 'g' then
            return ''
        end
    else do
        len = length(ky)
        q = len % 2
        ha = a'.'len || left(ky, 80) || substr(ky,
            , len % 2 - 40, 85-length(len)-length(a)) || right(ky, 80)
        if symbol('M.ha.0') == 'VAR' then do
            do i=1 to m.ha.0
                if m.ha.i == ky then
                    return copies(ha'v'i, f \== 'a')
                end
            end
        else do
            i = 1
            end
        if f == 'g' then
            return ''
        if i > 9 then
            call err 'overflow long key' y 'in map' a 'hash' ha'.K.'i
        m.ha.0 = i
        m.ha.i = ky
        res = ha'v'i
        end
    if m.map_keys.a \== '' then
        call mAdd m.map_keys.a, ky
    return res
endProcedure mapAdr

/* copy map 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>.** and m.<mbr>_**: every rexx Module (copy) should only
               allocate these addresses to avoid address conficts
               with <mbr> the name of therexx 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
    ax = m.m_area.0 + 1
    m.m_area.0 = ax
    m.m_area.ax = nm
    if adr == '=' then
        adr = nm
    else if adr == '' then
        adr = 'M.'ax
    if symbol('m.m_2a.nm') == 'VAR' then
        call err 'area name' nm 'already used'
    if symbol('m.m_2a.adr') == 'VAR' then
        call err 'adr' adr 'for area' nm 'already used'
    m.m_2a.adr = adr
    m.m_2a.nm  = adr
    m.adr.0 = 0
    m.m_free.adr.0 = 0
    return nm
endProcedure mNewArea

mNew: procedure expose m. ggArea
parse arg name
    if symbol('m.m_2a.name') \== 'VAR' then
        call err 'area' name 'does not exists'
    adr = m.m_2a.name
    if m.m_free.adr.0 > 0 then do
        fx = m.m_free.adr.0
        m.m_free.adr.0 = fx-1
        return m.m_free.adr.fx
        end
    m.adr.0 = m.adr.0 + 1
    return adr'.'m.adr.0
endProcedure mNew

mFree: procedure expose m.
parse arg m
    adr = left(m, lastPos('.', m)-1)
    fx = m.m_free.adr.0 + 1
    m.m_free.adr.0  = fx
    m.m_free.adr.fx = m
    return ''
endProcedure mFree
/*--- iterate over all allocate elements of an area ------------------*/
mIterBegin: procedure expose m.
parse arg nm
    return m.m_2a.nm'.0'
endProcedure mIterBegin

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

/*--- get m.a --------------------------------------------------------*/
mGet: procedure expose m.
parse arg a
    return m.a
endProcedure mGet

/*--- put value v into m.a -------------------------------------------*/
mPut: procedure expose m.
parse arg a, v
    m.a = v
    return v
endProcedure mPut

/*--- 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

/*--- create the inverse map of a stem -------------------------------*/
mInverse: procedure expose m.
parse arg a, i
    do x=1 to m.a.0
        v = m.a.x
        m.i.v = x
        end
    return m.a.0
endProcedure inverse

/*--- add to m.dst.* a (sub)sequence of m.src.* ----------------------*/
mAddSt: procedure expose m.
    parse arg dst, src, fx , tx
    dx = m.dst.0
    if fx == '' then
        fx = 1
    if tx == '' then
        tx = m.src.0
    do sx = fx to tx
        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
    return mCatFT(st, 1, m.st.0, fmt)

mCatFT: procedure expose m.
parse arg st, fx, tx, fmt
    f2 = fmt'%#?a%c%#? %#?e%# %& %&a'
    if tx < fx then
        return f(f2'%##e')
    res = f(f2'%##a', m.st.fx)
    do sx=fx+1 to tx
        res = res || f(f2, m.st.sx)
        end
    return res
endProcedure mCatFT

fGenCat: procedure expose m.
parse arg s, ax
    do fx=1 ??????? until \ scanLit(s, '%,')
        f.fx = fGen(s)
        end
    if \ scanLit(s, '%)') then
        call scanErr s, 'no %) after @fGenCat%('
    if \ scanEnd(s) then
        call scanErr s, 'mGenCat not at end'
    if fx < 2 | f.2 == "''" then
        f.2 = fGen(scanSrc(f_u, '%c'))
    if fx < 3 then
        f.3 = "''"
    if fx < 4 then
        f.4 = "''"
    adr = m.s.src'%'
    if f.1 == "''" then
        m.f_gen.adr.1 = 'return' f.2
    else
        m.f_gen.adr.1 = 'return' f.1 '||' f.2
    m.f_gen.adr.2 = 'return' f.4
    if f.3 == "''" then
        return f.2
    else
        return f.3 '||' f.2
endProcedure fGenCat

mIni: procedure expose m.
    if m.m_ini == 1 then
        return
    m.m_ini = 1
    call utIni
    m.mBase64 = m.ut_alfUC || m.ut_alfLc || m.ut_digits'+-'
    m.m_area.0 = 0
    call mNewArea
    return
endProcedure mIni

/* copy m end *********************************************************/
/* copy fTab begin *****************************************************
    output Modes: t = tableMode 1 line per object
                  c = colMode   1 line per column/field of object

    lifeCycle fTab           sql

        fTabReset            sqlFTabReset
        fTabAdd    *         sqlFTabAdd *
                             sqlFTabOthers ?
        fTabGenerate
        fTabBegin                                      header lines
        fTab1 * / tTabCol *
        fTabEnd                                        trailer lines
***********************************************************************/
fTabIni: procedure expose m.
    if m.fTab_ini == 1 then
        return
    m.fTab_ini = 1
    call classIni
    m.fTab_class = classNew("n FTab u ORun, m oRun call fTab m")
    return
endProcedure fTabIni

fTabReset: procedure expose m.
parse arg m, m.m.titBef, m.m.titAft, m.m.opt
    call fTabIni
    if m.m.titBef == '' & m.m.titaft == '' then do
        m.m.titBef = 'c 1'
        m.m.titAft = '1 c'
        end
    if m.m.titBef == '-' then
        m.m.titBef = ''
    if m.m.titAft == '-' then
        m.m.titAft = ''
    m.m.generated = ''
    m.m.0 = 0
    m.m.len = 0
    m.m.cols = ''
    m.m.sqlOthers = 1
    m.m.set.0 = 0
    return oMutate(m, m.fTab_class)
endProcedure fTabReset

/* add a piece to title tx at current pos */
fTabAddTit: procedure expose m.
parse arg m, cx, tx, t1
    m.m.generated = ''
    if ty < m.m.cx.tit.0 then do
        do xx=m.m.cx.tit.0+1 to tx-1
            m.m.cx.tit.xx = ''
            end
        m.m.cx.tit.0 = tx
        end
    m.m.cx.tit.tx = t1
    return m
endProcedure fTabAddTit

/*--- set the infos for one column -----------------------------------*/
fTabSet: procedure expose m.
parse arg m, c1 aDone, f1, l1
    sx = m.m.set.0 + 1
    m.m.set.0 = sx
    m.m.set.sx = c1 aDone
    m.m.set.sx.fmt = f1
    m.m.set.sx.labelCy = l1
    m.m.set.sx.labelTi = c1
    m.m.set.c1 = sx
    return
endProcedure fTabSet

fTabAdd: procedure expose m.     /* old interface, new is ...RCT */
parse arg m, c1Done, f1, l1
    call fTabAddRCT m, c1Done, f1, , l1
    ox = m.m.0
    m.m.ox.tit.0 = max(arg()-3, 1)
    do tx=2 to m.m.ox.tit.0
        m.m.ox.tit.tx = arg(tx+3)
        end
    return
endProcedure fTabAdd

fTabAddRCT: procedure expose m.
parse arg m, rxNm aDone, f1, cyNm, tiNm
    cx = m.m.0 + 1
    m.m.generated = ''
    m.m.0 = cx
    m.m.cx.tit.0 = max(arg()-4, 1)
    m.m.cx.tit.1 = ''
    do tx=2 to m.m.cx.tit.0
        m.m.cx.tit.tx = arg(tx+4)
        end
    r1 = rxNm
    if rxNm == '' then
        r1 = '='
    else if rxNm == '=' then
        rxNm = ''
    m.m.cols = m.m.cols r1
    if words(m.m.cols) <> cx then
        call err 'mismatch of column number' cx 'col' rxNm / 0
    if length(aDone) > 1 | wordPos('<'aDone'>', '<> <0> <1>') < 1 then
        call err 'bad done' length(aDone) '<'aDone'> after rxNm' rxNm
    m.m.cx.col = rxNm
    m.m.cx.done = aDone \== 0
    if cyNm == '' then
        m.m.cx.labelCy = r1
    else
        m.m.cx.labelCy = cyNm
    if tiNm == '' then
        m.m.cx.labelTi = m.m.cx.labelCy
    else
        m.m.cx.labelTi = tiNm
    px = pos('%', f1)
    ax = pos('@', f1)
    if px < 1 | (ax > 0 & ax < px) then
        m.m.cx.fmt = f1
    else
        m.m.cx.fmt = left(f1, px-1)'@.'rxNm || substr(f1, px)
    return m
endProcedure fTabAddRCT

fTabGenerate: procedure expose m.
parse arg m, sep
    f = ''
    tLen = 0
    m.m.tit.0 = words(m.m.titBef m.m.titAft) + 5
    do tx=1 to m.m.tit.0
        m.m.tit.tx = ''
        end
    do kx=1 to m.m.0
       rxNm = m.m.kx.col
       call mPut 'F_TEMP'left('.', rxNm\=='')rxNm, m.m.kx.labelTi
       t1 = f(m.m.kx.fmt, 'F_TEMP')
       m.m.kx.len = length(t1)
       if pos(strip(t1), m.m.kx.labelTi) < 1 then
           t1 = left(left('', max(0, verify(t1, ' ') -1)) ,
                 || m.m.kx.labelTi, length(t1))
       m.m.kx.tit.1 = t1
       if kx = 1 then do
           f = m.m.kx.fmt
           end
       else do
           tLen = tLen + length(sep)
           f = f || sep || m.m.kx.fmt
           end
       m.m.kx.start = tLen+1
       do tx=1 to m.m.kx.tit.0
           if m.m.kx.tit.tx \== '' then
               if tx > 1 | pos('-', m.m.opt) < 1 then
                   m.m.tit.tx = left(m.m.tit.tx, tLen) ,
                       || strip(m.m.kx.tit.tx, 't')
               else if \ abbrev(m.m.kx.tit.tx, ' ') then
                   m.m.tit.tx = left(m.m.tit.tx, tLen, '-') ,
                       || strip(m.m.kx.tit.tx, 't')
               else
                   m.m.tit.tx = left(m.m.tit.tx, tLen, '-') ,
                          || right(strip(m.m.kx.tit.tx),
                                , length(m.m.kx.tit.tx), '-')
           end
       tLen = tLen + m.m.kx.len
       end
    m.m.len = tLen
    if pos('-', m.m.opt) > 0 then
        m.m.tit.1 = left(m.m.tit.1, tLen +3, '-')
    m.m.fmt = fCache('%.', f)

    cSta = m.m.tit.0+3          /* compute cycle titles */
    cycs = ''
    cyEq = 1
    do cEnd=cSta until kx > m.m.0
        cycs = cycs cEnd
        cx = cSta
        firstRound = 1
        do kx=1 to m.m.0
            if firstRound then
                m.m.tit.cx =  left('', m.m.kx.start-1)m.m.kx.labelCy
            else if length(m.m.tit.cx) <= m.m.kx.start - 2 then
                m.m.tit.cx = left(m.m.tit.cx, m.m.kx.start - 1) ,
                             || m.m.kx.labelCy
            else
                leave
            if cyEq then
               cyEq = translate(m.m.kx.labelCy) ,
                    = translate(m.m.kx.labelTi)
            cx = cx + 1
            if cx > cEnd then do
                cx = cSta
                firstRound = 0
                end
            end
        end
    m.m.cycles = strip(cycs)
    if cyEq & words(cycs) <=  1 then
        m.m.cycles = ''
    m.m.generated = m.m.generated't'
    return
endProcedure fTabGenerate

fTabColGen: procedure expose m.
parse arg m
    do kx=1 to m.m.0
        t = m.m.kx.labelTI
        l = if(m.m.kx.labelCy == t, , m.m.kx.labelCy)
        f = lefPad(lefPad(strip(l), 10) t, 29)
        if length(f) > 29 then
           if length(l || t) < 29 then
               f = l || left('', 29 - length(l || t))t
           else
               f = lefPad(strip(l t), 29)
        g = strip(m.m.kx.fmt)
        o = right(g, 1)
        if pos(o, 'dief') > 0 then
            f = f '@'m.m.kx.col'%12e @'m.m.kx.col'%18c'
        else if o = 'C' then
            f = f left(g, length(g)-1)'c'
        else
            f = f g
        m.m.kx.colFmt = f
        end
    m.m.generated = m.m.generated'c'
    return
endProcedure fTabColGen

fTab: procedure expose m.
parse arg m, rdr
    call fTabBegin m
    call fAll m.m.fmt, rdr
    return fTabEnd(m)
endProcedure fTab

fTabCol: procedure expose m.
parse arg m, i
    do cx=1 to m.m.0
        call out f(m.m.cx.colFmt, i)
        end
    return 0
endProcedure fTabCol

fTabBegin: procedure expose m.
parse arg m
    if pos('t', m.m.generated) < 1 then
        call fTabGenerate m, ' '
    return fTabTitles(m, m.m.titBef)
endProcedure fTabBegin

fTabEnd: procedure expose m.
parse arg m
    return fTabTitles(m, m.m.titAft)

fTabTitles: procedure expose m.
parse arg m, list
    list = repAll(list, 'c', m.m.cycles)
    do tx=1 to words(list)
        t1 = word(list, tx)
        call out m.m.tit.t1
        end
    return m
endProcedure fTabTitles

/*--- auto tables detect format from data ---------------------------*/
/*--- detect fmt from a rdr and write it formatted to stdOut --------*/
fTabAuto: procedure expose m.
parse arg m, rdr, wiTi
    if m == '' then
        m = fTabReset(f_auto, 1)
    i = in2Buf(rdr)
    if m.i.buf.0 <= 0 then
        return m
    call fTabDetect m, i'.BUF', wiTi
    return fTab(m, i)
endProcedure fTabAuto

/*--- generate format for all fields of a stem of objects -----------*/
fTabDetect: procedure expose m.
parse arg m, b
    ff = oFlds(m.b.1)
    do fx=1 to m.ff.0
        call fTabAddDetect m, m.ff.fx, b
        end
    return
endProcedure fTabDetect

/*--- generate format for all fields of a stem of objects -----------*/
sqlfTabDetect: procedure expose m.
parse arg m, b
    cx = m.m.sqlX
    ff = m.sql.cx.fetchFlds
    do fx=1 to words(ff)
        call fTabAddDetect m, word(ff, fx), b, m.sql.cx.d.fx.sqlName
        end
    return
endProcedure fTabDetect

/*--- detect format for one field in stem st ------------------------*/
fTabAddDetect: procedure expose m.
parse arg m, c1 aDone, st, cyNm, tiNm
    lMa = -1
    rMa = -1
    bMa = -1
    aDiv = 0
    nMi =  9e999
    nMa = -9e999
    eMi =  9e999
    eMa = -9e999
    eDa = 2
    dMa = -9e999
    suf = left('.', c1 \== '')c1
    do sx=1 to m.st.0
        v = mGet(m.st.sx || suf)
        lMa = max(lMa, length(strip(v, 't')))
        rMa = max(rMa, length(strip(v, 'l')))
        bMa = max(bMa, length(strip(v, 'b')))
        if \ dataType(v, 'n') then do
            if length(v) > 100 then
                aDiv = 99
            else if aDiv <=3 then
                if aDiv.v \== 1 then do
                    aDiv.v = 1
                    aDiv = aDiv + 1
                    end
            iterate
            end
        v = strip(v)
        nMi = min(nMi, v)
        nMa = max(nMa, v)
        ex = verify(v, 'eEfF', 'm')
        if ex > 0 then do
            eMa = max(eMa, substr(v, ex+1))
            eMi = min(eMi, substr(v, ex+1))
            v = left(v, ex-1)
            do while pos(left(v,1), '+-0') > 0
                v = substr(v, 2)
                end
            eDa = max(eDa, length(v) - (pos('.', v) > 0))
            end
        dx = pos('.', v)
        if dx > 0 then do
            do while right(v, 1) == 0
                v = left(v, length(v)-1)
                end
            dMa = max(dMa, length(v)-dx)
            end
        end
    if nMi > nMa | aDiv > 3 then
        newFo = '-'max(1, (lMa+0))'C'
    else if eMi <= eMa then do
        newFo = ' ' || (eDa+max(length(eMa), length(eMi))+3) ,
             || '.'||(eDa-1)'e'
        end
    else do
        be = max(length(trunc(nMi)), length(trunc(nMa)))
        if dMa <= 0 then
            newFo = max(be, bMa)'I'
        else
            newFo = max(be+1+dMa, bMa)'.'dMa'I'
        end
    call fTabAddRCT m, c1 aDone, '%'newFo, cyNm, tiNm
  /*  say c1 '????==> %'newFo */
   return newFo
endProcedure fTabAddDetect

/*--- format with units seconds ==> 3d13 oder 5.43e6 ==> 5m43 --------*/
fTime: procedure expose m.
?????????????? use f(%kd) ????????????????
fDec: procedure expose m.
?????????????? use f(%kd) ????????????????

fUnits: procedure expose m.
parse arg v, scale, aLen, aPrec, plus
    if \ dataType(v, 'n') then do
        f1 = fUnitsF1(v, scale, aLen, length(plus), length(plus), aPrec)
        return right(v, m.f1.len)
        end
    if v >= 0 then
        sign = plus
    else
        sign = '-'

    v = abs(v)  /* always get rid also of sign of -0 | */
    f1 = fUnitsF1(v, scale, aLen, length(plus), length(sign), aPrec)

    do forever
        w = format(v * m.f1.fact, , m.f1.prec)
        if pos('E-', w) > 0 then
            w = format(0, , m.f1.prec)
        if w < m.f1.lim2 then do
            if m.f1.kind == 'r' then
                x = sign || w || m.f1.unit
            else if m.f1.kind == 'm' then
                x = sign || (w % m.f1.mod) || m.f1.unit ,
                    || right(w // m.f1.mod, m.f1.len2, 0)
            else
                call err 'bad kind' m.f1.kind 'in f1' f1
            if length(x) <= m.f1.len then
                return right(x, m.f1.len)
            end
        if m.f1.next == '' then
            return left(sign, m.f1.len, '+')
        f1 = m.f1.next
        end
endProcedure fUnits

fUnitsF1: procedure expose m.
parse arg v, scale, len, pLen, sLen, aPrec
    slp = 'F_Unit.'scale'.'len'.'pLen'.'sLen'.'aPrec
    if symbol('m.slp.0') \== 'VAR' then do
        sc = 'F_Unit.'scale
        if symbol('m.sc.0') \== 'VAR' then do
            call fUnitsF1Ini1
            if symbol('m.sc.0') \== 'VAR' then
                call err 'bad scale' sc
            end

        if scale = 'd' | scale = 'b' then do
            if aPrec == '' then
                aPrec = 0
            if len = '' then
                len = aPrec + (aPrec >= 0) + 4 + pLen
            dLen = len - sLen
            l2 = '1e' || (dLen - aPrec - (aPrec > 0))
            call fUnitsF1I0 slp, 'nn', 'nn', , , , len
            do x=m.sc.min to m.sc.0
                si = fUnitsF1I0(slp, x, m.sc.x.kind, m.sc.x.unit,
                          , m.sc.x.fact, l2, len)
                m.si.lim1 = m.si.lim2 / m.si.fact
                m.si.prec = aPrec
                m.si.next = slp'.' || (x+1)
                end
            if aPrec > 0 then do
                y = x-1
                si = fUnitsF1I0(slp, x, m.sc.y.kind, m.sc.y.unit,
                          , m.sc.y.fact, ('1e' || dLen), len)
                m.si.lim1 = m.si.lim2 / m.si.fact
                m.si.prec = 0
                end
            end
        else if scale = 't' then do
            if len = '' then
                len = 5 + pLen
            dLen = len - sLen
            call fUnitsF1I0 slp, 'nn', 'nn', , , , len
            do x=m.sc.min to m.sc.0
                si = fUnitsF1I0(slp, x, m.sc.x.kind, m.sc.x.unit,
                          , m.sc.x.fact, m.sc.x.lim2, len ,
                          , m.sc.x.mod, m.sc.x.len2)
                if x = m.sc.0 - 1 then
                    m.si.lim2 = '24e' || (dLen-3)
                else if x = m.sc.0 then
                    m.si.lim2 = '1e' || (dLen-1)
                m.si.lim1 = m.si.lim2 / m.si.fact
                m.si.prec = 0
                m.si.next = slp'.' || (x+1)
                end
            end
        else
            call err implement
        x = m.slp.0
        m.slp.x.next = ''
        end
    if \ datatype(v, 'n') then
        return slp'.nn'
    do q=11 to m.slp.0-1 while v >= m.slp.q.lim1
        end
    if q = 11 & v <> trunc(v) then do
        do q=10 by -1 to m.slp.min+1 while v < m.slp.q.lim1
            end
        q = q + 1
        end
    return slp'.'q
endProcedure fUnitsF1

fUnitsF1Ini1: procedure expose m.
      /*  0    5   10    5   20 */
    iso = '    afpnum kMGTPE   '
    sc = 'F_Unit.d'
    call fUnitsF1i0 sc, 11, 'r', ' ',   1
    f = 1
    do x=1 to 6
        f = f * 1000
        call fUnitsF1i0 sc, 11+x, 'r', substr(iso, 11+x, 1), 1/f
        call fUnitsF1i0 sc, 11-x, 'r', substr(iso, 11-x, 1), f
        end
    sc = 'F_Unit.b'
    f = 1
    do x=11 to 17
        call fUnitsF1i0 sc, x, 'r', substr(iso, x, 1), 1/f
        f = f * 1024
        end
    sc = 'F_Unit.t'
    call fUnitsF1i0 sc, 11, 'm', 's', 100,   6000, , 100, 2
    call fUnitsF1i0 sc, 12, 'm', 'm',   1,   3600, ,  60, 2
    call fUnitsF1i0 sc, 13, 'm', 'h', 1/60,  1440, ,  60, 2
    call fUnitsF1i0 sc, 14, 'm', 'd', 1/3600,    , ,  24, 2
    call fUnitsF1i0 sc, 15, 'r', 'd', 1/3600/24
    return
endProcedure fUnitsF1Ini0

fUnitsF1I0: procedure expose m.
parse arg sc, ix
    si = sc'.'ix
parse arg , , m.si.kind, m.si.unit, m.si.fact,
                , m.si.lim2, m.si.len,
                , m.si.mod, m.si.len2
    if \ datatype(ix, 'n') then
        return si
    if symbol('m.sc.0') \== 'VAR' then do
        m.sc.0   = ix
        m.sc.min = ix
        end
    else do
        m.sc.0   = max(ix, m.sc.0)
        m.sc.min = min(ix, m.sc.min)
        end
    return si
endProcedure fUnitsF1I0
/* copy fTab end   ****************************************************/
/* copy f begin *******************************************************/
/*--- format with the given format ggA1, ggA2, etc. -----------------*/
f: procedure expose m.
parse arg ggFmt, ggA1, ggA2
    if symbol('M.f_gen.ggFmt') \== 'VAR' then
        call fCache ggFmt, ggFmt
    interpret m.f_gen.ggFmt
endProcedure f

fImm: procedure expose m.
parse arg ggFmt, ggA1
    interpret m.ggFmt
endProcedure fImm

/*--- compile format fmt put in the cache with address a ------------*/
fCache: procedure expose m.
parse arg a, fmt
    if a == '%.' then do
        if symbol('m.f_gen0') == 'VAR' then
            m.f_gen0 = m.f_gen0 + 1
        else
            m.f_gen0 = 1
        a = '%.'m.f_gen0
        end
    else if symbol('M.f_gen.a') == 'VAR' then
        return a
    cy = -2
    nm = ' '
    gen = ' '
    opt = 0
    do forever        /* split preprocesser clauses */
        cx = cy+3
        cy = pos('%#', fmt, cx)
        if cy < 1 then
            act = substr(fmt, cx)
        else
            act = substr(fmt, cx, cy-cx)
        do ax=1
            ay = pos('%&', act)
            if ay < 1 then
                leave
            ct = substr(act, ay+2, 1)
            if symbol('f.ct') \== 'VAR' then
                call err 'undefined %&'ct 'in format' fmt
            act = left(act, ay-1) || f.ct || substr(act, ay+3)
            if ax > 100 then
                say 'fGen' fmt nm '==>' act 'actPos' substr(fmt, cx)
            end
        if cy <> 1 & (\ opt | symbol('f.nm') \== 'VAR') then
            f.nm = act
        if cy < 1 | length(fmt) <= cy+1 then
            leave
        nm = substr(fmt, cy+2, 1)
        opt =  nm == '?'
        if pos(nm, '?;#') > 0 then do
            if nm == '#' then do
               if length(fmt) <> cy+3 then
                   call err 'fCache bad %##'nm 'in' fmt
               else if a == fmt then
                   a = left(a, cy-1)
               leave
               end
            cy = cy+1
            nm = substr(fmt, cy+2, 1)
            if nm == ';' then do
               gen = nm
               iterate
               end
            end
        if pos(nm, m.ut_alfa' ') < 1 then
            call err 'fCache bad name %#'nm 'in' fmt
        if pos(nm, gen) < 1 then
            gen = gen || nm
        end
    if symbol('m.f_s_0') \== 'VAR' | m.f_s_0 == 0 then
        m.f_s_0 = 1
    else do
        m.f_s_0 = m.f_s_0 + 1
        f_s = 'F_S_'m.f_s_0
        end
    do cx=1 to length(gen)
        nm = substr(gen, cx, 1)
        act = f.nm
        a2 = a
        if nm == ' ' then
            a2 = a
        else
            a2 = a'%##'nm
        call scanSrc f_s, act
        m.f_gen.a2 = fGen(f_s)
        if \ scanEnd(f_s) then
            call scanErr f_s, "bad specifier '"m.f_s.tok"'"
        end
    m.f_s_0 = m.f_s_0 - 1
    return a
endProcedure fCache

/*--------------------------------------------------------------------
 Format generator    should be compatible with fPrint|
 <<<< + extension of fPrint, - in fPrint but not implemented

   %%  %@ the escaped char
   ('@' argN? '.'? field)?'%' flags? width? ('.' precision)? specifier

 specifier: is the most significant one and defines the type

 - c  Character rigPad or lefPad, prec ==> substr(..., prec)
 -  C Cut %-nC = left(v,n), %nC = right(v,n) %n.mC = substr(m, n)
 - hH Characters in hex
 - iI Signed decimal integer (padded or cut)
 - eE Scientific notation (mantissa/exponent) using e character 3.92e+2
 - S  Strip (both)
 - txy time date formatting from format x to format y see fTstGen
 - kx  units Formatting x = t=time d=decimal b=binary (k) B=binary (kB)
 Flags:
 - -  Left-justify within the given field width; Right is default
 - +  print '+' before non negative numbers
 -' ' print ' ' before non negative numbers
 - /  cut to length

 preprocessor implemented in fCache
%#v   before contents of variable v (1 alfa or 1 space),
      stored at address%##v
%#?v  define variable v if not yet defined
%#;   restart of variables to generate
%&v   use of previously defined variable v
----------------------------------------------------------------------*/
fGen: procedure expose m.
parse arg f_s
    ax = 0
    cd = ''
    cp = ''
    do forever
        txt = fText(f_s)
        if txt \== '' then
            cd = cd '||' quote(txt, "'")
        if scanEnd(f_s) then
            leave
        if \ scanLit(f_s, '@') then do
            ax = ax + 1
            af = ''
            hasDot = 0
            end
        else do
            if scanWhile(f_s, '0123456789') then
                ax = m.f_s.tok
            else if ax < 1 then
                ax = 1
            hasDot = scanLit(f_s, '.')
            af = fText(f_s)
            end
        if \ scanLit(f_s, '%') then
            call scanErr f_s, 'missing %'
        call scanWhile f_s, '-+ /'
        flags = m.f_s.tok
        if scanWhile(f_s, '0123456789') then
            len = m.f_s.tok
        else
            len = ''
        if \ scanLit(f_s, '.') then
            prec  = ''
        else do
            call scanWhile f_s, '0123456789'
            prec = m.f_s.tok
            end
        call scanChar f_s, 1
        sp = m.f_s.tok
        if ax < 3 | ass.ax == 1 then
            aa = 'ggA'ax
        else do
            aa = 'arg(' || (ax+1) || ')'
            if af \== '' then do
                 cp = cp 'ggA'ax '=' aa';'
                 aa = 'ggA'ax
                 ass.ax = 1
                 end
            end
        if af \== '' | hasDot then
            aa = rxMGet(aa, af)
        if sp == 'c' then do
            if prec \== '' then
                aa = 'substr('aa',' prec')'
            if len == '' then
                cd = cd '||' aa
            else if pos('-', flags) > 0 then
                cd = cd '|| lefPad('aa',' len')'
            else
                cd = cd '|| rigPad('aa',' len')'
            end
        else if sp == 'C' then do
            if prec \== '' then do
                cd = cd '|| substr('aa',' prec
                if len == '' then
                    cd = cd')'
                else
                    cd = cd',' len')'
                end
            else if len == '' then
                cd = cd '||' aa
            else if pos('-', flags) > 0 then
                cd = cd '|| left('aa',' len')'
            else
                cd = cd '|| right('aa',' len')'
            end
        else if sp == 'H' then
            cd = cd "|| fH("aa"," len',' (pos('-', flags) > 0)')'
        else if sp == 'h' then
            cd = cd "|| translate(fH("aa", '"siL"'), 'abcdef','ABCDEF')"
        else if sp == 'i' then
            cd = cd "|| fI("aa"," len", '"flags"'," word(prec 0, 1)")"
        else if sp == 'I' then
            cd = cd "|| fI("aa"," len", '/"flags"'," word(prec 0, 1)")"
        else if sp == 'E' | sp == 'e' then do
            if len == '' then
                len = 8
            if prec = '' then
                prec = len - 6
            cd = cd "|| fE("aa"," len"," prec", '"sp"', '"flags"')"
            end
        else if sp = 'S' then
            cd = cd '|| strip('aa')'
        else if sp = 't' then do
            call scanChar f_s, 2
            cd = cd '||' fTstGen(m.f_s.tok, aa)
            end
        else if sp = 'k' then do
            call scanChar f_s, 1
            if pos(m.f_s.tok, 'tdbBiI') < 1 then
                call scanErr f_s, "bad unit type" m.f_s.tok
            if pos('+', flags) > 0 then
                pl = ", '+'"
            else if pos(' ', flags) > 0 then
                pl = ", ' '"
            else
                pl = ''
            cd = cd "|| fUnits("aa", '"m.f_s.tok"'," len"," prec pl")"
            end
  /*    else if sp = '(' then do
            if af == '' | flags \== '' | len \== 0 | prec \== '' then
                call scanErr f_s, "bad call shoud be @sub%("
            interpret "cRes = fGen"af"(f_s, ax)"
            cd = cd '||' cRes
            if \ scanLit(f_s, '%)') then
                if \ scanEnd(f_s) then
                    call scanErr f_s, '%) to end call' af 'expected'
            end     */
        else do
            call scanBack f_s, '%'sp
            leave
            end
        end
    if cd \== '' then
        return strip(cp 'return' substr(cd, 5))
    else
        return "return ''"
endProcedure fGen

fText: procedure expose m.
parse arg f_s
    res = ''
    do forever
        if scanUntil(f_s, '@%') then
            res = res || m.f_s.tok
        if \ scanLit(f_s, '%%', '%@') then
            return res
        res = res || substr(m.f_s.tok, 2)
        end
endProcedure fText

fAll: procedure expose m.
parse arg fmt, rdr
    i = jOpen(in2File(rdr), '<')
    do while jRead(i)
        call out f(fmt, m.i)
        end
    call jClose i
    return
endProcedure fAll

/*--- format character2hex (if not sql null) -------------------------*/
fH: procedure expose m.
parse arg v, l, leftJ
    if v \== m.sqlNull then
        v = c2x(v)
    if length(v) > l then
        return v
    else if leftJ \== 1 then
        return right(v, l)
    else
        return left(v, l)
endProcedure fH

/*--- format integer or fixPoint Decimal -----------------------------*/
fI: procedure expose m.
parse arg v, l, flags, d
    if \ datatype(v, 'n') then
        return fRigLeft(strip(v), l, flags)
    v = format(v, , d, 0)
    if pos('+', flags) > 0 then
        if \ abbrev(v, '-') then
            v = '+'v
    if length(v) > l then
        if pos('/', flags) > 0 then
            return left('', l, '*')
        else
            return v
    return fRigLefPad(v, l, flags)
endProcedure fI

/*--- format with exponent l=total output len
                           d=number of digits after . in mantissa
                           c=exponent character
                           flags: - to ouput text left justified
    differences: exponent is always printed but without +
                 overflow ==> expand exponent, *****, 0e-999 ---------*/
fE: procedure expose m.
parse arg v, l, d, c, flags
    if \ datatype(v, 'n') then
        return fRigLeft(v, l, flags)
    if pos(' ', flags) < 1 then
        if v >=  0 then
            if pos('+', flags) > 0 then
                return '+'substr(fE(v, l, d, c, ' 'flags), 2)
            else
                return substr(fE(v, l+1, d+1, c, ' 'flags), 2)
    x = format(v, 2, d, 7, 0)
    m = 2 + d + (d>0)
    call assert "length(x) == m+9", 'm x length(x)'
    if substr(x, m+1) = '' then
        return left(x, m)c || left('', l-m-1, 0)
    call assert "substr(x, m+1, 1) == 'E'","exponenent in x not at m"
    y = verify(x, '0', 'n',  m+3)
    call assert 'y>0'
    if substr(x, m+1, 2) == 'E+' then do
        if m+10-y <= l-m-1 then
             return left(x,m)c || right(x, l-m-1)
        z = l - 4 - (m+10-y)
        end
    else if substr(x, m+1, 2) == 'E-' then do
        if m+10-y <= l-m-2 then
             return left(x,m)c'-'right(x, l-m-2)
        z = l - 5 - (m+10-y)
        end
    else
        call err 'bad x' x
    if z >= -1 & max(0, z) < d then
        return fE(v, l, max(0, z), c, flags)
    else if substr(x, m+1, 2) == 'E-' then
        return left(x,1)'0'c'-'left('', l-4, 9)
    else
        return left('', l, '*')
endProcedure fE

/*--- right or left with truncation ----------------------------------*/
fRigLeft: procedure expose m
parse arg s, len, flags
    if length(s) = len then
        return s
    else if pos('-', flags) > 0 | length(s) > len then
        return left(s, len)
    else
        return right(s, len)
endProcedure fRigLefPad

/*--- right or left pad without truncation --------------------------*/
fRigLefPad: procedure expose m
parse arg s, len, flags
    if pos('-', flags) > 0 then
        if length(strip(s, 't')) >= len then
            return strip(s, 't')
        else
            return left(s, len)
    else
        if length(strip(s, 'l')) >= len then
            return strip(s, 'l')
        else
            return right(s, len)
endProcedure fRigLefPad

/*--- generate timestamp formats: from format c to format d ----------*/
fTstGen: procedure expose m.
parse arg c 2 d, s
             /* special L = LRSN in Hex
                        l = lrsn (6 or 10 Byte) */

    if c == 'L' then
        return fTstGen('S'd, 'timeLRSN2LZT('s')')
    if c == 'l' then
        return fTstGen('S'd, 'timeLRSN2LZT(c2x('s'))')
    cd = c || d
    if symbol('m.f_tstFo.c') \== 'VAR' ,
         | symbol('m.f_tstFo.d') \== 'VAR' then do
         if m.f_tstIni == 1 then
             call err "bad timestamp from or to format '"cd"'"
        m.f_tstIni = 1
        a = 'F_TSTFO.'
                      /* Y: A = 2010, M:B=Januar,
                         H=Stunde A=0 B=10 C=20
                         jkl = julian day
                         yyyy-mm-dd-hh.mm.ss.ffffff    */
        call mPut a'S', 'yz34-56-78-hi.mn.st.abcdef'
        call mPut a's', 'yz34-56-78-hi.mn.st'
        call mPut a' '
        call mPut a'D', 'yz345678'
        call mPut a'd',   '345678'
        call mPut a't',            'hi.mn.st'
        call mPut a'T',            'hi:mn:st.abcdef'
        call mPut a'E', '78.56.yz34'
        call mPut a'e', '78.56.34'
        call mPut a'Y',      'YM78'
        call mPut a'M',    'M78himns'
        call mPut a'A',    'A8himnst'
        call mPut a'H',           'Himnst'
        call mPut a'n', 'yz345678 hi:mn:st'
        call mPut a'N', 'yz345678 hi:mn:st.abcdef'
        return fTstGen(cd, s)
        end

    if pos('(', s) > 0 | length(s) > 20 then
        return "fImm('F_GEN."fCache('%t'cd, '%t'cd)"'," s")"
    if c == ' ' then do
        if pos(d, 'SN') > 0 then
            o = "Ndate('S') time('L')"
        else if pos(d, 'sMAn ') > 0 then
            o = "ndate('S') time()"
        else if pos(d, 'DdEeY') > 0 then
            o = "Ddate('S')"
        else if pos(d, 'tH') > 0 then
            o = "ttime()"
        else if pos(d, 'T') > 0 then
            o = "Ttime('L')"
        else
            call err 'fTstGen implement d='d
        parse var o o1 2 o2
        return "fImm('F_GEN."fCache('%t'o1 || d, '%t'o1 || d)"',"o2")"
        end
    f = m.f_tstFo.c
    if d = ' ' then
        d = 's'
    t = m.f_tstFo.d
    if f == t then
        return s
    r = "translate("quote(t, "'")"," s "," quote(f, "'")")"

    if pos('yz34', t) > 0 & pos('yz34', f) < 1 then do
        if pos('34', f) > 0 then
            r = "overlay('20'," r"," pos('yz', t)")"
        else if pos('Y', f) > 0 then
            r = fTstGY2Year(r, "substr("s"," pos('Y', f)", 1)", 4,
                , pos('yz34', t))
        else
            r = "overlay('0000'," r"," pos('yz34', t)")"
        end
    else if pos('34', t) > 0 & pos('34', f) < 1 then do
        if pos('Y', f) > 0 then
            r = fTstGY2Year(r, "substr("s"," pos('Y', f)", 1)", 2,
                   , pos('34', t))
        else
            r = "overlay('00'," r"," pos('34', t)")"
        end
    if pos('Y', t) > 0 & pos('Y', f) < 1 then do
        if pos('yz34', f) > 0 then
            r = fTstGYear2Y(r, "substr("s"," pos('yz34', f)", 4)" ,
                   , pos('Y', t))
        else if pos('34', f) > 0 then
            r = fTstGYear2Y(r, "substr("s"," pos('34', f)", 2)" ,
                   , pos('Y', t))
        else
            r = "overlay('?'," r"," pos('Y', t)")"
        end
    if pos('M', t) > 0 & pos('M', f) < 1 then do
        if pos('56', f) > 0 then
            r = fTstG02A(r, "substr("s"," pos('56', f)", 2)" ,
                   , pos('M', t))
        else
            r  = "overlay('?'," r"," pos('M', t)")"
        end
    if pos('56', t) > 0 & pos('56', f) < 1 then
        if pos('M', f) > 0 then
            r = fTstGA2N2(r, "substr("s"," pos('M', f)", 1)" , "-1",
                   , pos('56', t))
        else
            r = "overlay('01'," r"," pos('56', t)")"
    if pos('78', t) > 0 & pos('78', f) < 1 then
        r = "overlay('01'," r"," pos('78', t)")"
    if pos('A8', t) > 0 & pos('A8', f) < 1 then
        if pos('78', f) > 0 then
            r = fTstG02A(r, "substr("s"," pos('78', f)", 1)" ,
                   , pos('A8', t))
        else
            r = "overlay('A1'," r"," pos('A8', t)")"
    if pos('hi', t) > 0 & pos('hi', f) < 1 then do
        if pos('H', f) > 0 then
            r = fTstGA2N1(r, "substr("s"," pos('H', f)", 1)" , "-1",
                   , pos('hi', t))
        else
            r = "overlay('00'," r"," pos('hi', t)")"
        end
    if pos('i', t) > 0 & pos('i', f) < 1 then
        r = "overlay('0'," r"," pos('i', t)")"
    if pos('H', t) > 0 & pos('H', f) < 1 then do
        if pos('hi', f) > 0 then
            r = fTstG02A(r, "substr("s"," pos('hi', f)", 1)" ,
                   , pos('H', t))
        else
            r  = "overlay('A'," r"," pos('H', t)")"
        end
    if pos('mn', t) > 0 & pos('mn', f) < 1 then
        r = "overlay('00'," r"," pos('mn', t)")"
    if pos('st', t) > 0 then do
        if pos('st', f) < 1 then
            if pos('s', f) > 0 then
                r = "overlay('0'," r"," pos('t', t)")"
            else
                r = "overlay('00'," r"," pos('st', t)")"
        end
    else if pos('s', t) > 0 & pos('s', f) < 1 then
        r = "overlay('0'," r"," pos('s', t)")"
    if pos('abcdef', t) > 0 & pos('abcdef', f) < 1 then
        r = "overlay('000000'," r"," pos('abcdef', t)")"
    return r
endProcedure fTstGen

/*--- from Number overlay Character A=0, B=1 etc.. ------------------*/
fTstG02A: procedure expose m.
parse arg r, n, tx
     return "overlay(substr(m.ut_alfUc," n "+ 1, 1)," r"," tx")"

/*--- from year overlay Y-Character  A=2010 etc..--------------------*/
fTstGYear2Y: procedure expose m.
parse arg r, y, tx
     return fTstG02A(r, "("y "+ 10) // 20", tx)

/*--- from character A=0 overlay 1 digit number ---------------------*/
fTstGA2N1: procedure expose m.
parse arg r, y, dlt, tx
     return "overlay(right(pos("y", m.ut_alfUc)" dlt", 1)," r"," tx")"

/*--- from character A=0 overlay 2 digit number ---------------------*/
fTstGA2N2: procedure expose m.
parse arg r, y, dlt, tx
     return "overlay(right('00' || (pos("y", m.ut_alfUc)" dlt"), 2)",
                    "," r"," tx")"

/*--- from Y yearCharacter overlay 2 or 4 digit year ----------------*/
fTstGY2Year: procedure expose m.
parse arg r, y, len, tx
     if m.f_tstY2ini \== 1 then do  /* generate lookup table */
          m.f_tstY2ini = 1
          n = left(f('%t  '), 4)
          do i = n-15 to n+4   /* 20 values: from -15 years to + 4 */
              j = left(f('%tDY', i'0101'), 1)
              m.f_tstY24.j = i
              m.f_tstY22.j = right(i, 2)
           /* say j m.f_tstY22.j m.f_tstY24.j  */
              end
          end
     if wordPos(len, 2 4) < 1 then
         call err "fTstGYear2Y bad len" len
     return "overlay(mGet('F_TSTY2"len".'"y")," r"," tx")"
endProcedure fTstGY2Year

fWords: procedure expose m.
parse arg fmt, wrds
    f2 = fmt'%#?a%c%#? %#?e%# %& %&a'
    if wrds = '' then
        return f(f2'%##e')
    res = f(f2'%##a', word(wrds, 1))
    do wx=2 to words(wrds)
        res = res || f(f2, word(wrds, wx))
        end
    return res
endProcedure fWords

/* copy f end   *******************************************************/
/* copy err begin *** errorhandling, messages, help    ****************/
errIni: procedure expose m.
    if m.err.ini == 1 then
        return
    m.err.ini     = 1
    m.err.handler = ''
    m.err.cleanup = ';'
    m.err.opt     = ''
    parse source m.err.os .
    m.err.ispf    = 0
    m.err.screen  = 0
    if m.err.os \== 'LINUX' then do
        address tso 'profile MsgId'   /* brauchen wir in tsoAlloc| */
        if sysVar('sysISPF') = 'ACTIVE' then do
            m.err.ispf = 1
            address ispExec 'vget zScreen shared'
            m.err.screen = zScreen
            end
        end
    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 & m.err.ispf then
        address ispExec '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 stackHistory
    if ggOpt == '' & m.err.handler \== '' then
        interpret m.err.handler
    call errSay 'f}'ggTxt
    call errCleanup
    if ggOpt == '' then
        ggOpt = m.err.opt
    upper ggOpt
    if pos('T', ggOpt) > 0  then do
        trace ?r
        say 'trace ?r in err'
        end
    if pos('H', ggOpt) > 0 & m.err.eCat == 'f' then
        x = show + stackHistory + by + bad + arithmetic + conversion
    call errSay ' }errorhandler exiting with exit(12)'
    exit errSetRc(12)
endSubroutine err

/*--- set rc for ispf: -------------------------------------------------
    if a cmd is run by ispStart, its RC is ignored,
         but ISPF passes the value of the shared variable zIspfRc
         back as return code
----------------------------------------------------------------------*/
errSetRc: procedure expose m.
parse arg zIspfRc
    if m.err.ispf then
        address ispExec vput 'zIspfRc' shared
    return zIspfRc
endProcedure errSetRc

/*--- error routine: user message cleanup exit -----------------------*/
errAddCleanup: procedure expose m.
parse arg code
    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
    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
    errCleanup = m.err.cleanup
    if errCleanup <> ';' then do
        m.err.cleanup = ';'
        say 'err cleanup begin' errCleanup
        interpret errCleanup
        say 'err cleanup end' errCleanup
        end
    if symbol('m.tso_ddAll') == 'VAR' then
        call tsoFree m.tso_ddAll, 1
    return
endProcedure errCleanup

/*--- say an errorMessage msg with pref and linesplits --------------*/
errSay: procedure expose m.
parse arg msg
    return sayNl(errMsg(msg))

/*--- prefix an errormessage with pref,
          split it into lines at \n to stem m.err -------------------*/
errMsg: procedure expose m.
parse arg msg
    m.err.eCat = 'f'
    do while substr(msg, 2, 1) == '}'
        parse var msg m.err.eCat '}' msg
        end
    res = msg
    if m.err.eCat <> '' then do
       pTxt = ',error,fatal error,input error,s-}scanErr,warning,'
 /*    pTxt = ',error,fatal error,input error,syntax error,warning,' */
       px = pos(','m.err.eCat, pTxt)
       if px < 1 then do
           m.err.eCat = 'f'
           px = pos(','m.err.eCat, pTxt)
           end
       res = substr(pTxt, px+1, pos(',', pTxt, px+2)-px-1)
       if substr(res, 3, 1) == '}' then
           parse var res 2 opt 3 br 4 res
       if opt == '-' then
           res = res msg
       else do
           parse source . . s3 .              /* current rexx */
           res = res 'in' s3':' msg
           end
       end
    return res
endProcedure errMsg

splitNL: procedure expose m.
parse arg st, msg
    bx = 1
    do lx=1 to 20
        ex = pos('\n', msg, bx)
        if ex < bx then
            leave
        m.st.lx = substr(msg, bx, ex-bx)
        bx = ex+2
        end
    m.st.lx = substr(msg, bx)
    m.st.0 = lx
    return st
endProcedure splitNL

outNL: procedure expose m.
parse arg msg
    bx = 1
    do lx=1 to 20
        ex = pos('\n', msg, bx)
        if ex < bx then
            leave
        call out substr(msg, bx, ex-bx)
        bx = ex+2
        end
    call out substr(msg, bx)
    return
endProcedure outNL

/*--- say (part of) the lines of a stem ----------------------------*/
sayNl: procedure expose m.
parse arg msg
    bx = 1
    do lx=1 to 20
        ex = pos('\n', msg, bx)
        if ex < bx then
            leave
        say strip(substr(msg, bx, ex-bx), 't')
        bx = ex+2
        end
    say strip(substr(msg, bx), 't')
    return 0
endProcedure sayNl

/*--- say a trace message if m.trace is set --------------------------*/
debug: procedure expose m.
parse arg msg
    if m.debug == 1 then
        say 'debug:' msg
    return
endProcedure debug

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

/*--- assert that the passed rexx expression evaluates to true -------*/
assert:
    interpret 'if ('arg(1)') == 1 then return 1'
    interpret 'call err' quote('assert failed' arg(1) ':' arg(2) '==>'),
                        arg(2)
endProcedure assert

/*--- abend with Message after displaying help -----------------------*/
errHelp: procedure expose m.
parse arg msg
    call errSay 'i}'msg
    call help 0
    call err 'i}'msg
endProcedure errHelp

/*--- display the first comment block of the source as help ----------*/
help: procedure expose m.
parse arg doClear
    if doClear \== 0 then
        address tso 'clear'
    parse source . . s3 .
    say right(' help for rexx' s3, 72, '*')
    do lx=1 while pos('/*', sourceLine(lx)) < 1
        if lx > 10 then
            return err('initial commentblock not found for help')
        end
    doInc = 1
    ho = m.err.helpOpt
    do lx=lx+1 to sourceline() while pos('*/', sourceline(lx)) = 0
        li = strip(sourceLine(lx), 't')
        cx = lastPos('{', li)
        if cx > 0 then do
            if length(ho) = 1 then
                doInc = cx = length(li) | pos(ho, li, cx+1) > 0
            li = left(li, cx-1)
            end
        if doInc then
            say li
        end
    say right(' end help for rexx' s3, 72, '*')
    return 4
endProcedure help

/* copy err end   *****************************************************/
/* copy ut begin  *****************************************************/
utIni: procedure expose m.
    if m.ut_ini == 1 then
        return
    m.ut_ini = 1
    m.ut_digits = '0123456789'
    m.ut_alfLC  = 'abcdefghijklmnopqrstuvwxyz'
    m.ut_alfUC  = translate(m.ut_alfLc)
    m.ut_Alfa   = m.ut_alfLc || m.ut_alfUC
    m.ut_alfNum = m.ut_alfa || m.ut_digits
    m.ut_alfDot = m.ut_alfNum || '.'
    m.ut_alfId  = m.ut_alfNum'_' /* avoid rexx allowed @ # $ ¬ . | ? */
    m.ut_alfIdN1 = m.ut_digits    /* not as first character */
    m.ut_rxId   = m.ut_AlfNum'@#$?' /* charset puff mit ¬*/
    m.ut_rxDot  = '.'m.ut_rxId
    m.ut_rxN1   = '.0123456789'
    m.ut_alfPrint = m.ut_alfNum'+-*/=()¢!{}<> .:,;?|''"%&#@$£\_'
    return
endProcedure utIni
/*--- 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 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 ------------------------------*/
utTime: procedure expose m.
    return time() 'ela='time('E') 'cpu='sysvar('syscpu'),
            'su='sysvar('syssrv')

/*--- 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

/*--- left without truncation ----------------------------------------*/
lefPad: procedure expose m
parse arg s, len
    if length(strip(s, 't')) >= len then
        return strip(s, 't')
    return left(s, len)
endProcedure lefPad

/*--- right without truncation ---------------------------------------*/
rigPad: procedure expose m
parse arg s, len
    if length(strip(s, 'l')) >= len then
        return strip(s, 'l')
    return right(s, len)
endProcedure rigPad

/*--- 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

/*--- translate to lowercase -----------------------------------------*/
ut2lc: procedure expose m.
parse arg s
    return translate(s, m.ut_alfLc, m.ut_alfUc)

/*--- verify an id ---------------------------------------------------*/
verifId: procedure expose m.
    parse arg src, extra
    if pos(left(src, 1), m.ut_alfIdN1) > 0 then
        return 1
    else
        return verify(src, m.ut_alfId || extra, 'n')

/*--- 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
    do ax = 2 by 2 to arg()
        src = repAl2(src, src, arg(ax), arg(ax+1))
        end
    return src
endProcedure repAll

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

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

utc2d: procedure expose m.
parse arg ch
    cx = length(ch) // 3
    if cx = 0 then
        cx = 3
    res = c2d(left(ch, cx))
    do cx=cx+1 by 3 to length(ch)
        res = res * 16777216 + c2d(substr(ch, cx, 3))
        end
    return res

utInter: procedure expose m.
    interpret arg(1)
    return
endProcedure utInter

/* copy ut end ********************************************************/
}¢--- A540769.WK.REXX(WSH5) cre=2015-07-20 mod=2015-07-20-15.36.15 A540769 -----
/* rexx ****************************************************************
  wsh: walter's rexx shell                                   version 5.0
  interfaces:                                                   17. 7.15
      edit macro: for adhoc evaluation or programming
              either block selection: q or qq and b or a
              oder mit Directives ($#...) im Text
      wsh i:  tso interpreter
      batch:  input in dd wsh
      docu:   http://chw20025641/host/db2wiki/pmwiki.php?n=Main.Wsh
      syntax: http://chw20025641/host/db2wiki/pmwiki.php?n=Main.WshSyn
|||achtung $@.sqlRdr() funktioniert nicht nur $@..¢sqlRdr() $!
|||    sqlSel schreib !$#out |||||
|||    einheitliches sql select/rdr syntax in wsh (mit ftab oder ohne|)
|||sql select aus rz2 muss wie csmExRx erfolgen (via WSH) ||||

--- history ------------------------------------------------------------
16. 1.15 f: get/put read/write in/out Object/Strings transparent weiter
*********/ /*** end of help ********************************************
17.11.14 f: iirz2p ==> plex Buchstaben
17.06.14 f: %tS %tT und %tN mit MicroSekunden
16.06.14 csmCopy auch für LoadModule usw.
30.05.14 fix sql4obj fuer rcm profex
14.04.14 class vor obj, mit lazy
19.03.14 ii = installation Info
 9.01.14 walter: redesign formatting (fmt eliminiert), csm.div.p0.exec
 3.12.13 walter: db2 interface radikal geputzt
 3.10.13 walter: uCount fuer TSO <-> unitCount fuer Csm
23. 9.13 walter: ws2 syntax
 6. 2.13 w.keller csmCopy aendert mgmtClas für neue lokale Files
23. 1.13 w.keller sqlErrHandler sowie sqlRx und sql
11. 6.12 w.keller sqlUpdComLoop
23. 5.12 w.keller fix sqlStmt: drop accepts -204
31. 3.12 w.keller sql Query interface incl. sql über CSM
10. 2.12 w.keller div catTb* und eLong
 2. 6.11 w.keller sql error with current location and dsnTiar
 2. 5.11 w.keller sqlStmt etc..
16. 3.11 w.keller basic new r '' ==> r m.class_O
28. 2.11 w.keller compFile by exprBlock, fix $@do forever, |
 7. 2.11 w.keller cleanup block / with sqlPush....
 2. 2.11 w.keller simplified syntax, ast for blocks, ? for help
19. 6.10 w.keller open/close stacked mit jUsers, framed eliminiert
19. 5.10 w.keller class wird direkt erzeugt ohne temporary
18. 4.10 w.keller scanUtilInto ==> testCase noch einbauen
17. 4.10 w.keller oMutate in fileTsoList eingebaut
14. 1.10 w.keller batch interface no longer dies on openIfNotYet
                  CSM.RZ1.P0.EXEC korrigiert
***********************************************************************/
if 0 then do
    do y = left(date('s'), 4) - 17 to left(date('s'), 4) + 7
        say y timeYear2Y(y) timeY2Year(timeYear2Y(y))
        end
    do y = left(date('s'), 4) - 69 to left(date('s'), 4) + 30
        say y timeYear24(substr(y, 3))
        end
d = date('s')
say d 'b' date('b', d , 's')
say d 'b' date('b', 20150101, 's') 'jul' date('j')
say d 'l14' date('b', 20150101, 's') - date('b', 20140101, 's')
say d 'l16' date('b', 20170101, 's') - date('b', 20160101, 's')
exit
end
/*--- main code wsh --------------------------------------------------*/
    call errReset 'hI'
    m.myLib  = 'A540769.WK.REXX'
    m.myVers = 'v42 13. 3.15'
    call wshLog
    parse arg spec
    isEdit = 0
    if spec = '' & m.err.ispf then do /* z/OS edit macro */
        isEdit = adrEdit('macro (spec) NOPROCESS', '*') == 0
        if isEdit then do
            call adrEdit '(d) = dataset'
            call adrEdit '(m) = member'
            m.editDsn = dsnSetMbr(d, m)
            if spec = '' & abbrev(m.editDsn, 'A540769.WK.REXX(WS') ,
                    & length(dsnGetMbr(m.editDsn)) <= 4 then do
                spec = 't'
                isEdit = 0
                end
            end
        end
    spec = strip(spec)
    if spec = '?' then
        return help()
    else if translate(word(spec, 1)) == 'T' then
        return wshTst(subword(spec, 2))
    else if spec <> '' & \ abbrev(spec, '$#') then
        spec = '$#'spec
    rest = ''
    inp = ''
    out = ''
    call wshIni
    if m.err.os == 'TSO' then do
        if isEdit then do
            parse value wshEditBegin(spec) with inp out
            end
        else if sysvar('sysEnv') = 'FORE' then do
            end
        else do
            inp = file('dd(wsh)')
            useOut = listDsi('OUT FILE')
            if \ (useOut = 16 & sysReason = 2) then
                out = file('dd(out)')
            end
        end
    else if m.err.os == 'LINUX' then do
        inp = file('&in')
        out = file('&out')
        end
    else
        call err 'implement wsh for os' m.err.os
    m.wshInfo = 'compile'
    m.wsh_exitCC = 0
    call compRun spec, inp, out, wshInfo
    if isEdit then
        call wshEditEnd
exit m.wsh_exitCC

wshLog: procedure expose m.
parse arg msg, st
    lNm = 'dsn.wsh.log'
    f1 = dsnAlloc('dd(log) mod' lNm '::f', , , '*')
    if datatype(f1, 'n') then do
        lN2 = lNm'.R' || ( random() // 19)
        f1 = dsnAlloc('dd(log) old' lN2 '::f', , , '*')
        if datatype(f1, 'n') then do
            say 'could not allocate log' lNm lN2
            return
            end
        end
    parse source . . s3 .
    o.1 = m.myLib'('s3')' word(m.myVers, 1) sysvar(sysnode) ,
            'j='mvsvar('symdef', 'jobname') ,
             'u='userid() date('s') time()
    if msg <> '' then
        o.2 = left(msg, 80)
    ox = 1 + (msg <> '')
    if st <> '' then do sx=1 to m.st.0
        ox = ox+1
        o.ox = left(m.st.sx, 80)
        end
    call writedd log, o., ox
    call tsoClose log
    call tsoFree log
    return
endProcedure wshLog
/*--- test hook ----------------------------------------------------*/
wshHook_T: procedure expose m.
parse arg cmp
    rest = strip(scanLook(m.cmp.scan))
    call compEnd cmp
    return wshTst(rest)
endProcedure wshHook_t

wshTst: procedure expose m.
parse arg rest
    m.tst_csm = 1
    if rest = '' then do /* default */
        say funits(3e7, 'd')
        call err tstEnd
        call csmcopy 'CMN.DIV.P0.DB2J.#000197.LLB' ,
                   , 'RZ1/A540769.TST.LXB'
        return 0
        call csmcopy 'RZ1/A540769.TST.PS' ,
                   , 'RZ4/A540769.TST.PO3(EINS)'
        return 0
        call csmcopy 'RZ1/A540769.TST.LCTL(BBB)',
                   , 'RZ4/A540769.TST.PS'
        return 0
        call csmcopy 'A540769.WK.LLB' ,
                   , 'RZ1/A540769.TST.LLB'
        call csmCopL 'RZ4/A540769.WK.JCL(QZ*)',
                   , 'RZ1/A540769.TST.yCL'
        return 0
        call tstfTst
        call sqlConnect DBAF
        call catColCom 'cmnBatch', 'DSN_PGROUP_TABLE',
                     , 'cmnBatch', 'DSN_PGROUP_TABLE_new'
        call sqlDisConnect DBAF
        return 0
        end
    c = ''
    do wx=1 to words(rest)
        c = c 'call tst'word(rest, wx)';'
        end
    if wx > 2 then
        c = c 'call tstTotal;'
    say 'wsh interpreting' c
    interpret c
    exit 0
endProcedure wshTst

/*--- i hook: interpret user input: rexx, expr, data or shell -------*/
wshHook_I: procedure expose m.
parse arg cmp
    inp = strip(scanLook(m.cmp.scan))
    call scanClose m.cmp.scan
    mode = '*'
    do forever
        if pos(left(inp, 1), '/;:*@.-=') > 0 then
            parse var inp mode 2 inp
        if mode == '/' then
            exit 0
        mode = translate(mode, ';', ':')
        if inp <> '' then do
            say 'as' mode 'interpreting' inp
            if mode = ';' then
                interpret inp
            else if mode = '*' then
                interpret 'say' inp
            else do
                call errReset 'h', 'say "******* intercepting error";',
                    'call errSay ggTxt; return "";'
                call oRun compile(comp(jBuf(inp)), mode)
                call errReset 'h'
                end
            end
        say 'enter' mode 'expression, / for end, ; or * for Rexx' ,
                                                 '@ . - =  for wsh'
        parse pull inp
        end
endProcedure wshInter

/*--- sql hook -----------------------------------------------------*/
wshHook_S: procedure expose m.
parse arg cmp
    s = m.cmp.scan
    ki = '='
    call scanVerify s, m.comp_chSpa
    if scanVerify(s, m.comp_chKind) then
        ki = left(m.s.tok, 1)
    call scanChar s
    rest = m.s.tok
    call scanNl s
    dbSy = word(rest, 1)
    if abbrev(dbSy, '-') | \ (length(dbSy) = 4 ,
                   | (length(dbsy) = 8 & substr(dbSy,4,1) == '/')) then
        dbSy = ''
    else
        rest = subWord(rest, 2)
    res = compAST(cmp, 'P', ' f', '',
        , compAstAddOp(cmp, compUnit(cmp, ki, '$#'), '@'))
    call mAdd res, compAst(cmp, 'c', "call sqlConnect '"dbSy"'",
        "; if \ sqlStmts( , 'rb ret', '"rest"') then m.wsh_exitCC=8" ,
        "; call sqlDisConnect;" )
     return res
endProcedure wshHook_s


wshEditBegin: procedure expose m.
parse arg spec
    dst = ''
    li = ''
    m.wsh.editHdr = 1
    pc = adrEdit("process dest range Q", 0 4 8 12 16)
    if pc = 16 then
        call err 'bad range must be q'
    if pc = 0 | pc = 8 then do
        call adrEdit "(rFi) = lineNum .zFrange"
        call adrEdit "(rLa) = lineNum .zLrange"
    /*  say 'range' rFi '-' rLa */
        end
    else do
        rFi = ''
    /*  say 'no range' */
        end
    if pc = 0 | pc = 4 then do
        call adrEdit "(dst) = lineNum .zDest"
    /*  say 'dest' dst */
        dst = dst + 1
        end
    else do
    /*  say 'no dest' */
        if adrEdit("find first '$#out' 1", 4) = 0 then do
            call adrEdit "(dst) = cursor"
    /*      say '$#out' dst   */
            call adrEdit "(li) = line" dst
            m.wsh.editHdr = 1
            end
        end
    m.wsh.editDst = dst
    m.wsh.editOut = ''
    if dst \== '' then do
        eo = jOpen(jbufText(), '>')
        m.wsh.editOut = eo
        call adrEdit '(recl) = LRECL'
        m.eo.maxL = recL
        if m.wsh.editHdr then
            call jWrite eo, left(li, 50) date('s') time()
        end
    if rFi == '' then do
        call adrEdit "(zLa) = lineNum .zl"
        if adrEdit("find first '$#' 1", 4) = 0 then do
            call adrEdit "(rFi) = cursor"
            call adrEdit "(li) = line" rFi
            if abbrev(li, '$#out') | abbrev(li, '$#end') then
                rFi = 1
            if rFi < dst & dst \== '' then
                rLa = dst-1
            else
                rLa = zLa
            end
        else do
            rFi = 1
            rLa = zLa
            end
        end
    /*  say 'range' c1 'rc' pc':' rFi '-' rLa   'after' dst */

    m.wsh.editIn = jOpen(jBuf(), m.j.cWri)
    do lx=rFi to rLa
        call adrEdit "(li) = line" lx
        call jWrite m.wsh.editIn, li
        end
    call errReset 'h',
             , 'return wshEditErrH(ggTxt, ' rFi',' rLa')'
    return jClose(m.wsh.editIn) m.wsh.editOut
endProcedure wshEditBegin

wshEditEnd: procedure expose m.
    call errReset 'h'
    if m.wsh.editOut == '' then
        return 0
    call jClose(m.wsh.editOut)
    lab = wshEditInsLinSt(m.wsh.editDst, 0, , m.wsh.editOut'.BUF')
    call wshEditLocate max(1, m.wsh.editDst-7)
    return 1
endProcedure wshEditEnd

wshEditLocate: procedure
parse arg ln
    call adrEdit '(la) = linenum .zl'
    call adrEdit 'locate '  max(1, min(ln, la - 37))
    return
endProcedure wshEditLocate

wshEditErrH: procedure expose m.
parse arg ggTxt, rFi, rLa
    call errCleanup
    call errReset 'h'
    call splitNl err, errMsg(' }'ggTxt)
    call mMove err, 1, 2
    isScan = 0
    if wordPos("pos", m.err.4) > 0 ,
        & pos(" in line ", m.err.4) > 0 then do
        parse var m.err.4 "pos " pos .     " in line " lin":"
        if pos = '' then do
            parse var m.err.4 " line " lin":"
            pos = 0
            end
        isScan = lin \== ''
        end
    m.err.1 = '***' m.wshInfo 'error ***'
    if m.wshInfo=='compile' & isScan then do
        do sx=1 to m.err.0
            call out m.err.sx
            end
        lab = rFi + lin
        if pos \= '' then
            lab = wshEditInsLin(lab, 'msgline', right('*',pos))
        lab = wshEditInsLinSt((rFi+lin),0, 'msgline', err)
        call wshEditLocate rFi+lin-25
        end
    else do
        if m.wsh.editOut \== '' then do
            do sx=1 to m.err.0
                call jWrite m.wsh.editOut, m.err.sx
                end
            lab = wshEditInsLinSt(m.wsh.editDst, 0, ,
                , m.wsh.editOut'.BUF')
            call wshEditInsLinSt m.wsh.editDst, m.wsh.editHdr,
                , msgline, err
            call wshEditLocate max(1, m.wsh.editDst-7)
            end
        else do
            do sx=1 to m.err.0
                say m.err.sx
                end
            end
        end
    call errCleanup
    exit
endSubroutine wshEditErrH

wshEditInsLinCmd: procedure
parse arg wh
    if dataType(wh, 'n')  then do
        if adrEdit("label" wh "= .a", 0 8 12) \= 12 then
            return 'line_before .a ='
        else
            return 'line_after .zl ='
        end
    else if left(wh, 1) == '.' then
        return 'line_before' wh '='
    else
        return wh
endProcedure wshEditInsLinCmd

wshEditInsLin: procedure
parse arg wh, type
    cmd = wshEditInsLinCmd(wh)
    do ax=3 to arg()
        li = strip(arg(ax), 't')
  /*    if li == '' then   ???? leere Zeilen doch anzeigen
            iterate                 ????? */
        if translate(type) = 'MSGLINE' then do while length(li) > 72
            sx = lastPos(' ', li, 72)
            if sx < 10 then
                sx = 72
            one = left(li, sx)
            li = '    'strip(substr(li, sx))
            call adrEdit cmd type "(one)"
            end
        call adrEdit cmd type "(li)", 0 4
        end
    return cmd
endProcedure wshEditInsLin

wshEditInsLinSt: procedure expose m.
parse arg wh, pl, type, st
    if wh == '' then do
        do ox=1 to m.st.0
            say m.st.ox
            end
        return ''
        end
    wh = wh + pl
    cmd = wshEditInsLinCmd(wh)
    do ax=1 to m.st.0
        call wshEditInsLin cmd, type, m.st.ax
        end
    return cmd
endProcedure wshEditInsLinSt


/*** end wsh, begin all copies ****************************************/
/* copy tstAll begin  *************************************************/
/*----------- neu, noch versorgen |||||-------------------------------*/
tstRts: procedure expose m.
    call wshIni
    call sqlConnect dbaf
    call sqlQuery 3, "select * from sysibm.sysTableSpaceSTats" ,
                    "where dbName = 'MF01A1A' and name = 'A150A'",
                    "order by partition  asc"
    do while sqlFetch(3, rr)
        say f('@.DBNAME%-8C.@NAME%-8C @PARTITION %4C' ,rr)
        end
    call sqlDisconnect
endProcedure tstRts

tstWiki:
    call mapReset docs, 'k'
    call addFiles docs, 'n', '/media/wkData/literature/notes'
    call addFiles docs, 'd', '/media/wkData/literature/docs'

    in = jOpen(file('wiki.old'), '<')
    out = jOpen(file('wiki.new'), '>')
    abc = '(:abc: %l%'
    do cx=1 to length(m.ut_alfLC)
        c1 = substr(m.ut_alfLC, cx, 1)
        abc = abc '¢¢#'c1 '|' c1'!!'
        end
    call jWrite out, abc ':)'
    inTxt = 0
    li = m.i
    do lx=1 while jReadVar(in, i)
        if 0 then
            say length(m.i) m.i
        if m.i = '' then
            iterate
        li = m.i
        do forever
            bx = pos('¢=', li)
            if bx < 1 then
                leave
            ex = pos('=!', li)
            if ex <= bx then
                call err '=! before ¢= in' lx li
            li = left(li, bx-1)substr(li,bx+2, ex-bx-2)substr(li,ex+2)
            end
        li = strip(li)
        if abbrev(li, '|') then do
            w = word(substr(li, 2), 1)
            call jWrite out, '¢¢#'w'!! {$:abc}'
            call jWrite out, '|||' substr(li, 2)
            inTxt=1
            iterate
            end
        if \ inTxt then do
            call jWrite out, li
            iterate
            end
        if \ (abbrev(li, '->') | abbrev(li, '#') ,
                | abbrev(li, '¢')) then do
            call jWrite out, '-<' li
            iterate
            end
        cx = 1
        if substr(li, cx, 2) == '->' then
            cx = verify(li, ' ', 'n', cx+2)
        hasCross = substr(li, cx, 1) == '#'
        if hasCross then
            cx = verify(li, ' ', 'n', cx+1)
        ex = verify(li, '!:\, ', 'm', cx)
        ex = ex - (substr(li, ex, 1) \== '!')
        hasBr = substr(li, cx, 1) == '¢'
        if \ hasBr then
            w = substr(li, cx, ex+1-cx)
        else if substr(li, ex, 1) == '!' then
            w = substr(li, cx+1, ex-1-cx)
        else
            call err 'br not closed' substr(w, cx+1, ex-1-cx) 'in' lx li
        hasPdf = right(w, 4) == '.pdf'
        if hasPdf then
            w = left(w, length(w)-4)
        if verify(w, '#?', 'm') > 0 then do
            w = translate(w, '__', '#?')
            say '*** changing to' w 'in' lx li
            end
        o = '-< {def+'w'}'
        o = '-< ¢¢'w'!!'
        k = translate(w)
        if k.k == 1 then
            say '*** doppelter key' k 'in:' lx left(li,80)
        k.k = 1
        dT = ''
        if mapHasKey(docs, k) then do
            parse value mapGet(docs, k) with dT dC dN
            call mapPut docs, k, dT (dC+1) dN
            do tx=1 to length(dT)
                t1 = substr(dT, tx, 1)
                o = o '¢¢Lit'translate(t1)':'word(dN, tx) '|' t1 '!!'
                end
            end
        qSeq = 'nd'
        qq = left(qSeq, 1)
        qx = 0
        do forever
            qx = pos('@'qq, li, qx+1)
            if qx < 1 then do
                qq = substr(qSeq, 1 + pos(qq, qSeq), 1)
                qx=0
                if qq = '' then
                    leave
                else
                    iterate
                end
            if pos(qq, dT) < 1 then do
                say '*** @'qq 'document not found:' lx li
                iterate
                end
            do qb = qx-1 by -1 while substr(li, qb, 1) == ' '
                end
            do qe = qx+2 by 1 while substr(li, qe, 1) == ' '
                end
            if substr(li, qb, 1) == '.' & substr(li, qe, 1) == '.' then
                li = left(li, qb)substr(li, qe+1)
            else
                li = left(li, qb) substr(li, qe)
            end
        o = o':' strip(substr(li, ex+1+(substr(li,ex+1,1)==':')))
        if 0 then say left(li, 30) '==>' left(o, 30)
        call jWrite out, o
        end
    dk = mapKeys(docs)
    do dx=1 to m.dk.0
        parse value mapGet(docs, m.dk.dx) with dT dC dN
        if dC < 1 then
            say '*** document not used:' dT dC dn
        end
    call jClose in
    call jClose out
    return
endProcedure tstWiki

addFiles: procedure expose m.
parse arg m, ty, file
    fl = jOpen(fileList(file(file)), '<')
    do while jRead(fl)
        nm = substr(m.fl, lastPos('/', m.fl)+1)
        k = translate(left(nm, pos('.', nm)-1))
        if \ mapHasKey(m, k) then do
            call mapAdd m, k, ty 0 nm
            end
        else do
            parse value mapGet(m, k) with dT dC dN
            call mapPut m, k, dT || ty 0 dN nm
            end
        end
    call jClose fl
    return
endProcedure addFiles

tstAll: procedure expose m.
    say 'tstAll ws2 25.2.13...............'
    call tstBase
    call tstComp
    call tstDiv
    if m.err.os = 'TSO' then do
        call tstZos
        call tstTut0
        end
    return 0
endProcedure tstAll

/****** tstZos ********************************************************/
tstZOs:
    call tstTime
    call tstII
    call sqlIni
    call tstSqlRx
    call tstSql
    if m.tst_csm \== 0 then
        call tstSqlCsm
    call scanReadIni
    call tstSqlC
    call tstSqlCsv
    call tstSqlRxUpd
    call tstSqlUpd
    call tstSqlUpdPre
    call tstSqlE
    call tstSqlB
    call tstSqlO1
    call tstSqlO2
    call tstSqlStmt
    call tstSqlStmts
    call tstSqlUpdComLoop
    call tstSqls1
    call tstSqlO
    call tstSqlFTab
    call tstSqlFTab2
    call tstSqlFTab3
    call tstSqlFTab4
    call tstsql4obj
    call tstTotal
    return
endProcedure tstZOs

tstWshBatch:
    call adrTso 'alloc dd(WSH) shr dsn(WK.TEXT(WSHBATCH))'
    call wshBatch
    return adrTso('free dd(WSH)')

tstLmdTiming:
parse arg lev
    say timing() lev
    call lmdBegin abc, lev
    c = 0
    do while lmdNext(abc, st.)
        c = c + st.0
        end
    call lmdEnd   abc
    say timing() lev 'with group - without reading' c
    call adrIsp 'lmdinit listid(lmdId) level('lev')'
    do c=0 while adrIsp('lmdlist listid(&lmdId) dataset(abc)', 8) = 0
        end
    call adrIsp 'lmdfree listid(&lmdId)'
    say timing() lev 'with list' c
    return
endProcedure lmdTiming

tstCsi: procedure expose m.
    if 0 then do
        call lmd 'A540769.*K'
        call tstCsiCla 'A540769.WK.REXX'
        call tstCsiCla 'A540769.AAA.DATASETS'
        call tstCsiCla 'A540769.RRR.DATASETS'
        end
    if 0 then do
        call tstCsiOpNx 'A540769.WK.*E*'
        call tstCsiOpNx 'A540769.AAA.DATASETS'
        call tstCsiOpNx 'A540769.RRR.DATASETS'
        end
    if 1 then do
        call tstCsiNxCl 'A540769.WK.**'
        call tstCsiNxCl 'DBTF.M*.**'
        call tstCsiNxCl 'DBTF.BE*.**'
        end
    return
tstCsi: procedure expose m.
 /* call lmd 'A540769.*K' */
    call tstCsi1   'A540769.WK.REXX'
    call tstCsi1   'A540769.AAA.DATASETS'
    call tstCsi1   'A540769.RRR.DATASETS'
    return

tstCsiCla:
parse arg ds
    say ds '-->' csiCla(ds)
    return

tstCsiOpNx: procedure expose m.
parse arg ds
    m = 'NUE123'
    s = 'res89'
    flds = 'devtyp volser mgmtclas comudsiz NOBYTTRK UDATASIZ HARBA'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    do while csiNext(m, s)
        say m.s     'dev' c2x(m.s.devTyp) ,
                    'vol' m.s.volSer 'cla' m.s.mgmtclas,
                    'comuDsiz' m.s.comuDsiz 'noBytTrk' m.s.noBytTrk,
                    'udatasiz' c2x(m.s.udatasiz) ,
                    'harba' c2x(m.s.harba)
        end
    return

tstCsiNxCl: procedure expose m.
parse arg ds
    m = 'ABC123'
    s = 'efg89'
    flds = 'devtyp volser mgmtclas'
    say 'csiOpen' ds
    call csiOpen m, ds, flds
    say timing() 'begin'
    do i=1 while csiNext(m, s)
        nn = csiArcTape(m.s.volser, m.s.mgmtClas, m.s.devtyp, m.s)
/*      oo = csiCla(strip(m.s))
        if oo <> nn then
            say nn '<>' oo m.s
 */     if i // 1000 = 0 then
            say timing() i nn m.s
        end
    say timing() (i-1) nn m.s
    return
endProcedure tstCsiNxCl

listCatClass: procedure expose m.
parse upper arg dsn
    rt = adrTso("listcat volume entry('"dsn"')", 4)
    /* say 'listct rc =' rt 'lines' m.tso_trap.0 */
    cl = ''
    vo = ''
    if word(m.tso_trap.1, 3) \== dsn then
        call out 'err ??? for dsn' dsn 'bad first line' m.tso_trap.1
    else if pos('NOT FOUND', m.tso_trap.1) > 0 then
        return 'notFound'
    else if word(m.tso_trap.1, 1)\== 'NONVSAM' then
        call out 'err ??? for dsn' dsn 'bad first line' m.tso_trap.1
    do tx=2 to m.tso_trap.0 while vo = '' ,
              & left(m.tso_trap.tx, 1) = ' '
     /* say m.tso_trap.tx */
        p = pos('MANAGEMENTCLASS-', m.tso_trap.tx)
        if p > 0 then
            vo = strip(word(substr(m.tso_trap.tx, p+16), 1), 'l', '-')
        p = pos('VOLSER--', m.tso_trap.tx)
        if p > 0 then
            vo = strip(word(substr(m.tso_trap.tx, p+6), 1), 'l', '-')
        p = pos('DEVCLASS--', m.tso_trap.tx)
            dt = strip(word(substr(m.tso_trap.tx, p+8), 1), 'l', '-')
        end
   /*  say 'lc' cl 'vo' vo 'dt' dt 'dsn' dsn */
    if vo = '' then
        call out '??? err no volume for dsn' dsn
    else if vo = 'ARCIVE' then
        res =  'arcive'
    else if cl = '' then
        res = 'tape'
    else
        res = cl
    if   abbrev(res, 'ar') \= abbrev(dt, "X'0") ,
       | abbrev(res, 'ta') \= abbrev(dt, "X'7") ,
       | (left(res, 1) >= 'A') \= abbrev(dt, "X'3") then
       call out 'err ??? mismatch cl' cl 'vo' vo 'dt' dt 'dsn' dsn
    return res
endProcedure listCatClass

tstMbrList: procedure expose m.
/*
$=/tstMbrList/
    ### start tst tstMbrList ##########################################
    *** err: adrTso rc= 8 stmt=LISTDS 'A540769.TMP.TST.MBRLIST' MEMBERS+
    . .
    .    e 1: A540769.TMP.TST.MBRLIST
    .    e 2: IKJ58503I DATA SET 'A540769.TMP.TST.MBRLIST' NOT IN CATAL+
    OG
    #noPds: 0 mbrs in A540769.TMP.TST.MBRLIST
    #1: 1 mbrs in A540769.TMP.TST.MBRLIST
    1 EINS
    #0: 0 mbrs in A540769.TMP.TST.MBRLIST
    #4: 4 mbrs in A540769.TMP.TST.MBRLIST
    1 DREI
    2 FUENF
    3 VIER
    4 ZWEI
    #*IE*: 3 mbrs in A540769.TMP.TST.MBRLIST( *IE* )
    1 IE
    2 NNNIE
    3 VIER
    #*_IE*: 2 mbrs in A540769.TMP.TST.MBRLIST( *?IE* )
    1 NNNIE
    2 VIER
$/tstMbrList/
*/
    call tst t, 'tstMbrList'
 /* call tstMbrList1 "RZ2/A540769.WK.REXX(*DA?*)"  */
    pds = tstFileName('MbrList', 'r')
    da.1 = '2ine eins'
    call tstMbrList1 pds, '#noPds'
    call writeDsn pds'(eins) ::f', da., 1
    call tstMbrList1 pds, '#1'
    call adrTso "delete '"pds"(eins)'"
    call tstMbrList1 pds, '#0'
    call writeDsn pds'(zwei) ::f', da., 1
    call writeDsn pds'(drei) ::f', da., 1
    call writeDsn pds'(vier) ::f', da., 1
    call writeDsn pds'(fuenf) ::f', da., 1
    call tstMbrList1 pds, '#4'
    call writeDsn pds'(ie) ::f', da., 1
    call writeDsn pds'(nnnie) ::f', da., 1
    call tstMbrList1 pds"( *IE* )", '#*IE*'
    call tstMbrList1 pds"( *?IE* )", '#*_IE*'
    call adrTso "delete '"pds"'"
    call tstEnd t
    return
endProcedure tstMbrList
tstMbrList1: procedure expose m.
parse arg pds, txt
    call tstOut t, txt':' mbrList(tstMbrList, pds) 'mbrs in' pds
    do mx=1 to m.tstMbrList.0
        call tstOut t, mx m.tstMbrList.mx
        end
    return
endProdecure tstMbrList1
/****** tstDiv ********************************************************/
tstDiv:
    call tstSort
    call tstMat
    call tstMatch
    call tstTotal
    return
endProcedure tstDiv


tstSort: procedure expose m.
    call tstSortComp
    call tstSortComp '<<='
    call tstSortComp 'm.aLe <<= m.aRi'
    call tstSortComp 'if 1 then cmp = m.aLe <<= m.aRi; else call err sd'
    return
endProcedure tstSort

tstSortComp: procedure expose m.
parse arg cmp
/*
$=/tstSort/
    ### start tst tstSort #############################################
    sort 29  c ACHT DREI DREIZEHN EINS ELF FUENF M.I.25 M.I.26 M.I.27 M+
    ..I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI ZWOELF 0 1 1 1 2 2+
    . 3 3 4 4
    sort 22  c ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27 M.I.29 NEUN VIERZ+
    EHN ZEHN ZWOELF 0 1 1 1 2 2 3 3 4 4
    sort 15  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1 1 2 2 3 3 4 4
    sort 8  c M.I.25 M.I.26 M.I.27 M.I.29 0 1 1
    sort 1  M.I.29
    sortWords(also als a 05 4 1e2, cmp) a als also 05 1e2 4
    sortWords(also als a 05 4, cmp) a als also 05 4
    sortWords(also als a 05, cmp) a als also 05
    sortWords(also als a, cmp) a als also
    sortWords(also als, cmp) als also
    sortWords(also, cmp) also
    sortWords(, cmp) .
    sortWords(also als a 05 4 1e2, <) a als also 4 05 1e2
    sortWords(also als a 05 4 1e2, >) 1e2 05 4 also als a
$/tstSort/ */
/*
$=/tstSortAscii/
    ### start tst tstSortAscii ########################################
    sort 29  0 1 1 1 2 2 3 3 4 4 ACHT DREI DREIZEHN EINS ELF FUENF M.I.+
    25 M.I.26 M.I.27 M.I.29 NEUN SECHS SIEBEN VIER VIERZEHN ZEHN ZWEI Z+
    WOELF c
    sort 22  0 1 1 1 2 2 3 3 4 4 ACHT DREIZEHN ELF M.I.25 M.I.26 M.I.27+
    . M.I.29 NEUN VIERZEHN ZEHN ZWOELF c
    sort 15  0 1 1 1 2 2 3 3 4 4 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 8  0 1 1 M.I.25 M.I.26 M.I.27 M.I.29 c
    sort 1  M.I.29
$/tstSortAscii/ */
    say '### start with comparator' cmp '###'
    if m.err.os == 'LINUX' then
        call tst t, "tstSortAscii"
    else
        call tst t, "tstSort"
    call mAdd mCut(i, 0), eins, zwei, drei, vier, fuenf, sechs,
        ,sieben, acht, neun, zehn, elf, zwoelf, dreizehn, vierzehn
    call mAdd i, 1, 2, 3, 4, 4, 3, 2, 1, 0, 1,
        , 'M.I.25', 'M.I.26', 'M.I.27', 'c', 'M.I.29'
    do yy = m.i.0 by -1 to 1

        do x = 0 to yy
            m.i.0 = x
            call sort i, o, cmp
            m = ''
            la = ''
            if x <> m.o.0 then
                call err 'size mismatch' x '<>' m.o.0
            do y=1 to m.o.0
                m = m m.o.y
                if \ (la << m.o.y) then
                    call err 'sort mismatch' yy x y '\' la '<<' m.o.y
                end
            end
        if yy // 7 = 1 then
           call tstOut t, 'sort' yy m
        do x = 2 to yy
            x1 = x-1
            m.i.x1 = m.i.x
            end
        end
    wi = 'also als a 05 4 1e2'
    do l=words(wi) by -1 to 0
        call tstOut t, 'sortWords('subWord(wi, 1, l)', cmp)' ,
                        sortWords(subWord(wi, 1, l), cmp)
        end
    call tstOut t, 'sortWords('wi', <)' sortWords(wi, '<')
    call tstOut t, 'sortWords('wi', >)' sortWords(wi, '>')
    call tstEnd t
    return
endProcedure tstSort
tstMatch: procedure expose m.
/*
$=/tstMatch/
    ### start tst tstMatch ############################################
    match(eins, e?n*) 1 1 2,i,s trans(E?N*) EiNs
    match(eins, eins) 1 1 0 trans(EINS) EINS
    match(e1nss, e?n*) 1 1 2,1,ss trans(?*) 1ss
    match(eiinss, e?n*) 0 0 -9 trans(E?N*) .
    match(einss, e?n *) 0 0 -9 trans(E?N *) .
    match(ein s, e?n *) 1 1 2,i,s trans(E?N *) EiN s
    match(ein abss  , ?i*b*) 1 1 3,e,n a,ss   trans(?I*B*) eIn aBss  .
    match(ein abss wie gehtsssxdirx und auch , ) 0 0 -9 trans() .
    match(ies000, *000) 1 1 1,ies trans(*000) ies000
    match(xx0x0000, *000) 1 1 1,xx0x0 trans(*000) xx0x0000
    match(000x00000xx, 000*) 1 1 1,x00000xx trans(000*) 000x00000xx
    match(000xx, *0*) 1 1 2,00,xx trans(ab*cd*ef) ab00cdxxef
    match(abcdef, *abcdef*) 1 1 2,, trans(*ABCDEF*) ABCDEF
    match(abcdef, **abcdef***) 1 1 5,,,,, trans(**ABCDEF***) ABCDEF
    match(abcdef, *cd*) 1 1 2,ab,ef trans(*CD*) abCDef
    match(abcdef, *abc*def*) 1 1 3,,, trans(*ABC*DEF*) ABCDEF
    match(abcdef, *bc*e*) 1 1 3,a,d,f trans(*BC*E*) aBCdEf
    match(abcdef, **bc**ef**) 1 1 6,a,,d,,, trans(**BC**EF**) aBCdEF
$/tstMatch/
*/
    call tst t, "tstMatch"
    call tstOut t, tstMatch1('eins', 'e?n*'                         )
    call tstOut t, tstMatch1('eins', 'eins'                         )
    call tstOut t, tstMatch1('e1nss', 'e?n*', '?*'                  )
    call tstOut t, tstMatch1('eiinss', 'e?n*'                       )
    call tstOut t, tstMatch1('einss', 'e?n *'                       )
    call tstOut t, tstMatch1('ein s', 'e?n *'                       )
    call tstOut t, tstMatch1('ein abss  ', '?i*b*'                  )
    call tstOut t, tstMatch1('ein abss wie gehtsssxdirx und auch ' )
    call tstOut t, tstMatch1('ies000', '*000'                       )
    call tstOut t, tstMatch1('xx0x0000', '*000'                     )
    call tstOut t, tstMatch1('000x00000xx', '000*'                  )
    call tstOut t, tstMatch1('000xx', '*0*', 'ab*cd*ef'             )
    call tstOut t, tstMatch1('abcdef', '*abcdef*'                   )
    call tstOut t, tstMatch1('abcdef', '**abcdef***'                )
    call tstOut t, tstMatch1('abcdef', '*cd*'                       )
    call tstOut t, tstMatch1('abcdef', '*abc*def*'                  )
    call tstOut t, tstMatch1('abcdef', '*bc*e*'                     )
    call tstOut t, tstMatch1('abcdef', '**bc**ef**'                 )
    call tstEnd t
return

tstMatch1:
parse arg w, m, m2
    r = 'match('w',' m')' match(w, m) matchVars(w, m, vv) m.vv.0
    do x=1 to m.vv.0
        r = r','m.vv.x
        end
    if m2 = '' then
        m2 = translate(m)
    r = r 'trans('m2')' matchRep(w, m, m2)
    return r
endProcedure tstMatch1

tstIntRdr: procedure expose m.
    i.1 = "//A540769J JOB (CP00,KE50),'DB2 REO',"
    i.2 = "//         MSGCLASS=T,TIME=1440,"
    i.3 = "//         NOTIFY=&SYSUID,REGION=0M,SCHENV=DB2"
    i.4 = "//*MAIN CLASS=LOG"
    i.5 = "//S1       EXEC PGM=IEFBR14"
    call writeDsn 'RR2/intRdr', i., 5, 1
    return
endProcedure tstIntRdr

tstII: procedure expose m.
/*
$=/tstII/
    ### start tst tstII ###############################################
    iiDs(org)         ORG.U0009.B0106.KLEM43
    iiDs(db2)         DSN.DB2
    iiRz2C(RZ2)       2
    *** err: no rz=R?Y in ii II_RZ2C
    iiRz2C(R?Y)       0
    iiRz2C(RZY)       Y
    iiDbSys2C(de0G)   E
    *** err: no dbSys=D??? in ii II_DB2C
    iiDbSys2C(d???)   0
    iiDbSys2C(DBOF)   F
    iiSys2RZ(S27)     RZ2
    iiMbr2DbSys(DBP5) DVBP
    ii_rz             RZX RZY RZZ RQ2 RR2 RZ2 RZ4
    ii_rz2db.rzx      DE0G DEVG DX0G DPXG
    rr2/dvbp    RR2 R p=R d=RZ2, db DVBP P 1
    iiixVPut 1: RZ2 2 p=B d=RZ2, db DBOF F 0
    iiixVPut 1: RZ2 2 p=B d=RZ2, db DVBP P 1
    iiixVPut 1: RZ2 2 p=B d=RZ2, db DP2G Q 0
$/tstII/
*/
    call tst t, 'tstII'
    call tstOut t, 'iiDs(org)        '  iiDs('oRg')
    call tstOut t, 'iiDs(db2)        '  iiDs(db2)
    call tstOut t, 'iiRz2C(RZ2)      '  iiRz2C(RZ2)
    call tstOut t, 'iiRz2C(R?Y)      '  iiRz2C(R?Y)
    call tstOut t, 'iiRz2C(RZY)      '  iiRz2C(RZY)
    call tstOut t, 'iiDbSys2C(de0G)  '  iiDbSys2C('de0G')
    call tstOut t, 'iiDbSys2C(d???)  '  iiDbSys2C('d???')
    call tstOut t, 'iiDbSys2C(DBOF)  '  iiDbSys2C('DBOF')
    call tstOut t, 'iiSys2RZ(S27)    '  iiSys2RZ(S27)
    call tstOut t, 'iiMbr2DbSys(DBP5)'  iiMbr2DbSys(DBP5)
    call tstOut t, 'ii_rz            '  m.ii_rz
    call tstOut t, 'ii_rz2db.rzx     '  m.ii_rz2db.rzx
    call pipeIni
    call iiVPut 'rr2/ DvBp  '
    call tstOut t, 'rr2/dvbp   ' ,
             vGet('rz') vget('rzC') 'p='vget('rzP') 'd='vget('rzD'),
             || ', db' vGet('dbSys') vGet('dbSysC') vGet('dbSysElar')
    w1 = wordPos('RZ2/DBOF', m.ii_rzDb)
    do wx=w1 to w1+2
        call tstOut t, 'iiixVPut' iiIxVPut(wx)':' ,
             vGet('rz') vget('rzC') 'p='vget('rzP') 'd='vget('rzD'),
             || ', db' vGet('dbSys') vGet('dbSysC') vGet('dbSysElar')
        end
    call tstEnd t
    return
endProcedure tstII

tstTime: procedure
/*         Winterzeit dez 2011
$=/tstTime/
    ### start tst tstTime #############################################
    05-28-00.00 2days  735745
    05-28-04.00 2days  735745.166666667
    05-28-21.00 2days  735745.9
    05-29-00.00 2days  735746
    16-05-28-00 2days  736111
    16...12 - 15...06  366.250000000
    2016-05-28-12.23.45            .
    2016-05-28-12-23.45            bad timestamp 2016-05-28-12-23
    2016.05-28-12.23.45            bad timestamp 2016.05-28-12.23
    2016-05-28-12.23.45.987654     .
    2016-0b-28-12.23.45            bad timestamp 2016-0b-28-12.23
    2016-05-28-12.23.45.9876543    bad timestamp 2016-05-28-12.23
    2016-05-28-12.23.45.98-654     bad timestamp 2016-05-28-12.23
    2016-00-28-12.23.45            bad month in timestamp 2016-00
    2016-05-28-13.23.45            .
    2016-15-28-12.23.45            bad month in timestamp 2016-15
    2016-05-31-12.23.45            .
    2016-04-31-13.23.45            bad day in timestamp 2016-04-3
    2015-04-30-12.23.45            .
    2016-02-30-12.23.45            bad day in timestamp 2016-02-3
    2016-02-29-13.23.45            .
    2015-02-29-12.23.45            bad day in timestamp 2015-02-2
    2016-07-30-25.00.00            bad hour in timestamp 2016-07-
    2016-04-07-24.00.00.0          .
    2015-02-19-24.00.01            bad hour in timestamp 2015-02-
    Lrsn2Lzt(00C5E963363741000000) 2010-05-01-12.34.54.789008
    Achtung: output haengt von Winter/SommerZ & LeapSecs ab
    stckUnit    = 0.000000000244140625
    timeLeap    = 00000018CBA80000 = 106496000000 =        26.000 secs
    timeZone    = 00001AD274800000 = 29491200000000 =   7200.000 secs
    timeUQZero  = 207090001374976
    timeUQDigis = 35 digits ABCDEFGHIJKLMNOPQRSTUVWXYZ012345678
    2jul(2011-03-31-14.35.01.234567)  11090
    Lrsn2TAI10(00C5E963363741000000) 2010-05-01-10.35.20.789008
    Lrsn2Lzt(00C5E963363741000000) 2010-05-01-12.34.54.789008
    TAI102Lrsn(2011-03-31-14.35.01.234567) 00C78D87B86E38700000
    lzt2Lrsn(2011-03-31-14.35.01.234567) 00C78D6CFEC560700000
    Lrsn2TAI10(TAI102Lrsn(2011-03-31-14.35.01.234567) +
    2011-03-31-14.35.01.234567
    TAI102Lrsn(Lrsn2TAI10(00C5E963363741000000) 00C5E963363741000000
    Lrsn2LZt(LZt2Lrsn(2011-03-31-14.35.01.234567) 2011-03-31-14.35.01.2+
    34567
    LZt2Stc(Lrsn2LZt(00C5E963363741000000) 00C5E963363741000000
    Lrsn2uniq(00C5E963363741000000) CTNR6S7T back 00C5E963363740000000
    Lrsn2LZt(LZt2Lrsn(2051-10-31-14.35.01.234567) 2051-10-31-14.35.01+
     ..234567
    Lrsn2TAI10(01010000000000000000) 2043-04-09-14.36.53.414912
$/tstTime/
*/
    call jIni
    call tst t, 'tstTime'
    call out '05-28-00.00 2days ' timestamp2days('2015-05-28-00.00.00')
    call out '05-28-04.00 2days ' timestamp2days('2015-05-28-04.00.00')
    call out '05-28-21.00 2days ' timestamp2days('2015-05-28-21.36.00')
    call out '05-29-00.00 2days ' timestamp2days('2015-05-29-00.00.00')
    call out '16-05-28-00 2days ' timestamp2days('2016-05-28-00.00.00')
    call out '16...12 - 15...06 ' timestampDiff( '2016-05-28-12.23.45',
                                               , '2015-05-28-06.23.45')
    l = '2016-05-28-12.23.45 2016-05-28-12-23.45 2016.05-28-12.23.45',
       '2016-05-28-12.23.45.987654 2016-0b-28-12.23.45' ,
       '2016-05-28-12.23.45.9876543 2016-05-28-12.23.45.98-654' ,
       '2016-00-28-12.23.45 2016-05-28-13.23.45 2016-15-28-12.23.45',
       '2016-05-31-12.23.45 2016-04-31-13.23.45 2015-04-30-12.23.45',
       '2016-02-30-12.23.45 2016-02-29-13.23.45 2015-02-29-12.23.45',
       '2016-07-30-25.00.00 2016-04-07-24.00.00.0 2015-02-19-24.00.01'
    do lx=1 to words(l)
        call out left(word(l, lx), 30),
            strip(left(timestampCheck(word(l, lx)), 30), 't')
        end
    t1 = '2011-03-31-14.35.01.234567'
    t2 = '2051-10-31-14.35.01.234567'
    s1 = timeLrsnExp('C5E963363741')
    s2 = timeLrsnExp('0101')
    call out 'Lrsn2Lzt('s1')' timeLrsn2Lzt(s1)
    call out 'Achtung: output haengt von Winter/SommerZ & LeapSecs ab'
    numeric digits 15
    call out 'stckUnit    =' m.time_StckUnit
    call out 'timeLeap    =' d2x(m.time_Leap,16) '=' m.time_Leap ,
                   '=' format(m.time_Leap * m.time_StckUnit,9,3) 'secs'
    call out 'timeZone    =' d2x(m.time_Zone,16) '=' m.time_Zone,
                   '=' format(m.time_Zone * m.time_StckUnit,6,3) 'secs'
 /* call out "cvtext2_adr =" d2x(cvtExt2A, 8)  */
    call out 'timeUQZero  =' m.time_UQZero
    call out 'timeUQDigis =' ,
                    length(m.time_UQDigits) 'digits' m.time_UQDigits
    call out '2jul('t1') ' time2jul(t1)
    call out 'Lrsn2TAI10('s1')' timelrsn2TAI10(s1)
    call out 'Lrsn2Lzt('s1')' timeLrsn2Lzt(s1)
    call out 'TAI102Lrsn('t1')' timeTAI102Lrsn(t1)
    call out 'lzt2Lrsn('t1')' timeLzt2Lrsn(t1)
    call out 'Lrsn2TAI10(TAI102Lrsn('t1')' ,
          timeLrsn2TAI10(timeTAI102Lrsn(t1))
    call out 'TAI102Lrsn(Lrsn2TAI10('s1')' ,
        timeTAI102Lrsn(timelrsn2TAI10(s1))
    call out 'Lrsn2LZt(LZt2Lrsn('t1')' timeLrsn2LZt(timeLZt2Lrsn(t1))
    call out 'LZt2Stc(Lrsn2LZt('s1')'  timeLZt2Lrsn(timeLrsn2LZt(s1))
    call out 'Lrsn2uniq('s1')' timeLrsn2Uniq(s1) ,
                        'back' timeUniq2Lrsn(timeLrsn2Uniq(s1))
    call out 'Lrsn2LZt(LZt2Lrsn('t2')' timeLrsn2LZt(timeLZt2Lrsn(t2))
    call out 'Lrsn2TAI10('s2')' timelrsn2TAI10(s2)
    call tstEnd t
    return
endProcedure tstTime
tstMat: procedure expose m.
/*
$=/tstMat/
    ### start tst tstMat ##############################################
    .   0 sqrt  0 isPrime 0 nxPrime    3 permut 1 > 1 2 3 4 5
    .   1 sqrt  1 isPrime 0 nxPrime    3 permut 2 > 2 1 3 4 5
    .   2 sqrt  1 isPrime 1 nxPrime    3 permut 3 > 1 3 2 4 5
    .   3 sqrt  1 isPrime 1 nxPrime    3 permut 3 > 2 3 1 4 5
    .   4 sqrt  2 isPrime 0 nxPrime    5 permut 3 > 3 2 1 4 5
    .   5 sqrt  2 isPrime 1 nxPrime    5 permut 3 > 3 1 2 4 5
    .   6 sqrt  2 isPrime 0 nxPrime    7 permut 4 > 1 2 4 3 5
    .   7 sqrt  2 isPrime 1 nxPrime    7 permut 4 > 2 1 4 3 5
    .   8 sqrt  2 isPrime 0 nxPrime   11 permut 4 > 1 3 4 2 5
    .   9 sqrt  3 isPrime 0 nxPrime   11 permut 4 > 2 3 4 1 5
    .  10 sqrt  3 isPrime 0 nxPrime   11 permut 4 > 3 2 4 1 5
    .  11 sqrt  3 isPrime 1 nxPrime   11 permut 4 > 3 1 4 2 5
    .  12 sqrt  3 isPrime 0 nxPrime   13 permut 4 > 1 4 3 2 5
    .  13 sqrt  3 isPrime 1 nxPrime   13 permut 4 > 2 4 3 1 5
    .  14 sqrt  3 isPrime 0 nxPrime   17 permut 4 > 1 4 2 3 5
    .  15 sqrt  3 isPrime 0 nxPrime   17 permut 4 > 2 4 1 3 5
    .  16 sqrt  4 isPrime 0 nxPrime   17 permut 4 > 3 4 1 2 5
    .  17 sqrt  4 isPrime 1 nxPrime   17 permut 4 > 3 4 2 1 5
    .  18 sqrt  4 isPrime 0 nxPrime   19 permut 4 > 4 2 3 1 5
$/tstMat/
$/tstMat/
*/
    call tst t, 'tstMat'
    q = 'tst_Mat'
    do qx=1 to 20
        m.q.qx = qx
        end
    do i=0 to 18
        call permut q, i
        call tstOut t, right(i,4) 'sqrt' right(sqrt(i), 2) ,
        'isPrime' isPrime(i) 'nxPrime' right(nxPrime(i), 4) ,
            'permut' m.q.0 '>' m.q.1 m.q.2 m.q.3 m.q.4 m.q.5
        end
    call tstEnd t
    return
endProcedure tstMat

/****** tstSql ********************************************************/
tstSqlUtils: procedure expose m.
    call sqlConnect 'DBAF'
    id = 'A540769.dsnUtils'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "TEMPLATE TCOPYD",
            "DSN('&SSID..&JO..&US..P&PART(2)..D&DATE(3)..T&TIME.')",
            "DATACLAS(NULL12) MGMTCLAS(COM#A011) STORCLAS(FAR$N)",
            "SPACE (150,3750) TRK UNCNT 59;",
         "listdef abc include tablespace DA540769.A002* partlevel;",
         "listdef mf  include tablespace MF01A1A.A110A partlevel;",
         "copy list abc copyddn(tcopyd) shrlevel change;"
    st = translate(st)
    call sqlExec "call SYSPROC.DSNUTILS ( :id, :rst,",
                   ":st,:retcode, :ANY" copies(',:e,:e,:z',12) ")"
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.DSNUTILS'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    exit
endProcedure tstSqlUtils

tstSqlStored: procedure expose m.
    call sqlConnect 'DBAF'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "abc"
    call sqlExec "call SYSPROC.COMMAND ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE SYSPROC.COMMAND'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStored

tstSqlStoredWK: procedure expose m.
    call sqlConnect 'DBAF'
 /* st = "direct wkUtiSub"
    rst = 'NO'
    say 'before call st='st 'rst='rst
    call sqlExec "call A540769.WKUTILSUB ( :st, :rst)"
    say 'after call st='st 'rst='rst
 */ rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    prc = 'DB2UTIL.DB2UTIL'
    st = "DA540769.A2*" /*  DA540769.A1*" */
    say  "call" prc "("st", ...)"
    call sqlExec "call" prc "(:st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE' prc
 /* say 'results' results  */
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say ''
    say '***** utility output'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say '***** end output'
    call sqlDisconnect
    return 0
endProcedure tstSqlStoredWK

tstSqlStoredSQL1: procedure expose m.
    call sqlConnect 'DBIA'
    rst = 'NO'
    retcode = -9876
    e = ''
    z = 0
    st = "DA540769.A2* DA540769.A1*"
    call sqlExec "call A540769.WKSQL1 ( :st, :rst)"
    say 'after call st='st 'rst='rst
    call sqlExec ,
          'ASSOCIATE LOCATOR (:RESULTS) WITH PROCEDURE A540769.WKUTIL'
    say 'results' results
    call sqlExec 'ALLOCATE C111 CURSOR FOR RESULT SET :RESULTS'
    say 'allocated c111'
    do while sqlExec('fetch c111 into :seq, :txt', 0 100) = 0
        say 'sysPrint' seq strip(txt, 't')
        end
    call sqlExec 'close c111'
    say 'closed'
    call sqlDisconnect
    return
endProcedure tstSqlStoredSQL1

tstSqlTriggerTiming:
    parse upper arg tb ni
    m.noInsert = ni == 0
    cnt = 200000
    if tb = '' then
        TB = 'GDB9998.TWK511TRI'
    call sqlConnect dbaf
    say timing() 'noInsert' m.noInsert 'tb' tb
    call sql2St 'select max(pri) MX from' tb, cc
    if m.cc.1.mx == m.sqlNull then
        m.cc.1.mx = 0
    von = m.cc.1.mx + 1
    bis = m.cc.1.mx + cnt
    say m.cc.0 'max' m.cc.1.mx 'von' von 'bis' bis 'count' cnt tb
    if right(tb, 2) = 'A1' then do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long, par)' ,
                   'values (?, ?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    else do
        call sqlPrepare 3, 'insert into' tb '(pri, short, long)' ,
                   'values (?, ?, ?)'
        do ax=von to bis
            call sqlExecute 3, ax,
                  , ax 'wsh short', ax 'wsh long long long long long ',
                  , (ax-1) // 1000 + 1
            end
        end
    /*  call sqlLn 5,,, 'select * from' tb 'where pri >=' von */
    call sqlCommit
    say timing()
    call sqlDisconnect
    return
endProcedure tstSqlTriggerTiming

tstSqlRx: procedure expose m.
/*
$=/tstSqlRx/
    ### start tst tstSqlRx ############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s7 into :M.SQL.7.D from :src
    .    e 3: with into :M.SQL.7.D = M.SQL.7.D
    fetchA 1 ab=m.abcdef.123.AB=abc ef=efg
    fetchA 0 ab=m.abcdef.123.AB=abc ef=efg
    fetchB 1 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchB 0 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
    fetchBT 1 SYSTABLES
    fetchBT 0 SYSTABLES
    fetchBi 1 SYSINDEXES
    fetchBi 0 SYSINDEXES
$/tstSqlRx/ */
    call jIni
    call tst t, "tstSqlRx"
    call sqlRxConnect
    cx = 7
    call sqlRxQuery cx, 'select * from sysdummy'
    call sqlRxQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlRxFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    call sqlRxClose cx
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlRxQuery cx, sql, 'AB CD EF GH'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlRxFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    call sqlRxClose cx
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlRxQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlRxFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    call sqlRxClose cx
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call sqlQueryPrepare cx, "select name" ,
                        "from sysibm.sysTables" ,
                        "where creator = 'SYSIBM' and name = ?",':m.nm'
    call sqlQueryExecute cx, 'SYSTABLES'
    call out 'fetchBT' sqlRxFetch(cx) m.nm
    call out 'fetchBT' sqlRxFetch(cx) m.nm
    call sqlRxClose cx
    call sqlQueryExecute cx, 'SYSINDEXES'
    call out 'fetchBi' sqlRxFetch(cx) m.nm
    call out 'fetchBi' sqlRxFetch(cx) m.nm
    call tstEnd t
    call sqlRxDisconnect
    return
endProcedure tstSqlRx

tstSql: procedure expose m.
/*
$=/tstSql/
    ### start tst tstSql ##############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s7 into :M.SQL.7.D from :src
    .    e 3: with into :M.SQL.7.D = M.SQL.7.D
    fetchA 1 ab=m.abcdef.123.AB=abc ef=efg
    fetchA 0 ab=m.abcdef.123.AB=abc ef=efg
    fetchB 1 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchB 0 ab=a cd=2 ef=--- ind=-1 gh=d ind=0
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
    sql2St 1 st.0=1
    sql2St:1 a=a b=2 c=--- d=d
    sql2One a
    sql2One a=a b=2 c=--- d=d
    fetchBT 1 SYSTABLES
    fetchBT 0 SYSTABLES
    fetchBi 1 SYSINDEXES
    fetchBi 0 SYSINDEXES
$/tstSql/ */
    call jIni
    call tst t, "tstSql"
    call sqlConnect
    cx = 7
    call sqlQuery cx, 'select * from sysdummy'
    call sqlQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    call sqlClose cx
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlQuery cx, sql, 'AB CD EF GH'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    call sqlClose cx
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    call sqlClose cx
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call out 'sql2St' sql2St(sql, st) 'st.0='m.st.0
    do i=1 to m.st.0
        call out 'sql2St:'i ,
            'a='m.st.i.a 'b='m.st.i.b 'c='m.st.i.c 'd='m.st.i.d
        end
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    drop m.st.a m.st.b m.st.c m.st.d m.st.0
    call out 'sql2One' sql2One(sql, st)
    call out 'sql2One' ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
    drop m.st.a m.st.b m.st.c m.st.d m.st.0
    call sqlQueryPrepare cx, "select name" ,
                        "from sysibm.sysTables" ,
                        "where creator = 'SYSIBM' and name = ?",':m.nm'
    call sqlQueryExecute cx, 'SYSTABLES'
    call out 'fetchBT' sqlFetch(cx) m.nm
    call out 'fetchBT' sqlFetch(cx) m.nm
    call sqlClose cx
    call sqlQueryExecute cx, 'SYSINDEXES'
    call out 'fetchBi' sqlFetch(cx) m.nm
    call out 'fetchBi' sqlFetch(cx) m.nm
    call tstEnd t
    call sqlDisconnect
    return
endProcedure tstSql

tstSqlCsm: procedure expose m.
/*
$=/tstSqlCsm/
    ### start tst tstSqlCsm ###########################################
    *** err: SQLCODE = -204: S100447.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: subsys = DE0G, host = RZZ
    *** err: implement sqlCmsQuery fetchVars ? or : :m.dst.ab, :m.dst.ef
    fetchA 0 ab=m.abcdef.123.AB=M.abcdef.123.AB ef=M.abcdef.123.EF
    fetchA 0 ab=m.abcdef.123.AB=M.abcdef.123.AB ef=M.abcdef.123.EF
    fetchB 1 ab=a cd=2 ef=--- ind=M.abc.Def.123.EF.SQLIND gh=d ind=M.ab+
    c.Def.123.GH.SQLIND
    fetchB 0 ab=a cd=2 ef=--- ind=M.abc.Def.123.EF.SQLIND gh=d ind=M.ab+
    c.Def.123.GH.SQLIND
    fetchC 1 a=a b=2 c=--- d=d
    fetchC 0 a=a b=2 c=--- d=d
$/tstSqlCsm/ */
    call pipeIni
    call tst t, "tstSqlCsm"
    call sqlConnect 'RZZ/DE0G'
    cx = 7
    call sqlCsmQuery cx, 'select * from sysdummy'
    call sqlCsmQuery cx, "select 'abc' , 'efg'",
                         'from sysibm.sysDummy1', ':m.dst.ab, :m.dst.ef'
    a = 'abcdef'
    b = 123
    drop m.a.b.ab m.a.b.ef
    do i=1 to 2
        call out 'fetchA' sqlCsmFetch(cx, a || '.' || b) ,
            'ab=m.'a'.'||b'.'ab'='m.a.b.ab 'ef='m.a.b.ef
        end
    drop m.a.b.ab m.a.ab.ef a b c
    sql = "select 'a' a, 2 b, case when 1=0 then 1 else null end c",
                           ", case when 1=1 then 'd' else null end d",
                 "from sysibm.sysDummy1"
    call sqlCsmQuery cx, sql, 'AB CD EF GH'
    st = 'abc.Def.123'
    drop m.st.ab m.st.cd m.st.ef m.st.ef.sqlInd m.st.gh m.st.gh.sqlInd
    do i=1 to 2
        call out 'fetchB' sqlCsmFetch(cx, st) ,
            'ab='m.st.ab 'cd='m.st.cd 'ef='m.st.ef 'ind='m.st.ef.sqlInd,
                                      'gh='m.st.gh 'ind='m.st.gh.sqlInd
        end
    drop m.st.ab m.st.bc m.st.df m.st.df.sqlInd
    call sqlCsmQuery cx, sql
    st = 'abc.Def.123'
    drop m.st.a m.st.b m.st.c m.st.d
    do i=1 to 2
        call out 'fetchC' sqlCsmFetch(cx, st) ,
            'a='m.st.a 'b='m.st.b 'c='m.st.c 'd='m.st.d
        end
    drop m.st.a m.st.b m.st.c m.st.d
    drop m.st.1.a m.st.1.b m.st.1.c m.st.1.d  m.st.0
    call tstEnd t
    call sqlDisconnect
    return
endProcedure tstsqlCsm

tstSqlCSV: procedure expose m.
/*
$=/tstSqlCSV/
    ### start tst tstSqlCSV ###########################################
    NAME,CREATOR,MITCOM,MITQUO,MITNU,COL6
    SYSTABLES,SYSIBM  ,"a,b","a""b",1,8
    SYSTABLESPACE,SYSIBM  ,"a,b","a""b",---,8
    SYSTABLESPACESTATS,SYSIBM,"a,b","a""b",---,6
$/tstSqlCSV/ */
    call csvIni
    call scanReadIni
    call sqlConnect
    call tst t, "tstSqlCSV"
    r = csvWrt(sqlRdr("select name, creator, 'a,b' mitCom",
         ", 'a""b' mitQuo" ,
         ", case when name='SYSTABLES' then 1 else null end mitNu" ,
         ",length(creator)" ,
              "from sysibm.sysTables" ,
              "where creator = 'SYSIBM' and name like 'SYSTABLES%'",
              "fetch first 3 rows only"))
    call pipeWriteAll r
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlCsv

tstSqlB: procedure expose m.
/*
$=/tstSqlB/
    ### start tst tstSqlB #############################################
    #jIn 1# select strip(name) "tb", strip(creator) cr
    #jIn 2# , case when name = 'SYSTABLES' then 1 else null end
    #jIn 3# from sysibm.sysTables
    #jIn 4# where creator = 'SYSIBM' and name like 'SYSTABLES%'
    #jIn 5# .
    #jIn 6# order by name
    #jIn 7# fetch first 3 rows only
    #jIn eof 8#
    dest1.fet: SYSTABLES SYSIBM 1
    dest2.fet: SYSTABLESPACE SYSIBM ---
    dest3.fet: SYSTABLESPACESTATS SYSIBM ---
$/tstSqlB/ */
    call pipeIni
    call tst t, "tstSqlB"
    cx = 9
    call sqlConnect
    call jIni
    call mAdd mCut(t'.IN', 0),
      , 'select strip(name) "tb", strip(creator) cr' ,
      ,     ", case when name = 'SYSTABLES' then 1 else null end" ,
      ,   "from sysibm.sysTables" ,
      ,   "where creator = 'SYSIBM' and name like 'SYSTABLES%'", ,
      ,   "order by name",
      ,   "fetch first 3 rows only"
     call sqlQuery cx, in2Str(,' ')
     do qx=1 while sqlFetch(cx, 'dest'qx'.fet')
         dst = 'dest'qx'.fet'
         call out dst':' m.dst.tb m.dst.cr m.dst.col3
         drop m.dst.tb m.dst.cr m.dst.col3
         end
     call sqlClose cx
     call sqlDisconnect
     call tstEnd t
     return
endProcedure tstSqlB

tstSqlFTab: procedure expose m.
/*
$=/tstSqlFTab/
    ### start tst tstSqlFTab ##########################################
    UPDATESTATSTIME----------------NACTIVE------NPAGES-EXTENT-LOADRLAST+
    TIME--------------REORGLASTTIME--------------REORGINSERT-REORGDELET+
    E-REORGUPDATE-REORGUNCLUS-REORGDISORG-REORGMASSDE-REORGNEARIN-REORG+
    FARIND-STATSLASTTIME--------------STATSINSERT-STATSDELETE-STATSUPDA+
    TE-STATSMASSDE-COPYLASTTIME---------------COPYUPDATED-COPYCHANGES-C+
    OPYUP-COPYUPDATETIME-------------I---DBID---PSID-PARTIT-INSTAN---SP+
    ACE-TOTALRO-DATASIZ-UNCOMPR-DBNAME-------NAME---------REORGCL-REORG+
    SC-REORGHA-HASHLASTUS-DRI-L-STATS01-UPDATES-LASTDATACHANGE---------+
    ------
    --- modified
    allg vorher                     others vorher
    db-------ts---------part---inst-UPDATESTATSTIME----------------NACT+
    IVE------NPAGES-EXTENT-LOADRLASTTIME--------------REORGLASTTIME----+
    ----------REORGINSERT-REORGDELETE-REORGUPDATE-REORGUNCLUS-REORGDISO+
    RG-REORGMASSDE-REORGNEARIN-REORGFARIND-STATSLASTTIME--------------S+
    TATSINSERT-STATSDELETE-STATSUPDATE-STATSMASSDE-COPYLASTTIME--------+
    -------COPYUPDATED-COPYCHANGES-COPYUP-COPYUPDATETIME-------------I-+
    --DBID---PSID---SPACE-TOTALRO-DATASIZ-UNCOMPR-REORGCL-REORGSC-REORG+
    HA-HASHLASTUS-DRI-L-STATS01-UPDATES-LASTDATACHANGE---------------
    db-------ts---------part---inst-UPDATESTATSTIME----------------NACT+
    IVE------NPAGES-EXTENT-LOADRLASTTIME--------------REORGLASTTIME----+
    ----------REORGINSERT-REORGDELETE-REORGUPDATE-REORGUNCLUS-REORGDISO+
    RG-REORGMASSDE-REORGNEARIN-REORGFARIND-STATSLASTTIME--------------S+
    TATSINSERT-STATSDELETE-STATSUPDATE-STATSMASSDE-COPYLASTTIME--------+
    -------COPYUPDATED-COPYCHANGES-COPYUP-COPYUPDATETIME-------------I-+
    --DBID---PSID---SPACE-TOTALRO-DATASIZ-UNCOMPR-REORGCL-REORGSC-REORG+
    HA-HASHLASTUS-DRI-L-STATS01-UPDATES-LASTDATACHANGE---------------
    allg nachher                    others nachher
    DBNAME                   INSTANCE                                  +
    .    NPAGES                                        REORGLASTTIME   +
    .                                   REORGUPDATES                   +
    .     REORGMASSDELETE                     STATSLASTTIME            +
    .                          STATSUPDATES                            +
    .           COPYUPDATEDPAGES               COPYUPDATETIME          +
    .            PSID                   DATASIZE                REORGSC+
    ANACCESS            DRIVETYPE     UPDATESIZE
    .         NAME                   UPDATESTATSTIME                   +
    .                 EXTENTS                                          +
    .            REORGINSERTS                        REORGUNCLUSTINS   +
    .                  REORGNEARINDREF                                 +
    .   STATSINSERTS                        STATSMASSDELETE            +
    .                        COPYCHANGES                               +
    .    IBMREQD         SPACE                   UNCOMPRESSEDDATASIZE  +
    .  REORGHASHACCESS        LPFACILITY        LASTDATACHANGE
    .                  PARTITION                                NACTIVE+
    .                        LOADRLASTTIME                             +
    .                        REORGDELETES                        REORGD+
    ISORGLOB                      REORGFARINDREF                       +
    .              STATSDELETES                        COPYLASTTIME    +
    .                                   COPYUPDATELRSN                 +
    .     DBID                  TOTALROWS               REORGCLUSTERSEN+
    S        HASHLASTUSED     STATS01
$/tstSqlFTab/
*/
    call pipeIni
    call tst t, 'tstSqlFTab'
    call sqlConnect
    call sqlQuery 17, 'select * from sysibm.sysTablespaceStats' ,
                "where dbName = 'xxxDB06' and name = 'SYSTSTAB'"
    call sqlFTabOpts fTabReset(abc, 1, ,'-'), 17,  12
    call sqlFTabDef      abc, 492, '%7e'
    call sqlFTabOthers abc
    call sqlfTab abc
    call sqlClose 17
    call out '--- modified'
    call sqlQuery 17, 'select * from sysibm.sysTablespaceStats' ,
                "where dbName = 'xxxDB06' and name = 'SYSTSTAB'"
    call sqlFTabOpts fTabReset(abc, 2 1, 1 3 'c', '-'), 17,  12
    call sqlFTabDef      abc, 492, '%7e'
    call sqlFTabAdd      abc, DBNAME, '%-8C', 'db', 'allg vorher'  ,
                                                  , 'allg nachher'
    call sqlFTabAdd      abc, NAME  , '%-8C', 'ts'
    call sqlFTabAdd      abc, PARTITION , , 'part'
    call sqlFTabAdd      abc, INSTANCE  , , 'inst'
    ox = m.abc.0 + 1
    call sqlFTabOthers abc
    call fTabAddTit      abc, ox, 2,             'others vorher'
    call fTabAddTit      abc, ox, 3,             'others nachher'
    call sqlFTab abc
    call sqlClose 17
    call tstEnd t
    call sqlDisConnect
    return
endProcedure tstSqlFTab

tstSqlFTab2: procedure expose m.
/*
$=/tstSqlFTab2/
    ### start tst tstSqlFTab2 #########################################
    Und Eins Oder
    .          zw aber
    Und Eins---------------zw aber---
    . und eins                22223
    . und eins                22224
    Und Eins---------------zw aber---
    Und Eins Oder
    .          zw aber
    a-------------b---
    aaa         222
    a-------------b---
    --- row 1 ---------------------------------------------------------+
    -------------
    .           Und Eins Oder       und eins
    .           zw aber            2.2223000e04              22223
    --- row 2 ---------------------------------------------------------+
    -------------
    .           Und Eins Oder       und eins
    .           zw aber            2.2224000e04              22224
    --- end of 2 rows -------------------------------------------------+
    -------------
$/tstSqlFTab2/
*/
    call pipeIni
    call tst t, 'tstSqlFTab2'
    call sqlConnect
    sq1 = 'select '' und eins'' "Und Eins Oder"',
             ', 22222 + row_number() over() "zw aber" ',
            'from sysibm.sysTables fetch first 2 rows only'
    call sqlQuery 17, sq1
    call sqlFTab sqlFTabOthers(sqlfTabReset(tstSqlFtab2, 17))
    call sqlClose 17
    sq2 =             'select ''aaa'' "a", 222 "b"' ,
            'from sysibm.sysTables fetch first 1 rows only'
    call sqlQuery 17, sq2
    call sqlFTab sqlFTabOthers(sqlfTabReset(tstSqlFtab2, 17))
    call sqlClose 17
    call sqlQuery 15, sq1
    call sqlFTabCol sqlFTabOthers(sqlfTabReset(tstSqlFtab5, 15))
    call sqlClose 15
    call tstEnd t
    call sqlDisConnect
    return
endProcedure tstSqlFTab2
tstSqlFTab3: procedure expose m.
/*
$=/tstSqlFTab3/
    ### start tst tstSqlFTab3 #########################################
    Und Eins Oder
    .          zw aber
    Und Eins--z---
    . und eins 1
    . und eins 2
    Und Eins--z---
    Und Eins Oder
    .          zw aber
    a-----b---
    aaa 222
    a-----b---
$/tstSqlFTab3/
*/
    call pipeIni
    call tst t, 'tstSqlFTab3'
    call sqlConnect
    sq1 = 'select '' und eins'' "Und Eins Oder"',
             ', row_number() over() "zw aber" ',
            'from sysibm.sysTables fetch first 2 rows only'
    r = jOpen(sqlRdr(sq1), '<')
    f = sqlRdrfTabReset(r, 'tstSqFTab3')
    b = in2Buf(r)
    call sqlFTabDetect f, b'.BUF'
    call fTab f, b
    call jClose r
    sq2 =             'select ''aaa'' "a", 222 "b"' ,
            'from sysibm.sysTables fetch first 1 rows only'
    call sqlQuery 17, sq2
    f = sqlfTabReset('tstSqFTab3t', 17)
    st = 'tstSqFTab3st'
    call sqlFetch2St 17, st
    s2 = 'tstSqFTab3s2'
    do sx=1 to m.st.0
        m.s2.sx = st'.'sx
        end
    m.s2.0 = m.st.0
    call sqlFTabDetect f, s2
    call fTabBegin f
    do sx=1 to m.st.0
        call out f(m.f.fmt, st'.'sx)
        end
    call fTabEnd f
    call tstEnd t
    call sqlDisConnect
    return
endProcedure tstSqlFTab3

tstSqlFTab4: procedure expose m.
/*
$=/tstSqlFTab4/
    ### start tst tstSqlFTab4 #########################################
    a
    1
    1 rows fetched: select 1 "a" from sysibm.sysDummy1
    sqlCode -204: drop table gibt.EsNicht
    a
    2
    1 rows fetched: select 2 "a" from sysibm.sysDummy1
    *** err: SQLCODE = -104: ILLEGAL SYMBOL "Y". SOME SYMBOLS THAT MIGH+
    T
    .    e 1:     BE LEGAL ARE: , FROM INTO
    .    e 2: src select x frm y
    .    e 3:   >              <<<pos 14 of 14<<<
    .    e 4: sql = select x frm y
    .    e 5: stmt = prepare s10 into :M.SQL.10.D from :src
    .    e 6: with into :M.SQL.10.D = M.SQL.10.D
    sqlCode -104: select x frm y
    a
    3
    1 rows fetched: select 3 "a" from sysibm.sysDummy1
    dy  => 1
    a
    1
    1 rows fetched: select 1 "a" from sysibm.sysDummy1
    sqlCode -204: drop table gibt.EsNicht
    a
    2
    1 rows fetched: select 2 "a" from sysibm.sysDummy1
    SQLCODE = -104: ILLEGAL SYMBOL "Y". SOME SYMBOLS THAT MIGHT
    .    BE LEGAL ARE: , FROM INTO
    src select x frm y
    .  >              <<<pos 14 of 14<<<
    sql = select x frm y
    stmt = prepare s10 into :M.SQL.10.D from :src
    with into :M.SQL.10.D = M.SQL.10.D
    sqlCode 0: rollback
    ret => 0
$/tstSqlFTab4/
*/
    call pipeIni
    call tst t, 'tstSqlFTab4'
    call sqlConnect
    b = jBuf('select 1 "a" from sysibm.sysDummy1;' ,
         , 'drop table gibt.EsNicht;' ,
         , 'select 2 "a" from sysibm.sysDummy1;',
         , ' select x frm y;',
         , 'select 3 "a" from sysibm.sysDummy1;')
    call tstout t, 'dy  =>' sqlsOut(scanSqlStmtRdr(b, 0))
    call tstout t, 'ret =>' sqlsOut(scanSqlStmtRdr(b, 0), 'rb ret')
    call tstEnd t
    call sqlDisConnect
    return
endProcedure tstSqlFTab4

tstSql4Obj: procedure expose m.
/*
$=/tstSql4Obj/
    ### start tst tstSql4Obj ##########################################
    tstR: @tstWriteoV2 isA :tstClass-1 = -11
    tstR:  .a2i = -11
    tstR:  .b3b = b3
    tstR:  .D4 = D4-11+D4++++.
    tstR:  .fl5 = -111.1
    tstR:  .ex6 = -.111e-11
    insert into cr.insTb -- tstClass-1
    .   ( , a2i, b3b, D4, fl5, ex6
    .   ) values .
    .   ( -11, -11, 'b3', 'D4-11+D4++++', -111.1, -.111e-11
    .   ) ; .
    insert into cr.insTbHex -- tstClass-1
    .   ( , a2i, b3b, D4, fl5, ex6
    .   ) values .
    .   ( -11, -11, 'b3', x'C40760F1F14EC4F44E4E4E4E', -111.1, -.111e-1+
    1
    .   ) ; .
    tstR: @tstWriteoV4 isA :tstClass-2
    tstR:  .c = c83
    tstR:  .a2i = 83
    tstR:  .b3b = b3b8
    tstR:  .D4 = D483+D4++++++++++++++++++++++++++++++++++++++++++++++++
    .++++++++++++++++++++++++++++++.
    tstR:  .fl5 = .183
    tstR:  .ex6 = .11183e-8
    insert into cr.insTb -- tstClass-2
    .   ( c, a2i, b3b, D4, fl5, ex6
    .   ) values .
    .   ( 'c83', 83, 'b3b8'
    .   , 'D483+D4++++++++++++++++++++++++++++++++++++++++++++++++++++++
    '
    .   || '++++++++++++++++++++++++'
    .   , .183, .11183e-8
    .   ) ; .
    insert into cr.insTbHex -- tstClass-2
    .   ( c, a2i, b3b, D4, fl5, ex6
    .   ) values .
    .   ( 'c83', 83, 'b3b8'
    .   , x'C407F8F34EC4F44E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E'
    .   || '++++++++++++++++++++++++++++++++'
    .   || x'314E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E4E'
    .   , .183, .11183e-8
    .   ) ; .
$/tstSql4Obj/
*/
    call pipeIni
    call tst t, 'tstSql4Obj'
    call pipe '+N'
    call tstDataClassOut '. c3 a2i i b3b c5 D4 c23 fl5 f8n2 ex6 e9n3',
        , -11, -11
    call tstDataClassOut 'c c3 a2i i b3b c5 D4 c93 fl5 f8n2 ex6 e9n3',
        , 83, 83
    call pipe 'P|'
    do cx=1 while in()
        i = m.in
        call mAdd t'.'trans, className(objClass(i)) 'tstClass-'cx
        call out i
        call sql4Obj i, 'cr.insTb'
        m.i.d4 = overlay('07'x, m.i.d4, 2)
        if length(m.i.d4) >= 62 then
            m.i.d4 = overlay('31'x, m.i.d4, 62)
        call sql4Obj i, 'cr.insTbHex'
        end
    call pipe '-'
    call tstEnd t
    return
endProcedure tstSql4Obj
tstSqlC: procedure expose m.
call sqlIni
/*
$=/tstSqlCRx/
    ### start tst tstSqlCRx ###########################################
    *** err: SQLCODE = -104: ILLEGAL SYMBOL "?". SOME SYMBOLS THAT MIGH+
    T
    .    e 1:     BE LEGAL ARE: . <IDENTIFIER> JOIN INNER LEFT RIGHT FU+
    LL CROSS ,
    .    e 2:     HAVING GROUP
    .    e 3: src select * from sysibm?sysDummy1
    .    e 4:   >    >>>pos 21 of 30>>>
    .    e 5: sql = select * from sysibm?sysDummy1
    .    e 6: stmt = prepare s9 into :M.SQL.9.D from :src
    .    e 7: with into :M.SQL.9.D = M.SQL.9.D
    *** err: SQLCODE = -204: NONONO.SYSDUMMY1 IS AN UNDEFINED NAME
    .    e 1: sql = select * from nonono.sysDummy1
    .    e 2: stmt = prepare s9 into :M.SQL.9.D from :src
    .    e 3: with into :M.SQL.9.D = M.SQL.9.D
    sys  ==> server CHSKA000DP4G    .
    fetched a1=abc, i2=12, c3=---
    .  I1 C2
    .   1 eins
    2222 zwei
$/tstSqlCRx/
$=/tstSqlCCsm/
    ### start tst tstSqlCCsm ##########################################
    *** err: SQLCODE = -104: ILLEGAL SYMBOL "?". SOME SYMBOLS THAT MIGH+
    T
    .    e 1:     BE LEGAL ARE: . <IDENTIFIER> JOIN INNER LEFT RIGHT FU+
    LL CROSS ,
    .    e 2:     HAVING GROUP
    .    e 3: src select * from sysibm?sysDummy1
    .    e 4:   >    >>>pos 21 of 30>>>
    .    e 5: sql = select * from sysibm?sysDummy1
    .    e 6: subsys = DE0G, host = RZZ
    *** err: SQLCODE = -204: NONONO.SYSDUMMY1 IS AN UNDEFINED NAME
    .    e 1: sql = select * from nonono.sysDummy1
    .    e 2: subsys = DE0G, host = RZZ
    sys RZZ/DE0G ==> server CHROI00ZDE0G    .
    fetched a1=abc, i2=12, c3=---
    .  I1 C2
    .   1 eins
    2222 zwei
$/tstSqlCCsm/ */
    sql1 = "select 1 i1, 'eins' c2 from sysibm.sysDummy1" ,
        "union all select 2222 i1, 'zwei' c2 from sysibm.sysDummy1"
    do tx=1 to 1 +  (m.tst_Csm \== 0)
        if tx = 1 then do
            call tst t, "tstSqlCRx"
            sys = ''
            end
        else do
            call tst t, "tstSqlCCsm"
            sys =  'RZZ/DE0G'
            end
        call sqlConnect sys
        cx = 9
        call sqlQuery cx, 'select * from sysibm?sysDummy1'
        call sqlQuery cx, 'select * from nonono.sysDummy1'
        call sqlQuery cx, "select 'abc' a1, 12 i2, current server srv",
                     ", case when 1=0 then 1 else null end c3",
                 "from sysibm.sysDummy1"
        do while sqlFetch(cx, dst)
            call out 'sys' sys '==> server' m.dst.srv
            call out 'fetched a1='m.dst.a1', i2='m.dst.i2', c3='m.dst.c3
            end
        call fTabAuto , sqlRdr(sql1)
        call sqlDisconnect
        call tstEnd t
        end
    return
endProcedure tstSqlC

tstSqlUpd: procedure expose m.
/*
$=/tstSqlUpd/
    ### start tst tstSqlUpd ###########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table (update session.dgtt  set c2 = 'u' +
    || c2)
    stmt = prepare s9 into :M.SQL.9.D from :src
    with into :M.SQL.9.D = M.SQL.9.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstSqlUpd/ */
    call tst t, "tstSqlUpd"
    cx = 9
    qx = 3
    call sqlConnect
    call sqlUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call sqlUpdate,"insert into session.dgtt" ,
                    "values(1, 'eins', '2012-04-01 06.07.08')"
    call sqlUpdate,"insert into session.dgtt" ,
                    "values(2, 'zwei', '2012-02-29 15:44:33.22')"
    call out 'insert updC' m.sql..updateCount
    call sqlUpdate,"insert into session.dgtt" ,
                      "select i1+10, 'zehn+'||strip(c2), t3+10 days",
                           "from session.dgtt"
    call out 'insert select updC' m.sql..updateCount
    call sqlQuery cx, 'select d.*' ,
               ', case when mod(i1,2) = 1 then 1 else null end grad' ,
               'from session.dgtt d'
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlClose cx
    call sqlQuery cx, "select * from final table (update session.dgtt",
                   " set c2 = 'u' || c2)"
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSqlUpd
tstSqlUpdPre: procedure expose m.
/*
$=/tstSqlUpdPre/
    ### start tst tstSqlUpdPre ########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table ( update session.dgtt set c2 = ? ||+
    . c2)
    stmt = prepare s5 into :M.SQL.5.D from :src
    with into :M.SQL.5.D = M.SQL.5.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstSqlUpdPre/ */
    call tst t, "tstSqlUpdPre"
    cx = 5
    qx = 3
    call sqlConnect
    call sqlUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call sqlUpdatePrepare 5, "insert into session.dgtt" ,
                                   "values (?, ?, ?)"
    call sqlUpdateExecute 5, 1, 'eins', '2012-04-01 06.07.08'
    call sqlUpdateExecute 5, 2, 'zwei', '2012-02-29 15:44:33.22'
    call out 'insert updC' m.sql.5.updateCount
    call sqlUpdatePrepare 5,"insert into session.dgtt" ,
                      "select i1+?, 'zehn+'||strip(c2), t3+? days",
                           "from session.dgtt"
    call sqlUpdateExecute 5, 10, 10
    call out 'insert select updC' m.sql.5.updateCount
    call sqlQueryPrepare cx, 'select d.*' ,
               ', case when mod(i1,2) = ? then 0+? else null end grad' ,
               'from session.dgtt d'
    call sqlQueryExecute cx, 1, 1
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlClose cx
    call sqlQueryPrepare cx, "select * from final table (" ,
              "update session.dgtt set c2 = ? || c2)"
    call sqlQueryExecute cx, "u"
    do qx=qx+1 while sqlFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlDisconnect
    call tstEnd t
    return
endProcedure tstSqlUpdPre
tstsqlRxUpd: procedure expose m.
/*
$=/tstsqlRxUpd/
    ### start tst tstsqlRxUpd #########################################
    insert updC 1
    insert select updC 2
    dest4.fet: 1 eins 2012-04-01-06.07.08.000000 1 updC 0
    dest5.fet: 2 zwei 2012-02-29-15.44.33.220000 --- updC 0
    dest6.fet: 11 zehn+eins 2012-04-11-06.07.08.000000 1 updC 0
    dest7.fet: 12 zehn+zwei 2012-03-10-15.44.33.220000 --- updC 0
    SQLCODE = 000,  SUCCESSFUL EXECUTION
    warnings  4=W no where
    sql = select * from final table (update session.dgtt set c2 = 'u' +
    || c2)
    stmt = prepare s9 into :M.SQL.9.D from :src
    with into :M.SQL.9.D = M.SQL.9.D
    dest9.fet: 1 ueins 2012-04-01-06.07.08.000000 updC 4
    dest10.fet: 2 uzwei 2012-02-29-15.44.33.220000 updC 4
    dest11.fet: 11 uzehn+eins 2012-04-11-06.07.08.000000 updC 4
    dest12.fet: 12 uzehn+zwei 2012-03-10-15.44.33.220000 updC 4
$/tstsqlRxUpd/ */
    call tst t, "tstsqlRxUpd"
    cx = 9
    qx = 3
    call sqlRxConnect
    call sqlRxUpdate,"declare global temporary table session.dgtt",
                           "(i1 int, c2 varchar(20), t3 timestamp)"
    call sqlRxUpdate,"insert into session.dgtt" ,
                    "values(1, 'eins', '2012-04-01 06.07.08')"
    call sqlRxUpdate,"insert into session.dgtt" ,
                    "values(2, 'zwei', '2012-02-29 15:44:33.22')"
    call out 'insert updC' m.sql..updateCount
    call sqlRxUpdate,"insert into session.dgtt" ,
                      "select i1+10, 'zehn+'||strip(c2), t3+10 days",
                           "from session.dgtt"
    call out 'insert select updC' m.sql..updateCount
    call sqlRxQuery cx, 'select d.*' ,
               ', case when mod(i1,2) = 1 then 1 else null end grad' ,
               'from session.dgtt d'
    do qx=qx+1 while sqlRxFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlRxClose cx
    call sqlRxQuery cx, "select * from final table",
                 "(update session.dgtt set c2 = 'u' || c2)"

    do qx=qx+1 while sqlRxFetch(cx, 'dest'qx'.fet')
        dst = 'dest'qx'.fet'
        call out dst':' m.dst.i1 m.dst.c2 m.dst.t3 ,
           'updC' m.sql.cx.updateCount
        drop m.dst.i1 m.dst.c2 m.dst.t3 m.dst.grad
        end
    call sqlRxClose cx
    call sqlRxDisconnect
    call tstEnd t
    return
endProcedure tstsqlRxUpd

tstSqlE: procedure expose m.
/*
$=/tstSqlE/
    ### start tst tstSqlE #############################################
    *** err: SQLCODE = -713: THE REPLACEMENT VALUE FOR CURRENT SCHEMA I+
    S
    .    e 1:     INVALID
    .    e 2: sql = set current schema = 'sysibm'
    .    e 3: stmt = execute immediate :src
    -713 set schema ''
    0 set schema
    0 select
    fetch=1 SYSIBM
$/tstSqlE/
*/
    call sqlConnect
    call tst t, "tstSqlE"
    call tstOut t, sqlExecute(3, "set current schema = 'sysibm'") ,
                                 "set schema ''"
    call tstOut t, sqlExecute(3, " set current schema = sysibm ") ,
                                 "set schema"
    call tstOut t, sqlExecute(3, " select current schema c"      ,
                                      "from sysibm.sysDummy1") 'select'
    call tstOut t, 'fetch='sqlFetch(3, aa) m.aa.c
    call sqlClose 3
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlE
tstSqlO: procedure expose m.
/*
$=/tstSqlO/
    ### start tst tstSqlO #############################################
    sqlCode 0: set current schema = A540769
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: sql = select * from sysdummy
    .    e 2: stmt = prepare s10 into :M.SQL.10.D from :src
    .    e 3: with into :M.SQL.10.D = M.SQL.10.D
    sqlCode -204: select * from sysdummy
    REQD=Y col=123 case=--- .sqlInd:-1 col5=anonym geburri=1956-04-01+
    -06.00.00.000000
$/tstSqlO/
*/
    call sqlConnect
    call scanWinIni
    call tst t, "tstSqlO"
    call sqlStmts 'set current schema = A540769';
    call sqlStmts 'select * from sysdummy';
    r = sqlRdr( ,
          "select d.*, 123, timestamp('01.04.1956','06:00:00')" ,
                             '"geburri walter",',
                    'case when 1=0 then 1 else null end caseNull,',
                    "'anonym'" ,
               'from sysibm.sysdummy1 d')
    call jOpen r, '<'
    do while jRead(r)
        o = m.r
        call out 'REQD='m.o.IBMREQD 'col='m.o.col2,
                  'case='m.o.CASENULL '.sqlInd:'m.o.caseNull.sqlInd,
                  'col5='m.o.col5,
                  'geburri='m.o.GEBURRI
        end
    call jClose r
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlO

tstSqlUpdComLoop: procedure expose m.
/*
$=/tstSqlUpdComLoop/
    ### start tst tstSqlUpdComLoop ####################################
    sqlCode 0: declare global temporary table session.dgtt (i1 int) on +
    commit ....
    sqlCode 0, 123 rows inserted: insert into session.dgtt select row_n+
    umber()....
    CNT
    123
    1 rows fetched: select count(*) cnt from session.dgtt
    123 rows deleted, 10 commits: delete from session.dgtt d where i1 i+
    n (sele....
    C
    0
    1 rows fetched: select count(*) cnt from session.dgtt
$/tstSqlUpdComLoop/ */
    call pipeIni
    call tst t, "tstSqlUpdComLoop"
    call sqlConnect
    call sqlsOut "declare global temporary table session.dgtt",
                           "(i1 int) on commit preserve rows"
    call sqlsOut "insert into session.dgtt",
       "select row_number() over() from sysibm.sysTables",
           "fetch first 123 rows only"
    call sqlsOut "select count(*) cnt from session.dgtt"
    call out sqlUpdComLoop("delete from session.dgtt d where i1 in",
       "(select i1 from session.dgtt fetch first 13 rows only)")
    call sqlsOut "select count(*) cnt from session.dgtt"
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlUpdComLoop

tstSqlO1: procedure expose m.
/*
$=/tstSqlO1/
    ### start tst tstSqlO1 ############################################
    tstR: @tstWriteoV2 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART
    tstR: @tstWriteoV3 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART_HIST
    tstR: @tstWriteoV4 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLES
    tstR: @tstWriteoV5 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLESPACE
    --- writeAll
    tstR: @tstWriteoV6 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART
    tstR: @tstWriteoV7 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLEPART_HIST
    tstR: @tstWriteoV8 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLES
    tstR: @tstWriteoV9 isA :<tstSqlO1Type>
    tstR:  .CR = SYSIBM
    tstR:  .TB = SYSTABLESPACE
$/tstSqlO1/
*/
    call sqlConnect
    call tst t, "tstSqlO1"
    qr = sqlRdr("select strip(creator) cr, strip(name) tb",
                     "from sysibm.sysTables",
                     "where creator='SYSIBM' and name like 'SYSTABL%'",
                     "order by 2 fetch first 4 rows only")
    call jOpen qr, m.j.cRead
    call mAdd t.trans, className(m.qr.type) '<tstSqlO1Type>'
    do while jRead(qr)
        abc = m.qr
        if m.qr.rowCount = 1 then do
            cx = m.qr.cursor
            end
        call out abc
        end
    call jClose qr
    call out '--- writeAll'
    call pipeWriteAll qr
    call tstEnd t
    return 0
endProcedure tstSqlO1

tstSqlO2: procedure expose m.
/*
$=/tstSqlO2/
    ### start tst tstSqlO2 ############################################
    CR     TB                RR
    SYSIBM SYSTABLEPART       1
    SYSIBM SYSTABLEPART_HIST  4
    SYSIBM SYSTABLES          9
    SYSIBM SYSTABLESPACE     16
$/tstSqlO2/
*/
    call sqlConnect
    call pipeIni
    call tst t, "tstSqlO2"
    call pipe '+N'
    call out    "select strip(creator) cr, strip(name) tb,"
    call out         "(row_number()over())*(row_number()over()) rr"
    call out         "from sysibm.sysTables"
    call out         "where creator='SYSIBM' and name like 'SYSTABL%'"
    call out         "order by 2 fetch first 4 rows only"
    call pipe 'N|'
    call sqlSel
    call pipe 'P|'
    call fTabAuto fTabReset(abc, 1)
    call pipe '-'
    call tstEnd t
    return 0
endProcedure tstSqlO2
tstSqlS1: procedure expose m.
/*
$=/tstSqlS1/
    ### start tst tstSqlS1 ############################################
    select c, a from sysibm.sysDummy1
    tstR: @tstWriteoV2 isA :<cla sql c a>
    tstR:  .C = 1
    tstR:  .A = a
    select ... where 1=0
    tstR: @ obj null
$/tstSqlS1/
*/
    call sqlIni
    call tst t, "tstSqlS1"
    call sqlConnect
    s1 = jSingle( ,
        sqlRdr("select count(*) c, 'a' a from sysibm.sysdummy1"))
    call mAdd t.trans, className(objClass(s1)) '<cla sql c a>'
    call out 'select c, a from sysibm.sysDummy1'
    call tstWrite t, s1
    call out 'select ... where 1=0'
    call tstWrite t, jSingle( ,
        sqlRdr("select*from sysibm.sysdummy1 where 1=0"), '')
    call tstEnd t
    return
endProcedure tstSqlS1
tstSqlStmt: procedure expose m.
/*
$=/tstSqlStmt/
    ### start tst tstSqlStmt ##########################################
    *** err: SQLCODE = -713: THE REPLACEMENT VALUE FOR CURRENT SCHEMA I+
    S
    .    e 1:     INVALID
    .    e 2: sql = set current schema = 'sysibm'
    .    e 3: stmt = execute immediate :src
    sqlCode -713: set current schema = 'sysibm'
    sqlCode 0: set current schema =  sysibm
    tstR: @tstWriteoV2 isA :<sql?sc>
    tstR:  .C = SYSIBM
    tstR: @tstWriteoV3 isA :<sql?sc>
    tstR:  .C = SYSIBM
$/tstSqlStmt/
*/
    call sqlConnect
    call tst t, "tstSqlStmt"
    cn = className(classNew('n* Sql u f%v  C'))
    call mAdd t.trans, cn '<sql?sc>'
    call sqlStmts "set current schema = 'sysibm'"
    call sqlsOut "    set current schema =  sysibm "
    call sqlsOut "   select current schema c  from sysDummy1", , 'o'
    call sqlsOut "  (select current schema c from sysDummy1)", , 'o'
    call tstEnd t
    return
endProcedure tstSqlStmt
tstSqlStmts: procedure expose m.
/*
$=/tstSqlStmts/
   ### start tst tstSqlStmts #########################################
   *** err: SQLCODE = -104: ILLEGAL SYMBOL "BLABLA". SOME SYMBOLS THAT
   .    e 1:     MIGHT BE LEGAL ARE: <ERR_STMT> <WNG_STMT> GET SQL SAV+
   EPOINT HOLD
   .    e 2:     FREE ASSOCIATE
   .    e 3: src blabla
   .    e 4:   > <<<pos 1 of 6<<<
   .    e 5: sql = blabla
   sqlCode -104: blabla
   sqlCode 0: set current schema=  sysIbm
   c
   1
   1 rows fetched: select count(*) "c" from sysDummy1 with   ur
   c
   1
   1 rows fetched: select count(*) "c" from sysDummy1 with   ur
   #jIn 1# set current -- sdf
   #jIn 2# schema = s100447;
   #jIn eof 3#
   sqlCode 0: set current schema = s100447
$/tstSqlStmts/ */
    call jIni
    call sqlConnect
    call scanReadIni
    call scanWinIni
    call tst t, "tstSqlStmts"
    call sqlStmts "blabla ;;set current schema=  sysIbm "
    b = jBuf('select count(*) "c" from sysDummy1 --com' ,
             ,'with  /* comm */ ur;')
    call sqlStmts b
    call sqlStmts b
    call mAdd mCut(t'.IN', 0), 'set current -- sdf', 'schema = s100447;'
    call sqlStmts
    call sqlDisConnect
    call tstEnd t
    return
endProcedure tstSqlStmts

tstCatTb:     /* ???????????????????? tkr kopieren und testen */
/*
$=/tstCatTb/
    ### start tst tstCatTb ############################################
    ..
    select * from sysibm.SYSDUMMY1  .
    IBMREQD
    I .
    Y .
    I .
    IBMREQD
$/tstCatTb/
*/
    call sqlConnect
    call tst t, 'tstCatTb'
    call sqlCatTb 'sysDummy1'
    call sqlCatTb 'SYSTableSpaceStats',
             , "name = 'A403A1' and dbName = 'DA540769'"
    call tstEnd t
    return
endProcedure tstCatTb

tstSqlDisDb: procedure expose m.
    call sqlDsn di, 'DP4G', '-dis db(*) sp(*)' ,
                    'restrict advisory limit(*)', 12
    m.oo.0 = 0
    call sqlDisDb oo, di
    say 'di.0' m.di.0 '==> oo.0' m.oo.0
    trace ?r
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE)
    say 'DB2PDB6.RR2HHAGE  ==>' ix m.oo.ix.sta
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE, 3)
    say 'DB2PDB6.RR2HHAGE.3  ==>' ix m.oo.ix.sta
    ix = sqlDisDbIndex(oo, DB2PDB6,RR2HHAGE,22)
    say 'DB2PDB6.RR2HHAGE.22 ==>' ix m.oo.ix.sta
    return
endProcedure tstSqlDisDb

/****** tstComp ********************************************************
    test the wsh compiler
***********************************************************************/
tstComp: procedure expose m.
    call compIni
    call tstCompDataConst
    call tstCompDataVars
    call tstCompShell
    call tstCompPrimary
    call tstCompExpr
    call tstCompFile
    call tstCompStmt
    call tstCompDir
    call tstCompObj
    call tstCompORun
    call tstCompORu2
    call tstCompORuRe
    call tstCompDataIO
    call tstCompPipe
    call tstCompPip2
    call tstCompRedir
    call tstCompComp
    call tstCompColon
    call tstCompTable
    call tstCompSyntax
    if m.err.os == 'TSO' then
        call tstCompSql
    call tstTotal
    return
endProcedure tstComp

tstComp1: procedure expose m.
parse arg ty nm cnt
    c1 = 0
    if cnt = 0 | cnt = '+' then do
        c1 = cnt
        cnt = ''
        end
    call jIni
    src = jBuf()
    call jOpen src, m.j.cWri
    do sx=2 to arg()
        call jWrite src, arg(sx)
        end
    call tstComp2 nm, ty, jClose(src), , c1, cnt
    return
endProcedure tstComp1

tstComp2: procedure expose m.
parse arg nm, spec, src, compSt
    call compIni
    call tst t, nm, compSt
    if src == '' then do
        src = jBuf()
        call tst4dp src'.BUF', mapInline(nm'Src')
        end
    m.t.moreOutOk = abbrev(strip(arg(5)), '+')
    oldErr = m.err.count
    cmp = comp(src)
    call out 'compile' spec',' (m.src.buf.0) 'lines:' m.src.buf.1
    r = compile(cmp, spec)
    noSyn = m.err.count = oldErr
    coErr = m.t.err
    say if( noSyn, "compiled", "*** syntaxed") r ":" objMet(r, 'oRun')

    cnt = 0
    do ax = 5 to max(arg(), 5) while m.t.err = coErr & noSyn
        a1 = strip(arg(ax))
        if a1 == '' & arg() >= 5 then
            iterate
        if abbrev(a1, '+') then do
            m.t.moreOutOk = 1
            a1 = strip(substr(a1, 2))
            end
        if datatype(a1, 'n') then
            cnt = a1
        else if a1 \== '' then
            call err 'tstComp2 bad arg('ax')' arg(ax)
        if cnt = 0 then do
            call mCut 'T.IN', 0
            call out "run without input"
            end
        else  do
            call mAdd mCut('T.IN', 0),
                ,"eins zwei drei", "zehn elf zwoelf?",
                , "zwanzig 21 22 23 24 ... 29|"
            do lx=4 to cnt
                call mAdd 'T.IN', left(lx "line" lx, lx+9, '-')';'
                end
            call out "run with" cnt "inputs"
            end
        m.t.inIx = 0
        call oRun r
        end
    call tstEnd t
    return
endProcedure tstComp2

tstCompDataConst: procedure expose m.
/*
$=/tstCompDataConst/
    ### start tst tstCompDataConst ####################################
    compile =, 8 lines:       Lline one, $** asdf
    run without input
    .      Lline one, .
    line two..
    line threecontinued on 4
    line five  fortsetzung
    line six   fortsetzung
$/tstCompDataConst/ */
    call tstComp1 '= tstCompDataConst',
        , '      Lline one, $** asdf',
        , 'line two.',
        , 'line three$*+ bla bla' ,
        , 'continued on 4',
        , 'line five $*( und so',
        , 'weiter $abc $? $''$*)'' $"$*)" und weiter $*) fortsetzung',
        , 'line six  $*( und $*( $** $*( so',
        , 'weiter $abc $? $*)'' $"$*)" und weiter $*) fortsetzung'

/*
$=/tstCompDataConstBefAftComm1/
    ### start tst tstCompDataConstBefAftComm1 #########################
    compile =, 3 lines:     $*(anfangs com.$*)       $*(plus$*) $** x
    run without input
    the only line;
$/tstCompDataConstBefAftComm1/ */
    call tstComp1 '= tstCompDataConstBefAftComm1',
        , '    $*(anfangs com.$*)       $*(plus$*) $** x',
        , 'the only line;',
        , '      $*(end kommentar$*)              '

/*
$=/tstCompDataConstBefAftComm2/
    ### start tst tstCompDataConstBefAftComm2 #########################
    compile =, 11 lines:     $*(anfangs com.$*)       $*(plus$*) $*+ x
    run without input
    the first non empty line;
    .      .
    befor an empty line with comments;
$/tstCompDataConstBefAftComm2/ */

    call tstComp1 '= tstCompDataConstBefAftComm2',
        , '    $*(anfangs com.$*)       $*(plus$*) $*+ x',
        , '    $*(forts Zeile com.$*)       $*(plus$*) $** x',
        , ' $*(comment 2. Zeile$*) $*(plus$*) $*+ x',
        , ' $*(forts Zeile com.$*) $*(plus$*) $** x',
        , 'the first non empty line;',
        , '      ',
        , 'befor an empty line with comments;',
        , ' $*(comment 4. Zeile$*) $*(plus$*) $*+ x',
        , ' $*(forts 4.Zeile com.$*) $*(plus$*) $** x',
        , '      $*(end kommentar$*)              $*+',
        , ' $*(forts end com.$*) $*(plus$*) $** x'
     return
endProcedure tstCompDataComm

tstCompDataVars: procedure expose m.
/*
$=/tstCompDataVars/
    ### start tst tstCompDataVars #####################################
    compile =, 5 lines:       Lline one, $** asdf
    run without input
    .      Lline one, .
    lline zwei output
    lline 3 .
    variable v1 = valueV1 ${v1}= valueV1; .
    .      $-{""$v1} = valueV1; .
$/tstCompDataVars/ */
    call tstComp1 '= tstCompDataVars',
        , '      Lline one, $** asdf',
        , '   $$ lline zwei output',
        , 'lline 3 $=v1= valueV1 ' ,
        , 'variable v1 = $v1 $"${v1}=" ${  v1  }; ',
        , '      $"$-{""""$v1} =" $-{$""$"v1"}; '
    return
endProcedure tstCompDataVars

tstCompShell: procedure expose m.
/*
$=/tstCompShell3/
    ### start tst tstCompShell3 #######################################
    compile @, 8 lines: call tstOut "T",  "abc" $-¢2*3$! "efg"$-¢2*3$!"+
    hij"
    run without input
    abc 6 efg6hij
    insert into A540769x.tqt002 with n(n) as ( select 4 from sysibm.s
    insert into A540769x.tqt002 with n(n) as ( select 4 from sysibm.s  +
    .   union all .
    abc 6 efg6hij
$/tstCompShell3/ */
    call tstComp1 '@ tstCompShell3',
        , 'call tstOut "T",  "abc" $-¢2*3$! "efg"$-¢2*3$!"hij"',
        , 'ix=3' ,
        , 'call tstOut "T","insert into A540769x.tqt002" ,',
        ,     '"with n(n) as ( select" $-¢ix+1$! "from sysibm.s"',
        , 'call tstOut "T","insert into A540769x.tqt002"  ,  ',
        ,    '"with n(n) as ( select" $-¢ix+1$! "from sysibm.s" , ' ,
        ,    '"    union all "' ,
        , '$$ abc $-¢2*3$! efg$-¢2*3$!hij',
/*
$=/tstCompShell/
    ### start tst tstCompShell ########################################
    compile @, 12 lines:   $$  Lline one, $** asdf
    run without input
    Lline one,
    lline zwei output
    v1 = valueV1 ${v1}= valueV1|
    REXX OUT L5 CONTINUED L6 CONTINUED L7
    L8 ONE
    L9 TWO
    valueV1
    valueV1 valueV2
    out  valueV1 valueV2
    SCHLUSS
$/tstCompShell/ */
    call tstComp1 '@ tstCompShell',
        , '  $$  Lline one, $** asdf',
        , '   $$ lline zwei output',
        , '        $=v1= valueV1 ' ,
        , '$$       v1 = $v1 $"${v1}=" ${  v1  }| ' ,
        , 'call out rexx out l5, ' ,
        , '     continued l6 , ' ,
        , '     continued l7   ' ,
        , 'call out l8 one    ' ,
        , 'call out l9 two$=v2=valueV2  ',
        , '$$- $v1  $$- $v1 $v2   ',
        , 'call out   "out "     $v1 $v2   ',
        , '$$-   schluss    '
/*
$=/tstCompShell2/
    ### start tst tstCompShell2 #######################################
    compile @, 13 lines: $@do j=0 to 1 $@¢ $$ do j=$j
    run without input
    do j=0
    after if 0 $@¢ $!
    after if 0 $=@¢ $!
    do j=1
    if 1 then $@¢ a
    a2
    if 1 then $@=¢ b
    b2
    after if 1 $@¢ $!
    after if 1 $=@¢ $!
    end
$/tstCompShell2/ */
    call tstComp1 '@ tstCompShell2',
        , '$@do j=0 to 1 $@¢ $$ do j=$j' ,
        ,     'if $j then $@¢ ',
        ,          '$$ if $j then $"$@¢" a $$a2' ,
        ,          '$!',
        ,     'if $j then $@=¢ ',
        ,          '$$ if $j then $"$@=¢" b $$b2' ,
        ,          '$!',
        ,     'if $j then $@¢ $!' ,
        ,     '$$ after if $j $"$@¢ $!"' ,
        ,     'if $j then $@=¢ $!' ,
        ,     '$$ after if $j $"$=@¢ $!"' ,
        ,     '$!',
        , '$$ end'
    return
endProcedure tstCompShell

tstCompPrimary: procedure expose m.
    call compIni
/*
$=/tstCompPrimary/
    ### start tst tstCompPrimary ######################################
    compile =, 16 lines: Strings $"$""$""""$""" $'$''$''''$'''
    run without input
    Strings $"$""$" $'$''$'
    rexx $-¢ 3 * 5 $! = 15
    rexx $-¢ 5 * 7 $! = 35
    rexx $-// 7 * 11 $// = 77
    rexx $-/abcEf/ 11 * 13 $/abcEf/ = 143
    data  line three line four  bis hier
    shell line five line six bis hier
    var get   v1 value Eins, v1 value Eins .
    var isDef v1 1, v2 0 .
    #jIn eof 1#
    var read  >1 0 rr undefined
    #jIn eof 2#
    var read  >2 0 rr undefined
    no call abc$-¢4*5$! $-¢efg$-¢6*7$! abc20 EFG42
    brackets $-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$! 1000
    run with 3 inputs
    Strings $"$""$" $'$''$'
    rexx $-¢ 3 * 5 $! = 15
    rexx $-¢ 5 * 7 $! = 35
    rexx $-// 7 * 11 $// = 77
    rexx $-/abcEf/ 11 * 13 $/abcEf/ = 143
    data  line three line four  bis hier
    shell line five line six bis hier
    var get   v1 value Eins, v1 value Eins .
    var isDef v1 1, v2 0 .
    #jIn 1# eins zwei drei
    var read  >1 1 rr eins zwei drei
    #jIn 2# zehn elf zwoelf?
    var read  >2 1 rr zehn elf zwoelf?
    no call abc$-¢4*5$! $-¢efg$-¢6*7$! abc20 EFG42
    brackets $-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$! 1000
$/tstCompPrimary/ */
    call vRemove 'v2'
    call tstComp1 '= tstCompPrimary 3',
        , 'Strings $"$""$""""$"""' "$'$''$''''$'''",
        , 'rexx $"$-¢ 3 * 5 $! =" $-¢ 3 * 5 $!' ,
        , 'rexx $"$-¢ 5 * 7 $! =" $-¢ 5 * 7 $!' ,
        , 'rexx $"$-// 7 * 11 $// =" $-// 7 * 11 $//' ,
        , 'rexx $"$-/abcEf/ 11 * 13 $/abcEf/ ="',
            '$-/abcEf/ 11 * 13 $/abcEf/' ,
        , 'data $-=¢ line three',
        , 'line four $! bis hier'  ,
        , 'shell $-@¢ $$ line five',
        , '$$ line six $! bis hier' ,
        , '$= v1  =   value Eins  $=rr=undefined $= eins = 1 ',
        , 'var get   v1 $v1, v1 ${  v1  } ',
        , 'var isDef v1 ${? v${  eins  }  }, v2 ${?v2 } ',
        , 'var read  >1 ${> rr} rr $rr' ,
        , 'var read  >2 ${> rr} rr $rr',
        , 'no call $"abc$-¢4*5$! $-¢efg$-¢6*7$!"',
            'abc$-¢4*5$! $-¢efg$-¢6*7$!$!',
        , 'brackets $"$-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$!"',
            '$-¢$-¢1+9$!*$-¢7+3$!*$-/b/5+5$/b/$!'
    return
endProcedure tstCompPrimary

tstCompExpr: procedure expose m.
    call compIni
/*
$=/tstCompExprStr/
    ### start tst tstCompExprStr ######################################
    compile -, 3 lines: $=vv=vvStr
    run without input
    vv=vvStr
    o2String($.-vv)=vvStr
$/tstCompExprStr/ */
    call tstComp1 '- tstCompExprStr',
        , '$=vv=vvStr' ,
        , '"vv="$vv' ,
        , '$"o2String($.-vv)="o2String($.-vv)'
/*
$=/tstCompExprObj/
    ### start tst tstCompExprObj ######################################
    compile ., 5 lines: $=vv=vvStr
    run without input
    vv=
    vvStr
    s2o($.vv)=
    vvStr
$/tstCompExprObj/ */
    call tstComp1 '. tstCompExprObj',
        , '$=vv=vvStr' ,
        , '"!vv="', '$.-vv',
        , '$."s2o($.vv)="', 's2o($-vv)'
/*
$=/tstCompExprDat/
    ### start tst tstCompExprDat ######################################
    compile =, 4 lines: $=vv=vvDat
    run without input
    vv=vvDat
    $.-vv= !vvDat
    $.-¢"abc"$!=!abc
$/tstCompExprDat/ */
    call tstComp1 '= tstCompExprDat',
        , '$=vv=vvDat' ,
        , 'vv=$vv',
        , '$"$.-vv=" $.-vv',
        , '$"$.-¢""abc""$!="$.-¢"abc"$!'

/*
$=/tstCompExprRun/
    ### start tst tstCompExprRun ######################################
    compile @, 3 lines: $=vv=vvRun
    run without input
    vv=vvRun
    o2string($.-vv)=vvRun
$/tstCompExprRun/ */
    call tstComp1 '@ tstCompExprRun',
        , '$=vv=vvRun' ,
        , 'call out "vv="$vv',
        , 'call out $"o2string($.-vv)="o2string($.-vv)'
/*
$=/tstCompExprCon/
    ### start tst tstCompExprCon ######################################
    compile #, 2 lines: $$ in # drinnen
    run without input
    $$ in # drinnen
    call out "vv="$vv
$/tstCompExprCon/
$=/tstCompExprCo2/
    ### start tst tstCompExprCo2 ######################################
    compile #, 3 lines: $$ in # drinnen
    run without input
    $$ in # drinnen
    call out "vv="$vv
    nacgh $#@
$/tstCompExprCo2/
*/
    call tstComp1 '# tstCompExprCon',
        , '$$ in # drinnen' ,
        , 'call out "vv="$vv'

    call tstComp1 '# tstCompExprCo2',
        , '$$ in # drinnen' ,
        , 'call out "vv="$vv',
        , '$#@ $$ nacgh $"$#@"'

    return
endProcedure tstCompExpr

tstCompStmt: procedure expose m.
/*
$=/tstCompStmt1/
    ### start tst tstCompStmt1 ########################################
    compile @, 8 lines: $= v1 = value eins  $= v2  =- 3*5*7 .
    run without input
    data v1 value eins v2 105
    eins
    zwei
    drei
    vier
    fuenf
    elf
    . zwoelf  dreiZ  .
    . vierZ .
    fuenfZ
    lang v1 value eins v2 945
    oRun ouput 1
$/tstCompStmt1/ */
    call pipeIni
    call compIni
    call vPut 'oRun', oRunner('call out "oRun ouput" (1*1)')
    call vRemove 'v2'
    call tstComp1 '@ tstCompStmt1',
        , '$= v1 = value eins  $= v2  =- 3*5*7 ',
        , '$$ data v1 $v1 v2 ${   v2  }',
        , '$$eins $@¢$$ zwei $$ drei  ',
        , '   $@¢   $!  $@//   $// $@/q r s /   $/q r s /',
             '       $@/eins/ $@¢ $$vier $! $/eins/ $! $$fuenf',
        , '$$elf $@=¢$@=¢ zwoelf  dreiZ  $!  ',
        , '   $@=¢   $! $@=¢ $@=¢ vierZ $! $! $! $$fuenfZ',
        , '$$- "lang v1" $v1 "v2" ${v2}*9',
        , '$@oRun'
/*
$=/tstCompStmt2/
    ### start tst tstCompStmt2 ########################################
    compile @, 1 lines: $@for qq $$ loop qq $qq
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    loop qq eins zwei drei
    #jIn 2# zehn elf zwoelf?
    loop qq zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    loop qq zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
$/tstCompStmt2/ */
    call tstComp1 '@ tstCompStmt2 3',
        , '$@for qq $$ loop qq $qq'

/*
$=/tstCompStmt3/
    ### start tst tstCompStmt3 ########################################
    compile @, 9 lines: $$ 1 begin run 1
    2 ct zwei
    ct 4 mit assign .
    run without input
    1 begin run 1
    3 run 3 ctV = ct 4 assign ctV|
    run 5 procCall $@prCa
    out in proc at 8
    run 6 vor call $@prCa
    out in proc at 8
    9 run end
    run with 3 inputs
    1 begin run 1
    3 run 3 ctV = ct 4 assign ctV|
    run 5 procCall $@prCa
    out in proc at 8
    run 6 vor call $@prCa
    out in proc at 8
    9 run end
$/tstCompStmt3/ */
    call tstComp1 '@ tstCompStmt3 3',
        , '$$ 1 begin run 1',
        , '$@ct $$ 2 ct zwei',
        , '$$ 3 run 3 ctV = $ctV|',
        , '$@ct $@=¢ct 4 mit assign $=ctV = ct 4 assign ctV $!',
        , '$$ run 5 procCall $"$@prCa" $@prCa',
        , '$$ run 6 vor call $"$@prCa"',
        , '$@prCa',
        , '$@proc prCa $$out in proc at 8',
        , '$$ 9 run end'

/*
$=/tstCompStmt4/
    ### start tst tstCompStmt4 ########################################
    compile @, 4 lines: $=eins=vorher
    run without input
    eins vorher
    eins aus named block eins .
$/tstCompStmt4/ */
    call tstComp1 '@ tstCompStmt4 0',
        , '$=eins=vorher' ,
        , '$$ eins $eins' ,
        , '$=/eins/aus named block eins $/eins/' ,
        , '$$ eins $eins'
/*
$=/tstCompStmtDo/
    ### start tst tstCompStmtDo #######################################
    compile @, 2 lines: ti=0$@do y=3 to 4 $@do 2 $@¢
    run without input
    y=3 ti1 z=7
    y=3 ti1 z=8
    y=3 ti2 z=7
    y=3 ti2 z=8
    y=4 ti3 z=7
    y=4 ti3 z=8
    y=4 ti4 z=7
    y=4 ti4 z=8
$/tstCompStmtDo/ */
    call tstComp1 '@ tstCompStmtDo', 'ti=0$@do y=3 to 4 $@do 2 $@¢',
     ,    'ti = ti + 1',
        '$@do $*(sdf$*) z $*(sdf$*) =7 to 8 $$ y=$y ti$-¢ti$! z=$z $!'

/*
$=/tstCompStmtDo2/
    ### start tst tstCompStmtDo2 ######################################
    compile @, 7 lines: $$ $-=/sqlSel/
    run without input
    select 1 abc select 2 abc after table .
$/tstCompStmtDo2/ */
    call tstComp1 '@ tstCompStmtDo2',
         , '$$ $-=/sqlSel/',
         ,     '$=ty = abc ',
         ,     '$@do tx=1 to 2 $@=/table/',
         ,          'select $tx $ty',
         , '$/table/',
         ,     '$=ty = abc',
         ,     'after table',
         '$/sqlSel/'
/*
$=/tstCompStmtWith/
    ### start tst tstCompStmtWith #####################################
    compile @, 3 lines: $@with $.vA $$ fEins=$FEINS fZwei=$FZWEI va&fEi+
    ns=${vA&FEINS}
    run without input
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
    fEins=2Eins fZwei=2Zwei va&fEins=1Eins
    fEins=1Eins fZwei=1Zwei va&fEins=1Eins
$/tstCompStmtWith/
*/
    cl = classNew('n? tstStmtWith u f FEINS v, f FZWEI v')
    v1 = onew(cl)
    m.v1.feins = '1Eins'
    m.v1.fzwei = '1Zwei'
    v2 = oNew(cl)
    m.v2.feins ='2Eins'
    m.v2.fzwei ='2Zwei'
    call vPut 'vA', v1
    call vPut 'vB', v2
    stmt = '$$ fEins=$FEINS fZwei=$FZWEI va&fEins=${vA&FEINS}'
    call tstComp1 '@ tstCompStmtWith',
         , '$@with $.vA' stmt ,
         , '$@with $vA $@¢' stmt ,
         , '$@with $vB ' stmt stmt '$!'
/*
$=/tstCompStmtArg/
    ### start tst tstCompStmtArg ######################################
    compile :, 11 lines: v2 = var2
    run without input
    a1=eins a2=zwei, a3=elf b1= b2=
    after op= v2=var2 var2=zwei,
    a1=EINS a2=ZWEI a3= b1=ELF b2=
    after op=- v2=var2 var2=ZWEI
    a1=EINS a2=ZWEI a3= b1=ELF b2=
    after op=. v2=var2 var2=ZWEI
$/tstCompStmtArg/
*/
    call tstComp1 ': tstCompStmtArg',
         , 'v2 = var2',
         , '@% outArg eins zwei, elf',
         , '$$ after op= v2=$v2 var2=$var2',
         , '@% outArg - eins zwei, elf',
         , '$$ after op=- v2=$v2 var2=$var2',
         , '@% outArg . eins zwei, elf',
         , '$$ after op=. v2=$v2 var2=$var2',
         , 'proc $@:/outArg/' ,
         , 'arg a1 {$v2} a3, b1 b2',
         , '$$ a1=$a1 a2=${$v2} a3=$a3 b1=$b1 b2=$b2' ,
         , '$/outArg/'
     cl = classNew('n? tstStmtWith u f FEINS v, f FZWEI v')
     return
endProcedure tstCompStmt

tstCompSyntax: procedure expose m.
    call tstCompSynPrimary
    call tstCompSynAss
    call tstCompSynRun
    return
endProcedure tstCompSyntax

tstCompSynPrimary: procedure expose m.

/*
$=/tstCompSynPri1/
    ### start tst tstCompSynPri1 ######################################
    compile @, 1 lines: a $ =
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $ =
    .    e 2: pos 3 in line 1: a $ =
    *** err: no method oRun in class String
$/tstCompSynPri1/ */
    call tstComp1 '@ tstCompSynPri1 +', 'a $ ='

/*
$=/tstCompSynPri2/
    ### start tst tstCompSynPri2 ######################################
    compile @, 1 lines: a $. {
    *** err: scanErr primary block or expression expected expected
    .    e 1: last token  scanPosition . {
    .    e 2: pos 4 in line 1: a $. {
    *** err: scanErr primary block or expression expected expected
    .    e 1: last token  scanPosition . {
    .    e 2: pos 4 in line 1: a $. {
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $. {
    .    e 2: pos 3 in line 1: a $. {
    *** err: no method oRun in class String
$/tstCompSynPri2/ */
    call tstComp1 '@ tstCompSynPri2 +', 'a $. {'

/*
$=/tstCompSynPri3/
    ### start tst tstCompSynPri3 ######################################
    compile @, 1 lines: b $-  ¢  .
    *** err: scanErr primary block or expression expected expected
    .    e 1: last token  scanPosition -  ¢
    .    e 2: pos 4 in line 1: b $-  ¢
    *** err: scanErr primary block or expression expected expected
    .    e 1: last token  scanPosition -  ¢
    .    e 2: pos 4 in line 1: b $-  ¢
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $-  ¢
    .    e 2: pos 3 in line 1: b $-  ¢
    *** err: no method oRun in class String
$/tstCompSynPri3/ */
    call tstComp1 '@ tstCompSynPri3 +', 'b $-  ¢  '

/*
$=/tstCompSynPri4/
    ### start tst tstCompSynPri4 ######################################
    compile @, 1 lines: a ${ $*( sdf$*) } =
    *** err: scanErr var name expected
    .    e 1: last token  scanPosition } =
    .    e 2: pos 17 in line 1: a ${ $*( sdf$*) } =
$/tstCompSynPri4/ */
    call tstComp1 '@ tstCompSynPri4 +', 'a ${ $*( sdf$*) } ='

/*
$=/tstCompSynFile/
    ### start tst tstCompSynFile ######################################
    compile @, 1 lines: $@.<$*( co1 $*) $$abc
    *** err: scanErr primary, block or expression expected
    .    e 1: last token  scanPosition .<$*( co1 $*) $$abc
    .    e 2: pos 3 in line 1: $@.<$*( co1 $*) $$abc
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@.<$*( co1 $*) $$abc
    .    e 2: pos 1 in line 1: $@.<$*( co1 $*) $$abc
    *** err: no method oRun in class String
$/tstCompSynFile/ */
    call tstComp1 '@ tstCompSynFile +', '$@.<$*( co1 $*) $$abc'

    return
endProcedure tstCompSynPrimary

tstCompSynAss: procedure expose m.

/*
$=/tstCompSynAss1/
    ### start tst tstCompSynAss1 ######################################
    compile @, 1 lines: $=
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 3 in line 1: $=
$/tstCompSynAss1/ */
    call tstComp1 '@ tstCompSynAss1 +', '$='

/*
$=/tstCompSynAss2/
    ### start tst tstCompSynAss2 ######################################
    compile @, 2 lines: $=   .
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $=
$/tstCompSynAss2/ */
    call tstComp1 '@ tstCompSynAss2 +', '$=   ', 'eins'

/*
$=/tstCompSynAss3/
    ### start tst tstCompSynAss3 ######################################
    compile @, 2 lines: $=   $$
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition $$
    .    e 2: pos 6 in line 1: $=   $$
$/tstCompSynAss3/ */
    call tstComp1 '@ tstCompSynAss3 +', '$=   $$', 'eins'

/*
$=/tstCompSynAss4old/
    ### start tst tstCompSynAss4 ######################################
    compile @, 1 lines: $=   eins
    *** err: scanErr = expected in assignment after $= var
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $=   eins
$/tstCompSynAss4old/
$=/tstCompSynAss4/
    ### start tst tstCompSynAss4 ######################################
    compile @, 1 lines: $=   eins
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $=   eins
    .    e 2: pos 1 in line 1: $=   eins
    *** err: no method oRun in class String
$/tstCompSynAss4/ */
call tstComp1 '@ tstCompSynAss4 +', '$=   eins'

/*
$=/tstCompSynAss5/
    ### start tst tstCompSynAss5 ######################################
    compile @, 1 lines: $=  abc eins $$ = x
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $=  abc eins $$ = x
    .    e 2: pos 1 in line 1: $=  abc eins $$ = x
    *** err: no method oRun in class String
$/tstCompSynAss5/
$=/tstCompSynAss5old/
    ### start tst tstCompSynAss5 ######################################
    compile @, 1 lines: $=  abc eins $$ = x
    *** err: scanErr = expected in assignment after $= var
    .    e 1: last token  scanPosition eins $$ = x
    .    e 2: pos 9 in line 1: $=  abc eins $$ = x
$/tstCompSynAss5old/ */
    call tstComp1 '@ tstCompSynAss5 +', '$=  abc eins $$ = x'

/*
$=/tstCompSynAss6/
    ### start tst tstCompSynAss6 ######################################
    compile @, 1 lines: $=  abc =
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $=  abc =
$/tstCompSynAss6/ */
    call tstComp1 '@ tstCompSynAss6 +', '$=  abc ='

/*
$=/tstCompSynAss7/
    ### start tst tstCompSynAss7 ######################################
    compile @, 1 lines: $=  abc =..
    *** err: scanErr block or expression in assignment after $= expecte+
    d
    .    e 1: last token  scanPosition .
    .    e 2: pos 11 in line 1: $=  abc =..
$/tstCompSynAss7/ */
    call tstComp1 '@ tstCompSynAss7 +', '$=  abc =.'
    return
endProcedure tstCompSynAss

tstCompSynRun: procedure expose m.

/*
$=/tstCompSynRun1/
    ### start tst tstCompSynRun1 ######################################
    compile @, 1 lines: $@
    *** err: scanErr primary, block or expression expected
    .    e 1: last token  scanPosition .
    .    e 2: atEnd after line 1: $@
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@
    .    e 2: pos 1 in line 1: $@
    *** err: no method oRun in class String
$/tstCompSynRun1/ */
    call tstComp1 '@ tstCompSynRun1 +', '$@'

/*
$=/tstCompSynRun2/
    ### start tst tstCompSynRun2 ######################################
    compile @, 1 lines: $@=
    *** err: scanErr primary, block or expression expected
    .    e 1: last token  scanPosition =
    .    e 2: pos 3 in line 1: $@=
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@=
    .    e 2: pos 1 in line 1: $@=
$/tstCompSynRun2/ */
    call tstComp1 '@ tstCompSynRun2 +', '$@='

/*
$=/tstCompSynRun3/
    ### start tst tstCompSynRun3 ######################################
    compile @, 1 lines: $@: und
    *** err: scanErr bad kind : in compExpr
    .    e 1: last token  scanPosition und
    .    e 2: pos 5 in line 1: $@: und
    *** err: no method oRun in class Null
$/tstCompSynRun3/ */
    call tstComp1 '@ tstCompSynRun3 +', '$@: und'

/*
$=/tstCompSynFor4/
    ### start tst tstCompSynFor4 ######################################
    compile @, 1 lines: $@for
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $@for
$/tstCompSynFor4/ */
    call tstComp1 '@ tstCompSynFor4 +', '$@for'

/*
$=/tstCompSynFor5/
    ### start tst tstCompSynFor5 ######################################
    compile @, 2 lines: $@for
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 6 in line 1: $@for
$/tstCompSynFor5/ */
    call tstComp1 '@ tstCompSynFor5 +', '$@for', a

/*
$=/tstCompSynFor6/
    ### start tst tstCompSynFor6 ######################################
    compile @, 2 lines: a
    *** err: scanErr variable or named block after for
    .    e 1: last token  scanPosition .
    .    e 2: pos 15 in line 2:  b $@for   $$q
$/tstCompSynFor6/
    call tstComp1 '@ tstCompSynFor6 +', 'a', ' b $@for   $$q'
*/
/*
$=/tstCompSynFor7/
    ### start tst tstCompSynFor7 ######################################
    compile @, 3 lines: a
    *** err: scanErr var? statement after for expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 11 in line 2:  b $@for a
$/tstCompSynFor7/ */
    call tstComp1 '@ tstCompSynFor7 +', 'a', ' b $@for a', '  $$q'

/*
$=/tstCompSynCt8/
    ### start tst tstCompSynCt8 #######################################
    compile @, 3 lines: a
    *** err: scanErr ct statement expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 8 in line 2:  b $@ct
$/tstCompSynCt8/ */
    call tstComp1 '@ tstCompSynCt8 +', 'a', ' b $@ct', '  $$q'

/*
$=/tstCompSynProc9/
    ### start tst tstCompSynProc9 #####################################
    compile @, 2 lines: a
    *** err: scanErr var or namedBlock expected after proc
    .    e 1: last token  scanPosition .
    .    e 2: pos 15 in line 2:  b $@proc  $$q
$/tstCompSynProc9/ */
    call tstComp1 '@ tstCompSynProc9 +', 'a', ' b $@proc  $$q'

/*
$=/tstCompSynProcA/
    ### start tst tstCompSynProcA #####################################
    compile @, 2 lines: $@proc p1
    *** err: scanErr proc statement expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 10 in line 1: $@proc p1
$/tstCompSynProcA/ */
    call tstComp1 '@ tstCompSynProcA +', '$@proc p1', '  $$q'

/*
$=/tstCompSynCallB/
    ### start tst tstCompSynCallB #####################################
    compile @, 1 lines: $@% ¢roc p1$!
    *** err: scanErr primary, block or expression expected
    .    e 1: last token  scanPosition % ¢roc p1$!
    .    e 2: pos 3 in line 1: $@% ¢roc p1$!
    *** err: scanErr rexxShell expected: compile @ stopped before end o+
    f input
    .    e 1: last token  scanPosition $@% ¢roc p1$!
    .    e 2: pos 1 in line 1: $@% ¢roc p1$!
    *** err: no method oRun in class String
$/tstCompSynCallB/ */
    call tstComp1 '@ tstCompSynCallB +', '$@% ¢roc p1$!'

/*
$=/tstCompSynCallC/
    ### start tst tstCompSynCallC #####################################
    compile @, 1 lines: $@%¢call roc p1 !
    *** err: scanErr ending $! expected after ¢
    .    e 1: last token  scanPosition .
    .    e 2: atEnd after line 1: $@%¢call roc p1 !
$/tstCompSynCallC/ */
    call tstComp1 '@ tstCompSynCallC +', '$@%¢call roc p1 !'

/*
$=/tstCompSynCallD/
    ### start tst tstCompSynCallD #####################################
    compile @, 2 lines: $@^¢call( $** roc
    *** err: scanErr ending $! expected after ¢
    .    e 1: last token  scanPosition )
    .    e 2: pos 13 in line 2:  $*( p1 $*) )
$/tstCompSynCallD/ */
    call tstComp1 '@ tstCompSynCallD +',
        ,'$@^¢call( $** roc' , ' $*( p1 $*) )'
    return
endProcedure tstCompSynRun

tstCompObj: procedure expose m.
    call tstReset t
    call oIni
    cl = classNew('n? tstCompCla u v, f FEINS v, f FZWEI v')
    do rx=1 to 10
        o = oNew(cl)
        m.tstComp.rx = o
        m.o = 'o'rx
        if rx // 2 = 0 then do
            m.o.fEins = 'o'rx'.1'
            m.o.fZwei = 'o'rx'.fZwei'rx
            end
        else do
            m.o.fEins = 'o'rx'.fEins'
            m.o.fZwei = 'o'rx'.2'
            end
        call mAdd 'T.TRANS', m.tstComp.rx '<o'rx'>'
        end

/*
$=/tstCompObjRef/
    ### start tst tstCompObjRef #######################################
    compile @, 13 lines: o1=m.tstComp.1
    run without input
    out .$"string" o1
    string
    out . o1
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .{ o2 }
    tstR: @<o2> isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    out .¢ o3 $!
    tstR: @<o3> isA :tstCompCla = o3
    tstR:  .FEINS = o3.fEins
    tstR:  .FZWEI = o3.2
    out .¢ o4 $!
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    out ./-/ o5 $/-/
    tstR: @<o5> isA :tstCompCla = o5
    tstR:  .FEINS = o5.fEins
    tstR:  .FZWEI = o5.2
$/tstCompObjRef/ */
m.ttt=1
    call tstComp1 '@ tstCompObjRef' ,
        , 'o1=m.tstComp.1',
        , 'o2 = m.tstComp.2' ,
        , '$$ out $".$""string""" o1 $$."string"',
        , '$$ out . o1 $$. o1',
        , '$$ out .{ o2 } $$.¢ o2 $!',
        , '$$ out .¢ o3 $"$!" $$.¢ ', ' m.tstComp.3 ', ' $!',
        , '$$ out .¢ o4 $"$!" $$.¢ ', ' m.tstComp.4 ', ' $!',
        , '$$ out ./-/ o5 $"$/-/" $$./-/  m.tstComp.5 ', ' $/-/'

/*
$=/tstCompObjRefPri/
    ### start tst tstCompObjRefPri ####################################
    compile @, 9 lines: $$ out .$"$.{o1}" $$.¢ m.tstComp.1 $!
    run without input
    out .$.{o1}
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .$.-{o2}
    <o2>
    out .$.={o3}
    . m.tstComp.3 .
    out .$.@{out o4}
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    tstWriteO kindOf ORun oRun end   >>>
    out .$.@¢$$abc $$efg$!
    tstWriteO kindOf ORun oRun begin <<<
    abc
    efg
    tstWriteO kindOf ORun oRun end   >>>
    out .$.@¢o5$!
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o5> isA :tstCompCla = o5
    tstR:  .FEINS = o5.fEins
    tstR:  .FZWEI = o5.2
    abc
    tstWriteO kindOf ORun oRun end   >>>
$/tstCompObjRefPri/ */
    call tstComp1 '@ tstCompObjRefPri' ,
        , '$$ out .$"$.{o1}" $$.¢ m.tstComp.1 $!',
        , '$$ out .$"$.-{o2}" $$.-¢ m.tstComp.2 $!',
        , '$$ out .$"$.={o3}" $$.=¢ m.tstComp.3 $!',
        , '$$ out .$"$.@{out o4}" $$.@@¢ call out m.tstComp.4 $!',
    , '$$ out .$"$.@¢$$abc $$efg$!" $$. $.@@¢ $$abc ', ' ', ' $$efg $!',
        , '$$ out .$"$.@¢o5$!" $$. $.@@¢ $$. m.tstComp.5', '$$abc $!'

/*
$=/tstCompObjRefFile/
    ### start tst tstCompObjRefFile ###################################
    compile @, 7 lines: $$ out .$".<.¢o1!" $$.<.¢ m.tstComp.1 $!
    run without input
    out ..<.¢o1!
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .<$.-{o2}
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o2> isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$.<{o3}
    tstWriteO kindOf JRW jWriteNow begin <<<
    . m.tstComp.3 .
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$.<@{out o4}
    tstWriteO kindOf JRW jWriteNow begin <<<
    tstR: @<o4> isA :tstCompCla = o4
    tstR:  .FEINS = o4.1
    tstR:  .FZWEI = o4.fZwei4
    tstWriteO kindOf JRW jWriteNow end   >>>
    out .$<@¢$$abc $$efg$!
    tstWriteO kindOf JRW jWriteNow begin <<<
    abc
    efg
    tstWriteO kindOf JRW jWriteNow end   >>>
$/tstCompObjRefFile/ */

    call tstComp1 '@ tstCompObjRefFile' ,
        , '$$ out .$".<.¢o1!" $$.<.¢ m.tstComp.1 $!',
        , '$$ out .$"<$.-{o2}" $$<.¢ m.tstComp.2 $!',
        , '$$ out .$"$.<{o3}" $$<=¢ m.tstComp.3 $!',
        , '$$ out .$"$.<@{out o4}" $$<@¢ call out m.tstComp.4 $!',
    , '$$ out .$"$<@¢$$abc $$efg$!" $$<@¢ $$abc ', ' ', ' $$efg $!'

/*
$=/tstCompObjFor/
    ### start tst tstCompObjFor #######################################
    compile @, 2 lines: $@do rx=1 to 3 $$. m.tstComp.rx
    run without input
    FEINS=o1.fEins FZWEI=o1.2
    FEINS=o2.1 FZWEI=o2.fZwei2
    FEINS=o3.fEins FZWEI=o3.2
$/tstCompObjFor/
*/
    call tstComp1 '@ tstCompObjFor' ,
        , '$@do rx=1 to 3 $$. m.tstComp.rx' ,
        , '$| $@forWith witx $$ FEINS=$FEINS FZWEI=$FZWEI'

/*
$=/tstCompObjRun/
    ### start tst tstCompObjRun #######################################
    compile @, 4 lines: $$ out .$"$@¢o1!" $$@¢ $$. m.tstComp.1 $!
    run without input
    out .$@¢o1!
    tstWriteO kindOf ORun oRun begin <<<
    tstR: @<o1> isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstWriteO kindOf ORun oRun end   >>>
    out .$<@¢$$abc $$efg$!
    tstWriteO kindOf JRW jWriteNow begin <<<
    abc
    efg
    tstWriteO kindOf JRW jWriteNow end   >>>
$/tstCompObjRun/ */
    call tstComp1 '@ tstCompObjRun' ,
        , '$$ out .$"$@¢o1!" $$@¢ $$. m.tstComp.1 $!',
    , '$$ out .$"$<@¢$$abc $$efg$!" $$<@¢ $$abc ', ' ', ' $$efg $!'

    m.t.trans.0 = 0
/*
$=/tstCompObj/
    ### start tst tstCompObj ##########################################
    compile @, 6 lines: o1=m.tstComp.1
    run without input
    out . o1
    tstR: @tstWriteoV1 isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    out .{ o2 }
    tstR: @tstWriteoV2 isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
    out .¢ o1, o2!
    tstR: @tstWriteoV1 isA :tstCompCla = o1
    tstR:  .FEINS = o1.fEins
    tstR:  .FZWEI = o1.2
    tstR: @tstWriteoV2 isA :tstCompCla = o2
    tstR:  .FEINS = o2.1
    tstR:  .FZWEI = o2.fZwei2
$/tstCompObj/ */
    call tstComp1 '@ tstCompObj' ,
        , 'o1=m.tstComp.1',
        , 'o2 = m.tstComp.2' ,
        , '$$ out . o1 $$. o1',
        , '$$ out .{ o2 } $$.¢ o2 $!',
    , '$$ out .¢ o1, o2!$; $@<.¢  m.tstComp.1  ', '  m.tstComp.2  $!'
    return
    m.t.trans.0 = 0
endProcedure tstCompObj

tstCompORun: procedure expose  m.
/*
$=/tstCompORun/
    ### start tst tstCompORun #########################################
    compile @, 6 lines: $@oRun
    run without input
    oRun arg=1, v2=, v3=, v4=
    oRun arg=1, v2=, v3=, v4=
    oRun arg=2, v2=-{1 arg only} oder?, v3=, v4=
    oRun arg=2, v2=!.{1 obj only} oder?, v3=, v4=
    oRun arg=3, v2={2 args}, v3=und zwei?, v4=
    oRun arg=4, v2={3 args}, v3=zwei, v4=und drei?
$/tstCompORun/ */
    call compIni
    call vPut 'oRun', oRunner('parse arg , v2, v3, v4;',
        'call tstOut t, "oRun arg="arg()", v2="v2", v3="v3", v4="v4' )
    call tstComp1 '@ tstCompORun',
        , '$@oRun', '$@%¢oRun$!' ,
        , '    $@%¢oRun $"-{1 arg only}" oder?$!' ,
        , '    $@%¢oRun - $.".{1 obj only}" ''oder?''$! $=v2=zwei' ,
        , '    $@%¢oRun - $"{2 args}", "und" $v2"?"$!' ,
        , '    $@%¢oRun - $"{3 args}", $v2, "und drei?"$!'
    return
endProcedure tstCompORun

tstCompORu2: procedure expose  m.
/*
$=/tstCompORu2/
    ### start tst tstCompORu2 #########################################
    compile @, 6 lines: $@oRun
    run without input
    oRun arg=1, v2=, v3=, v4=
    oRun arg=1, v2=, v3=, v4=
    oRun arg=2, v2=eins, zwei, drei, v3=, v4=
    oRun arg=2, v2=eins, zwei, drei, v3=, v4=
    oRun arg=4, v2=-eins, v3=zwei, v4=DREI
    oRun arg=4, v2=-eins, v3=zwei, v4=DREI
$/tstCompORu2/ */
    call compIni
    call vPut 'oRun', oRunner('parse arg , v2, v3, v4;',
        'call tstOut t, "oRun arg="arg()", v2="v2", v3="v3", v4="v4' )
    call tstComp1 '@ tstCompORu2',
        , '$@oRun', '$@%oRun',
        , '$@% oRun  eins, zwei, drei' ,
        , '$@%¢ oRun eins, zwei, drei $!',
        , '$@% oRun  - "-eins", "zwei", drei' ,
        , '$@%¢ oRun - "-eins", "zwei", drei $!'
    return
endProcedure tstCompORu2

tstCompORuRe: procedure expose  m.
/*
$=/tstCompORuRe/
    ### start tst tstCompORuRe ########################################
    compile @, 9 lines: $$ primary $-^oRuRe eins, zwei
    run without input
    primary oRuRe(arg=1, v2=, v3=) eins, zwei
    oRuRe(arg=2, v2=expr, zwei, v3=)
    oRuRe(arg=3, v2=-expr, v3=zwei)
    oRuRe(arg=2, v2=block, zwei, v3=)
    oRuRe(arg=3, v2=-block, v3=zwei)
$/tstCompORuRe/ */
    call compIni
    call vPut 'oRuRe', oRunner('parse arg , v2, v3;',
        'return "oRuRe(arg="arg()", v2="v2", v3="v3")"' )
    call tstComp1 '@ tstCompORuRe',
        , '$$ primary $-^oRuRe eins, zwei' ,
        , '$$-^ oRuRe expr, zwei',
        , '$$-^ oRuRe - "-expr", "zwei"',
        , '$$-^¢oRuRe block, zwei$!' ,
        , '$$-^¢',, 'oRuRe - "-block", "zwei"' , , '$!'
    return
endProcedure tstCompORuRe

tstCompDataIO: procedure expose m.
/*
$=/tstCompDataHereData/
    ### start tst tstCompDataHereData #################################
    compile =, 13 lines:  herdata $@#/stop/    .
    run without input
    . herdata .
    heredata 1 $x
    heredata 2 $y
    nach heredata
    . herdata ¢ .
    heredata 1 xValue
    heredata 2 yValueY
    nach heredata ¢
    . herdata { .
    HEREDATA 1 xValue
    heredata 2 yValueY
    nach heredata {
$/tstCompDataHereData/ */
    call tstComp1 '= tstCompDataHereData',
        , ' herdata $@#/stop/    ',
        , 'heredata 1 $x',
        , 'heredata 2 $y',
        , '$/stop/ $$ nach heredata',
        , ' herdata ¢ $@=/stop/    ',
        , '$=x=xValue$=y=yValueY',
        , 'heredata 1 $x',
        , 'heredata 2 $y',
        , '$/stop/ $$ nach heredata ¢',
        , ' herdata { $@/st/',
        , '; call out heredata 1 $x',
        , '$$heredata 2 $y',
        , '$/st/ $$ nach heredata {'
/*
$=/tstCompDataIO/
    ### start tst tstCompDataIO #######################################
    compile =, 5 lines:  input 1 $@.<-=¢$dsn $*+.
    run without input
    . input 1 .
    readInp line 1                       .
    readInp line 2                       .
    . nach dsn input und nochmals mit & .
    readInp line 1                       .
    readInp line 2                       .
    . und schluiss..
$/tstCompDataIO/ */
    dsn = word(tstPdsMbr(tstFilename('lib37', 'r'), 'readInp'), 1)
    dsnFB = strip(dsn tstFB('::F37', 0))
    b = jBuf(tstFB('readInp line 1', 37),
            ,tstFB('readInp line 2', 37))
    extFd = tstFB('&dsn('dsn') dd(xyz)', 0)
    extFd = tstFB(dsn 'dd(xyz) ::f', 0)
    if extFd = '' then
        extFd = dsn
    call jCat file(dsnFB), b
    call vPut 'dsn', dsn
    say 'dsn' dsn 'extFD' extFD'?'
    call tstComp1 '= tstCompDataIO',
        , ' input 1 $@.<-=¢$dsn $*+',
        , tstFB('::f', 0) '$!',
        , ' nach dsn input und nochmals mit & ' ,
        , '         $@.<'extFD,
        , ' und schluiss.'
    return
endProcedure tstCompDataIO

tstObjVF: procedure expose m.
parse arg v, f
    obj  = oNew(classNew('n? TstClassVF u v, f FLD1 v'))
    m.obj = if(f=='','val='v, v)
    m.obj.fld1 = if(f=='','FLD1='v, f)
    return obj
endProcedure tstObjVF

tstCompFile: procedure expose m.
/*
$=/tstCompFileBloSrc/
    $=vv=value-of-vv
    ###file from empty # block
    $@<#¢
        $!
    ###file from 1 line # block
    $@<#¢
    the only $ix+1/0 line $vv
    $!
    ###file from 2 line # block
    $@<#¢
        first line /0 $*+ no comment
        second and last line $$ $wie
    $!
    ===file from empty = block
    $@<=¢     $*+ comment
        $!
    ===file from 1 line = block
    $@<=¢ the only line $!
    ===file from 2 line = block
    $@<=¢ first line$** comment
        second and last line  $!
    ---file from empty - block
    $@<-/s/
        $/s/
    ---file from 1 line - block
    $@<-/s/ the only "line" (1*1) $/s/
    ---file from 2 line = block
    $@<-// first "line" (1+0)
        second   and   "last  line" (1+1)  $//
    ...file from empty . block
    $@<.¢
        $!
    ...file from 1 line . block
    $@<.¢ tstObjVF('v-Eins', '1-Eins') $!
    ...file from 2 line . block
    $@<.¢ tstObjVF('v-Elf', '1-Elf')
        tstObjVF('zwoelf')  $!
    ...file from 3 line . block
    $@<.¢ tstObjVF('einUndDreissig')
            s2o('zweiUndDreissig' o2String($.-vv))
            tstObjVF('dreiUndDreissig')  $!
    @@@file from empty @ block
    $@<@¢
        $!
    $=noOutput=before
    @@@file from nooutput @ block
    $@<@¢ nop
        $=noOutput = run in block $!
    @@@nach noOutput=$noOutput
    @@@file from 1 line @ block
    $@<@¢ $$. tstObjVF('w-Eins', 'w1-Eins') $!
    @@@file from 2 line @ block
    $@<@¢ $$. tstObjVF('w-Elf', 'w1-Elf')
        y='zwoelf' $$- y  $!
    @@@file from 3 line @ block
    $@<@¢ $$. tstObjVF('w einUndDreissig')    $$ +
    zweiUndDreissig $$ 33 $vv$!
    {{{ empty ¢ block
    $@<¢     $!
    {{{ empty ¢ block with comment
    $@<¢    $*+ abc
          $!
    {{{ one line ¢ block
    $@<¢ the only $"¢...$!" line $*+.
        $vv $!
    {{{ one line -¢ block
    $@<-¢ the only $"-¢...$!"  "line" $vv  $!
    {{{ empty #¢ block
    $@<#¢
$!
    {{{ one line #¢ block
    $@<#¢ the only $"-¢...$!"  "line" $vv $¢vv${x}$!
$!
$/tstCompFileBloSrc/ */
/*
$=/tstCompFileBlo/
    ### start tst tstCompFileBlo ######################################
    compile =, 72 lines: $=vv=value-of-vv
    run without input
    ###file from empty # block
    ###file from 1 line # block
    the only $ix+1/0 line $vv
    ###file from 2 line # block
    first line /0 $*+ no comment
    second and last line $$ $wie
    ===file from empty = block
    ===file from 1 line = block
    . the only line .
    ===file from 2 line = block
    . first line
    second and last line  .
    ---file from empty - block
    ---file from 1 line - block
    THE ONLY line 1
    ---file from 2 line = block
    FIRST line 1
    SECOND AND last  line 2
    ...file from empty . block
    ...file from 1 line . block
    tstR: @tstWriteoV1 isA :TstClassVF = v-Eins
    tstR:  .FLD1 = 1-Eins
    ...file from 2 line . block
    tstR: @tstWriteoV2 isA :TstClassVF = v-Elf
    tstR:  .FLD1 = 1-Elf
    tstR: @tstWriteoV3 isA :TstClassVF = val=zwoelf
    tstR:  .FLD1 = FLD1=zwoelf
    ...file from 3 line . block
    tstR: @tstWriteoV4 isA :TstClassVF = val=einUndDreissig
    tstR:  .FLD1 = FLD1=einUndDreissig
    zweiUndDreissig value-of-vv
    tstR: @tstWriteoV5 isA :TstClassVF = val=dreiUndDreissig
    tstR:  .FLD1 = FLD1=dreiUndDreissig
    @@@file from empty @ block
    @@@file from nooutput @ block
    @@@nach noOutput=run in block
    @@@file from 1 line @ block
    tstR: @tstWriteoV6 isA :TstClassVF = w-Eins
    tstR:  .FLD1 = w1-Eins
    @@@file from 2 line @ block
    tstR: @tstWriteoV7 isA :TstClassVF = w-Elf
    tstR:  .FLD1 = w1-Elf
    zwoelf
    @@@file from 3 line @ block
    tstR: @tstWriteoV8 isA :TstClassVF = val=w einUndDreissig
    tstR:  .FLD1 = FLD1=w einUndDreissig
    zweiUndDreissig
    33 value-of-vv
    {{{ empty ¢ block
    {{{ empty ¢ block with comment
    {{{ one line ¢ block
    . the only ¢...$! line value-of-vv .
    {{{ one line -¢ block
    THE ONLY -¢...$! line value-of-vv
    {{{ empty #¢ block
    {{{ one line #¢ block
    . the only $"-¢...$!"  "line" $vv $¢vv${x}$!
$/tstCompFileBlo/ */
    call tstComp2 'tstCompFileBlo', '='
    m.t.trans.0 = 0

/*
$=/tstCompFileObjSrc/
    $=vv=value-vv-1
    $=fE=<¢ $!
    $=f2=. $.<.¢s2o("f2 line 1" o2String($.-vv))
         tstObjVF("f2 line2") $!
    ---empty file $"$@<$fE"
    $@fE
    ---file with 2 lines $"$@<$f2"
    $@.<.f2
    $=vv=value-vv-2
    ---file with 2 lines $"$@<$f2"
    $@.<.f2
    $= dsn =- word(tstPdsMbr(tstFilename('libvb', 'r'), 'fileObj'),1) +
                tstFB('::V', 0)
    $@¢
        fi=jOpen(file($dsn),'>')
        call jWrite fi, 'line one on' $"$dsn"
        call jWrite fi, 'line two on' $"$dsn"
        call jClose fi
    $!
    ---file on disk out
    $@<-dsn
$/tstCompFileObjSrc/ */
/*
$=/tstCompFileObj/
    ### start tst tstCompFileObj ######################################
    compile =, 20 lines: $=vv=value-vv-1
    run without input
    ---empty file $@<$fE
    ---file with 2 lines $@<$f2
    f2 line 1 value-vv-1
    tstR: @tstWriteoV1 isA :TstClassVF = val=f2 line2
    tstR:  .FLD1 = FLD1=f2 line2
    ---file with 2 lines $@<$f2
    f2 line 1 value-vv-1
    tstR: @tstWriteoV1 isA :TstClassVF = val=f2 line2
    tstR:  .FLD1 = FLD1=f2 line2
    ---file on disk out
    line one on $dsn
    line two on $dsn
$/tstCompFileObj/ */
    call tstComp2 'tstCompFileObj', '='

    return
endProcedure tstCompFile

tstCompPipe: procedure expose m.
/*
$=/tstCompPipe1/
    ### start tst tstCompPipe1 ########################################
    compile @, 1 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    (1 eins zwei drei 1)
    #jIn 2# zehn elf zwoelf?
    (1 zehn elf zwoelf? 1)
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    (1 zwanzig 21 22 23 24 ... 29| 1)
    #jIn eof 4#
$/tstCompPipe1/ */
    call tstComp1 '@ tstCompPipe1 3',
        , ' call pipePreSuf "(1 ", " 1)"'
/*
$=/tstCompPipe2/
    ### start tst tstCompPipe2 ########################################
    compile @, 2 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    ¢2 (1 eins zwei drei 1) 2!
    ¢2 (1 zehn elf zwoelf? 1) 2!
    ¢2 (1 zwanzig 21 22 23 24 ... 29| 1) 2!
$/tstCompPipe2/ */
    call tstComp1 '@ tstCompPipe2 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| call pipePreSuf "¢2 ", " 2!"'

/*
$=/tstCompPipe3/
    ### start tst tstCompPipe3 ########################################
    compile @, 3 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <3 ¢2 (1 eins zwei drei 1) 2! 3>
    <3 ¢2 (1 zehn elf zwoelf? 1) 2! 3>
    <3 ¢2 (1 zwanzig 21 22 23 24 ... 29| 1) 2! 3>
$/tstCompPipe3/ */
    call tstComp1 '@ tstCompPipe3 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| call pipePreSuf "¢2 ", " 2!"',
        , ' $| call pipePreSuf "<3 ", " 3>"'

/*
$=/tstCompPipe4/
    ### start tst tstCompPipe4 ########################################
    compile @, 7 lines:  call pipePreSuf "(1 ", " 1)"
    run without input
    #jIn eof 1#
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <3 ¢222 ¢221 ¢21 ¢20 (1 eins zwei drei 1) 20! 21! 221! 222! 3>
    <3 ¢222 ¢221 ¢21 ¢20 (1 zehn elf zwoelf? 1) 20! 21! 221! 222! 3>
    <3 ¢222 ¢221 ¢21 ¢20 (1 zwanzig 21 22 23 24 ... 29| 1) 20! 21! 221!+
    . 222! 3>
$/tstCompPipe4/ */
    call tstComp1 '@ tstCompPipe4 3',
        , ' call pipePreSuf "(1 ", " 1)"' ,
        , ' $| $@¢    call pipePreSuf "¢20 ", " 20!"',
        ,        ' $| call pipePreSuf "¢21 ", " 21!"',
        ,        ' $| $@¢      call pipePreSuf "¢221 ", " 221!"',
        ,                 ' $| call pipePreSuf "¢222 ", " 222!"',
        ,     '$!     $! ',
        , ' $| call pipePreSuf "<3 ", " 3>"'
    return
endProcedure tstCompPipe


tstCompPip2: procedure expose m.
/*
$=/tstCompPip21/
    ### start tst tstCompPip21 ########################################
    compile @, 3 lines:  $<¢ zeile eins .
    run without input
    (1  zeile eins  1)
    (1    zeile zwei  1)
    run with 3 inputs
    (1  zeile eins  1)
    (1    zeile zwei  1)
$/tstCompPip21/ */
    call tstComp1 '@ tstCompPip21 3',
        , ' $<¢ zeile eins ' ,
        , '   zeile zwei $!' ,
        , ' call pipePreSuf "(1 ", " 1)"'
/*
$=/tstCompPip22/
    ### start tst tstCompPip22 ########################################
    compile @, 3 lines: if ${>i1} then $@¢
    run without input
    #jIn eof 1#
    nachher
    run with 3 inputs
    #jIn 1# eins zwei drei
    <zeile 1: eins zwei drei>
    <zwei>
    nachher
$/tstCompPip22/ */
    call tstComp1 '@ tstCompPip22 3',
        , 'if ${>i1} then $@¢'          ,
        , ' $$ zeile 1: $i1 $$ zwei $| call pipePreSuf "<",">" $!',
        , ' $$ nachher '
    return
endProcedure tstCompPip2

tstCompRedir: procedure expose m.
/*
$=/tstCompRedir/
    ### start tst tstCompRedir ########################################
    compile @, 6 lines:  $=eins=<@¢ $@for vv $$ <$vv> $! .
    run without input
    #jIn eof 1#
    output eins .
    output piped zwei .
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    output eins <eins zwei drei> <zehn elf zwoelf?> +
    <zwanzig 21 22 23 24 ... 29|>
    output piped zwei ab<eins zwei drei>yz ab<zehn elf zwoelf?>yz a+
    b<zwanzig 21 22 23 24 ... 29|>yz
$/tstCompRedir/ */
    call pipeIni
    call vRemove 'eins'  /* alte Variable loswerden */
    dsn = word(tstPdsMbr(tstFilename('libvb', 'r'), 'redir1'), 1)
    call vPut 'dsn', dsn
    say  'dsn' $dsn
    call tstComp1 '@ tstCompRedir 3' ,
        , ' $=eins=<@¢ $@for vv $$ <$vv> $! ',
        , ' $$ output eins $-=¢$.eins$! $; ',
        , ' $@for ww $$b${ww}y ' ,
        , '    $>-= $-¢ $dsn $! 'tstFB('::v', 0),
        ,         '$| call pipePreSuf "a", "z" $<.eins' ,
        , ' $; $$ output piped zwei $-=¢$@<$-dsn$!'
/*
$=/tstCompRedi2/
    ### start tst tstCompRedi2 ########################################
    compile @, 12 lines: call mAdd t.trans, $var "dsnTestRedi"
    run without input
    >1<dsnTestRedi currTimeRedi
    >2<$"dsnTestRedi" currTimeRedi
    >3<$"dsnTestRedi" ::v currTimeRedi
    >4<$-var" currTimeRedi
    >5<$dsnTestRedi" currTimeRedi
$/tstCompRedi2/
*/
    call vPut 'var', tstFileName('compRedi', 'r')
    call vPut 'tst', translate(date()'+'time()'+testRedi2', '_', ' ')
    call tstComp1 '@ tstCompRedi2 ' ,
        , 'call mAdd t.trans, $var "dsnTestRedi"',
        , 'call mAdd t.trans, $tst "currTimeRedi"',
        , '$<> $>'vGet('var') '::v $$ $">1<'vGet('var')'" $tst',
        , '$<> $<'vGet('var') '    $@ call pipeWriteAll' ,
        , '$<> $>$"'vGet('var')' ::v" $$ $">2<$""'vGet('var')'""" $tst',
        , '$<> $<$"'vGet('var') '"   $@ call pipeWriteAll',
   , '$<> $>$"'vGet('var')'" ::v $$ $">3<$""'vGet('var')'"" ::v" $tst',
        , '$<> $<$"'vGet('var') '"   $@ call pipeWriteAll',
        , '$<> $>-var  $$ $">4<$"-var" $tst',
        , '$<> $<-var  $@ call pipeWriteAll',
        , '$<> $>$var ::v $$ $">5<$"$var" $tst',
        , '$<> $<$var  $@ call pipeWriteAll'
    return
endProcedure tstCompRedir

tstCompComp: procedure expose m.
/*
$=/tstCompCompShell/
    ### start tst tstCompCompShell ####################################
    compile @, 5 lines: $$compiling shell $; $= rrr =. $.^compile $<@#/+
    aaa/
    run without input
    compiling shell
    running einmal
    RUN 1 COMPILED einmal
    #jIn eof 1#
    running zweimal
    RUN 1 COMPILED zweimal
    #jIn eof 2#
    run with 3 inputs
    compiling shell
    running einmal
    RUN 1 COMPILED einmal
    #jIn 1# eins zwei drei
    compRun eins zwei dreieinmal
    #jIn 2# zehn elf zwoelf?
    compRun zehn elf zwoelf?einmal
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    compRun zwanzig 21 22 23 24 ... 29|einmal
    #jIn eof 4#
    running zweimal
    RUN 1 COMPILED zweimal
    #jIn eof 5#
$/tstCompCompShell/ */
    call tstComp1 '@ tstCompCompShell 3',
        ,  "$$compiling shell $; $= rrr =. $.^compile $<@#/aaa/",
        ,  "call out run 1*1*1 compiled $cc;" ,
            "$@for v $$ compRun $v$cc" ,
        ,  "$/aaa/ $;",
        ,  "$=cc=einmal $$ running $cc $@rrr",
        ,  "$=cc=zweimal $$ running $cc $@rrr"
/*
$=/tstCompCompData/
    ### start tst tstCompCompData #####################################
    compile @, 5 lines: $$compiling data $; $= rrr =. $.^¢compile = +
    =$!  $<@#/aaa/
    run without input
    compiling data
    running einmal
    call out run 1*1*1 compiled einmal
    running zweimal
    call out run 1*1*1 compiled zweimal
    run with 3 inputs
    compiling data
    running einmal
    call out run 1*1*1 compiled einmal
    running zweimal
    call out run 1*1*1 compiled zweimal
$/tstCompCompData/ */
    call tstComp1 '@ tstCompCompData 3',
        ,  "$$compiling data $; $= rrr =. $.^¢compile = =$!  $<@#/aaa/",
        ,  "call out run 1*1*1 compiled $cc",
        ,  "$/aaa/ $;",
        ,  "$=cc=einmal $$ running $cc $@rrr",
        ,  "$=cc=zweimal $$ running $cc $@rrr"
    return
endProcedure tstCompComp

tstCompDir: procedure expose m.
/*
$=/tstCompDirSrc/
  'in src v1='$v1
  $#@ call out 'src @ out v1='$v1
  $#. $*(komm$*) s2o('src . v1=')
       $.-v1
  $#-
    'src - v1='$v1
  $#=
    src = v1=$v1
$/tstCompDirSrc/

$=/tstCompDir/
    ### start tst tstCompDir ##########################################
    compile @call out 'before v1='$v1 $=v1=eins $#. s2o('. v1='$-v1) $#+
    @ call out '@ v1='$v1 $#= = v1=$v1 $#- '- v1='$v1 $#-, 8 lines: 'in+
    . src v1='$v1
    run without input
    before v1=v1Before
    .. v1=eins
    @ v1=eins
    = v1=eins .
    - v1=eins
    in src v1=eins
    src @ out v1=eins
    src . v1=
    eins
    src - v1=eins
    src = v1=eins
$/tstCompDir/ */
    call compIni
    call vPut 'v1', 'v1Before'
    call tstComp2 'tstCompDir', "@call out 'before v1='$v1 $=v1=eins" ,
        "$#. s2o('. v1='$-v1) $#@ call out '@ v1='$v1" ,
        "$#= = v1=$v1 $#- '- v1='$v1 $#-"
/*
$=/tstCompDirPiSrc/
  zeile 1 v1=$v1
  zweite Zeile vor $"$@$#-"
  $#@  $@proc pi2 $@-¢
  $'zeile drei nach $@$#- v1='v1
  vierte und letzte Zeile $!
$/tstCompDirPiSrc/ */
/*
$=/tstCompDirPi/
    ### start tst tstCompDirPi ########################################
    compile @call pipePreSuf '<','>' $=v1=eiPi $<.pi2 $#=, 5 lines: ze+
    ile 1 v1=$v1
    run without input
    <zeile drei nach $@$#- v1=V1>
    <VIERTE UND LETZTE ZEILE>
    zeile 1 v1=eiPi
    zweite Zeile vor $@$#-
$/tstCompDirPi/ */
    call tstComp2 'tstCompDirPi',
            , "@call pipePreSuf '<','>' $=v1=eiPi $<.pi2 $#="
    return
endProcedure tstCompDir

tstCompColon: procedure expose m.
/*
$=/tstCompColon1/
    ### start tst tstCompColon1 #######################################
    compile :, 12 lines: vA = valueVonA
    run without input
    vA = valueVonA
    vA=valueVonA vB=valueVonB vC=valueVonC
    vC=valueVonC vD=valueVonD vE=valueVonvE
    vF=6
$/tstCompColon1/ */
    call tstComp1 ': tstCompColon1',
        , 'vA = valueVonA' ,
        , ' $$ vA = $vA' ,
        , '        * kommentar ' ,
        , '=vB=- "valueVonB"' ,
        , '=/vC/valueVonC$/vC/' ,
        , ' $$ vA=$vA vB=$vB vC=$vC' ,
        , '$=/vD/valueVonD' ,
        , '$/vD/ vE=valueVonvE' ,
        , '        * kommentar ' ,
        , ' $$ vC=$vC vD=$vD vE=$vE',
        , 'vF=- 2*3 $=vG=@@¢ $$ vF=$vF$!' ,
        , '@vG'

/*
$=/tstCompColon2/
    ### start tst tstCompColon2 #######################################
    compile :, 7 lines: ix=0
    run without input
    #jIn eof 1#
    proc p1 arg(2) total 0 im argumentchen
    run with 3 inputs
    #jIn 1# eins zwei drei
    #jIn 2# zehn elf zwoelf?
    #jIn 3# zwanzig 21 22 23 24 ... 29|
    #jIn eof 4#
    <<for 1 -> eins zwei drei>>
    <<for 2 -> zehn elf zwoelf?>>
    <<for 3 -> zwanzig 21 22 23 24 ... 29|>>
    proc p1 arg(2) total 3 im argumentchen
$/tstCompColon2/

*/
    call tstComp1 ': tstCompColon2 3',
        , 'ix=0' ,
        , 'for v @:¢ix=- $ix+1',
        , ' $$ for $ix -> $v' ,
        , '! | @¢call pipePreSuf "<<",">>"',
        , '$! @%¢p1 total $ix im argumentchen$!',
        , 'proc @:/p1/$$- "proc p1 arg(2)" arg(2)' ,
        , '/p1/'
/*
$=/tstCompColon3/
    ### start tst tstCompColon3 #######################################
    compile :, 11 lines: tc3Eins=freeVar1
    run without input
    tc3Eins=freeVar1 o2&tc3Eins= o2&tc3Zwei=
    tc3Eins=freeVar1 o2&tc3Eins=with3Eins o2&tc3Zwei=with3Zwei
    tc3Eins=freeVar1 o2&tc3Eins=ass4Eins o2&tc3Zwei=with5Zwei
    o3&tc3Eins=ass4Eins o3&tc3Zwei=with5 o3 Zwei
    tc3Eins=freeVar1 o2&tc3Eins=ass4Eins o2&tc3Zwei=with5Zwei
$/tstCompColon3/
*/
    call classNew 'n? TstCompColon3 u f tc3Eins v, f tc3Zwei v'
    showO2 = 'tc3Eins=$tc3Eins' ,
            'o2&tc3Eins=${o2&tc3Eins} o2&tc3Zwei=${o2&tc3Zwei}'
    showO3 = 'o3&tc3Eins=${o3&tc3Eins} o3&tc3Zwei=${o3&tc3Zwei}'
    call tstComp1 ': tstCompColon3',
        , 'tc3Eins=freeVar1' ,
     , 'o2 =. oNew("TstCompColon3")' ,
        , '$$' showO2 ,
        , 'with $o2 $@:¢tc3Eins = with3Eins',
        ,     'tc3Zwei = with3Zwei',
        ,    '! $$' showO2 ,
        , '{o2&tc3Eins} = ass4Eins',
        , 'with $o2 $=tc3Zwei = with5Zwei',
        , '$$' showO2 ,
        , 'with o3 =. oCopy($o2) $=tc3Zwei = with5 o3 Zwei',
        , '$$' showO3 '$$' showO2
    return
endProcedure tstCompColon

tstCompTable: procedure expose m.
/*
$=/tstCompTable1/
    ### start tst tstCompTable1 #######################################
    compile :, 6 lines: table $*( sdf $*)   .
    run without input
    tstR: @tstWriteoV2 isA :<TstCT1Class>
    tstR:  .fEins = v1
    tstR:  .fZwei = valueZwei undD
    tstR:  .fDrei = rei
    zweite
    tstR: @tstWriteoV3 isA :<TstCT1Class>
    tstR:  .fEins = w1 wZwe
    tstR:  .fZwei = i
    tstR:  .fDrei = wwwDrei
$/tstCompTable1/
 */
    call wshIni
    cl = classNew('n* CompTable u f fEins v, f fZwei v, f fDrei v')
    c2 = classNew('n* CompTable u f fEins v, f fDrei v')
    call tstComp1 ': tstCompTable1',
        , 'table $*( sdf $*)   ' ,
        , 'fEins   fZwei $*(....$*) fDrei  ' ,
        , '$@¢call mAdd t.trans, className("'cl'") "<TstCT1Class>"$!',
        , '    v1     valueZwei undDrei     ' ,
        , '$$ zweite',
        , ' w1 wZwei                    wwwDrei     '


/*
$=/tstCompWithNew/
    ### start tst tstCompWithNew ######################################
    compile :, 12 lines: withNew
    run without input
    tstR: @tstWriteoV2 isA :<TstCT1Class>
    tstR:  .fEins = withNewValue fEins
    tstR:  .fZwei = withNewValue fZwei
    tstR:  .fDrei = withNewValuel drei
    tstR: @tstWriteoV3 isA :<TstCT1Class>
    tstR:  .fEins = withNewValue fEinsB
    tstR:  .fZwei = withNewValue fZweiB
    tstR:  .fDrei = withNewValue fDreiB
    tstR: @tstWriteoV5 isA :<TstCT2Class>
    tstR:  .fEins = withValue fEinsC
    tstR:  .fDrei = withValue fDreiC
$/tstCompWithNew/
*/
    call tstComp1 ': tstCompWithNew',
        , 'withNew' ,
        , 'fEins = withNewValue fEins' ,
        , 'fZwei = withNewValue fZwei' ,
        , '$@¢call mAdd t.trans, className("'cl'") "<TstCT1Class>"',
        , '$@:¢   fDrei = withNewValuel drei $! $! ' ,
        , 'withNew ' ,
        , 'fEins = withNewValue fEinsB' ,
        , 'fZwei = withNewValue fZweiB',
        , 'fDrei = withNewValue fDreiB',
        , 'withNew fEins = withValue fEinsC' ,
        , '$@¢call mAdd t.trans, className("'c2'") "<TstCT2Class>"',
        , '$@¢$=fDrei = withValue fDreiC$! $! '
/*
$=/tstCompWithNeRe/
    ### start tst tstCompWithNeRe #####################################
    compile :, 11 lines: withNew
    run without input
    tstR: @tstWriteoV2 isA :<TstClassR2>
    tstR:  .rA = value rA
    tstR:  .rB refTo @!value rB isA :w
    tstR: @tstWriteoV4 isA :<TstClassR2>
    tstR:  .rA = val33 rA
    tstR:  .rB refTo @!VAL33 RB isA :w
    tstR: @tstWriteoV5 isA :<TstClassR2>
    tstR:  .rA = val22 rA
    tstR:  .rB refTo @!VAL22 RB isA :w
    tstR: @tstWriteoV6 isA :<TstCT1Class>
    tstR:  .fEins = withNewValue fEins
    tstR:  .fZwei = withNewValue fZwei
    tstR:  .fDrei = withNewValuel drei
    vOth=value vOth fZwei=fZwei Wert vorher ?fDrei=0
$/tstCompWithNeRe/
*/
    cR = classNew("n* CompTable u f rA v, f rB r")
    call vRemove 'fDrei'
    call vPut 'fZwei', 'fZwei Wert vorher'
    call tstComp1 ': tstCompWithNeRe',
        , 'withNew' ,
        , 'fEins = withNewValue fEins' ,
        , '@:¢withNew rA =value rA $=rB=. "!value rB" ' ,
        , '$@ call mAdd t.trans, className("'cR'") "<TstClassR2>"$!',
        , 'fZwei = withNewValue fZwei' ,
        , '$@¢call mAdd t.trans, className("'cl'") "<TstCT1Class>"',
        , '$@:¢withNew rA =val22 rA $=rB=. !val22 rB ' ,
        , '{vOth} = value vOth',
        , '$@:¢withNew rA =val33 rA $=rB=. !val33 rB  $! $!' ,
        , '$@:¢   fDrei = withNewValuel drei $! $! ',
        , '$<> $$ vOth=$vOth fZwei=$fZwei ?fDrei=${?fDrei}'
    return
endProcedure tstCompTable

tstCompSql: procedure expose m.
/*
$=/tstCompSqlSrc/
$@=¢
   select strip(creator) cr, strip(name) tb,
            (row_number()over())*(row_number()over()) rr
       from sysibm.sysTables
       where creator='SYSIBM' and name like 'SYSTABL%'
       order by 2 fetch first 4 rows only
$!
$| call sqlSel
$| call fTabAuto
$/tstCompSqlSrc/
$=/tstCompSql/
    ### start tst tstCompSql ##########################################
    compile @, 9 lines: $@=¢
    run without input
    CR     TB                RR
    SYSIBM SYSTABLEPART       1
    SYSIBM SYSTABLEPART_HIST  4
    SYSIBM SYSTABLES          9
    SYSIBM SYSTABLESPACE     16
$/tstCompSql/
$=/tstCompSqlFTabSrc/
$$ select 'ahaaaax' ahaCol, 'buuuuh' buhhhh from sysibm.sysDummy1
$| $@. vPut('lc', sqlRdr(scanSqlIn2Stmt()))
$| call fTab  sqlFTabOthers(sqlRdrFTabReset($.lc, tstCompSql1))
$<>
$$ select 'aOh' ahaOhne, 'buuVar' buhVar from sysibm.sysDummy1
$| call sqlSel
$| t2 = fTabReset(sqlRdrFTabReset( , tstCompS2), '2 1', '2 c', '-')
   ox = m.t2.0 + 1
   call sqlFTabOthers t2
   call fTab fTabAddTit(t2, ox, 2, '-----')
$<>
$$ select 'aOh' aDrei, 'buuDre' buhDrei from sysibm.sysDummy1
$| call sql2Tab
$/tstCompSqlFTabSrc/
$=/tstCompSqlFTab/
    ### start tst tstCompSqlFTab ######################################
    compile @, 13 lines: $$ select 'ahaaaax' ahaCol, 'buuuuh' buhhhh fr+
    om sysibm.sysDummy1
    run without input
    AHACOL--BUHHHH---
    ahaaaax buuuuh
    AHACOL--BUHHHH---
    -----
    AHA-BUHVAR---
    aOh buuVar
    -----
    AHAOHNE
    .    BUHVAR
    ADREI
    .    BUHDREI
    ADR-BUHDRE---
    aOh buuDre
    ADR-BUHDRE---
    ADREI
    .    BUHDREI
$/tstCompSqlFTab/
    ### start tst tstCompSql ##########################################
*/
    call sqlConnect
    call tstComp2 'tstCompSql', '@'
    call tstComp2 'tstCompSqlFTab', '@'
    call sqlDisConnect
    return
endProcedure tstCompSql
tstTut0: procedure expose m.
/*
$=/tstTut01Src/
$#=
$*+>.fSub()                               Kommentar
$*+>~tmp.jcl(t)                           Kommentar
$*+@=¢                                    Kommentar
$=subsys=DP4G
$=db=DA540769
$=ts=A977A
$*+@<~wk.jcl(jc)                          Kommentar
??* ?-¢sysvar(sysnode) date() time()?!ts=$ts 10*len=$-¢length($ts)*10$!
//P02     EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769C.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
  $@¢if right($ts, 2) == '7A' then $@=¢
    FULL YES
  $! else
    $$ $''    FULL NO
  $!
    SHRLEVEL CHANGE
$*+!                                      Kommentar
$#out original/src
$/tstTut01Src/
$=/tstTut01/
    ### start tst tstTut01 ############################################
    compile , 28 lines: $#=
    run without input
    ??* ?-¢sysvar(sysnode) date() time()?!ts=A977A 10*len=50
    //P02     EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A540769C.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977A*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    FULL YES
    SHRLEVEL CHANGE
$/tstTut01/
$=/tstTut02Src/
$#@
$**>.fSub()
$**@¢
$=subsys=DP4G
$=db=DA540769
$=jx=0
$@do tx = 976 to 977 $@=¢
    $=ts=A$tx
    $=jx=- $jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$**!
$#out original/src
$/tstTut02Src/
$=/tstTut02/
    ### start tst tstTut02 ############################################
    compile , 28 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut02/
$=/tstTut03Src/
$#@
$=subsys=DP4G
$@:¢table
      db         ts
      DGDB9998   A976
      DA540769   A977
$!
$** $| call fTabAuto
$**    $#end
$|
$=jx=0
$@forWith o $@=¢
    $=jx=- $jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$#out original/src
$/tstTut03Src/
$=/tstTut03/
    ### start tst tstTut03 ############################################
    compile , 31 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut03/
$=/tstTut04Src/
$#@
$=subsys=DP4G
$=db=DA540769
call sqlConnect $subsys
$@=¢  select dbName  db , tsName  ts
          from sysibm.sysTables
          where creator = 'SYSIBM' and name like 'SYSINDEXPAR%'
          order by name desc
$!
$| call sqlSel
$** $| call fTabAuto
$** $#end
$|
$=jx=0
$@forWith o $@=¢
    $=jx=- $jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$TS    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $DB.$TS*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
call sqlDisConnect
$#out original/src
$/tstTut04Src/
$=/tstTut04/
    ### start tst tstTut04 ############################################
    compile , 35 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CSYSHIST     EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DSNDB06 .SYSHIST *   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CSYSTSIPT    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DSNDB06 .SYSTSIPT*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut04/
$=/tstTut05Src/
$#:
subsys = DP4G
lst =<:¢withNew out :¢
    db = DGDB9998
    ts =<:¢table
             ts
             A976
             A977
    $!
    db = DA540769
    <|/ts/
             ts
             A976
             A975
    /ts/
!
$** $$. $lst
$** $@ct $@¢$=tool =. $.compile@ $<~.wk.rexx(wshtut06)$!
$** $@$tool
$@do sx=1 to ${lst.0} $@¢
    $=db = ${lst.$sx.db}
    $** $$. ${lst.$sx}
    $@do tx=1 to ${lst.$sx.ts.0} $@=¢
        $*+ $$. ${lst.$sx.ts.$tx}
        $=ts= ${lst.$sx.ts.$tx.ts}
        $@¢ say $-=¢subsys $subsys db $db ts $ts $! $!
        $@copy()
        $!
    $!
$@ct $@¢
cl = classNew('n? DbTsList u s' ,
     classNew('n? DbTs u f db v, f ts s' ,
     classNew('n? Ts u f ts v')))
$=lst=. oNew(cl)
$!
$@proc copy $@=¢
$@ct $=jx=0
$=jx=- $jx+1
//A540769$jx JOB (CP00,KE50),'DB2 REO',
//         MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//C$ts    EXEC PGM=DSNUTILB,
//             PARM='$subsys,A540769$jx.FULCOPL'
//SYSPRINT  DD SYSOUT=*
//UTPRINT   DD SYSOUT=*
//SYSUDUMP  DD SYSOUT=*
//SYSTEMPL  DD DSN=$subsys.DBAA.LISTDEF(TEMPL),DISP=SHR
//SYSIN     DD *
LISTDEF C#LIST
  INCLUDE TABLESPACE $db.$ts*   PARTLEVEL
OPTIONS EVENT(ITEMERROR, SKIP)
COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$!
$#out original/src
$/tstTut05Src/
$=/tstTut05/
    ### start tst tstTut05 ############################################
    compile , 56 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DGDB9998.A977*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407693 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407693.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A976*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
    //A5407694 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA975    EXEC PGM=DSNUTILB,
    //             PARM='DP4G,A5407694.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DP4G.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A975*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut05/
   tstTut06   ==> platz für copy aus tut05
$=/tstTut07Src/
$**$>.fEdit()
call sqlConnect dp4g
$@:¢table
   ts
VTCASHFLOW
VTCASHFLOWAMOUNT
VTINSTRFLATDATA
$!
$| $@=¢
    select name, statstime, strip(dbName) || '.' || strip(tsName) dbts
       from sysibm.sysTables
       where creator = 'VDPS2' and name in
  $=co=(
  $@forWith t $@=¢
                                           $co '$ts'
      $=co=,
  $!
                                           )
$!
$| call sqlSel
$** $| call fTabAuto
$|
$=jx=0
$@forWith t $@=¢
$=jx=- $jx+1
//A540769$jx JOB (CP00,KE50),
//       'CATALOG',MSGCLASS=T,TIME=1440,
//         NOTIFY=&SYSUID,REGION=0M
//STEP$jx EXEC PGM=DSNUTILB,TIME=1440,
//   PARM=(DP4G,'A540769$jx.RUNSTA'),
//   REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//UTPRINT  DD SYSOUT=*
//SYSOUT   DD SYSOUT=*
//RNPRIN01 DD SYSOUT=*
//SYSIN DD *
  LISTDEF LST#STA   INCLUDE TABLESPACE $DBTS
   OPTIONS EVENT (ITEMERROR, SKIP)

   RUNSTATS TABLESPACE LIST LST#STA
         SHRLEVEL CHANGE
           INDEX(ALL KEYCARD)
           REPORT YES UPDATE ALL
$!
call sqlDisconnect
$#out original/src
$/tstTut07Src/
$=/tstTut07/
$=/tstTut07/
    ### start tst tstTut07 ############################################
    compile , 47 lines: $**$>.fEdit()
    run without input
    //A5407691 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP1 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407691.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV27A1T.VDPS329
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
    //A5407692 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP2 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407692.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV28A1T.VDPS390
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
    //A5407693 JOB (CP00,KE50),
    //       'CATALOG',MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //STEP3 EXEC PGM=DSNUTILB,TIME=1440,
    //   PARM=(DP4G,'A5407693.RUNSTA'),
    //   REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSUDUMP DD SYSOUT=*
    //UTPRINT  DD SYSOUT=*
    //SYSOUT   DD SYSOUT=*
    //RNPRIN01 DD SYSOUT=*
    //SYSIN DD *
    LISTDEF LST#STA   INCLUDE TABLESPACE VV21A1T.VDPS004
    OPTIONS EVENT (ITEMERROR, SKIP)
    ..
    RUNSTATS TABLESPACE LIST LST#STA
    SHRLEVEL CHANGE
    INDEX(ALL KEYCARD)
    REPORT YES UPDATE ALL
$/tstTut07/
$=/tstTut08Src/
$/tstTut08Src/
$=/tstTut08/
$/tstTut08/
*/
    call sqlIni
    call sqlDisconnect '*'
    call tstComp2 'tstTut01'
    call tstComp2 'tstTut02'
    call tstComp2 'tstTut03'
    if m.err.os == 'TSO' then do
        call tstComp2 'tstTut04'
        /* call tstComp2 'tstTut05' */
     /* call tstComp2 'tstTut07'  ???? anderes Beispiel ???? */
        end
    call tstTotal
    return
endProcedure tstTut0
/****** tstBase ********************************************************
     test the basic classes
***********************************************************************/
tstBase: procedure expose m.
    call tstTstSay
    call tstM
    call tstUtc2d
    call tstMap
    call tstMapVia
    call tstClass
    call tstClass2
    call tstClass3
    call tstClass4
    call tstO
    call oIni
    call tstF
    call tstFWords
    call tstFtst
    call tstFCat
    call tstOEins
    call tstO2Text
    call jIni
    call tstJSay
    call tstJ
    call tstJ2
    call tstScanSqlStmt
    call catIni
    call tstCat
    call pipeIni
    CALL TstEnv
    CALL TstEnvCat
    call tstPipe
    call tstPipeS
    call tstEnvVars
    call tstvWith
    call tstTotal
    call tstPipeLazy
    call tstEnvClass
    if m.tst_csm then
        call tstDsnEx
    call tstFile
    call tstFileList
    call tstMbrList
    call tstFE
    call tstFTab
    call tstFmt
    call tstfUnits
    call tstCsv
    call tstTotal
    call tstSb
    call tstSb2
    call tstScan
    call ScanReadIni
    call tstScanRead
    call tstScanUtilInto
    call tstScanWin
    call tstScanSQL
    call tstTotal
    return
endProcedure tstBase

/*--- test the tst Module, check the output visually  ----------------*/
tstTstSay: procedure expose m.
    call tstIni
    oldErr = m.tst.err
    oldNames = m.tst.errNames
    say '+++ tstTstSay start with' oldErr 'totErrs and',
            m.tst.tests 'tests'
/*
$=/tstTstSayEins/
    ### start tst tstTstSayEins #######################################
    test eins einzige testZeile
$/tstTstSayEins/
$=/tstTstSayZwei/
    ### start tst tstTstSayZwei #######################################
    zwei 1. testZeile
    zwei 2. und letzte testZeile
$/tstTstSayZwei/
$=/tstTstSayDrei/
    ### start tst tstTstSayDrei #######################################
    drei 1. testZeile vor empty Zeile
    ..
    drei 3. testZeile vor 10 space
    .          .
    drei 5. testZeile ziemlich lang 66                                 +
    .                                77  5   10   15++++++++++++++++++++
    .+++++++++++++++++++++++++++++++++++++++++++.
$/tstTstSayDrei/
*/
    call tst x, 'tstTstSayEins'
    call tstOut x, "test eins einzige testZeile"
    call tstEnd x, 'err 0'


    call tst x, 'tstTstSayZwei'
    call tstOut x, "zwei 1. testZeile"
    call tstOut x, "zwei 2. und letzte testZeile"
    call tstEnd x, 'err 0'

    call tst x, 'tstTstSayZwei'
    call tstOut x, "zwei 1. testZeile "    /* ein space zuviel */
    call tstOut x, "zwei 2. und letzte testZeile"
    call tstOut x, "zwei 3. zuviel"
    call tstEnd x, 'err 3'

    call tst y, 'tstTstSayDrei'
    call tstOut y, 'drei 1. testZeile vor empty Zeile'
    call tstOut y, ''
    call tstOut y, 'drei 3. testZeile vor 10 space'
    call tstOut y, left('', 10)
    call tstOut y, 'drei 5. testZeile ziemlich lang',
                left(66, 66) left('77  5   10   15', 77, '+')
    call tstEnd y, 'err 0'
    if m.y.err <> 0 then
        call err '+++ tstTstSay errs' m.x.err 'expected' 0
    if m.tst.err <> oldErr + 3 then
        call err '+++ tstTstSay totErrs' m.tst.err 'expected' 3
    say '+++ tstTstSay end Ok with' m.tst.err 'totErrs and' ,
        m.tst.tests 'tests'
    m.tst.err = oldErr
    m.tst.errNames = oldNames
    return
endProcedure tstTstSay

tstMark: procedure expose m.
parse arg m, msg
    if symbol('m.m') == 'VAR' then
        m.m = msg';' m.m
    else
        m.m = msg 'new'
    return m
endProcedure tstMark
tstM: procedure expose m.
/*
$=/tstMa/
    ### start tst tstMa ###############################################
    mNew() 1=newM1 2=newM2
    mNew(tst...) 2=2 new 3=4; 3; 1 new 4=5 new
    iter 4; 3; 1 new
    iter 2 new
    iter 5 new
$/tstMa/
*/
    call tst t, 'tstMa'
    m1 = mNew()
    m2 = mNew()
    m.m1 = 'newM1'
    m.m2 = 'newM2'
    call tstOut t, 'mNew() 1='m.m1 '2='m.m2
    call mNewArea 'tst'm1
    t1 = tstMark(mNew('tst'm1), '1')
    t2 = tstMark(mNew('tst'm1), '2')
    call mFree tstMark(t1, '3')
    t3 = tstMark(mNew('tst'm1), '4')
    t4 = tstMark(mNew('tst'm1), '5')
    call tstOut t, 'mNew(tst...) 2='m.t2 '3='m.t3 '4='m.t4
    i = mIterBegin('tst'm1)
    do while assNN('i', mIter(i))
        call tstOut t, 'iter' m.i
        end
    call tstEnd t
/*
$=/tstM/
    ### start tst tstM ################################################
    symbol m.b LIT
    symbol m.a LIT
    mAdd a A.2
    mAdd a A.3
    m.a: 3: 1=eins 2=zwei 3=drei 4=M.A.4
    m.c: 5: 1=c vorAddSt a 2=eins 3=zwei 4=drei 5=c nacAddSt a 6=M.C.6
$/tstM/ */


    drop m.b m.a m.a.0 m.a.1 m.a.2
    call tst t, 'tstM'
    call tstOut t, 'symbol m.b' symbol('m.b')
    m.b = 1
    call tstOut t, 'symbol m.a' symbol('m.a')
    call tstOut t, 'mAdd a' mAdd(mCut(a, 0), 'eins', 'zwei')
    call tstOut t, 'mAdd a' mAdd(a, 'drei')
    call tstOut t, 'm.a:' m.a.0': 1='m.a.1 '2='m.a.2 '3='m.a.3 '4='m.a.4
    call mAdd mCut(c, 0), 'c vorAddSt a'
    call mAddSt c, a
    call mAdd c, 'c nacAddSt a'
    call tstOut t, 'm.c:' m.c.0': 1='m.c.1 '2='m.c.2 '3='m.c.3,
                    '4='m.c.4 '5='m.c.5 '6='m.c.6
    call tstEnd t
    return
endProcedure tstM

tstFCat: procedure expose m.
/*
$=/tstFCat/
    ### start tst tstFCat #############################################
    fCat(                     ,0) =;
    fCat(1                    ,0) =;
    fCat(112222               ,0) =;
    fCat(3%#a1%c2             ,0) =;
    fCat(4%#a1%c2@%c333       ,0) =;
    fCat(5%#a1%c2@%c3@%c4     ,0) =;
    fCat(                     ,1) =eins;
    fCat(1                    ,1) =eins;
    fCat(112222               ,1) =eins;
    fCat(3%#a1%c2             ,1) =1eins2;
    fCat(4%#a1%c2@%c333       ,1) =1eins2eins333;
    fCat(5%#a1%c2@%c3@%c4     ,1) =1eins2eins3eins4;
    fCat(                     ,2) =einszwei;
    fCat(1                    ,2) =eins1zwei;
    fCat(112222               ,2) =eins112222zwei;
    fCat(3%#a1%c2             ,2) =1eins231zwei2;
    fCat(4%#a1%c2@%c333       ,2) =1eins2eins33341zwei2zwei333;
    fCat(5%#a1%c2@%c3@%c4     ,2) =1eins2eins3eins451zwei2zwei3zwei4;
    fCat(                     ,3) =einszweidrei;
    fCat(1                    ,3) =eins1zwei1drei;
    fCat(112222               ,3) =eins112222zwei112222drei;
    fCat(3%#a1%c2             ,3) =1eins231zwei231drei2;
    fCat(4%#a1%c2@%c333       ,3) =1eins2eins33341zwei2zwei33341drei2dr+
    ei333;
    fCat(5%#a1%c2@%c3@%c4     ,3) =1eins2eins3eins451zwei2zwei3zwei451d+
    rei2drei3drei4;
$/tstFCat/ */
    call pipeIni
    call tst t, "tstFCat"
     m.qq.1 = "eins"
     m.qq.2 = "zwei"
     m.qq.3 = "drei"
     do qx = 0 to 3
         m.qq.0 = qx
         call tstFCat1 qx
         call tstFCat1 qx, '1'
         call tstFCat1 qx, '112222'
         call tstFCat1 qx, '3%#a1%c2'
         call tstFCat1 qx, '4%#a1%c2@%c333'
         call tstFCat1 qx, '5%#a1%c2@%c3@%c4'
         end
     call tstEnd t
     return
endProcedure tstFCat

tstFCat1: procedure expose m.
parse arg m.qq.0, fmt
    call out left("fCat("fmt, 26)","m.qq.0") ="fCat(fmt, qq)";"
return
endProcedure tstFCat1

tstMap: procedure expose m.
/*
$=/tstMap/
    ### start tst tstMap ##############################################
    mapNew m keys m-keys 0
    map m zwei --> 2
    map m Zwei is not defined
    map stem m-keys 4
    map m eins --> 1
    map m zwei --> 2
    map m drei --> 3
    map m vier --> 4
    *** err: duplicate in mapAdd(m, eins, 1)
    map m zwei is not defined
    q 2 zw dr
    map stem Q 2
    map Q zw --> 2Q
    map Q dr --> 3Q
    map stem m 3
    map m eins --> 1
    map m zwei --> 2PUT
    map m vier --> 4PUT
    *** err: duplicate in mapAdd(m, zwei, 2ADDDUP)
    tstMapLong eins keys 3
    tstMapLong zweiMal keys 48
    tstMapLong dreiMal keys 93
    tstMapLong vier    keys 138
    tstMapLong <fuenf> keys 188
    tstMap clear keys 0
    inline1 3
    inline1 1 ==    inline1 eins==
    inline1 2 ====
    inline1 3 ==    inline1 drei==
    inline2 1 1 ==    inline2 eins==
    inline3 ?
$/tstMap/ */
/*
$=/tstMapInline1/
    inline1 eins

    inline1 drei
$/tstMapInline1/ */
/*
$=/tstMapInline2/
    inline2 eins
$/tstMapInline2/ */

    call tst t, 'tstMap'
    m = mapNew('K')
    ky = mapKeys(m)
    call mAdd t'.TRANS', m 'm', ky 'm-keys'
    call tstOut t, 'mapNew' m 'keys' ky m.ky.0
    call mapAdd m, 'eins', 1
    call mapAdd m, 'zwei', 2
    call mapAdd m, 'drei', 3
    call mapAdd m, 'vier', 4
    call tstMapShow m, 'zwei'
    call tstMapShow m, 'Zwei'
    call tstMapShowSt m, mapKeys(m)
    call mapAdd m, 'eins', 1
    call mapReset m, '='
    call tstMapShow m, 'zwei'
    call mapAdd m, 'eins', 1
    call mapAdd m, 'zwei', 2
    call mapPut m, 'zwei', 2Put
    call mapPut m, 'vier', 4Put
    call mapReset q, '='
    call mapAdd q, 'zw', 2q
    call mapAdd q, 'dr', 3q
    call tstOut t, 'q' m.q.0 m.q.1 m.q.2
    call tstMapShowSt q, mapKeys(q)
    call tstMapShowSt m, mapKeys(m)
    call mapAdd m, 'zwei', 2addDup
    call tstMapLong m, 'eins'      ,201, 2000, -2, 2
    call tstMapLong m, 'zweiMal'   ,201, 2000, -2, 2
    call tstMapLong m, 'dreiMal'   ,201, 2000,  2,-2
    call tstMapLong m, 'vier   '   ,2010, 201, -2, 2
    call tstMapLong m, '<fuenf>'   ,2010, 201,  2,-2
    call mapClear m
    keys = mapKeys(m)
    call tstOut t, 'tstMap clear keys' m.keys.0
    i = mapInline('tstMapInline1')
    call tstOut t, 'inline1' m.i.0
    do x=1 to m.i.0
        call tstOut t, 'inline1' x '=='m.i.x'=='
        end
    i = mapInline('tstMapInline2')
    call tstOut t, 'inline2' m.i.0 '1 =='m.i.1'=='
    call tstOut t, 'inline3' mapInline('tstMapInline3', 'r')'?'
    call tstEnd t
    return
endProcedure tstMap

tstMapLong: procedure expose m.
parse arg m, w, f1, t1, f2, t2
    if f1 < t1 then
        b1 = 201
    else
        b1 = -201
    if f2 < t2 then
        b2 = 1
    else
        b2 = -1
    lo = copies(w, 2100 % length(w))
    keys = mapKeys(m)
    keyCn = m.keys.0
    call tstOut t, 'tstMapLong' w 'keys' keyCn
    do x = f1 by b1 to t1
        do y = x+f2 by b2 to x+t2
            k = left(lo, y)
            if mapHasKey(m, k) then
                call err 'mapLong hasKey before' w y
            call mapAdd m, k, w y
            if \ mapHasKey(m, k) then
                call err 'mapLong \ hasKey after' w y
            if mapGet(m, k) \== w y then
                call err 'mapLong \ get <> ' w y
            keys = mapKeys(m)
            if keyCn + 1 \= m.keys.0 then
                call err 'mapLong keys .0 <> ' w y
            keyCn = m.keys.0
            if k \== m.keys.keyCn then
                call err 'mapLong keys . ' keyCn '<>' w y
            end
        end
    return
endProcedure tstMapLong

tstMapVia: procedure expose m.
/*
$=/tstMapVia/
    ### start tst tstMapVia ###########################################
    map M K --> A
    mapVia(m, K)      A
    *** err: missing m.A at 3 in mapVia(M, K|)
    mapVia(m, K|)     M.A
    mapVia(m, K|)     valAt m.a
    mapVia(m, K|)     valAt m.a
    *** err: missing m.A.aB at 5 in mapVia(M, K|aB)
    mapVia(m, K|aB)   M.A.aB
    mapVia(m, K|aB)   valAt m.A.aB
    *** err: missing m.valAt m.a at 4 in mapVia(M, K||)
    mapVia(m, K||)    M.valAt m.a
    mapVia(m, K||)    valAt m.valAt m.a
    mapVia(m, K||F)   valAt m.valAt m.a.F
$/tstMapVia/ */
    call tst t, 'tstMapVia'
    u = 'A.aB'
    v = 'valAt m.a'
    drop m.a m.u m.v m.v.f
    call mapReset m, 'K'
    call mapAdd m, k, a
    call tstMapShow m, k
    call tstOut t, 'mapVia(m, K)     ' mapVia(m, 'K')
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    m.a = v
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    call tstOut t, 'mapVia(m, K|)    ' mapVia(m, 'K|')
    call tstOut t, 'mapVia(m, K|aB)  ' mapVia(m, 'K|aB')
    m.u = 'valAt m.'u
    call tstOut t, 'mapVia(m, K|aB)  ' mapVia(m, 'K|aB')
    call tstOut t, 'mapVia(m, K||)   ' mapVia(m, 'K||')
    v = m.a
    m.v = 'valAt m.'v
    m.v.f = 'valAt m.'v'.F'
    call tstOut t, 'mapVia(m, K||)   ' mapVia(m, 'K||')
    call tstOut t, 'mapVia(m, K||F)  ' mapVia(m, 'K||F')
    call tstEnd t
    return
endProcedure tstMapVia

tstMapShow: procedure expose m.
parse arg a, key
    if mapHasKey(a, key) then
        call tstOut t, 'map' a key '-->' mapGet(a, key)
    else
        call tstOut t, 'map' a key 'is not defined'
    return
endProcedure tstMapShow

tstMapShowSt: procedure expose m.
parse arg a, st
    call tstOut t, 'map stem' st m.st.0
    do wx=1 to m.st.0
        call tstMapShow a, m.st.wx
        end
    return
endProcedure tstMapShow

tstClass2: procedure expose m.
/*
$=/tstClass2/
    ### start tst tstClass2 ###########################################
    @CLASS.9 :class = u
    . choice u union
    .  .NAME = class
    . stem 8
    .  .1 refTo @CLASS.3 :class = u
    .   choice u union
    .    .NAME = v
    .   stem 2
    .    .1 refTo @CLASS.1 :class = m
    .     choice m union
    .      .NAME = o2String
    .      .MET = return m.m
    .     stem 0
    .    .2 refTo @CLASS.2 :class = m
    .     choice m union
    .      .NAME = o2File
    .      .MET = return file(m.m)
    .     stem 0
    .  .2 refTo @CLASS.12 :class = c
    .   choice c union
    .    .NAME = u
    .   stem 1
    .    .1 refTo @CLASS.11 :class = u
    .     choice u union
    .      .NAME = .
    .     stem 1
    .      .1 refTo @CLASS.10 :class = f
    .       choice f union
    .        .NAME = NAME
    .       stem 1
    .        .1 refTo @CLASS.3 done :class @CLASS.3
    .  .3 refTo @CLASS.13 :class = c
    .   choice c union
    .    .NAME = f
    .   stem 1
    .    .1 refTo @CLASS.11 done :class @CLASS.11
    .  .4 refTo @CLASS.15 :class = c
    .   choice c union
    .    .NAME = s
    .   stem 1
    .    .1 refTo @CLASS.14 :class = u
    .     choice u union
    .      .NAME = .
    .     stem 0
    .  .5 refTo @CLASS.16 :class = c
    .   choice c union
    .    .NAME = c
    .   stem 1
    .    .1 refTo @CLASS.11 done :class @CLASS.11
    .  .6 refTo @CLASS.17 :class = c
    .   choice c union
    .    .NAME = r
    .   stem 1
    .    .1 refTo @CLASS.14 done :class @CLASS.14
    .  .7 refTo @CLASS.20 :class = c
    .   choice c union
    .    .NAME = m
    .   stem 1
    .    .1 refTo @CLASS.19 :class = u
    .     choice u union
    .      .NAME = .
    .     stem 2
    .      .1 refTo @CLASS.10 done :class @CLASS.10
    .      .2 refTo @CLASS.18 :class = f
    .       choice f union
    .        .NAME = MET
    .       stem 1
    .        .1 refTo @CLASS.3 done :class @CLASS.3
    .  .8 refTo @CLASS.22 :class = s
    .   choice s union
    .   stem 1
    .    .1 refTo @CLASS.21 :class = r
    .     choice r union
    .     stem 1
    .      .1 refTo @CLASS.9 done :class @CLASS.9
$/tstClass2/
*/

    call classIni
    call tst t, 'tstClass2'
    call classOut m.class_C, m.class_C
    call tstEnd t
    return
endProcedure tstClass2

tstClass3: procedure expose m.
/*
$=/tstClass3/
    ### start tst tstClass3 ###########################################
    met v#o2String return m.m
    met w#o2String return substr(m, 2)
    met w#o2String return substr(m, 2)
    *** err: no method nonono in class w
    met w#nonono 0
    t1 4 flds FV, FR
    clear q1 FV= FR= FW=! FO=
    orig R1 FV=valFV FR=refFR FW=!valFW FO=obj.FO
    copy <s1> FV=valFV FR=refFR FW=!valFW FO=obj.FO
    t2 2 flds , EINS.ZWEI
    clear q2 EINS.ZWEI= val=
    orig R2 EINS.ZWEI=valR2.eins.zwei val=valR2Self
    copy <s2> EINS.ZWEI=valR2.eins.zwei val=valR2Self
    t3 0 flds M.<class tst...Tf33>.FLDS.1, M.<class tst...Tf33>.FLDS.2
    clear q3 s1.0=0
    orig R3 s1.0=1 s1.1=M.R3.S1.1 s1.1.f1=M.R3.S1.1.F1 s1.1.s2.0=2 s1.1+
    ..s2.1.f2=M.R3.S1.1.S2.1.F2 s1.1.s2.2.f2=M.R3.S1.1.S2.2.F2
    copy <s3> s1.0=1 s1.1=M.R3.S1.1 s1.1.f1=M.R3.S1.1.F1 s1.1.s2.0=2 s1+
    ..1.s2.1.f2=M.R3.S1.1.S2.1.F2 s1.1.s2.2.f2=M.R3.S1.1.S2.2.F2
$/tstClass3/ */

    call oIni
    call tst t, 'tstClass3'
    call mAdd t.trans, m.class_C '<class class>'
    call tstOut t, 'met v#o2String' classMet(m.class_V, 'o2String')
    call tstOut t, 'met w#o2String' classMet(m.class_W, 'o2String')
    call tstOut t, 'met w#o2String' classMet(m.class_W, 'o2String')
    call tstOut t, 'met w#nonono' classMet(m.class_W, 'nonono')
    all =  classNew('n? tstClassTf31 u f FV v, f FR r, f FW w, f FO o'),
           classNew('n? tstClassTf32 u f EINS f ZWEI v, v') ,
           classNew('n? tstClassTf33 u f S1' classNew('s u v, f F1 v,',
                           'f S2 s f F2 v'))
    call mAdd t.trans, word(all, 3) '<class tst...Tf33>'
    m.r1.fv = 'valFV'
    m.r1.fr = 'refFR'
    m.r1.fw = '!valFW'
    m.r1.fo = 'obj.FO'
    m.r2    = 'valR2Self'
    m.r2.eins.zwei  = 'valR2.eins.zwei'
    m.r3.s1.0 = 1
    m.r3.s1.1.s2.0 = 2
    o.1 = "q 'FV='m.q.FV 'FR='m.q.fr 'FW='m.q.fw 'FO='m.q.fo"
    o.2 = "q 'EINS.ZWEI='m.q.EINS.zwei 'val='m.q"
    o.3 = "q 's1.0='m.q.s1.0"
    p.1 = o.1
    p.2 = o.2
    p.3 = "q 's1.0='m.q.s1.0 's1.1='m.q.s1.1 's1.1.f1='m.q.s1.1.f1" ,
            "'s1.1.s2.0='m.q.s1.1.s2.0 's1.1.s2.1.f2='m.q.s1.1.s2.1.f2",
                                      "'s1.1.s2.2.f2='m.q.s1.1.s2.2.f2"
    do tx=1 to words(all)
        t1 = word(all, tx)
        u1 = classFlds(t1)
        q = 'q'tx
        call tstOut t, 't'tx m.u1.0 'flds' m.u1.1',' m.u1.2
        call utInter("m='"q"';" classMet(t1, 'oClear'))
        interpret "call tstOut t, 'clear'" o.tx
        q = 'R'tx
        interpret "call tstOut t, 'orig'" p.tx
        q = utInter("m='"q"';t='';" classMet(t1, 'oCopy'))
        call mAdd t.trans, q '<s'tx'>'
        interpret "call tstOut t, 'copy'" p.tx
        end
    call tstEnd t
    return
endProcedure tstClass3

tstClass: procedure expose m.
/*
$=/tstClass/
    ### start tst tstClass ############################################
    Q u =className= tstClassTf12
    Q.eins.zwei v ==> M.Q.eins.zwei
    *** err: bad type v: classNew(v tstClassTf12)
    *** err: missing key in mapGet(CLASS_N2C, 0)
    R u =className= uststClassTf12
    R u =className= uststClassTf12in
    R u =className= tstClassTf12
    R.eins.zwei v ==> M.R.eins.zwei
    R s =stem.0= 2
    R.1 r ==> M.R.1 :CLASS.7
    R.1 u =className= tstClassTf12
    R.1.eins.zwei v ==> M.R.1.eins.zwei
    R.2 r ==> M.R.2 :CLASS.7
    R.2 u =className= tstClassTf12
    R.2.eins.zwei v ==> M.R.2.eins.zwei
    S u =className= TstClass7
    S s =stem.0= 2
    S.1 u =className= TstClass7s
    S.1.eins v ==> M.S.1.eins
    S.1 m =met-metA--> say "metA"
    S.1 m =met-metB--> say "metB"
    S.2 u =className= TstClass7s
    S.2.zwei v ==> M.S.2.zwei
    S.2 m =met-metA--> say "metA"
    S.2 m =met-metB--> say "metB"
    class of mutate qq tstClassTf12
$/tstClass/ */

    f = 'eins.zwei'
    e = 'eins'
    z = 'zwei'
    drop m.q.f m.r.f m.r.0 m.r.1 m.r.1.f m.r.2 m.r.2.f
    drop m.s.0 m.s.1 m.s.1.e m.s.2 m.s.2.z
    call classIni
    call tst t, 'tstClass'
    t1  =  classNew('n? tstClassTf12 u f eins f zwei v')
    call tstClassOut t, t1, q
    z = m.class.0
    if class4name('tstClassB', '') == '' then do
        t2 = classNew('n tstClassB u n tstClassC u tstClassTf12,',
            's u v tstClassTf12')
        end
    else do /*  the second time we would get a duplicate error */
        call tstOut t, '*** err: bad type v: classNew(v tstClassTf12)'
        call tstOut t, '*** err: missing key in mapGet(CLASS_N2C, 0)'
        end
    t2 = classNew('n? uststClassTf12 u' ,
           'n? uststClassTf12in u tstClassTf12',
        , classNew('s u r, tstClassTf12'))
    m.r.0 = 2
    call tstClassOut t, t2, r
    t3 = classNew('n? TstClass7 u s',
         classNew('n? TstClass7s u c 1 f eins v, c 2 f zwei v',
            ,'m', 'metA say "metA"', 'metB say "metB"'))
    m.s.0 = 2
    m.s.1 = 1
    m.s.2 = 2
    call tstClassOut t, t3, s
    call oMutatName qq, 'tstClassTf12'
    tt = objClass(qq)
    call tstOut t, 'class of mutate qq' className(tt)
    call tstEnd t
    return
endProcedure tstClass

tstClassOut: procedure expose m.
parse arg o, t, a
    if wordPos(t, m.class_V m.class_W m.class_O) > 0 then
        return tstOut(o, a m.t.name '==>' m.a)
    if m.t == 'r' then
        return tstOut(o, a m.t '==>' m.a ':'if(m.t.0==0,'',m.t.1))
    if m.t == 'u' & m.t.name \== '' then
        call tstOut o, a m.t '=className=' m.t.name
    if m.t == 'f' then
        return tstClassOut(o, m.t.1, a'.'m.t.name)
    if m.t = 'u' then do
        do ux=1 to m.t.0
            call tstClassOut o, m.t.ux, a
            end
        return 0
        end
    if m.t = 's' then do
        call tstOut o, a m.t '=stem.0=' m.a.0
        do ux=1 to m.a.0
            call tstClassOut o, m.t.1, a'.'ux
            end
        return 0
        end
    if m.t = 'c' then do
        if m.t.name = m.a then
            call tstClassOut o, m.t.1, a
         return 0
        end
    if m.t = 'm' then
        return tstOut(o, a m.t '=met-'m.t.name'-->' m.t.met)
    call err 'bad class type' m.t
endProcedure tstClassOut

tstClass4: procedure expose m.
parse arg
/*
$=/tstClass4/
    ### start tst tstClass4 ###########################################
    f 1 eins
    f 2 zwei
    f 3 drei
    f 4 vier
    f 5 acht
    s 1 fuenf
    s 2 sechs
    s 3 sie
$/tstClass4/
*/
    call classIni
    call tst t, 'tstClass4'
    x = classNew('n* TstClass4a u f eins v, f%v zwei drei, f vier v',
                             ', f%s-v fuenf sechs sie, f acht v')
    ff = classFlds(x)
    do fx=1 to m.ff.0
        call tstOut t, 'f' fx m.ff.fx
        end
    st = classMet(x, 'stms')
    do sx=1 to m.st.0
        call tstOut t, 's' sx m.st.sx
        end
    call tstEnd t
    return
endProcedure tstClass4

tstO: procedure expose m.
/*
$=/tstO/
    ### start tst tstO ################################################
    o1.class <class_S>
    o1.class <class T..1>
    o1#met1 metEins
    o1#met2 metZwei
    o1#new return classClear('<class T..1>', oMutate(mNew('<class T..1>+
    '), '<class T..1>'))
$/tstO/
*/
    call mIni
    call tst t, 'tstO'
    call oIni
say m.class_s
    call mAdd t.trans, m.class_s '<class_S>'
    c1 = classNew('n? TstOCla1 u', 'm', 'met1 metEins', 'met2 metZwei')
    call mAdd t.trans, c1 '<class T..1>'
    o1 = 'tst_o1'
    call tstOut t, 'o1.class' objClass(o1)
    o1 = oMutate('o1', c1)
    call tstOut t, 'o1.class' objClass(o1)
    call tstOut t, 'o1#met1' objMet(o1, 'met1')
    call tstOut t, 'o1#met2' objMet(o1, 'met2')
    call tstOut t, 'o1#new' objMet(o1, 'new')
    call tstEnd t
    return
endProcedure tstO


tstOEins: procedure expose m.
/*
$=/tstOEins/
    ### start tst tstOEins ############################################
    class method calls of TstOEins
    .  met Eins.eins M
     flds of <obj e of TstOEins> FEINS, FZWEI
     methodcalls of object e of TstOEins
    .  met Eins.eins <obj e of TstOEins>
    .  met Eins.zwei <obj e2 of TstOEins>
    *** err: no method nein in class TstOEins
    *** err: no method nein in class String
    class method calls of TstOEins
    .  met Elf.zwei M
    flds of <obj f of TstOElf> FEINS, FZWEI, FELF
    methodcalls of object f of TstOElf
    .  met Eins.eins <obj f of TstOElf>
    .  met Elf.zwei <obj f of TstOElf>
    .  met Elf.drei <obj f of TstOElf>
    oCopy c1 of class TstOEins, c2
    C1 u =className= TstOEins
    C1.FEINS v ==> M.C1.FEINS
    C1.FZWEI v ==> M.C1.FZWEI
    C1 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C1 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    C2 u =className= TstOEins
    C2.FEINS v ==> M.C1.FEINS
    C2.FZWEI v ==> M.C1.FZWEI
    C2 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C2 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    oCopy c3 of class TstOElf, c4
    C4 u =className= TstOElf
    C4 u =className= TstOEins
    C4.FEINS v ==> M.C3.FEINS
    C4.FZWEI v ==> M.C3.FZWEI
    C4 m =met-eins--> call tstOut t, "  met Eins.eins" m
    C4 m =met-zwei--> call tstOut t, "  met Eins.zwei" m
    C4.FELF r ==> M.C3.FELF :<class O>
    C4 m =met-zwei--> call tstOut t, "  met Elf.zwei" m
    C4 m =met-drei--> call tstOut t, "  met Elf.drei" m
    tEinsDop <class TstOEins>
    oRun 7*3 21
    oRun 12*12 144
$/tstOEins/ */
    call oIni
    call tst t, 'tstOEins'
    tEins = classNew('n? TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
    call mAdd t.trans, tEins '<class TstOEins>' ,
                   , m.class_o '<class O>'
    call tstOut t, 'class method calls of TstOEins'
    interpret classMet('TstOEins', 'eins')
    e = oNew('TstOEins')
    e2 = oNew('TstOEins')
    call mAdd t.trans, e '<obj e of TstOEins>'
    call mAdd t.trans, e2 '<obj e2 of TstOEins>'
    call tstOut t, 'flds of' e mCat(oFlds(e), ', ')
    call tstOut t, 'methodcalls of object e of TstOEins'
    call tstOmet e, 'eins'
    call tstOmet e2, 'zwei'
    call tstOmet e, 'nein'
    call tstOmet 'noObj', 'nein'
    tElf = classNew('n? TstOElf u TstOEins, f FELF r', 'm',
        , 'zwei call tstOut t, "  met Elf.zwei" m',
        , 'drei call tstOut t, "  met Elf.drei" m')
    call tstOut t, 'class method calls of TstOEins'
    interpret classMet('TstOElf', 'zwei')
    f = oNew('TstOElf')
    call mAdd t.trans, f '<obj f of TstOElf>'
    call tstOut t, 'flds of' f mCat(oFlds(f), ', ')
    call tstOut t, 'methodcalls of object f of TstOElf'
    call tstOmet f, 'eins'
    call tstOmet f, 'zwei'
    call tstOmet f, 'drei'
    call oMutatName c1, 'TstOEins'
    call tstOut t, 'oCopy c1 of class TstOEins, c2'
    call tstClassOut t, tEins, c1
    call oCopy c1, c2
    call tstClassOut t, tEins, c2
    call tstOut t, 'oCopy c3 of class TstOElf, c4'
    call oMutatName c3, 'TstOElf'
    call oCopy c3, c4
    call tstClassOut t, tElf, c4

/*    tEinsDop = classNew('n TstOEins u f FEINS v,f FZWEI v', 'm',
           , 'eins call tstOut t, "  met Eins.eins" m',
           , 'zwei call tstOut t, "  met Eins.zwei" m')
*/ tEinsDop = tEins
    call tstOut t, 'tEinsDop' tEinsDop
    e3 = oNew('TstOEins')
    if e3 <<= e | e3 <<= e2 then
        call err 'doppelt reinitialised objects'
    rr = oRunner('return 7 * 3')
    call tstOut t, 'oRun 7*3' oRun(rr)
    r12 = oRunner('return 12 * 12')
    call tstOut t, 'oRun 12*12' oRun(r12)
    call tstEnd t
    return
endProcedure tstOEins

tstOmet: procedure expose m.
parse arg m, met
    interpret objMet(m, met)
    return
endProcedure tstOmet

tstO2Text: procedure expose m.
/*
$=/o2Text/
    ### start tst o2Text ##############################################
    .             > .
    und _s abc   > und so
    und _s lang  > und so und so und so und so und so und so und so und+
    . so und so ....
    !und _w abc  > und so
    o1           > tstO2T1=¢fEins=v_o1_fEins fZwei=v_o1_fZwei fDrei=v_o+
    1_fDrei!
    o1 lang      > tstO2T1=¢fEins=v_o1_fEins fZwei=v_o1_fZweiv_o1_fZwei+
    v_o1_fZweiv....
    runner       > <tstRunObj>=¢<tstRunCla>!
    file         > <tstFileObj>=¢File!
$/o2Text/
*/
    call catIni
    cl = classNew('n* TstO2Text1 u f fEins v, f fZwei v, f fDrei v')
    o1 = 'tstO2T1'
    call oMutate o1, cl
    call mPut o1'.fEins', 'v_o1_fEins'
    call mPut o1'.fZwei', 'v_o1_fZwei'
    call mPut o1'.fDrei', 'v_o1_fDrei'
    call tst t, 'o2Text'
    maxL = 66
    call tstOut t, '             >' o2Text('         ', maxL)
    call tstOut t, 'und _s abc   >' o2Text('und so   ', maxL)
    call tstOut t, 'und _s lang  >' o2Text(copies('und so ',33), maxL)
    call tstOut t, '!und _w abc  >' o2Text('und so   ', maxL)
    call tstOut t, 'o1           >' o2Text(o1         , maxL)
    call mPut o1'.fZwei', copies('v_o1_fZwei',33)
    call tstOut t, 'o1 lang      >' o2Text(o1         , maxL)
    f = file('abc.efg')
    r = oRunner('say o2Text test')
    call mAdd t.trans, r '<tstRunObj>',
                     , className(objClass(r)) '<tstRunCla>' ,
                     , f '<tstFileObj>'
    call tstOut t, 'runner       >' o2Text(r          , maxL)
    call tstOut t, 'file         >' o2Text(f          , maxL)
    call mAdd t.trans, r '<tstRunnerObj>',
                     , className(objClass(r)) '<tstRunnerCla>'
    call tstEnd t
    return
endProcedure tstO2Text

tstJSay: procedure expose m.
/*
$=/tstJSay/
    ### start tst tstJSay #############################################
    *** err: bad option openArg in jOpen(<obj j of JRW>, openArg)
    *** err: jWrite(<obj j of JRW>, writeArg) but not opened w
    *** err: can only write JSay.jOpen(<obj s of JSay>, <)
    *** err: jWrite(<obj s of JSay>, write s vor open) but not opened+
    . w
    *** err: can only read JRWEof.jOpen(<obj e of JRWEof>, >)
    *** err: jRead(<obj e of JRWEof>) but not opened r
    read e vor open 0 m.xx valueBefore
    read e nach open 0 m.xx valueBefore
    out eins
    #jIn 1# tst in line 1 eins ,
    out zwei in 1 vv=readAdrVV
    #jIn 2# tst in line 2 zwei ;   .
    out drei in 1 vv=readAdrVV Schluss
$/tstJSay/ */

    call jIni
    call tst t, 'tstJSay'
    jrw = oNew('JRW')
    call mAdd t'.TRANS', jrw '<obj j of JRW>'
    call jOpen jrw, 'openArg'
    call jWrite jrw, 'writeArg'
    s = oNew('JSay')
    call mAdd t'.TRANS', s '<obj s of JSay>'
    call jOpen s, m.j.cRead
    s = oNew('JSay')
    call mAdd t'.TRANS', s '<obj s of JSay>'
    call jWrite s, 'write s vor open'
    call jOpen s, '>'
    call jWrite s, 'write s nach open'
    e = oNew('JRWEof')
    call mAdd t'.TRANS', e '<obj e of JRWEof>'
    call jOpen e, '>'
    e = oNew('JRWEof')
    call mAdd t'.TRANS', e '<obj e of JRWEof>'
    m.xx = 'valueBefore'
    call tstOut t, 'read e vor open' jReadVar(e, xx) 'm.xx' m.xx
    call jOpen e, m.j.cRead
    call tstOut t, 'read e nach open' jReadVar(e, xx) 'm.xx' m.xx
    call out 'out eins'
    vv = 'readAdrVV'
    m.vv = 'readAdrVVValueBefore'
    call out 'out zwei in' in() 'vv='vv
    m.vv = 'readAdrVVValueBefore'
    call out 'out drei in' inVar(vv) 'vv='vv 'Schluss'
    call tstEnd t
    return
endProcedure tstJSay

tstJ: procedure expose m.
/*
$=/tstJ/
    ### start tst tstJ ################################################
    out eins
    #jIn 1# tst in line 1 eins ,
    1 in() tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    2 in() tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    3 in() tst in line 3 drei .schluss..
    #jIn eof 4#
    in() 3 reads vv VV
    line buf line one
    line buf line two
    line buf line three
    line buf line four
    *** err: jWrite(<buf b>, buf line five while reading) but not opene+
    d w
$/tstJ/ */

    call jIni
    call tst t, "tstJ"
    b = jOpen(jBuf(), '>')
    call mAdd t'.TRANS', b '<buf b>'
    call out 'out eins'
    do lx=1 by 1 while in()
        call out lx 'in()' m.in
        end
    call out 'in()' (lx-1) 'reads vv' vv
    call jOpen b, '>'
    call jWrite b, 'buf line one'
    call jClose b
    call mAdd m.b.stem, 'buf line two', 'buf line three'
    call jWrite b, 'buf line four'
    call jClose b
    call jOpen b, m.j.cRead
    do while jRead(b)
        call out 'line' m.b
        end
    call jWrite b, 'buf line five while reading'
    call jClose b
    call tstEnd t
    return
endProcedure tstJ

tstJ2: procedure expose m.
/*
$=/tstJ2/
    ### start tst tstJ2 ###############################################
    class1 <Tst?1 class> <Tst?1 name>
    class2 <Tst?1 class> <Tst?1 name>
    class3 <Tst?1 class> <Tst?1 name>
    b read EINS feld eins, ZWEI feld zwei, DREI feld drei
    b read EINS feld eins, ZWEI feld zwei 2, DREI feld drei
    c read EINS feld eins, ZWEI feld zwei, DREI drei cat 1
    tstR: @tstWriteoV3 isA :<Tst?1 name>
    tstR:  .EINS = feld eins
    tstR:  .ZWEI = feld zwei
    tstR:  .DREI = drei cat 1
    c read EINS feld eins, ZWEI feld zwei 2, DREI drei cat 2
    tstR: @tstWriteoV4 isA :<Tst?1 name>
    tstR:  .EINS = feld eins
    tstR:  .ZWEI = feld zwei 2
    tstR:  .DREI = drei cat 2
$/tstJ2/ */

    call jIni
    call tst t, "tstJ2"
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call mAdd t'.TRANS', ty '<Tst?1 class>'
    call mAdd t'.TRANS', m.ty.name '<Tst?1 name>'
    call tstOut t, 'class1' ty m.ty.name
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call tstOut t, 'class2' ty m.ty.name
    ty = classNew('n* Tst u f EINS v, f ZWEI v, f DREI v')
    call tstOut t, 'class3' ty m.ty.name
    call oMutate qq, ty
    m.qq.eins = 'feld eins'
    m.qq.zwei = 'feld zwei'
    m.qq.drei = 'feld drei'
    b = jBuf()
    call jOpen b, '>'
    call jWrite b, oCopy(qq)
    m.qq.zwei = 'feld zwei 2'
    call jWrite b, qq
    call jOpen jClose(b), m.j.cRead
    c = jOpen(jBuf(), '>')
    do xx=1 while jRead(b)
        res = m.b
        call out 'b read EINS' m.res.eins', ZWEI' m.res.zwei,
                                     || ', DREI' m.res.drei
        m.res.drei = 'drei cat' xx
        call jWrite c, res
        end
    call jOpen jClose(c), m.j.cRead
    do while jRead(c)
        ccc = m.c
        call out 'c read EINS' m.ccc.eins', ZWEI' m.ccc.zwei,
                                     || ', DREI' m.ccc.drei
        call out ccc
        end
    call tstEnd t
    return
endProcedure tstJ2

tstCat: procedure expose m.
/*
$=/tstCat/
    ### start tst tstCat ##############################################
    catRead 1 line 1
    catRead 2 line 2
    catRead 3 line 3
    appRead 1 line 1
    appRead 2 line 2
    appRead 3 line 3
    appRead 4 append 4
    appRead 5 append 5
$/tstCat/ */
    call catIni
    call tst t, "tstCat"
    i = cat(jBuf('line 1', 'line 2'), jBuf('line 3'))
    call jOpen i, m.j.cRead
    do lx=1 by 1 while jRead(i)
        call tstOut t, 'catRead' lx m.i
        end
    call jOpen jClose(i), m.j.cApp
    call jWrite i, 'append 4'
    call jWrite i, 'append 5'
    call jOpen jClose(i), m.j.cRead
    do lx=1 by 1 while jRead(i)
        call tstOut t, 'appRead' lx m.i
        end
    call tstEnd t
    return
endProcedure tstCat

tstEnv: procedure expose m.
    call pipeIni
/*
$=/tstEnv/
    ### start tst tstEnv ##############################################
    before pipeBeLa
    after pipeEnd
    *** err: jWrite(<jBuf c>, write nach pop) but not opened w
    #jIn 1# tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    before writeNow 2 c --> std
    before writeNow 1 b --> c
    b line eins
    b zwei |
    nach writeNow 1 b --> c
    add nach pop
    after push c only
    tst in line 1 eins ,
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    nach writeNow 2 c --> std
    *** err: jWrite(<jBuf c>, ) but not opened w
$/tstEnv/ */

    call tst t, "tstEnv"
    c = jBuf()
    call mAdd t'.TRANS', c '<jBuf c>'
    call out 'before pipeBeLa'
    b = jBuf("b line eins", "b zwei |")
    call pipe '+Ff', c, b
    call out 'before writeNow 1 b --> c'
    call pipeWriteNow
    call out 'nach writeNow 1 b --> c'
    call pipe '-'
    call out 'after pipeEnd'
    call jWrite c, 'write nach pop'
    call mAdd c'.BUF', 'add nach pop'
    call pipe '+A', c
    call out 'after push c only'
    call pipeWriteNow
    call pipe '-'
    call pipe '+f', , c
    call out 'before writeNow 2 c --> std'
    call pipeWriteNow
    call out 'nach writeNow 2 c --> std'
    call pipe '-'
    call jWrite c
    call tstEnd t
    return
endProcedure tstEnv

tstEnvCat: procedure expose m.
    call pipeIni
/*
$=/tstEnvCat/
    ### start tst tstEnvCat ###########################################
    c1 contents
    c1 line eins |
    before writeNow 1 b* --> c*
    b1 line eins|
    b2 line eins
    b2 zwei |
    c2 line eins |
    after writeNow 1 b* --> c*
    c2 contents
    c2 line eins |
$/tstEnvCat/ */

    call tst t, "tstEnvCat"

    b0= jBuf()
    b0= jBuf()
    b1= jBuf("b1 line eins|")
    b2 = jBuf("b2 line eins", "b2 zwei |")
    c1 = jBuf("c1 line eins |")
    c2 = jBuf("c2 line eins |")
    call pipe '+Affff', c1, b0, b1, b2, c2
    call out 'before writeNow 1 b* --> c*'
    call pipeWriteNow
    call out 'after writeNow 1 b* --> c*'
    call pipe '-'
    call out 'c1 contents'
    call pipe '+f' , , c1
    call pipeWriteNow
    call pipe '-'
    call pipe '+f' , , c2
    call out 'c2 contents'
    call pipeWriteNow
    call pipe '-'
    call tstEnd t
    return
endProcedure tstEnvCat

tstPipe: procedure expose m.
    call pipeIni
/*
$=/tstPipe/
    ### start tst tstPipe #############################################
    .+0 vor pipeBegin
    #jIn 1# tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    .+7 nach pipeLast
    ¢7 +6 nach pipe 7!
    ¢7 +2 nach pipe 7!
    ¢7 +4 nach nested pipeLast 7!
    ¢7 (4 +3 nach nested pipeBegin 4) 7!
    ¢7 (4 (3 +1 nach pipeBegin 3) 4) 7!
    ¢7 (4 (3 tst in line 1 eins , 3) 4) 7!
    ¢7 (4 (3 tst in line 2 zwei ;    3) 4) 7!
    ¢7 (4 (3 tst in line 3 drei .schluss. 3) 4) 7!
    ¢7 (4 (3 +1 nach writeNow vor pipe 3) 4) 7!
    ¢7 (4 +3 nach preSuf vor nested pipeLast 4) 7!
    ¢7 +4 nach preSuf vor nested pipeEnd 7!
    ¢7 +5 nach nested pipeEnd vor pipe 7!
    ¢7 +6 nach writeNow vor pipeLast 7!
    .+7 nach writeNow vor pipeEnd
    .+8 nach pipeEnd
$/tstPipe/ */

    say 'x0' m.pipe.0
    call tst t, 'tstPipe'
    call out '+0 vor pipeBegin'
    say 'x1' m.pipe.0
    call pipe '+N'
    call out '+1 nach pipeBegin'
    call pipeWriteNow
    call out '+1 nach writeNow vor pipe'
    call pipe 'N|'
    call out '+2 nach pipe'
    call pipe '+N'
    call out '+3 nach nested pipeBegin'
    call pipePreSuf '(3 ', ' 3)'
    call out '+3 nach preSuf vor nested pipeLast'
    call pipe 'P|'
    call out '+4 nach nested pipeLast'
    call pipePreSuf '(4 ', ' 4)'
    call out '+4 nach preSuf vor nested pipeEnd'
    call pipe '-'
    call out '+5 nach nested pipeEnd vor pipe'
    call pipe 'N|'
    call out '+6 nach pipe'
    call pipeWriteNow
    say 'out +6 nach writeNow vor pipeLast'
    call out '+6 nach writeNow vor pipeLast'
    call pipe 'P|'
    call out '+7 nach pipeLast'
    call pipePreSuf '¢7 ', ' 7!'
    call out '+7 nach writeNow vor pipeEnd'
    call pipe '-'
    call out '+8 nach pipeEnd'
    say 'xx' m.pipe.0
    call tstEnd t
    return
endProcedure tstPipe

tstPipeS: procedure expose m.
/*
$=/tstPipeS/
    ### start tst tstPipeS ############################################
    eine einzige zeile
    nach all einzige Zeile
    select strip(creator) cr, strip(name) tb,
    (row_number()over())*(row_number()over()) rr
    from sysibm.sysTables
$/tstPipeS/
*/
    call pipeIni
    call tst t, "tstPipeS"
    call pipe '+s',, 'eine einzige zeile'
    call pipeWriteAll
    call out 'nach all einzige Zeile'
    call pipe 'sss',,
              , "select strip(creator) cr, strip(name) tb," ,
              ,      "(row_number()over())*(row_number()over()) rr" ,
              ,      "from sysibm.sysTables"
    call pipeWriteAll
    call pipe '-'
    call tstEnd t
    return
endProcedure tstPipeS

tstEnvVars: procedure expose m.
    call pipeIni
/*
$=/tstEnvVars/
    ### start tst tstEnvVars ##########################################
    put v1 value eins
    v1 hasKey 1 get TST.ADR1
    v2 hasKey 0
    one to theBur
    two to theBuf
    v1=TST.ADR1 o=TST.ADR1
    v3=v3WieGehts? o=v3WieGehts?
    v4=!v4WieGehts? o=!v4WieGehts?
    o o0=<o0>
    s o0=<o0>
    o o0=<o0>
    s o0=<o0>
    o0&fSt0=rexx o0.fSt0 o=rexx o0.fSt0
    o0&fRe0=!rexx o0.fRe0 o=!rexx o0.fRe0
    o0&=rexx o0-value o=rexx o0-value
    o o0=<o0>
    s o0=<o0>
    o0&fSt0=put o0.fSt0 o=put o0.fSt0
    o0&fRe0=!putO o0.fRe0 o=!putO o0.fRe0
    o0&=put o0-value o=put o0-value
$/tstEnvVars/
$=/tstEnvVars1/
    ### start tst tstEnvVars1 #########################################
    m.o1=put-o1-value m.o1.fStr=put-o1.fStr m.o1.fRef=<o0>
    o o1=<o1> s o1=<o1>
    o1&fStr=put-o1.fStr o=put-o1.fStr
    o1&=put-o1-value o=put-o1-value
    o1&fRef=<o0> o=<o0>
    o1&fRef>fSt0=put o0.fSt0 o=put o0.fSt0
    o1&fRef>fRe0=!putO o0.fRe0 o=!putO o0.fRe0
    m.o1&fNest.fSt0= put-o1.fNest.fSt0 m.o1&fNest.fRe0= !put-o1&fNest.f+
    Re0
    o1&fNest.fSt0=put-o1.fNest.fSt0 o=put-o1.fNest.fSt0
    o1&fNest&fRe0=!put-o1&fNest.fRe0 o=!put-o1&fNest.fRe0
$/tstEnvVars1/
$=/tstEnvVars2/
    ### start tst tstEnvVars2 #########################################
    o2=<o2> getO(o2)=<o2> getO(o2&fRef)=<o1>
    o2&fRef>fStr=put-o1.fStr o=put-o1.fStr
    o2&fRef>=put-o1-value o=put-o1-value
    o2&fRef>fRef=<o0> o=<o0>
    o2&fRef>fRef>fSt0=put o0.fSt0 o=put o0.fSt0
    o2&fRef>fRef>fRe0=!putO o0.fRe0 o=!putO o0.fRe0
    o2&fRef>fNest.fSt0=put-o1.fNest.fSt0 o=put-o1.fNest.fSt0
    o2&fRef>fNest&fRe0=!put-o1&fNest.fRe0 o=!put-o1&fNest.fRe0
$/tstEnvVars2/
$=/tstEnvVarsS/
    ### start tst tstEnvVarsS #########################################
    oS=<oS> oS&fStS=<put oS.fStS>
    oS&fStV.0=1 oS&fStV.1=<put oS.fStV.1>
    m.oS.fStR.0=2 .2=!<put oS.fStR.2>
    oS&fStR.0=2 .1=!<put oS.fStR.1> .2=!<put oS.fStR.2>
    m.oS.0=9876 .1234=<put oS.1234>
    *** err: undefined var oS&12
    oS&0=9876 .12=M. .1234=<put oS.1234>
$/tstEnvVarsS/
$=/tstEnvVars3/
    ### start tst tstEnvVars3 #########################################
    m.<o0>=*o0*val vGet(<o0>>)=*o0*val
    m.<o0>.fSt0=*o0.fSt0*val vGet(<o0>>fSt0)=*o0.fSt0*val
    m.<o0>.fRe0=<o1> vGet(<o0>>fRe0)=<o1>
    m.<o1>=*o1*val vGet(<o0>>fRe0>)=*o1*val
    m.<o1>.fStr=*o1.fStr*val vGet(<o0>>fRe0>fStr)=*o1.fStr*val
    m.V.tstEnvVar0=<o0> vGet(tstEnvVar0)=<o0>
    m.V.tstEnvVar0=<o0> vGet(tstEnvVar0&)=<o0>
    m.<o0>=*o0*val vGet(tstEnvVar0&>)=*o0*val
    m.<o0>.fSt0=*o0.fSt0*val vGet(tstEnvVar0&fSt0)=*o0.fSt0*val
    m.<o0>.fRe0=<o1> vGet(tstEnvVar0&fRe0)=<o1>
    m.<o1>=*o1*val vGet(tstEnvVar0&fRe0>)=*o1*val
    m.<o1>.fStr=*o1.fStr*val vGet(tstEnvVar0&fRe0>fStr)=*o1.fStr*val
    m.<o1>.fVar=tstEnvVar2 vGet(tstEnvVar0&fRe0>fVar)=tstEnvVar2
    m.<o2>=*o2*val vGet(tstEnvVar0&fRe0>fVar&)=<o2>
    m.<o2>=*o2*val vGet(tstEnvVar0&fRe0>fVar&>)=*o2*val
    m.<o2>.fStr=*o2.fStr*val vGet(tstEnvVar0&fRe0>fVar&fStr)=*o2.fStr*v+
    al
    m.<o0>=*o0*put2 vGet(<o0>>)=*o0*put2
    m.<o0>.fSt0=*o0.fSt0*put2 vGet(<o0>>fSt0)=*o0.fSt0*put2
    m.<o1>=*o0>fRe0>put2 vGet(<o0>>fRe0>)=*o0>fRe0>put2
    m.<o1>.fStr=*o0>fRe0>fStr*put2 vGet(<o0>>fRe0>fStr)=*o0>fRe0>fStr*p+
    ut2
    m.<o0>=*v0&>*put3 vGet(tstEnvVar0&>)=*v0&>*put3
    m.<o0>.fSt0=*v0&fSt0*put3 vGet(tstEnvVar0&fSt0)=*v0&fSt0*put3
    m.<o1>=*v0&fRe0>*put3 vGet(tstEnvVar0&fRe0>)=*v0&fRe0>*put3
    m.<o1>.fStr=*v0&fRe0>fStr*put3 vGet(tstEnvVar0&fRe0>fStr)=*v0&fRe0>+
    fStr*put3
    m.<o2>=*v0&fRe0>fVar&>*put3 vGet(tstEnvVar0&fRe0>fVar&>)=*v0&fRe0>f+
    Var&>*put3
    m.<o2>.fStr=*v0&fRe0>fVar&fStr*put3 vGet(tstEnvVar0&fRe0>fVar&fStr)+
    =*v0&fRe0>fVar&fStr*put3
$/tstEnvVars3/
 */
    c0 = classNew('n? TstEnvVars0 u f fSt0 v, f = v, f fRe0 r')
    c1 = classNew('n? TstEnvVars1 u f fStr v,f fRef r' ,
              ', f fNest TstEnvVars0, f = v, f fVar v')
    o0 = oNew(c0)
    o1 = oNew(c1)
    o2 = oNew(c1)
    call tst t, "tstEnvVars3"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'
    fSt0 = 'fSt0'
    fRe0 = 'fRe0'
    fStr = 'fStr'
    fRef = 'fRef'
    fVar = 'fVar'
    v0 = 'tstEnvVar0'
    v2 = 'tstEnvVar2'
    m.o0 = '*o0*val'
    m.o0.fSt0 = '*o0.fSt0*val'
    m.o0.fRe0 = o1
    m.o1 = '*o1*val'
    m.o1.fStr = '*o1.fStr*val'
    m.o1.fRef = o2
    m.o1.fVar = v2
    m.o2 = '*o2*val'
    m.o2.fStr = '*o2.fStr*val'
    m.v.v0 = o0
    m.v.v2 = o2
    call tstEnvVarsMG o0, o0'>'
    call tstEnvVarsMG o0'.'fSt0, o0'>'fSt0
    call tstEnvVarsMG o0'.'fRe0, o0'>'fRe0
    call tstEnvVarsMG o1, o0'>'fRe0'>'
    call tstEnvVarsMG o1'.'fStr, o0'>'fRe0'>'fStr
    call tstEnvVarsMG v'.'v0, v0
    call tstEnvVarsMG v'.'v0, v0'&'
    call tstEnvVarsMG o0, v0'&>'
    call tstEnvVarsMG o0'.'fSt0, v0'&'fSt0
    call tstEnvVarsMG o0'.'fRe0, v0'&'fRe0
    call tstEnvVarsMG o1, v0'&'fRe0'>'
    call tstEnvVarsMG o1'.'fStr, v0'&'fRe0'>'fStr
    call tstEnvVarsMG o1'.'fVar, v0'&'fRe0'>'fVar
    call tstEnvVarsMG o2, v0'&'fRe0'>'fVar'&'
    call tstEnvVarsMG o2, v0'&'fRe0'>'fVar'&>'
    call tstEnvVarsMG o2'.'fStr, v0'&'fRe0'>'fVar'&fStr'
    call vPut o0'>', '*o0*put2'
    call tstEnvVarsMG o0, o0'>'
    call vPut o0'>'fSt0, '*o0.fSt0*put2'
    call tstEnvVarsMG o0'.'fSt0, o0'>'fSt0
    call vPut o0'>'fRe0'>', '*o0>fRe0>put2'
    call tstEnvVarsMG o1, o0'>'fRe0'>'
    call vPut o0'>'fRe0'>'fStr, '*o0>fRe0>fStr*put2'
    call tstEnvVarsMG o1'.'fStr, o0'>'fRe0'>'fStr
    call vPut v0'&>', '*v0&>*put3'
    call tstEnvVarsMG o0, v0'&>'
    call vPut v0'&'fSt0, '*v0&fSt0*put3'
    call tstEnvVarsMG o0'.'fSt0, v0'&'fSt0
    call vPut v0'&'fRe0'>', '*v0&fRe0>*put3'
    call tstEnvVarsMG o1, v0'&'fRe0'>'
    call vPut v0'&'fRe0'>'fStr, '*v0&fRe0>fStr*put3'
    call tstEnvVarsMG o1'.'fStr, v0'&'fRe0'>'fStr
    call vPut v0'&'fRe0'>'fVar'&>', '*v0&fRe0>fVar&>*put3'
    call tstEnvVarsMG o2, v0'&'fRe0'>'fVar'&>'
    call vPut v0'&'fRe0'>'fVar'&fStr', '*v0&fRe0>fVar&fStr*put3'
    call tstEnvVarsMG o2'.'fStr, v0'&'fRe0'>'fVar'&fStr'
    call tstEnd t, "tstEnvVars"
    call tst t, "tstEnvVars"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'
    call vRemove 'v2'
    m.tst.adr1 = 'value eins'
    put1 = vPut('v1', oMutate(tst'.'adr1, m.class_V))
    call tstOut t, 'put v1' m.put1
    call tstOut t, 'v1 hasKey' vHasKey('v1') 'get' vGet('v1')
    call tstOut t, 'v2 hasKey' vHasKey('v2')
    if 0 then
        call tstOut t, 'v2 get'    vGet('v2')
    call vPut 'theBuf', jBuf()
    call pipe '+F' , vGet('theBuf')
    call out 'one to theBur'
    call out 'two to theBuf'
    call pipe '-'
    call pipe '+f',, vGet('theBuf')
    call pipeWriteNow
    call pipe '-'
    call tstOut t, 'v1='vGet('v1') 'o='vGet('v1')
    call vPut 'v3', 'v3WieGehts?'
    call tstOut t, 'v3='vGet('v3') 'o='vGet('v3')
    call vPut 'v4', s2o('v4WieGehts?')
    call tstOut t, 'v4='vGet('v4') 'o='vGet('v4')

    call vPut 'o0', o0
    call tstOut t, 'o o0='vGet('o0')
    call tstOut t, 's o0='vGet('o0')
    fSt0 = 'fSt0'
    fRe0 = 'fRe0'
    m.o0 = 'rexx o0-value'
    m.o0.fSt0 = 'rexx o0.fSt0'
    m.o0.fRe0 = s2o('rexx o0.fRe0')
    call tstOut t, 'o o0='vGet('o0')
    call tstOut t, 's o0='vGet('o0')
    call tstOut t, 'o0&fSt0='vGet('o0&fSt0') 'o='vGet('o0&fSt0')
    call tstOut t, 'o0&fRe0='vGet('o0&fRe0') 'o='vGet('o0&fRe0')
    call tstOut t, 'o0&='vGet('o0&>') 'o='vGet('o0&>')
    call vPut 'o0&>', 'put o0-value'
    call vPut 'o0&fSt0', 'put o0.fSt0'
    call vPut 'o0&fRe0', s2o('putO o0.fRe0')
    call tstOut t, 'o o0='vGet('o0')
    call tstOut t, 's o0='vGet('o0')
    call tstOut t, 'o0&fSt0='vGet('o0&fSt0') 'o='vGet('o0&fSt0')
    call tstOut t, 'o0&fRe0='vGet('o0&fRe0') 'o='vGet('o0&fRe0')
    call tstOut t, 'o0&='vGet('o0&>') 'o='vGet('o0&>')

    call tstEnd t
    call tst t, "tstEnvVars1"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'

    call vPut 'o1', o1
    call vPut 'o1&>', 'put-o1-value'
    call vPut 'o1&fStr', 'put-o1.fStr'
    call vPut 'o1&fRef', vGet('o0')
    call tstOut t, 'm.o1='m.o1 'm.o1.fStr='mGet(o1'.fStr'),
         'm.o1.fRef='mGet(o1'.fRef')
    call tstOut t, 'o o1='vGet('o1') 's o1='vGet('o1')
    call tstOut t, 'o1&fStr='vGet('o1&fStr') 'o='vGet('o1&fStr')
    call tstOut t, 'o1&='vGet('o1&>') 'o='vGet('o1&>')
    call tstOut t, 'o1&fRef='vGet('o1&fRef') 'o='vGet('o1&fRef')
    call tstOut t, 'o1&fRef>fSt0='vGet('o1&fRef>fSt0') ,
        'o='vGet('o1&fRef>fSt0')
    call tstOut t, 'o1&fRef>fRe0='vGet('o1&fRef>fRe0'),
         'o='vGet('o1&fRef>fRe0')

    call vPut 'o1&fNest.fSt0', 'put-o1.fNest.fSt0'
    call vPut 'o1&fNest.fRe0', s2o('put-o1&fNest.fRe0')
    call tstOut t, 'm.o1&fNest.fSt0=' mGet(o1'.fNest.fSt0') ,
            'm.o1&fNest.fRe0=' mGet(o1'.fNest.fRe0')
    call tstOut t, 'o1&fNest.fSt0='vGet('o1&fNest.fSt0'),
         'o='vGet('o1&fNest.fSt0')
    call tstOut t, 'o1&fNest&fRe0='vGet('o1&fNest.fRe0'),
         'o='vGet('o1&fNest.fRe0')
    call tstEnd t

    call tst t, "tstEnvVars2"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>'
    call vPut 'o2', o2
    call vPut 'o2&fRef', vGet('o1')
    call tstOut t, 'o2='o2 'getO(o2)='vGet('o2'),
        'getO(o2&fRef)='vGet('o2&fRef')

    call tstOut t, 'o2&fRef>fStr='vGet('o2&fRef>fStr'),
         'o='vGet('o2&fRef>fStr')
    call tstOut t, 'o2&fRef>='vGet('o2&fRef>'),
     'o='vGet('o2&fRef>')

    call tstOut t, 'o2&fRef>fRef='vGet('o2&fRef>fRef') ,
        'o='vGet('o2&fRef>fRef')
    call tstOut t, 'o2&fRef>fRef>fSt0='vGet('o2&fRef>fRef>fSt0') ,
        'o='vGet('o2&fRef>fRef>fSt0')
    call tstOut t, 'o2&fRef>fRef>fRe0='vGet('o2&fRef>fRef>fRe0'),
         'o='vGet('o2&fRef>fRef>fRe0')
    call tstOut t, 'o2&fRef>fNest.fSt0='vGet('o2&fRef>fNest.fSt0'),
         'o='vGet('o2&fRef>fNest.fSt0')
    call tstOut t, 'o2&fRef>fNest&fRe0='vGet('o2&fRef>fNest.fRe0'),
         'o='vGet('o1&fNest.fRe0')
    call tstEnd t

    cS = classNew('n? TstEnvVarsS u f fStS v,f fStV s v, f fStR s r',
        ', f fNeS s TstEnvVars0, f = s v')
    oS = oNew(cS)
    call vPut 'oS', oS
    oT = oNew(cS)
    call tst t, "tstEnvVarsS"
    call mAdd t.trans, o0 '<o0>', o1 '<o1>', o2 '<o2>',
        , oS '<oS>', oT '<oT>'
    call mPut oS'.fStS', '<put oS.fStS>'
    call tstOut t, 'oS='vGet('oS') 'oS&fStS='vGet('oS&fStS')
    call mPut oS'.fStV.1', '<put oS.fStV.1>'
    call mPut oS'.fStV.0', 1
    call tstOut t, 'oS&fStV.0='vGet('oS&fStV.0'),
         'oS&fStV.1='vGet('oS&fStV.1')
    call mPut oS'.fStR.1', s2o('<put oS.fStR.1>')
    call mPut oS'.fStR.2', s2o('<put oS.fStR.2>')
    call mPut oS'.fStR.0', 2
    call tstOut t, 'm.oS.fStR.0='mGet(oS'.fStR.0'),
     '.2='mGet(oS'.fStR.2')
    call tstOut t, 'oS&fStR.0='vGet('oS&fStR.0'),
         '.1='vGet('oS&fStR.1') '.2='vGet('oS&fStR.2')
    call mPut oS'.1234', '<put oS.1234>'
    call mPut oS'.0', 9876
    call mPut oS'.fStR.0', 2
    call tstOut t, 'm.oS.0='mGet(oS'.0'),
     '.1234='mGet(oS'.1234')
    call tstOut t, 'oS&0='vGet('oS&0'),
         '.12='vGet('oS&12') '.1234='vGet('oS&1234')
    call tstEnd t
    return
endProcedure tstEnvVars
tstEnvVarsMG: procedure expose m.
parse arg m, g
     call tstOut t, 'm.'m'='m.m 'vGet('g')='vGet(g)
     return

tstvWith: procedure expose m.
/*
$=/tstEW2/
    ### start tst tstEW2 ##############################################
    tstK1             TSTEW1
    tstK1&            !get1 w
    tstK1&f1          get1.f1 v
    tstK1&f2          !get1.f2 w
    tstK1&F3          get1.f3 v
    ttstK1&F3.FEINS   get1.f3.fEins v
    tstK1&F3.FZWEI    !get1.f3.fZwei w
    tstK1&F3.FDREI o  !get1.f3.fDrei w
    tstK1&F3.FDREI    !get1.f3.fDrei w
    tstK1&F3.1        !get1.f3.1 w
    tstK1&F3.2        TSTEW1
    tstK1&F3.2>F1     get1.f1 v
    tstK1&F3.2>F3.2>F2 !get1.f2 w
    *** err: undefined var F1
    F1          M..
    F1          get1.f1 v
    f2          !get1.f2 w
    F3          get1.f3 v
    F3.FEINS    get1.f3.fEins v
    F3.FZWEI    !get1.f3.fZwei w
    F3.FDREI o  !get1.f3.fDrei w
    F3.1        !get1.f3.1 w
    pu1 F1      get1.f1 v
    pu2 F1      get2.f1 v
    po-2 F1     get1.f1 v
    *** err: undefined var F1
    po-1 F1     M..
$/tstEW2/  */
    call pipeIni
    c0 = classNew('n? TstEW0 u f FEINS v,f FZWEI w, f FDREI r,v,s r')
    call classMet c0, 'oFlds' /* new would do it, but we donot use it */
    cl = classNew('n? TstEW u r TstEW0, f F1 v, f F2 r, f F3 TstEW0')
    call classMet cl, 'oFlds' /* new would do it, but we donot use it */
    call oMutate tstEW1, cl
    m.tstEW1    = s2o('get1 w')
    m.tstEW1.f1 = 'get1.f1 v'
    m.tstEW1.f2 = s2o('get1.f2 w')
    m.tstEW1.f3 = 'get1.f3 v'
    m.tstEW1.f3.fEins = 'get1.f3.fEins v'
    m.tstEW1.f3.fZwei = s2o('get1.f3.fZwei w')
    m.tstEW1.f3.fDrei = s2o('get1.f3.fDrei w')
    m.tstEW1.f3.0 = 3
    m.tstEW1.f3.1 = s2o('get1.f3.1 w')
    m.tstEW1.f3.2 = tstEW1
    m.tstEW1.f3.3 = s2o('get1.f3.3 w')
    call oMutate tstEW2, cl
    m.tstEW2    = s2o('get2 w')
    m.tstEW2.f1 = 'get2.f1 v'
    m.tstEW2.f2 = s2o('get2.f2 w')
    call vPut 'tstK1', tstEW1

    call tst t, 'tstEW2'
    call tstOut t, 'tstK1            ' vGet('tstK1')
    call tstOut t, 'tstK1&           ' vGet('tstK1&>')
    call tstOut t, 'tstK1&f1         ' vGet('tstK1&F1')
    call tstOut t, 'tstK1&f2         ' vGet('tstK1&F2')
    call tstOut t, 'tstK1&F3         ' vGet('tstK1&F3')
    call tstOut t, 'ttstK1&F3.FEINS  ' vGet('tstK1&F3.FEINS')
    call tstOut t, 'tstK1&F3.FZWEI   ' vGet('tstK1&F3.FZWEI')
    call tstOut t, 'tstK1&F3.FDREI o ' vGet('tstK1&F3.FDREI')
    call tstOut t, 'tstK1&F3.FDREI   ' vGet('tstK1&F3.FDREI')
    call tstOut t, 'tstK1&F3.1       ' vGet('tstK1&F3.1')
    call tstOut t, 'tstK1&F3.2       ' vGet('tstK1&F3.2')
    call tstOut t, 'tstK1&F3.2>F1    ' vGet('tstK1&F3.2>F1')
    call tstOut t, 'tstK1&F3.2>F3.2>F2' ,
                                vGet('tstK1&F3.2>F3.2>F2')
    call tstOut t, 'F1         ' vGet('F1')
    call vWith '+', tstEW1
    call tstOut t, 'F1         ' vGet('F1')
    call tstOut t, 'f2         ' vGet('F2')
    call tstOut t, 'F3         ' vGet('F3')
    call tstOut t, 'F3.FEINS   ' vGet('F3.FEINS')
    call tstOut t, 'F3.FZWEI   ' vGet('F3.FZWEI')
    call tstOut t, 'F3.FDREI o ' vGet('F3.FDREI')
    call tstOut t, 'F3.1       ' vGet('F3.1')
    call tstOut t, 'pu1 F1     ' vGet('F1')
    call vWith '+', tstEW2
    call tstOut t, 'pu2 F1     ' vGet('F1')
    call vWith '-'
    call tstOut t, 'po-2 F1    ' vGet('F1')

    call vWith '-'
    call tstOut t, 'po-1 F1    ' vGet('F1')
    call tstEnd t
/*
$=/tstEW3/
    ### start tst tstEW3 ##############################################
    .          s c3&F1          = v(c3&f1)
    *** err: null address at &FEINS in c3&F1&FEINS
    *** err: undefined var c3&F1&FEINS
    .          s c3&F1&FEINS    = M..
    *** err: null address at &FEINS in c3&F3&FEINS
    *** err: null address at &FEINS in c3&F3&FEINS
    *** err: undefined var c3&F3&FEINS
    .          s c3&F3&FEINS    = M..
    .          s c3&F3.FEINS    = val(c3&F3.FEINS)
    *** err: undefined var c3&FEINS
    .          s c3&FEINS       = M..
    getO c3&
    aft Put   s c3&>FEINS      = v&&fEins
    Push c3   s F3.FEINS       = val(c3&F3.FEINS)
    aftPut=   s F3.FEINS       = pushPut(F3.FEINS)
    push c4   s F1             = v(c4&f1)
    put f2    s F2             = put(f2)
    put ..    s F3.FEINS       = put(f3.fEins)
    popW c4   s F1             = v(c3&f1)
    *** err: undefined var F1
    popW c3   s F1             = M..
    .          s F222           = f222 pop stop
$/tstEW3/
*/
    call tst t, 'tstEW3'
    c3 = oNew('TstEW')
    call mAdd t.trans, c3 '<c3>'
    m.c3.f1 = 'v(c3&f1)'
    call vPut 'c3', c3
    call tstEnvSG , 'c3&F1'
    call tstEnvSG , 'c3&F1&FEINS'
    call tstEnvSG , 'c3&F3&FEINS'
    call vPut 'c3&F3.FEINS', 'val(c3&F3.FEINS)'
    call tstEnvSG , 'c3&F3.FEINS'
    call tstEnvSG , 'c3&FEINS'
    call tstOut t,  'getO c3&', vGet('c3&')
    call vPut 'c3&>', oNew('TstEW0')
    call vPut 'c3&>FEINS', 'v&&fEins'
    call tstEnvSG 'aft Put', 'c3&>FEINS'
    call vWith '+', c3
    call tstEnvSG 'Push c3', 'F3.FEINS'
    call vPut 'F3.FEINS', 'pushPut(F3.FEINS)'
    call tstEnvSG 'aftPut=', 'F3.FEINS'

    c4 = oNew('TstEW')
    call mAdd t.trans, c4 '<c4>'
    m.c4.f1 = 'v(c4&f1)'
    call vPut f222, 'f222 no stop'
    call vWith '+',  c4
    call tstEnvSG 'push c4', f1
    call vPut f2, 'put(f2)'
    call tstEnvSG 'put f2', f2
    call vPut f222, 'f222 stopped', 1
    call vPut 'F3.FEINS', 'put(f3.fEins)'
    call tstEnvSG 'put .. ', 'F3.FEINS'
    call vWith '-'
    call tstEnvSG 'popW c4', f1
    call vWith '-'
    call vPut f222, 'f222 pop stop'
    call tstEnvSG 'popW c3', f1
    call tstEnvSG          , f222
    call tstEnd t
    return
endProcedure tstvWith

tstEnvSG: procedure expose m. t
parse arg txt, nm
    call tstOut t, left(txt,10)'s' left(nm, 15)'=' vGet(nm)
    return

tstPipeLazy: procedure expose m.
    call pipeIni
/*
$=/tstPipeLazy/
    ### start tst tstPipeLazy #########################################
    a1 vor pipeBegin loop lazy 0 writeNow *** <class TstPipeLazyBuf>
    bufOpen <
    bufClose
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    a5 vor 2 writeNow in inIx 4
    a2 vor writeNow jBuf
    jBuf line 1
    jBuf line 2
    a3 vor writeNow in inIx 1
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    a4 vor barLast inIx 4
    a6 vor barEnd inIx 4
    a7 nach barEnd lazy 0 writeNow ***
    b1 vor barBegin lazy 0 writeNow *** <class TstPipeLazyRdr>
    RdrOpen <
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    rdrClose
    b4 vor writeNow
    b2 vor writeNow rdr inIx 2
    jRead lazyRdr
    tst in line 3 drei .schluss..
    jRead lazyRdr
    b3 vor barLast inIx 4
    b5 vor barEnd inIx 4
    b6 nach barEnd lazy 0 writeNow ***
    a1 vor pipeBegin loop lazy 1 writeAll *** +
        .<class TstPipeLazyBuf>
    a5 vor 2 writeAll in inIx 0
    a2 vor writeAll jBuf
    bufOpen <
    jBuf line 1
    jBuf line 2
    bufClose
    a3 vor writeAll in inIx 0
    #jIn 1# tst in line 1 eins ,
    tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    #jIn eof 4#
    a4 vor barLast inIx 0
    a6 vor barEnd inIx 4
    a7 nach barEnd lazy 1 writeAll ***
    b1 vor barBegin lazy 1 writeAll *** <class TstPipeLazyRdr>
    b4 vor writeAll
    b2 vor writeAll rdr inIx 1
    RdrOpen <
    jRead lazyRdr
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    jRead lazyRdr
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    jRead lazyRdr
    #jIn eof 4#
    rdrClose
    b3 vor barLast inIx 1
    b5 vor barEnd inIx 4
    b6 nach barEnd lazy 1 writeAll ***
$/tstPipeLazy/ */
    call tst t, "tstPipeLazy"
    do lz=0 to 1
        if lz then
            w = 'writeAll'
        else
            w = 'writeNow'
        m.t.inIx = 1-lz
        ty = class4Name('TstPipeLazyBuf', '')
        if ty == '' then do
            ty = classNew('n TstPipeLazyBuf u JRWDeleg', 'm',
            , 'jOpen call tstOut "T", "bufOpen" opt;',
              'call jOpen m.m.deleg, opt',
            , 'jClose call tstOut "T", "bufClose";',
              'call jClose m.m.deleg')
            end
        if \ lz then
             call mAdd t'.TRANS', ty '<class TstPipeLazyBuf>'
        call out 'a1 vor pipeBegin loop lazy' lz w '***' ty
        call pipe '+N'
        call out 'a2 vor' w 'jBuf'
        b = oNew('TstPipeLazyBuf', jBuf('jBuf line 1','jBuf line 2'))
        interpret 'call pipe'w 'b'
        call out 'a3 vor' w 'in inIx' m.t.inIx
        interpret 'call pipe'w
        call out 'a4 vor barLast inIx' m.t.inIx
        call pipe 'P|'
        call out 'a5 vor 2' w 'in inIx' m.t.inIx
        interpret 'call pipe'w
        call out 'a6 vor barEnd inIx' m.t.inIx
        call pipe '-'
        call out 'a7 nach barEnd lazy' lz w '***'

        ty = class4Name('TstPipeLazyRdr', '')
        if ty == '' then
            ty = classNew('n TstPipeLazyRdr u JRW', 'm',
            , 'jOpen call tstOut "T", "RdrOpen" opt',
            , 'jRead call out "jRead lazyRdr";' ,
                  'mr = m.m.rdr; if \ jRead(mr) then return 0;',
                          "m.m = m.mr; return 1",
            , 'jClose call tstOut "T",  "rdrClose";')
        if \ lz then
             call mAdd t'.TRANS', ty '<class TstPipeLazyRdr>'

        r = oNew('TstPipeLazyRdr')
            m.r.rdr = m.j.in
        if lz then
            call mAdd t'.TRANS', r '<lazyRdr>'
     m.t.inIx = 2-lz
     call out 'b1 vor barBegin lazy' lz w '***' ty
     call pipe '+N'
     call out 'b2 vor' w 'rdr inIx' m.t.inIx
     interpret 'call pipe'w 'r'
     call out 'b3 vor barLast inIx' m.t.inIx
     call pipe 'P|'
        call out 'b4 vor' w
        interpret 'call pipe'w
        call out 'b5 vor barEnd inIx' m.t.inIx
        call pipe '-'
     call out 'b6 nach barEnd lazy' lz w '***'
        end
    call tstEnd t
    return
endProcedure tstPipeLazy

tstEnvClass: procedure expose m.
    call pipeIni
/*
$=/tstEnvClass/
    ### start tst tstEnvClass #########################################
    a0 vor pipeBegin loop lazy 0 writeNow *** TY
    #jIn 2# tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    #jIn eof 4#
    a5 vor writeNow
    a1 vor jBuf()
    a2 vor writeNow b
    tstR: @<o20 of TstEnvClass10> isA :TstEnvClass10
    tstR:  .f11 = .
    tstR:  .F12 = value F12 of o1 <o20 of TstEnvClass10>
    tstR:  .f13 = .
    WriteO o2
    tstR: @<oc0 of TstEnvClass20> isA :TstEnvClass20 = valueO2Lazy0
    tstR:  .f24 = .
    tstR:  .F25 = value F25 of o20 <o20 of TstEnvClass20>
    a3 vor writeNow
    tst in line 2 zwei ;   .
    tst in line 3 drei .schluss..
    a4 vor barLast inIx 4
    a6 vor barEnd
    a7 nach barEnd lazy 0 writeNow ***
    a0 vor pipeBegin loop lazy 1 writeAll *** TY
    a5 vor writeAll
    a1 vor jBuf()
    a2 vor writeAll b
    tstR: @<o21 of TstEnvClass10> isA :TstEnvClass10
    tstR:  .f11 = .
    tstR:  .F12 = value F12 of o1 <o21 of TstEnvClass10>
    tstR:  .f13 = .
    WriteO o2
    tstR: @<oc1 of TstEnvClass20> isA :TstEnvClass20 = valueO2Lazy1
    tstR:  .f24 = .
    tstR:  .F25 = value F25 of o21 <o21 of TstEnvClass20>
    a3 vor writeAll
    #jIn 1# tst in line 1 eins ,
    tst in line 1 eins ,
    #jIn 2# tst in line 2 zwei ;   .
    tst in line 2 zwei ;   .
    #jIn 3# tst in line 3 drei .schluss..
    tst in line 3 drei .schluss..
    #jIn eof 4#
    a4 vor barLast inIx 0
    a6 vor barEnd
    a7 nach barEnd lazy 1 writeAll ***
$/tstEnvClass/ */

    call tst t, "tstEnvClass"
    t10 = classNew('n? TstEnvClass10 u f f11 v, f F12 v, f f13 v')
    t20 = classNew('n? TstEnvClass20 u v, f f24 v, f F25 v')
    do lz=0 to 1
        if lz then
            w = 'writeAll'
        else
            w = 'writeNow'
        m.t.inIx = 1-lz
        call out 'a0 vor pipeBegin loop lazy' lz w '***' ty
        call pipe '+N'
        call out 'a1 vor jBuf()'
        b = jOpen(jBuf(), m.j.cWri)
        o1 = oNew('TstEnvClass10')
        m.o1.F12 = 'value F12 of o1' o1
        call mAdd t'.TRANS', o1 '<o2'lz 'of TstEnvClass10>'
        call jWrite b, o1
        call jWrite b, 'WriteO o2'
        o2 = oNew('TstEnvClass20')
        m.o2 = 'valueO2Lazy'lz
        m.o2.F25 = 'value F25 of o2'lz o2
        oc = oCopy(oCopy(o2))
        call mAdd t'.TRANS', o2 '<o2'lz 'of TstEnvClass20>'
        call mAdd t'.TRANS', oc '<oc'lz 'of TstEnvClass20>'
        call jWrite b, oc
        call out 'a2 vor' w 'b'
        interpret 'call pipe'w jClose(b)
        call out 'a3 vor' w
        interpret 'call pipe'w
        call out 'a4 vor barLast inIx' m.t.inIx
        call pipe 'P|'
        call out 'a5 vor' w
        interpret 'call pipe'w
        call out 'a6 vor barEnd'
        call pipe '-'
        call out 'a7 nach barEnd lazy' lz w '***'
        end
    call tstEnd t
    m.t.trans.0 = 0
    return
endProcedure tstEnvClass

tstDsnEx: procedure expose m.
/*
$=/tstDsnEx/
    ### start tst tstDsnEx ############################################
    dsnExists(A540769.WK.rexx) 1
    dsnExists(rzZ/A540769.WK.rexx) 1
    dsnExists(A540769.WK.wk.rexxYY) 0
    dsnExists(rzZ/A540769.WK.wk.rexxYY) 0
    dsnExists(A540769.WK.rexx(wsh)) 1
    dsnExists(rzZ/A540769.WK.rexx(wsh)) 1
    dsnExists(A540769.WK.rexx(nonono)) 0
    dsnExists(rzZ/A540769.WK.rexx(nonono)) 0
    dsnExists(A540769.WK.rxxYY(nonon)) 0
    dsnExists(rzZ/A540769.WK.rxxYY(nonon)) 0
    *** err: error in csm mbrList ?ZZ/A540769.WK.RXXYY(NONON) .
    .    e 1: CSMSI77E INVALID SYSTEM NAME (MUST BE * OR A VALID NAME) +
    (COL:8)
    .    e 2: CSMSI77E?????SYSTEM=?ZZ                                  +
    .                                                                  +
    .                                                           ???????+
    ??????????
    dsnExists(qzZ/A540769.WK.rxxYY(nonon)) 0
$/tstDsnEx/
*/
    call tst t, 'tstDsnEx'
    lst = 'rexx wk.rexxYY rexx(wsh) rexx(nonono) rxxYY(nonon)'

    do lx =1 to words(lst)
         d1 = 'A540769.WK.'word(lst,lx)
         call tstOut t, 'dsnExists('d1')' dsnExists(d1)
         call tstOut t, 'dsnExists(rzZ/'d1')' dsnExists('RZZ/'d1)
         end
    call mAdd t'.TRANS', '00'x '?', '0A'x '?'
    call tstOut t, 'dsnExists(qzZ/'d1')' dsnExists('?ZZ/'d1)
    call tstEnd t
    return
endProceudre tstDsnEx

tstFile: procedure expose m.
    call catIni
/*
$=/tstFile/
    ### start tst tstFile #############################################
    write read 0 last 10 vor anfang
    write read 1 last 80  links1 1   und rechts |  ..
    write read 2 last 80 liinks2 2   und rechts |  ..
    write read 5 last 80 links5 5 rechts5
    write read 99 last 80 links99 99 rechts
    write read 100 last 80 links100 100 rechts
    write read 101 last 80 links101 101 rechts
    write read 999 last 80 links999 999 rechts
    write read 1000 last 80 links1000 1000 rechts
    write read 1001 last 80 links1001 1001 rechts
    write read 2109 last 80 links2109 2109 rechts
    out > eins 1                                                       +
    .             .
    out > eins 2 schluss.                                              +
    .             .
    buf eins
    buf zwei
    buf drei
    out > zwei mit einer einzigen Zeile                                +
    .             .
    . links1 1   und rechts |  .                                       +
    .              .
$/tstFile/ */
    call tst t, "tstFile"
    pds = tstFilename('lib', 'r')
    call tstFileWr pds, 0, ' links0', '  und rechts |  .  '
    call tstFileWr pds, 1, ' links1', '  und rechts |  .  '
    call tstFileWr pds, 2, 'liinks2', '  und rechts |  .  '
    call tstFileWr pds, 5, 'links5', 'rechts5'
    call tstFileWr pds, 99, 'links99', 'rechts'
    call tstFileWr pds, 100, 'links100', 'rechts'
    call tstFileWr pds, 101, 'links101', 'rechts'
    call tstFileWr pds, 999, 'links999', 'rechts'
    call tstFileWr pds, 1000, 'links1000', 'rechts'
    call tstFileWr pds, 1001, 'links1001', 'rechts'
    call tstFileWr pds, 2109, 'links2109', 'rechts'
    pd2 = tstFilename('li2', 'r')
    call pipeIni
    call pipe '+F', s2o(tstPdsMbr(pd2, 'eins'))
    call out tstFB('out > eins 1') /* simulate fixBlock on linux */
    call out tstFB('out > eins 2 schluss.')
    call pipe '-'
    call pipe '+F', s2o(tstPdsMbr(pd2, 'zwei'))
    call out tstFB('out > zwei mit einer einzigen Zeile')
    call pipe '-'
    b = jBuf("buf eins", "buf zwei", "buf drei")
    call pipe '+ffffff', , s2o(tstPdsMbr(pd2, 'eins')), b,
                    ,jBuf(),
                    ,s2o(tstPdsMbr(pd2, 'zwei')),
                    ,s2o(tstPdsMbr(pds, 'wr0')),
                    ,s2o(tstPdsMbr(pds, 'wr1'))
    call pipeWriteNow
    call pipe '-'
    call tstEnd t
    return
endProcedure tstFile

/*--- simulate fixBlock 80 on linux ---------------------------------*/
tstFB: procedure expose m.
parse arg line, recL
    if m.err.os \== 'LINUX' then
        return line
    else if recL == '' then
        return left(line, 80)
    else
        return left(line, recL)
endProcedure tstFB

tstPdsMbr: procedure expose m.
parse arg pds, mbr
    if m.err.os = 'TSO' then
        return pds'('mbr') ::F'
    if m.err.os = 'LINUX' then
        return pds'.'mbr
    call err 'tstPdsMbr implement os' m.err.os
endProcedure tstPdsMbr

tstFileWR: procedure expose m.
parse arg dsn, num, le, ri
    io = file(tstPdsMbr(dsn, 'wr'num))
    call jOpen io, m.j.cWri
    do x = 1 to num /* simulate fixBlock 80 on LINUX*/
        call jWrite io, tstFB(le x ri)
        end
    call jClose io
    if num > 100 then
        call jReset io, tstPdsMbr(dsn, 'wr'num)

    call jOpen io, m.j.cRead
    m.io = 'vor anfang'
    do x = 1 to num
        if \ jRead(io) then
            call err x 'not jRead'
        else if m.io <> le x ri then
            call err x 'read mismatch' m.io
        end
    if jRead(io) then
        call err x 'jRead but should be eof 1'
    if jRead(io) then
        call err x'+1 jjRead but should be eof 2'
    call jClose io
    call tstOut t, 'write read' num 'last' length(m.io) strip(m.io,'t')
    return
endProcedure tstFileWr

tstFileList: procedure expose m.
    call catIni
/*
$=/tstFileList/
    ### start tst tstFileList #########################################
    empty dir
    filled dir
    <<pref 2 List>>eins
    <<pref 2 List>>zwei
    <<pref 2 List>>drei
    <<pref 2 List>>vier
    filled dir recursive
    <<pref 2 List>>eins
    <<pref 2 List>>zwei
    <<pref 2 List>>drei
    <<pref 2 List>>vier
    <<pref 1 vier>>eins
    <<pref 1 vier>>zwei
    <<pref 1 vier>>drei
$/tstFileList/ */
/*
$=/tstFileListTSO/
    ### start tst tstFileListTSO ######################################
    empty dir
    filled dir
    <<pref 2 LIST>>DREI
    <<pref 2 LIST>>EINS
    <<pref 2 LIST>>ZWEI
    filled dir recursive
    <<pref 2 LIST>>DREI
    <<pref 2 LIST>>EINS
    <<pref 1 VIER>>DREI
    <<pref 1 VIER>>EINS
    <<pref 1 VIER>>ZWEI
    <<pref 2 LIST>>ZWEI
$/tstFileListTSO/ */
    if m.err.os = 'TSO' then
        call tst t, "tstFileListTSO"
    else
        call tst t, "tstFileList"
    fi = file(tstFileName('FileList', 'r'))
    call fileMkDir fi
    fl = fileList(fi)
    call tstOut t, 'empty dir'
    call jWriteNow t, fl
    call tstFileListMake t, fi, 2
    call tstOut t, 'filled dir'
    call jWriteNow t, fl
    call tstOut t, 'filled dir recursive'
    call jWriteNow t, fileList(fi, 'r')
    call tstEnd t
    return
endProcedure tstFileList

tstFileListMake: procedure expose m.
parse arg t, fi, lev
    if \ fileIsDir(fi) then
        call fileMkDir fi
    call mAdd t'.TRANS', filePath(fi) || m.file.sep ,
                '<<pref' lev right(filePath(fi),4)'>>'
    call jCat fileChild(fi, 'eins','::F'),jBuf('zeile eins','eins' lev)
    call jCat fileChild(fi, 'zwei','::F'),jBuf('zeile zwei','zwei' lev)
    call jCat fileChild(fi, 'drei','::F'),jBuf('zeile drei','drei' lev)
    if lev > 1 then
        call tstFileListMake t, fileChild(fi, 'vier'), lev-1
    return
endProcedure tstFileListMake

tstMail: procedure expose m.
do i=1 to 2
    call mailHead xy, 'mail from walter''s rexx' time() i, A540769
    call mailText xy, 'und hier kommt der text' ,
                , 'und zeile zwei timestamp' i':' date('s') time() ,
                , left('und eine lange Zeile 159', 156, '+')159 ,
                , left('und eine lange Zeile 160', 157, '+')160 ,
                , left('und eine lange Zeile 161', 158, '+')161 ,
                , '<ol><li>'left('und eine lange', 200, '+')203 '</li>',
                , '<li bgcolor=yellow>und kurz</li></ol>' ,
                , '<h1>und Schluss mit html</h1>'
    call mailSend xy
    call sleep 3
    end
    return
endprocedure tstMail

tstF: procedure expose m.
/*
$=/tstF/
    ### start tst tstF ################################################
    f(1 23%c345%c67%%8, eins,  zwei ) =1 23eins345 zwei 67%8;
    f(1\S23%c345%S67%%8, eins,  zwei ) =1\S23eins345zwei67%8;
    f(1 23%C345%C67%%8, eins,  zwei ) =1 23eins345 zwei 67%8;
    f(1 23%c345%S67%%8, eins,  zwei ) =1 23eins345zwei67%8;
    f(1%S2%c3@2%S4@%c5, eins,  zwei ) =1eins2 zwei 3zwei4 zwei 5;
    f(1%-2C2%3C3@2%3.2C4, eins,  zwei ) =1ei2ei 3zwe4;
    f(1@F1%c2@f2%c3@F3%c4, eins,  zwei ) =1fEins2fZwei3fDrei4;
    tstF2 _ %-9C @%5I @%8I @%+8I @%-8I -----
    _ 0             0        0       +0 0       .
    _ -1.2         -1       -1       -1 -1      .
    _ 2.34          2        2       +2 2       .
    _ -34.8765    -35      -35      -35 -35     .
    _ 567.91234   568      568     +568 568     .
    _ -8901     -8901    -8901    -8901 -8901   .
    _ 23456     23456    23456   +23456 23456   .
    _ -789012   *****  -789012  -789012 -789012 .
    _ 34e6      ***** 34000000 ******** 34000000
    _ -56e7     ***** ******** ******** ********
    _ 89e8      ***** ******** ******** ********
    _ txtli     txtli    txtli    txtli txtli   .
    _ undEinLan undEi undEinLa undEinLa undEinLa
    tstF2 _ %-9C @%5.2I @%12.2I @%+12.2I @%-12.2I -----
    _ 0          0.00         0.00        +0.00 0.00        .
    _ -1.2      -1.20        -1.20        -1.20 -1.20       .
    _ 2.34       2.34         2.34        +2.34 2.34        .
    _ -34.8765  *****       -34.88       -34.88 -34.88      .
    _ 567.91234 *****       567.91      +567.91 567.91      .
    _ -8901     *****     -8901.00     -8901.00 -8901.00    .
    _ 23456     *****     23456.00    +23456.00 23456.00    .
    _ -789012   *****   -789012.00   -789012.00 -789012.00  .
    _ 34e6      *****  34000000.00 +34000000.00 34000000.00 .
    _ -56e7     ***** ************ ************ ************
    _ 89e8      ***** ************ ************ ************
    _ txtli     txtli        txtli        txtli txtli       .
    _ undEinLan undEi undEinLanger undEinLanger undEinLanger
    tstF2 _ %-9C @%7e @% 8E @% 9.3e @% 11.4E -----
    _ 0         0.00e00  0.00E00  0.000e00  0.0000E000
    _ -1.2      -1.2e00 -1.20E00 -1.200e00 -1.2000E000
    _ 2.34      2.34e00  2.34E00  2.340e00  2.3400E000
    _ -34.8765  -3.5e01 -3.49E01 -3.488e01 -3.4877E001
    _ 567.91234 5.68e02  5.68E02  5.679e02  5.6791E002
    _ -8901     -8.9e03 -8.90E03 -8.901e03 -8.9010E003
    _ 23456     2.35e04  2.35E04  2.346e04  2.3456E004
    _ -789012   -7.9e05 -7.89E05 -7.890e05 -7.8901E005
    _ 34e6      3.40e07  3.40E07  3.400e07  3.4000E007
    _ -56e7     -5.6e08 -5.60E08 -5.600e08 -5.6000E008
    _ 89e8      8.90e09  8.90E09  8.900e09  8.9000E009
    _ txtli       txtli    txtli     txtli       txtli.
    _ undEinLan undEinL undEinLa undEinLan undEinLange
    _ 8.76e-07  8.76e-7  8.76E-7  8.760e-7  8.7600E-07
    _ 5.43e-11  5.4e-11  5.4E-11  5.43e-11  5.4300E-11
    _ -8.76e-07 -8.8e-7 -8.76E-7 -8.760e-7 -8.7600E-07
    _ -5.43e-11 -5e-011 -5.4E-11 -5.43e-11 -5.4300E-11
    tstF2 _ %-9C @%kt @%kd @%kb -----
    _ 0          0s00    0     0 .
    _ -1.2      -1s20   -1    -1 .
    _ 2.34       2s34 2340m    2 .
    _ -34.8765  -0m35  -35   -35 .
    _ 567.91234  9m28  568   568 .
    _ -8901     -2h28   -9k   -9k
    _ 23456      6h31   23k   23k
    _ -789012   -9d03 -789k -771k
    _ 34e6       394d   34M   32M
    _ -56e7     -++++ -560M -534M
    _ 89e8      +++++ 8900M 8488M
    _ txtli     txtli txtli txtli
    _ undEinLan Text? Text? Text?
    _ 8.76e-07   0s00  876n    0 .
    _ 5.43e-11   0s00   54p    0 .
    _ -8.76e-07 -0s00 -876n   -0 .
    _ -5.43e-11 -0s00  -54p   -0 .
$/tstF/ */
    call tst t, 'tstF'
    call tstF1 '1 23%c345%c67%%8'
    call tstF1 '1\S23%c345%S67%%8'
    call tstF1 '1 23%C345%C67%%8'
    call tstF1 '1 23%c345%S67%%8'
    call tstF1 '1%S2%c3@2%S4@%c5'
    call tstF1 '1%-2C2%3C3@2%3.2C4'
    call tstF1 '1@F1%c2@f2%c3@F3%c4'
    nums = '0 -1.2 2.34 -34.8765 567.91234 -8901 23456' ,
                '-789012 34e6 -56e7 89e8 txtli undEinLangerText?'
    call tstF2 '_ %-9C @%5I @%8I @%+8I @%-8I', nums
    call tstF2 '_ %-9C @%5.2I @%12.2I @%+12.2I @%-12.2I', nums
    num2 = ' 8.76e-07  5.43e-11 -8.76e-07  -5.43e-11'
    call tstF2 '_ %-9C @%7e @% 8E @% 9.3e @% 11.4E', nums num2
    call tstF2 '_ %-9C @%kt @%kd @%kb', nums num2
    call tstEnd t
    return
endProcedure tstF

tstF1: procedure expose m.
parse arg fmt
    e='eins'
    z=' zwei '
    f2 = 'f2'
    m.e.f1 = 'fEins'
    m.e.f2 = 'fZwei'
    m.e.f3 = 'fDrei'
    call tstOut t, "f("fmt"," e"," z") ="f(fmt, e, z)";"
    return
endProcedure tstF1

tstF2: procedure expose m.
parse arg fmt, vals
    call tstOut t, 'tstF2' fmt '-----'
    do vx=1 to words(vals)
        call tstOut t, f(fmt, word(vals, vx))
        end
    return
endProcedure tstF2

tstFWords: procedure expose m.
/*
$=/tstFWords/
    ### start tst tstFWords ###########################################
    ??empty??  .
    1space     .
    , %#e--    --
    %#a%9c     .
    *%#a%-7c   .
    ??empty??  eins
    1space     eins
    , %#e--    eins
    %#a%9c          eins
    *%#a%-7c   eins   .
    ??empty??  einszwei
    1space     eins zwei
    , %#e--    eins, zwei
    %#a%9c          eins     zwei
    *%#a%-7c   eins   *zwei   .
    ??empty??  einszweidrei
    1space     eins zwei drei
    , %#e--    eins, zwei, drei
    %#a%9c          eins     zwei     drei
    *%#a%-7c   eins   *zwei   *drei   .
$/tstFWords/
*/
    ws = '  eins zwei   drei '
    call tst t, 'tstFWords'
    do l=0 to 3
      call tstOut t, '??empty?? ' fWords(            ,subword(ws,1,l))
      call tstOut t, '1space    ' fWords(' '         ,subword(ws,1,l))
      call tstOut t, ', %#e--   ' fWords(', %#e--'   ,subword(ws,1,l))
      call tstOut t, '%#a%9c    ' fWords('%#a%9c'    ,subword(ws,1,l))
      call tstOut t, '*%#a%-7c  ' fWords('*%#a%-7c'  ,subword(ws,1,l))
      end
    call tstEnd t
    return
endProcedure tstFWords
tstFe: procedure expose m.
/*
$=/tstFe/
  ### start tst tstFe ###############################################
  .                   1 < 1.00e00> <1.00e00>
  .                   0 < 0.00e00> <0.00e00>
  .                -2.1 <-2.10e00> <-2.1e00>
  .                  .3 < 3.00e-1> <3.00e-1>
  .             -.45678 <-4.57e-1> <-4.6e-1>
  .                 901 < 9.01e02> <9.01e02>
  .               -2345 <-2.35e03> <-2.3e03>
  .              678e90 < 6.78e92> <6.78e92>
  .              123e-4 < 1.23e-2> <1.23e-2>
  .             567e-89 < 5.7e-87> <5.7e-87>
  .              12e456 < 1.2e457> <1.2e457>
  .             78e-901 < 8e-0900> <8e-0900>
  .           2345e5789 < 2e05792> <2e05792>
  .           123e-4567 < 1e-4565> <1e-4565>
  .          8901e23456 < 9e23459> <9e23459>
  .          -123e-4567 <-1e-4565> <-0e-999>
  .          567e890123 <********> <*******>
  .       45678e-901234 < 0e-9999> <0e-9999>
  .                kurz <    kurz> <kurz   >
  .       undLangerText <undLange> <undLang>
$/tstFe/
*/
    call tst t, 'tstFe'
    vAll = '1 0 -2.1 .3 -.45678 901 -2345 678e90 123e-4' ,
            '567e-89 12e456 78e-901 2345e5789  123e-4567 8901e23456' ,
            '-123e-4567 567e890123 45678e-901234' ,
            'kurz undLangerText'
    do vx=1 to words(vAll)
        v = word(vAll, vx)
        call tstOut t, right(v, 20)  '<'fe(v, 8, 2, 'e', ' ')'>' ,
                                     '<'fe(v, 7, 1, 'e', '-')'>'
        end
    call tstEnd t
    return
endProcedure

tstFTst: procedure expose m.
/*
$=/tstFTstS/
    ### start tst tstFTstS ############################################
    1956-01-29-23.34.56.987654     SS => 1956-01-29-23.34.56.987654|
    1956-01-29-23.34.56.987654     Ss => 1956-01-29-23.34.56|
    1956-01-29-23.34.56.987654     S  => 1956-01-29-23.34.56|
    1956-01-29-23.34.56.987654     SD => 19560129|
    1956-01-29-23.34.56.987654     Sd => 560129|
    1956-01-29-23.34.56.987654     SE => 29.01.1956|
    1956-01-29-23.34.56.987654     Se => 29.01.56|
    1956-01-29-23.34.56.987654     St => 23.34.56|
    1956-01-29-23.34.56.987654     ST => 23:34:56.987654|
    1956-01-29-23.34.56.987654     SY => GB29|
    1956-01-29-23.34.56.987654     SM => B2923345|
    1956-01-29-23.34.56.987654     SH => C33456|
    1956-01-29-23.34.56.987654     Sj => 56029|
    1956-01-29-23.34.56.987654     SJ => 714076|
$/tstFTstS/
$=/tstFTsts/
    ### start tst tstFTsts ############################################
    2014-12-23-16.57.38            sS => 2014-12-23-16.57.38.000000|
    2014-12-23-16.57.38            ss => 2014-12-23-16.57.38|
    2014-12-23-16.57.38            s  => 2014-12-23-16.57.38|
    2014-12-23-16.57.38            sD => 20141223|
    2014-12-23-16.57.38            sd => 141223|
    2014-12-23-16.57.38            sE => 23.12.2014|
    2014-12-23-16.57.38            se => 23.12.14|
    2014-12-23-16.57.38            st => 16.57.38|
    2014-12-23-16.57.38            sT => 16:57:38.000000|
    2014-12-23-16.57.38            sY => EM23|
    2014-12-23-16.57.38            sM => M2316573|
    2014-12-23-16.57.38            sH => B65738|
    2014-12-23-16.57.38            sj => 14357|
    2014-12-23-16.57.38            sJ => 735589|
    2014-12-23-16.57.38            su +> E1J8X3NE|
    2014-12-23-16.57.38            sL +> 00CE3F3AFA6570000000|
$/tstFTsts/
$=/tstFTstD/
    ### start tst tstFTstD ############################################
    23450618                       DS => 2345-06-18-00.00.00.000000|
    23450618                       Ds => 2345-06-18-00.00.00|
    23450618                       D  => 2345-06-18-00.00.00|
    23450618                       DD => 23450618|
    23450618                       Dd => 450618|
    23450618                       DE => 18.06.2345|
    23450618                       De => 18.06.45|
    23450618                       Dt => 00.00.00|
    23450618                       DT => 00:00:00.000000|
    23450618                       DY => PG18|
    23450618                       DM => G1800000|
    23450618                       DH => A00000|
    23450618                       Dj => 45169|
    23450618                       DJ => 856296|
$/tstFTstD/
$=/tstFTstd/
    ### start tst tstFTstd ############################################
    120724                         dS => 2012-07-24-00.00.00.000000|
    120724                         ds => 2012-07-24-00.00.00|
    120724                         d  => 2012-07-24-00.00.00|
    120724                         dD => 20120724|
    120724                         dd => 120724|
    120724                         dE => 24.07.2012|
    120724                         de => 24.07.12|
    120724                         dt => 00.00.00|
    120724                         dT => 00:00:00.000000|
    120724                         dY => CH24|
    120724                         dM => H2400000|
    120724                         dH => A00000|
    120724                         dj => 12206|
    120724                         dJ => 734707|
$/tstFTstd/
$=/tstFTstE/
    ### start tst tstFTstE ############################################
    09.12.1345                     ES => 1345-12-09-00.00.00.000000|
    09.12.1345                     Es => 1345-12-09-00.00.00|
    09.12.1345                     E  => 1345-12-09-00.00.00|
    09.12.1345                     ED => 13451209|
    09.12.1345                     Ed => 451209|
    09.12.1345                     EE => 09.12.1345|
    09.12.1345                     Ee => 09.12.45|
    09.12.1345                     Et => 00.00.00|
    09.12.1345                     ET => 00:00:00.000000|
    09.12.1345                     EY => PM09|
    09.12.1345                     EM => M0900000|
    09.12.1345                     EH => A00000|
    09.12.1345                     Ej => 45343|
    09.12.1345                     EJ => 491228|
$/tstFTstE/
$=/tstFTste/
    ### start tst tstFTste ############################################
    31.05.2467                     eS => 2024-05-31-00.00.00.000000|
    31.05.2467                     es => 2024-05-31-00.00.00|
    31.05.2467                     e  => 2024-05-31-00.00.00|
    31.05.2467                     eD => 20240531|
    31.05.2467                     ed => 240531|
    31.05.2467                     eE => 31.05.2024|
    31.05.2467                     ee => 31.05.2467|
    31.05.2467                     et => 00.00.00|
    31.05.2467                     eT => 00:00:00.000000|
    31.05.2467                     eY => OF31|
    31.05.2467                     eM => F3100000|
    31.05.2467                     eH => A00000|
    31.05.2467                     ej => 24152|
    31.05.2467                     eJ => 739036|
$/tstFTste/
$=/tstFTstt/
### start tst tstFTstt ############################################
    12.34.56                       tS => 0001-01-01-12.34.56.000000|
    12.34.56                       ts => 0001-01-01-12.34.56|
    12.34.56                       t  => 0001-01-01-12.34.56|
    12.34.56                       tD => 00010101|
    12.34.56                       td => 010101|
    12.34.56                       tE => 01.01.0001|
    12.34.56                       te => 01.01.01|
    12.34.56                       tt => 12.34.56|
    12.34.56                       tT => 12:34:56.000000|
    12.34.56                       tY => ??01|
    12.34.56                       tM => ?0112345|
    12.34.56                       tH => B23456|
    12.34.56                       tj => 01001|
    12.34.56                       tJ => 0|
$/tstFTstt/
$=/tstFTstT/
    ### start tst tstFTstT ############################################
    23.45.06.784019                TS => 0001-01-01-23.45.06.784019|
    23.45.06.784019                Ts => 0001-01-01-23.45.06|
    23.45.06.784019                T  => 0001-01-01-23.45.06|
    23.45.06.784019                TD => 00010101|
    23.45.06.784019                Td => 010101|
    23.45.06.784019                TE => 01.01.0001|
    23.45.06.784019                Te => 01.01.01|
    23.45.06.784019                Tt => 23.45.06|
    23.45.06.784019                TT => 23.45.06.784019|
    23.45.06.784019                TY => ??01|
    23.45.06.784019                TM => ?0123450|
    23.45.06.784019                TH => C34506|
    23.45.06.784019                Tj => 01001|
    23.45.06.784019                TJ => 0|
$/tstFTstT/
$=/tstFTstY/
    ### start tst tstFTstY ############################################
    FE25                           YS => 2015-04-25-00.00.00.000000|
    FE25                           Ys => 2015-04-25-00.00.00|
    FE25                           Y  => 2015-04-25-00.00.00|
    FE25                           YD => 20150425|
    FE25                           Yd => 150425|
    FE25                           YE => 25.04.2015|
    FE25                           Ye => 25.04.15|
    FE25                           Yt => 00.00.00|
    FE25                           YT => 00:00:00.000000|
    FE25                           YY => FE25|
    FE25                           YM => E2500000|
    FE25                           YH => A00000|
    FE25                           Yj => 15115|
    FE25                           YJ => 735712|
$/tstFTstY/
$=/tstFTstM/
    ### start tst tstFTstM ############################################
    I2317495                       MS => 0001-08-23-17.49.50.000000|
    I2317495                       Ms => 0001-08-23-17.49.50|
    I2317495                       M  => 0001-08-23-17.49.50|
    I2317495                       MD => 00010823|
    I2317495                       Md => 010823|
    I2317495                       ME => 23.08.0001|
    I2317495                       Me => 23.08.01|
    I2317495                       Mt => 17.49.50|
    I2317495                       MT => 17:49:50.000000|
    I2317495                       MY => ?I23|
    I2317495                       MM => I2317495|
    I2317495                       MH => B74950|
    I2317495                       Mj => 01235|
    I2317495                       MJ => 234|
$/tstFTstM/
$=/tstFTstH/
    ### start tst tstFTstH ############################################
    B23456                         HS => 0001-01-01-12.34.56.000000|
    B23456                         Hs => 0001-01-01-12.34.56|
    B23456                         H  => 0001-01-01-12.34.56|
    B23456                         HD => 00010101|
    B23456                         Hd => 010101|
    B23456                         HE => 01.01.0001|
    B23456                         He => 01.01.01|
    B23456                         Ht => 12.34.56|
    B23456                         HT => 12:34:56.000000|
    B23456                         HY => ??01|
    B23456                         HM => ?0112345|
    B23456                         HH => B23456|
    B23456                         Hj => 01001|
    B23456                         HJ => 0|
$/tstFTstH/
$=/tstFTstn/
    ### start tst tstFTstn ############################################
    19560423 17:58:29              nS => 1956-04-23-17.58.29.000000|
    19560423 17:58:29              ns => 1956-04-23-17.58.29|
    19560423 17:58:29              n  => 1956-04-23-17.58.29|
    19560423 17:58:29              nD => 19560423|
    19560423 17:58:29              nd => 560423|
    19560423 17:58:29              nE => 23.04.1956|
    19560423 17:58:29              ne => 23.04.56|
    19560423 17:58:29              nt => 17.58.29|
    19560423 17:58:29              nT => 17:58:29.000000|
    19560423 17:58:29              nY => GE23|
    19560423 17:58:29              nM => E2317582|
    19560423 17:58:29              nH => B75829|
    19560423 17:58:29              nj => 56114|
    19560423 17:58:29              nJ => 714161|
$/tstFTstn/
$=/tstFTstN/
    ### start tst tstFTstN ############################################
    32101230 10:21:32.456789       NS => 3210-12-30-10.21.32.456789|
    32101230 10:21:32.456789       Ns => 3210-12-30-10.21.32|
    32101230 10:21:32.456789       N  => 3210-12-30-10.21.32|
    32101230 10:21:32.456789       ND => 32101230|
    32101230 10:21:32.456789       Nd => 101230|
    32101230 10:21:32.456789       NE => 30.12.3210|
    32101230 10:21:32.456789       Ne => 30.12.10|
    32101230 10:21:32.456789       Nt => 10.21.32|
    32101230 10:21:32.456789       NT => 10:21:32.456789|
    32101230 10:21:32.456789       NY => AM30|
    32101230 10:21:32.456789       NM => M3010213|
    32101230 10:21:32.456789       NH => B02132|
    32101230 10:21:32.456789       Nj => 10364|
    32101230 10:21:32.456789       NJ => 1172426|
$/tstFTstN/
*/
    say "f('%t  ')" f('%t  ')
    call timeIni
    allOut = 'Ss DdEetTYMHjJ'
    allIn  = 'S1956-01-29-23.34.56.987654' ,
             's2014-12-23-16.57.38' ,
             'D23450618' ,
             'd120724'   ,
             'E09.12.1345' ,
             'e31.05.2467' ,
             't12.34.56'  ,
             'T23.45.06.784019' ,
             'YFE25' ,
             'MI2317495' ,
             'HB23456' ,
             'n19560423*17:58:29' ,
             'N32101230*10:21:32.456789'
    do ix=1 to words(allIn)
        parse value word(allIn, ix) with iF 2 iV
        iv = translate(iv, ' ', '*')
        call tst t, "tstFTst"iF
        do ox=1 to length(allOut)
            ft = iF || substr(allOut, ox, 1)
            call tstOut t, left(iV, 30) ft  '=>' f('%t'ft, iV)'|'
            if 0 & iF = 'Y' then
                say '???' ft '>>>' mGet('F_GEN.%t'ft)
            end
        if ix=2 then do
            call tstOut t, left(iV, 30) iF'u'  '+>' f('%t'iF'u', iV)'|'
            call tstOut t, left(iV, 30) iF'L'  '+>' f('%t'iF'L', iV)'|'
            end
        call tstEnd t
        end
    return
endProcedure tstFTst

tstFmt: procedure expose m.
    call pipeIni
/*
$=/tstFmt/
    ### start tst tstFmt ##############################################
    =   a2i b3b   d4                       fl5          ex6
    -11 -11 b3    d4-11+d4++++    -111.1000000 -1.11000e-12
    -1  -10 b     d4-10+d4+++            null1        null3
    -    -9 b3b-9 d4-9+d4+++       -11.0000000 -1.11900e-10
    -8+  -8 b3b-  d4-8+d4++        -18.0000000 -1.18000e010
    -7   -7 b3b   d4-7+d4+          -7.0000000 -1.70000e-07
    -    -6 b3    d4-6+d4           -0.1111160 -6.00000e006
    -5+  -5 b     d4-5+d                 null2        null2
    -4   -4 b3b-4 d4-4+         -11114.0000000 -1.11140e008
    -    -3 b3b-  d4-3              -0.1130000 -1.13000e-04
    -2+  -2 b3b   d4-               -0.1200000 -1.20000e001
    -1   -1 b3    d4                -0.1000000 -1.00000e-02
    0     0 b     d                      null1        null1
    1+    1 b3    d4                 0.1000000  1.00000e-02
    2++   2 b3b   d42                0.1200000  1.20000e001
    3     3 b3b3  d43+               0.1130000  1.13000e-04
    4+    4 b3b4+ d44+d          11114.0000000  1.11140e008
    5++   5 b     d45+d4                 null2        null2
    6     6 b3    d46+d4+            0.1111160  1.11116e005
    7+    7 b3b   d47+d4++           0.1111117  7.00000e-08
    8++   8 b3b8  d48+d4+++          8.0000000  1.80000e009
    9     9 b3b9+ d49+d4++++         0.9000000  1.19000e-08
    10   10 b     d410+d4++++            null1        null3
    11+  11 b3    d411+d4+++++       0.1110000  1.00000e-12
    1    12 b3b   d412+d4++++++  11112.0000000  2.00000e012
    13   13 b3b1  d               1111.3000000  1.13000e-12
    14+  14 b3b14 d4            111111.0000000  1.40000e013
    1    15 b     d41                    null2        null1
    16   16 b3    d416               6.0000000  1.16000e003
    17+  17 b3b   d417+              0.7000000  1.11170e-03
    1    18 b3b1  d418+d            11.0000000  1.11800e003
    19   19 b3b19 d419+d4            0.1190000  9.00000e-05
    20+  20 b     d420+d4+               null1        null2
    2    21 b3    d421+d4++         11.1210000  1.11000e-05
    22   22 b3b   d422+d4+++     11111.2000000  2.00000e007
    23+  23 b3b2  d423+d4++++        0.1111123  1.11230e-09
    c3L      a2i drei  d4                 fl5          ex6
    -11 -1.10E01 b3    d4-11+d   -111.1000000 -1.11000e-12
    -1  -1.00E01 b     d4-10+d          null1        null3
    -   -9.00E00 b3b-9 d4-9+d4    -11.0000000 -1.11900e-10
    -8+ -8.00E00 b3b-  d4-8+d4    -18.0000000 -1.18000e010
    -7  -7.00E00 b3b   d4-7+d4     -7.0000000 -1.70000e-07
    -   -6.00E00 b3    d4-6+d4     -0.1111160 -6.00000e006
    -5+ -5.00E00 b     d4-5+d           null2        null2
    -4  -4.00E00 b3b-4 d4-4+   -11114.0000000 -1.11140e008
    -   -3.00E00 b3b-  d4-3        -0.1130000 -1.13000e-04
    -2+ -2.00E00 b3b   d4-         -0.1200000 -1.20000e001
    -1  -1.00E00 b3    d4          -0.1000000 -1.00000e-02
    0    0.00E00 b     d                null1        null1
    1+   1.00E00 b3    d4           0.1000000  1.00000e-02
    2++  2.00E00 b3b   d42          0.1200000  1.20000e001
    3    3.00E00 b3b3  d43+         0.1130000  1.13000e-04
    4+   4.00E00 b3b4+ d44+d    11114.0000000  1.11140e008
    5++  5.00E00 b     d45+d4           null2        null2
    6    6.00E00 b3    d46+d4+      0.1111160  1.11116e005
    7+   7.00E00 b3b   d47+d4+      0.1111117  7.00000e-08
    8++  8.00E00 b3b8  d48+d4+      8.0000000  1.80000e009
    9    9.00E00 b3b9+ d49+d4+      0.9000000  1.19000e-08
    10   1.00E01 b     d410+d4          null1        null3
    11+  1.10E01 b3    d411+d4      0.1110000  1.00000e-12
    1    1.20E01 b3b   d412+d4  11112.0000000  2.00000e012
    13   1.30E01 b3b1  d         1111.3000000  1.13000e-12
    14+  1.40E01 b3b14 d4      111111.0000000  1.40000e013
    1    1.50E01 b     d41              null2        null1
    16   1.60E01 b3    d416         6.0000000  1.16000e003
    17+  1.70E01 b3b   d417+        0.7000000  1.11170e-03
    1    1.80E01 b3b1  d418+d      11.0000000  1.11800e003
    19   1.90E01 b3b19 d419+d4      0.1190000  9.00000e-05
    20+  2.00E01 b     d420+d4          null1        null2
    2    2.10E01 b3    d421+d4     11.1210000  1.11000e-05
    22   2.20E01 b3b   d422+d4  11111.2000000  2.00000e007
    23+  2.30E01 b3b2  d423+d4      0.1111123  1.11230e-09
$/tstFmt/ */

    call tst t, "tstFmt"
    b = jBuf()
    st = b'.BUF'
    call pipe '+F', b
    call tstDataClassOut '. c3 a2i i b3b c5 d4 c13 fl5 f8n2 ex6 e9n3',
        , -11, + 23
    call pipe '-'
    call fTabAuto fTabReset(abc, 1), b
    call fTabReset abc, 1
    call fTabAddDetect abc,      , st   , , 'c3L'
    call fTabAdd       abc, 'a2i', '% 8E'
    call fTabAddDetect abc, 'b3b', st   , ,'drei'
    call fTabAdd       abc, 'd4', '%-7C'
    call fTabAddDetect abc, 'fl5', st
    call fTabAddDetect abc, 'ex6', st
    call fTab abc, b
    call tstEnd t
    return
endProcedure tstFmt

tstFTab: procedure expose m.
/*
$=/tstFTab/
    ### start tst tstFTab #############################################
    testData begin
    ..---------a2i-b3b------------------d4------fl5-----ex6---
    -11       -11 b3           -11+d4++++ -111.100 -1e-012
    -1        -10 b            4-10+d4+++    null1   null3
    -          -9 b3b-9        d4-9+d4+++  -11.000 -1e-010
    -8+        -8 b3b-          d4-8+d4++  -18.000 -1.2e10
    -7         -7 b3b            d4-7+d4+   -7.000 -1.7e-7
    -          -6 b3              d4-6+d4   -0.111 -6.0e06
    -5+        -5 b                d4-5+d    null2   null2
    -4         -4 b3b-4             d4-4+ ******** -1.1e08
    -          -3 b3b-               d4-3   -0.113 -1.1e-4
    -2+        -2 b3b                 d4-   -0.120 -1.2e01
    -1         -1 b3                   d4   -0.100 -1.0e-2
    0           0 b                     d    null1   null1
    1+          1 b3                   d4    0.100 1.00e-2
    2++         2 b3b                 d42    0.120 1.20e01
    3           3 b3b3               d43+    0.113 1.13e-4
    4+          4 b3b4+             d44+d ******** 1.11e08
    5++         5 b                d45+d4    null2   null2
    6           6 b3              d46+d4+    0.111 1.11e05
    7+          7 b3b            d47+d4++    0.111 7.00e-8
    8++         8 b3b8          d48+d4+++    8.000 1.80e09
    9           9 b3b9+        d49+d4++++    0.900 1.19e-8
    10         10 b            410+d4++++    null1   null3
    11+        11 b3           11+d4+++++    0.111 1.0e-12
    1          12 b3b          2+d4++++++ ******** 2.00e12
    13         13 b3b1                  d 1111.300 1.1e-12
    14+        14 b3b14                d4 ******** 1.40e13
    1          15 b                   d41    null2   null1
    16         16 b3                 d416    6.000 1.16e03
    17+        17 b3b               d417+    0.700 1.11e-3
    1          18 b3b1             d418+d   11.000 1.12e03
    19         19 b3b19           d419+d4    0.119 9.00e-5
    20+        20 b              d420+d4+    null1   null2
    2          21 b3            d421+d4++   11.121 1.11e-5
    22         22 b3b          d422+d4+++ ******** 2.00e07
    23+        23 b3b2         423+d4++++    0.111 1.11e-9
    ..---------a2i-b3b------------------d4------fl5-----ex6---
    testData end
$/tstFTab/ */

    call pipeIni
    call tst t, "tstFTab"
    b = jBuf()
    st = b'.BUF'
    call pipe '+F', b
    call tstDataClassOut '. c3 a2i i b3b c5 d4 c13 fl5 f8n2 ex6 e9n3',
        , -11, + 23
    call pipe 'P|'
    call fTabReset ft, 2 1, 1 3, '-'
    call fTabAddRCT   ft, '='   , '%-6C', '.', , 'testData begin',
                                                , 'testData end'
    call fTabAddRCT   ft, 'a2i' , '%6i'
    call fTabAddRCT   ft, 'b3b' , '%-12C'
    call fTabAddRCT   ft, 'd4'  , '%10C'
    call fTabAddRCT   ft, 'fl5' , '%8.3I'
    call fTabAddRCT   ft, 'ex6' , '%7e'
    call fTab ft
    call pipe '-'
    call tstEnd t
    return
endProcedure tstFTab

tstCSV: procedure
/*
$=/tstCSV/
    ### start tst tstCSV ##############################################
    value,value eins,value zwei
    value,"value, , eins",value zwei
    value,"","value ""zwei"" oder?"
    value,,"value ""zwei"" oder?"
$/tstCSV/ */
    m.tstCsv.c.1 = ''
    m.tstCsv.c.2 = eins
    m.tstCsv.c.3 = zwei
    m.tstCsv.c.0 = 3
    call tst t, "tstCSV"
    m.tstCsv.o      = 'value'
    m.tstCsv.o.eins = 'value eins'
    m.tstCsv.o.zwei = 'value zwei'
    call tstOut t, csv4Obj(tstCsv'.'o, tstCsv'.'c, 0)
    m.tstCsv.o.eins = 'value, , eins'
    call tstOut t, csv4Obj(tstCsv'.'o, tstCsv'.'c, 0)
    m.tstCsv.o.eins = ''
    m.tstCsv.o.zwei = 'value "zwei" oder?'
    call tstOut t, csv4Obj(tstCsv'.'o, tstCsv'.'c, 0)
    m.tstCsv.o.eins = '---'
    call tstOut t, csv4Obj(tstCsv'.'o, tstCsv'.'c, 1, '---')
    call tstEnd t
    return
endProcedure tstCSV

tstfUnits: procedure
/*
$=/tstfUnits/
    ### start tst tstfUnits ###########################################
    .             1 ==>    1  =->   -1  =+>    +1  =b>    1 .
    .             5 ==>    5  =->   -5  =+>    +5  =b>    5 .
    .            13 ==>   13  =->  -13  =+>   +13  =b>   13 .
    .           144 ==>  144  =-> -144  =+>  +144  =b>  144 .
    .          1234 ==> 1234  =->   -1k =+> +1234  =b> 1234 .
    .          7890 ==> 7890  =->   -8k =+> +7890  =b> 7890 .
    .             0 ==>    0  =->    0  =+>    +0  =b>    0 .
    .         234E3 ==>  234k =-> -234k =+>  +234k =b>  229k
    .          89E6 ==>   89M =->  -89M =+>   +89M =b>   85M
    .         123E9 ==>  123G =-> -123G =+>  +123G =b>  115G
    .     4567891E9 ==> 4568T =->   -5P =+> +4568T =b> 4154T
    .         0.123 ==>  123m =-> -123m =+>  +123m =b>    0 .
    .  0.0000456789 ==>   46u =->  -46u =+>   +46u =b>    0 .
    .   345.567E-12 ==>  346p =-> -346p =+>  +346p =b>    0 .
    .  123.4567E-15 ==>  123f =-> -123f =+>  +123f =b>    0 .
    .           ABC ==>   ABC =->  -ABC =+>    ABC =b>   ABC
    ABCDEFGHIJKLMN ==> JKLMN =-> JKLMN =+> IJKLMN =b> JKLMN
    .          1E77 ==> +++++ =-> -++++ =+> ++++++ =b> +++++.
    .         1E-77 ==>    0f =->   -0f =+>    +0f =b>    0 .
    .     18.543E18 ==>   19E =->  -19E =+>   +19E =b>   16E
    .     20.987E20 ==> 2099E =-> -++++ =+> +2099E =b> 1820E
    .             1 ==>  1.000  =-> -1.000  =+> +1.000  =b>  1.000 .
    .             5 ==>  5.000  =-> -5.000  =+> +5.000  =b>  5.000 .
    .            13 ==> 13.000  =-> -0.013k =+> +0.013k =b> 13.000 .
    .           144 ==>  0.144k =-> -0.144k =+> +0.144k =b>  0.141k
    .          1234 ==>  1.234k =-> -1.234k =+> +1.234k =b>  1.205k
    .          7890 ==>  7.890k =-> -7.890k =+> +7.890k =b>  7.705k
    .             0 ==>  0.000  =->  0.000  =+> +0.000  =b>  0.000 .
    .         234E3 ==>  0.234M =-> -0.234M =+> +0.234M =b>  0.223M
    .          89E6 ==> 89.000M =-> -0.089G =+> +0.089G =b> 84.877M
    .         123E9 ==>  0.123T =-> -0.123T =+> +0.123T =b>  0.112T
    .     4567891E9 ==>  4.568P =-> -4.568P =+> +4.568P =b>  4.057P
    .         0.123 ==>  0.123  =-> -0.123  =+> +0.123  =b>  0.123 .
    .  0.0000456789 ==> 45.679u =-> -0.046m =+> +0.046m =b>  0.000 .
    .   345.567E-12 ==>  0.346n =-> -0.346n =+> +0.346n =b>  0.000 .
    .  123.4567E-15 ==>  0.123p =-> -0.123p =+> +0.123p =b>  0.000 .
    .           ABC ==>     ABC =->    -ABC =+>     ABC =b>     ABC
    ABCDEFGHIJKLMN ==> HIJKLMN =-> HIJKLMN =+> HIJKLMN =b> HIJKLMN
    .          1E77 ==> +++++++ =-> -++++++ =+> +++++++ =b> +++++++.
    .         1E-77 ==>  0.000f =-> -0.000f =+> +0.000f =b>  0.000 .
    .     18.543E18 ==> 18.543E =->    -19E =+>    +19E =b> 16.083E
    .     20.987E20 ==>   2099E =->  -2099E =+>  +2099E =b>   1820E
$/tstfUnits/
$=/tstfUnitst/
    ### start tst tstfUnitst ##########################################
    .            .3 ==>  0s30 ++>   0s30 -+> -0s30 -->  -0s30
    .            .8 ==>  0s80 ++>   0s80 -+> -0s80 -->  -0s80
    .             1 ==>  1s00 ++>   1s00 -+> -1s00 -->  -1s00
    .           1.2 ==>  1s20 ++>   1s20 -+> -1s20 -->  -1s20
    .            59 ==> 59s00 ++>  59s00 -+> -0m59 --> -59s00
    .         59.07 ==> 59s07 ++>  59s07 -+> -0m59 --> -59s07
    .        59.997 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .            60 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .          60.1 ==>  1m00 ++>   1m00 -+> -1m00 -->  -1m00
    .           611 ==> 10m11 ++>  10m11 -+> -0h10 --> -10m11
    .        3599.4 ==> 59m59 ++>  59m59 -+> -1h00 --> -59m59
    .        3599.5 ==>  1h00 ++>   1h00 -+> -1h00 -->  -1h00
    .          3661 ==>  1h01 ++>   1h01 -+> -1h01 -->  -1h01
    .         83400 ==> 23h10 ++>  23h10 -+> -0d23 --> -23h10
    .         84700 ==> 23h32 ++>  23h32 -+> -1d00 --> -23h32
    .         86400 ==>  1d00 ++>   1d00 -+> -1d00 -->  -1d00
    .         89900 ==>  1d01 ++>   1d01 -+> -1d01 -->  -1d01
    .       8467200 ==> 98d00 ++>  98d00 -+>  -98d --> -98d00
    .    8595936.00 ==> 99d12 ++>  99d12 -+>  -99d --> -99d12
    .    8638704.00 ==>  100d ++>   100d -+> -100d -->  -100d
    .       8640000 ==>  100d ++>   100d -+> -100d -->  -100d
    .     863913600 ==> 9999d ++>  9999d -+> -++++ --> -9999d
    .     863965440 ==> +++++ ++>  +++++ -+> -++++ --> -+++++.
    .     8.6400E+9 ==> +++++ ++>  +++++ -+> -++++ --> -+++++.
$/tstfUnitst/ */
    call jIni
    call tst t, "tstfUnits"
    d = 86400
    lst = 1 5 13 144 1234 7890 0 234e3  89e6 123e9,
          4567891e9 0.123 0.0000456789 345.567e-12 123.4567e-15 ,
           abc abcdefghijklmn   1e77 1e-77 18.543e18 20.987e20
    do wx=1 to words(lst)
        call tstOut t, right(word(lst, wx), 14) ,
                 '==>' fUnits(    word(lst, wx), 'd') ,
                 '=->' fUnits( '-'word(lst, wx), 'd') ,
                 '=+>' fUnits(    word(lst, wx), 'd',  ,   , '+'),
                 '=b>' fUnits(    word(lst, wx), 'b')
        end
    do wx=1 to words(lst)
        call tstOut t, right(word(lst, wx), 14) ,
                 '==>' fUnits(    word(lst, wx), 'd', 7, 3) ,
                 '=->' fUnits( '-'word(lst, wx), 'd', 7, 3) ,
                 '=+>' fUnits(    word(lst, wx), 'd', 7, 3, '+'),
                 '=b>' fUnits(    word(lst, wx), 'b', 7, 3)
        end
    call tstEnd t
    call tst t, "tstfUnitst"
    d = 86400
    lst = .3 .8 1 1.2 59 59.07 59.997 60 60.1 611 ,
          3599.4 3599.5 3661 d-3000 d-1700 d d+3500 ,
          d * 98 d * 99.49 d * 99.985 d*100 d * 9999 d * 9999.6 ,
          d * 1e5
    do wx=1 to words(lst)
        call tstOut t, right(word(lst, wx), 14) ,
                 '==>' fUnits(    word(lst, wx), 't'   ) ,
                 '++>' fUnits(    word(lst, wx), 't', , , ' '),
                 '-+>' fUnits('-'word(lst, wx),  't' ),
                 '-->' fUnits('-'word(lst, wx), 't', , , ' ')
        end
    call tstEnd t
    return
endProcedure tstfUnits

tstSb: procedure expose m.
/*
$=/tstSb/
    ### start tst tstSb ###############################################
    end        : 0
    char  3    : 1 abc
    lit   d?   : 0 .
    lit   de   : 1 de
    lit   de ? fg fgh: 1 fg
    while HIJ  : 0 .
    end        : 0
    while Jih  : 1 hi
    while ? klj: 1 jklkl ?
    end        : 1
    while ? klj: 0 .
    char  3    : 0 .
    lit        : 0 .
    until cba  : 0 .
    until ?qd  : 1 abc
    until ?qr  : 1 defdef .
    until ?qr  : 0 .
    strEnd ?   : 1 ?
    strEnd ?   : 0 ?
    strEnd ?   : 1 ab??cd????gh?
    strEnd ") ": 1 ab) .
    strEnd ") ": 1 ab) cd) ) gh) .
    string     : 1 'eins?''' v=eins?'
    space      : 1  >
    string     : 1 "zwei""" v=zwei"
    string ?   : 1 ?drei??? v=drei?
    *** err: scanErr ending Apostroph missing
    .    e 1: last token " scanPosition noEnd
    .    e 2: pos 28 in string 'eins?''' "zwei"""?drei???"noEnd
    string     : 0 " v=noEnd
$/tstSb/ */
    call pipeIni
    call tst t, 'tstSb'
    call scanSrc s, 'abcdefghijklkl ?'
    call out 'end        :' scanEnd(s)
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit   d?   :' scanLit(s, 'd?') m.s.tok
    call out 'lit   de   :' scanLit(s, 'de') m.s.tok
    call out 'lit   de ? fg fgh:',
            scanLit(s, 'de', '?', 'fg', 'fgh') m.s.tok
    call out 'while HIJ  :' scanWhile(s, 'HIJ') m.s.tok
    call out 'end        :' scanEnd(s)
    call out 'while Jih  :' scanWhile(s, 'Jih') m.s.tok
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'end        :' scanEnd(s)
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit        :' scanLit(s) m.s.tok
    call scanSrc s, 'abcdefdef ?'
    call out 'until cba  :' scanUntil(s, 'cba') m.s.tok
    call out 'until ?qd  :' scanUntil(s, '?qd') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab??cd????gh?ijk'
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab) cd) ) gh) jk) )'
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call scanSrc s, "'eins?'''" '"zwei"""?drei???"noEnd'
    call out 'string     :' scanString(s) m.s.tok 'v='m.s.val
    call out 'space      :' scanWhile(s, ' ') m.s.tok'>'
    call out 'string     :' scanString(s) m.s.tok 'v='m.s.val
    call out 'string ?   :' scanString(s, '?') m.s.tok 'v='m.s.val
    call out 'string     :' scanString(s) m.s.tok 'v='m.s.val
    call tstEnd t
    return
endProcedure tstSb

tstSb2: procedure expose m.
/*
$=/tstSb2/
    ### start tst tstSb2 ##############################################
    end        : 0
    char  3    : 1 abc
    lit   d?   : 0 .
    lit   de   : 1 de
    lit   de ? fg fgh: 1 fg
    while HIJ  : 0 .
    end        : 0
    while Jih  : 1 hi
    while ? klj: 1 jklkl ?
    end        : 1
    while ? klj: 0 .
    char  3    : 0 .
    lit        : 0 .
    until cba  : 0 .
    until ?qd  : 1 abc
    until ?qr  : 1 defdef .
    until ?qr  : 0 .
    strEnd ?   : 1 ?
    strEnd ?   : 0 ?
    strEnd ?   : 1 ab??cd????gh?
    strEnd ") ": 1 ab) .
    strEnd ") ": 1 ab) cd) ) gh) .
$/tstSb2/ */
    call pipeIni
    call tst t, 'tstSb2'
    call scanSrc s, 'abcdefghijklkl ?'
    call out 'end        :' scanEnd(s)
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit   d?   :' scanLit(s, 'd?') m.s.tok
    call out 'lit   de   :' scanLit(s, 'de') m.s.tok
    call out 'lit   de ? fg fgh:',
            scanLit(s, 'de', '?', 'fg', 'fgh') m.s.tok
    call out 'while HIJ  :' scanWhile(s, 'HIJ') m.s.tok
    call out 'end        :' scanEnd(s)
    call out 'while Jih  :' scanWhile(s, 'Jih') m.s.tok
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'end        :' scanEnd(s)
    call out 'while ? klj:' scanWhile(s, '? klj') m.s.tok
    call out 'char  3    :' scanChar(s, 3) m.s.tok
    call out 'lit        :' scanLit(s) m.s.tok
    call scanSrc s, 'abcdefdef ?'
    call out 'until cba  :' scanUntil(s, 'cba') m.s.tok
    call out 'until ?qd  :' scanUntil(s, '?qd') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'until ?qr  :' scanUntil(s, '?qr') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab??cd????gh?ijk'
    call out 'strEnd ?   :' scanStrEnd(s, '?') m.s.tok
    call scanSrc s, 'ab) cd) ) gh) jk) )'
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call out 'strEnd ") ":' scanStrEnd(s, ') ') m.s.tok
    call tstEnd t
    return
endProcedure tstSb2

tstScan: procedure expose m.
/*
$=/tstScan.1/
    ### start tst tstScan.1 ###########################################
    scan src a034,Und hr123sdfER"st1" 'str2''mit''apo''s' .
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    scan n tok 3: Und key  val .
    scan v tok 1:   key  val .
    scan n tok 10: hr123sdfER key  val .
    scan q tok 5: "st1" key  val st1
    scan v tok 1:   key  val st1
    scan a tok 19: 'str2''mit''apo''s' key  val str2'mit'apo's
    scan v tok 1:   key  val str2'mit'apo's
$/tstScan.1/ */
    call tst t, 'tstScan.1'
    call tstScan1 'l"litEins"l"litZwei"ndv" "aq1' ,
        ,"a034,Und hr123sdfER""st1"" 'str2''mit''apo''s' "
    call tstEnd t

/*
$=/tstScan.2/
    ### start tst tstScan.2 ###########################################
    scan src a034,Und hr123sdfER"st1" 'str2''mit''apo''s' .
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    scan n tok 3: Und key  val .
    scan b tok 1:   key  val .
    scan n tok 10: hr123sdfER key  val .
    scan " tok 5: "st1" key  val st1
    scan b tok 1:   key  val st1
    scan ' tok 19: 'str2''mit''apo''s' key  val str2'mit'apo's
    scan b tok 1:   key  val str2'mit'apo's
$/tstScan.2/ */
    call tst t, 'tstScan.2'
    call tstScan1 'ndsb1' ,
        ,"a034,Und hr123sdfER""st1"" 'str2''mit''apo''s' "
    call tstEnd t

/*
$=/tstScan.3/
    ### start tst tstScan.3 ###########################################
    scan src a034,'wie 789abc
    scan n tok 4: a034 key  val .
    scan , tok 1: , key  val .
    *** err: scanErr ending Apostroph missing
    .    e 1: last token ' scanPosition wie 789abc
    .    e 2: pos 7 in string a034,'wie 789abc
    scan w tok 1: w key  val wie 789abc
    scan n tok 2: ie key  val wie 789abc
    scan s tok 1:   key  val wie 789abc
    *** err: scanErr illegal char after number 789
    .    e 1: last token 789 scanPosition abc
    .    e 2: pos 14 in string a034,'wie 789abc
    scan d tok 3: 789 key  val wie 789abc
    scan n tok 3: abc key  val wie 789abc
$/tstScan.3/ */
    call tst t, 'tstScan.3'
    call tstScan1 'nds1' ,
        ,"a034,'wie 789abc"
    call tstEnd t

/*
$=/tstScan.4/
    ### start tst tstScan.4 ###########################################
    scan src litEinsefr 23 sdfER'str1'litZwei "str2""mit quo"s .
    scan l tok 7: litEins key  val .
    scan n tok 3: efr key  val .
    scan b tok 1:   key  val .
    scan d tok 2: 23 key  val .
    scan b tok 1:   key  val .
    scan n tok 5: sdfER key  val .
    scan a tok 6: 'str1' key  val str1
    scan l tok 7: litZwei key  val str1
    scan b tok 1:   key  val str1
    scan q tok 15: "str2""mit quo" key  val str2"mit quo
    scan n tok 1: s key  val str2"mit quo
    scan b tok 1:   key  val str2"mit quo
$/tstScan.4/ */
    call tst t, 'tstScan.4'
    call tstScan1 'l"litEins"l"litZwei"ndbaq1' ,
           ,"litEinsefr 23 sdfER'str1'litZwei ""str2""""mit quo""s "
    call tstEnd t

/*
$=/tstScan.5/
    ### start tst tstScan.5 ###########################################
    scan src  aha q3  =  f ab=cdEf eF='strIng' .
    scan s tok 1:   key  val .
    scan k tok 0:  key aha val def
    scan k tok 1: f key q3 val f
    scan s tok 1:   key q3 val f
    scan k tok 4: cdEf key ab val cdEf
    scan s tok 1:   key ab val cdEf
    scan k tok 8: 'strIng' key eF val strIng
    scan s tok 1:   key eF val strIng
$/tstScan.5/ */
    call tst t, 'tstScan.5'
    call tstScan1 'k1'," aha q3  =  f ab=cdEf eF='strIng' "
    call tstEnd t
    return
endProcedure tstScan

/*--- one single test scan with lines to scan in stem ln -------------*/
tstScan1:
parse arg classs, ln
    call tstOut t, 'scan src' ln
    call scanSrc scanOpt(s), ln
    m.s.key = ''
    m.s.val = ''
    do forever
        x = tstScanType(s, classs)
        if x == '' then
           leave
        call tstOut t, 'scan' x 'tok' length(m.s.tok)':' m.s.tok ,
                  'key' m.s.key 'val' m.s.val
        end
    return
endProcedure tstScan1

tstScanType: procedure expose m.
parse arg s, opt
    cx = 1
    a2 = ''
    res = 0
    do while cx <= length(opt)
        f = substr(opt, cx, 1)
        cx = cx + 1
        if pos(substr(opt, cx, 1), "'""") > 0 then do
            m.tstScanType.src = opt
            m.tstScanType.pos = cx
            call scanString tstScanType
            a2 = m.tstScanType.val
            cx = m.tstScanType.pos
            end
        if      f == 'a' then
            res = scanString(s, "'")
        else if f == 'b' then
            res = scanSpace(s)
        else if f == 'c' then
            res = scanChar(s, a2)
        else if f == 'd' then
            if a2 == 0 then
                res = scanNatIA(s)
            else
                res = scanNat(s)
        else if f == 'k' then
            res = scanKeyValue(s, 'def')
        else if f == 'l' then
            res = scanLit(s, a2)
        else if f == 'q' then
            res = scanString(s, '"')
        else if f == 'v' then
            res = scanVerify(s, a2)
        else if f == 'w' then
            res = scanWord(s)
        else if f == 'y' then
            res = scanVerify(s, a2, 'm')
        if res then
            return f
        end
    return scanType(s)
endProcedure tstScanType

tstScanRead: procedure expose m.
/*
$=/tstScanRead/
    ### start tst tstScanRead #########################################
    name erste
    space
    name Zeile
    space
    nextLine
    nextLine
    space
    name dritte
    space
    name Zeile
    space
    name schluss
    space
$/tstScanRead/ */
    call scanReadIni
    call tst t, 'tstScanRead'
    b = jBuf('erste Zeile  ',,'  dritte Zeile  schluss  ')
    s = jOpen(jReset0(scanRead(b)), m.j.cRead)
    do while \scanEnd(s)
        if scanName(s) then             call tstOut t, 'name' m.s.tok
        else if scanVerify(s, ' ') then call tstOut t, 'space'
        else if scanReadNL(s) then      call tstOut t, 'nextLine'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        end
    call jClose s
    call tstEnd t

/*
$=/tstScanReadMitSpaceLn/
    ### start tst tstScanReadMitSpaceLn ###############################
    name erste
    spaceLn
    name Zeile
    spaceLn
    name dritte
    spaceLn
    name Zeile
    spaceLn
    name schluss
    spaceLn
$/tstScanReadMitSpaceLn/ */
    call tst t, 'tstScanReadMitSpaceLn'
    s = scanReadOpen(scanRead(b))
    do forever
        if scanName(s) then         call out 'name' m.s.tok
        else if scanSpace(s) then call out 'spaceLn'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        else                        leave
        end
    call scanReadClose s
    call tstEnd t

/*
$=/tstScanJRead/
    ### start tst tstScanJRead ########################################
    1 jRead n tok erste val .
    2 jRead s tok   val .
    3 jRead n tok Zeile val .
    4 jRead s tok   val .
    5 jRead n tok dritte val .
    6 jRead s tok   val .
    7 jRead n tok Zeile val .
    8 jRead s tok   val .
    9 jRead n tok schluss val .
    10 jRead s tok   val .
    11 jRead 0 tok 1 val 1
    12 jRead s tok   val 1
    13 jRead + tok + val 1
    14 jRead s tok   val 1
    15 jRead 0 tok 2. val 2..
    16 jRead s tok   val 2..
    17 jRead + tok + val 2..
    18 jRead . tok . val 2..
    19 jRead s tok   val 2..
    20 jRead 0 tok +.3 val +.3
    21 jRead 0 tok -45e-3 val -45E-3
    22 jRead s tok   val -45E-3
    23 jRead " tok "a""b" val a"b
    24 jRead s tok   val a"b
    25 jRead ' tok 'c''d' val c'd
    className 1: ScanRes 18: ScanRes
$/tstScanJRead/ */

    call tst t, 'tstScanJRead'
    call jWrite jOpen(b,'>>'), '1  + 2. +. +.3-45e-3 "a""b"' "'c''d'"
    s = jOpen(jReset0(scanRead(jClose(b))), '<')
    do x=1 while jRead(s)
        v = m.s
        call out x 'jRead' m.v.type 'tok' m.v.tok 'val' m.v.val
        v.x = v
        end
    call jClose s
    call out 'className 1:' className(objClass(v.1)),
                    '18:' className(objClass(v.18))
    call tstEnd t
/*
$=/tstScanReadPos/
    ### start tst tstScanReadPos ######################################
    1
    2
    345678
    4
    5678
    4
$/tstScanReadPos/ */
    call tst t, 'tstScanReadPos'
    b = jBuf(1, 2, 345678, 4)
    call scanReadOpen scanReadReset(scanOpt(tstScn), b)
    do while scanNat(scanSkip(tstScn))
         call tstOut t, m.tstScn.tok
         end
    call scanSetPos tstScn, 3 3
    do while scanNat(scanSkip(tstScn))
         call tstOut t, m.tstScn.tok
         end
    call tstEnd t
    return
endProcedure tstScanRead

tstScanUtilInto: procedure expose m.
/*
$=/tstScanUtilIntoL/
TEMPLATE P3
     DSN('DBAF.DA540769.A802A.P00003.BV5I3NRN.REC')
     DISP(OLD,KEEP,KEEP)
TEMPLATE P4
     DSN('DBAF.DA540769.A802A.P00004.BV5I3NTK.REC')
     DISP(OLD,KEEP,KEEP)
LOAD DATA        LOG NO RESUME NO REPLACE COPYDDN(TCOPYD)
           STATISTICS INDEX(ALL) REPORT NO UPDATE ALL
 EBCDIC  CCSID(00500,00000,00000)
 SORTKEYS
  -- ENFORCE NO
  SORTDEVT DISK
  SORTNUM 160
  WORKDDN(TSYUTD,TSOUTD)
  INTO TABLE OA1P.TWB981 PART 1 INDDN TREC134
 WORKDDN(TSYUTS,TSOUTS)
 INTO TABLE "A540769"
   ."TWK802A1"
 PART 00001 INDDN P0
 WHEN(00001:00002) = X'0041'
 ( "DE1"
  POSITION(  00003:00010) DECIMAL
 , "CH2"
  POSITION(  00011:00015) CHAR(00005)
 , "TS3"
  POSITION(  00016:00041) TIMESTAMP EXTERNAL
 , "TI4"
  POSITION(  00042:00049) TIME EXTERNAL
 , "DA5"
  POSITION(  00050:00059) DATE EXTERNAL
 , "IN6"
  POSITION(  00060:00063) INTEGER
 , "RE7"
  POSITION(  00064:00067) FLOAT(21)
 )
 INTO TABLE "A540769"."TWK802A1"
 PART 00002 INDDN P0
 WHEN(00001:00002) = X'0041'
 ( "DE1"
  POSITION(  00003:00010) DECIMAL
 , "CH2"
  POSITION(  00011:00015) CHAR(00005)
 )
 dobido
$/tstScanUtilIntoL/
$=/tstScanUtilInto/
  ### start tst tstScanUtilInto #####################################
  -- 1 scanUtilInto
  . ( "DE1"
  .  POSITION(  00003:00010) DECIMAL
  . , "CH2"
  .  POSITION(  00011:00015) CHAR(00005)
  . , "TS3"
  .  POSITION(  00016:00041) TIMESTAMP EXTERNAL
  . , "TI4"
  .  POSITION(  00042:00049) TIME EXTERNAL
  . , "DA5"
  .  POSITION(  00050:00059) DATE EXTERNAL
  . , "IN6"
  .  POSITION(  00060:00063) INTEGER
  . , "RE7"
  .  POSITION(  00064:00067) FLOAT(21)
  . ) .
  .  -- table OA1P.TWB981 part 00001
  -- 2 scanUtilInto
  . ( "DE1"
  .  POSITION(  00003:00010) DECIMAL
  . , "CH2"
  .  POSITION(  00011:00015) CHAR(00005)
  . ) .
  .  -- table A540769.TWK802A1 part 00002
  -- 3 scanUtilInto
$/tstScanUtilInto/ */

    call scanReadIni
    b = jBuf()
    call mAddst b'.BUF', mapInline('tstScanUtilIntoL')
    call tst t, 'tstScanUtilInto'
    s = jOpen(jReset0(scanUtilOpt(ScanRead(b))), '<')
    do ix=1
        call out '--' ix 'scanUtilInto'
        if \ scanUtilInto(s) then
            leave
        call out '  -- table' m.s.tb 'part' m.s.part
        end
    call tstEnd t
    return
endProcedure tstSCanUtilInto

tstScanWin: procedure expose m.
/*
$=/tstScanWin/
    ### start tst tstScanWin ##########################################
    info 0: last token  scanPosition erste     Zeile                 dr+
    itteZe\npos 1 in line 1: erste     Zeile
    name erste
    spaceNL
    name Zeile
    spaceNL
    name dritteZeeeile
    info 5: last token dritteZeeeile scanPosition    zeile4            +
    .    fuenfueberSechs\npos 1 in line 4:    zeile4
    spaceNL
    name zeile4
    spaceNL
    name fuenfueberSechsUnddSiebenUNDundUndUAcht
    spaceNL
    info 10: last token   scanPosition undZehnueberElfundNochWeiterZwoe+
    lfundim1\npos 9 in line 10:         undZehn
    name undZehnueberElfundNochWeiterZwoelfundim13
    spaceNL
    name Punkt
    infoE 14: last token Punkt scanPosition \natEnd after line 13: im13+
    .      Punkt
$/tstScanWin/ */
    call scanWinIni
    call tst t, 'tstScanWin'
    b = jBuf('?erste     Zeile?',,'?  dritteZeeeile?', '?   zeile4 ',
       ,'?          fuenf?', '?ueberSechsUnddS?', '?iebenUNDundUndU?',
       ,'?Acht           ?', '?               ?', '?        undZehn?',
       ,'?ueberElfundNoch?', '?WeiterZwoelfund?', '?im13      Punkt?')
    s = jOpen(jReset0(scanWin(b, '15@2')), m.j.cRead)
    call tstOut t, 'info 0:' scanInfo(s)
    do sx=1 while \scanEnd(s)
        if scanSpace(s) then call tstOut t, 'spaceNL'
        else if scanName(s) then        call tstOut t, 'name' m.s.tok
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        if sx // 5 = 0 then
            call tstOut t, 'info' sx':' scanInfo(s)
        end
    call tstOut t, 'infoE' sx':' scanInfo(s)
    call tstEnd t

/*
$=/tstScanWinRead/
    ### start tst tstScanWinRead ######################################
    info 0: last token  scanPosition erste     Zeile                z3 +
    com Ze\npos 1 in line 1: erste     Zeile
    name erste
    spaceNL
    name Zeile
    spaceNL
    name z3
    info 5: last token z3 scanPosition  com Zeeeile z4 come4          f+
    uenf\npos 4 in line 3:  z3 com Zeeeile
    spaceNL
    name z4
    spaceNL
    name fuenf
    spaceNL
    info 10: last token   scanPosition com    Sechs  com  sieben   comA+
    cht  com\npos 15 in line 5:     fuenf     c
    name com
    spaceNL
    name Sechs
    spaceNL
    name com
    info 15: last token com scanPosition   sieben   comAcht  com com   +
    . com\npos 2 in line 7: m  sieben   com
    spaceNL
    name sieben
    spaceNL
    name Acht
    spaceNL
    info 20: last token   scanPosition ueberElfundNochWeit com elfundim+
    13\npos 1 in line 11: ueberElfundNoch
    name ueberElfundNochWeit
    spaceNL
    name im13
    spaceNL
    name Punkt
    info 25: last token Punkt scanPosition \natEnd after line 13: im13 +
    .     Punkt
    infoE 26: last token Punkt scanPosition \natEnd after line 13: im13+
    .      Punkt
$/tstScanWinRead/ */
    call tst t, 'tstScanWinRead'
    b = jBuf('?erste     Zeile?',,'? z3 com Zeeeile?', '? z4 come4 ',
       ,'?    fuenf     c?', '?om    Sechs  co?', '?m  sieben   com?',
       ,'?Acht  com com  ?', '?  com          ?', '?  com   undZehn?',
       ,'?ueberElfundNoch?', '?Weit com elfund?', '?im13      Punkt?')
    s = jReset0(scanWin(b, '15@2'))
    call scanOpt s, , , 'com'
    call tstOut t, 'info 0:' scanInfo(jOpen(s, m.j.cRead))
    do sx=1 while \scanEnd(s)
        if scanName(s) then        call tstOut t, 'name' m.s.tok
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else if \scanEnd(s) then        call scanErr s, 'cannot scan'
        if sx // 5 = 0 then
            call tstOut t, 'info' sx':' scanInfo(s)
        end
    call tstOut t, 'infoE' sx':' scanInfo(s)
    call tstEnd t
/*
$=/tstScanWinPos/
    ### start tst tstScanWinPos #######################################
    infoA1 1: last token 1 scanPosition                    2           +
    .        3\npos 2 in line 1: 1
    1
    2
    345678
    4
    infoB1: last token  scanPosition \natEnd after line 4: 4
    infoC1: last token  scanPosition 678              4\npos 4 in line+
    . 3: 345678
    678
    4
    infoA0 1: last token -2 scanPosition          -1         -0      1 +
    .        2\npos 3 in line -2: -2
    -2
    -1
    -0
    1
    2
    345678
    4
    infoB0: last token  scanPosition \natEnd after line 4: 4
    infoC0: last token  scanPosition 5678    4\npos 3 in line 3: 345678
    5678
    4
$/tstScanWinPos/ */
    call tst t, 'tstScanWinPos'
    b = jBuf(1, 2, 345678, 4)
    do ox=1 to 0 by -1
        if ox then
            s = scanWinOpen(scanWinReset(scanOpt(tstScn), b, 20))
        else
            s = scanWinOpen(scanWinReset(scanOpt(tstScn), b, 10),
                ,'-2         -1         -0')
        do nx=1 while scanNum(scanSkip(s))
             if nx = 1 then
                 call tstOut t, 'infoA'ox nx':' scanInfo(s)
             call tstOut t, m.s.tok
             end
        call tstOut t, 'infoB'ox':' scanInfo(s)
        call scanSetPos s, 3 3+ox
        call tstOut t, 'infoC'ox':' scanInfo(s)
        do while scanNat(scanSkip(s))
             call tstOut t, m.s.tok
             end
        call scanClose s
        end
    call tstEnd t
    return
endProcedure tstScanWin

tstScanSqlStmt: procedure expose m.
/*
$=/tstScanSqlStmt/
    ### start tst tstScanSqlStmt ######################################
    cmd1 select   current time                 stamp from s.1
    cmd2 update ";--""'/*"
    cmd3 delete '*/''"' / 3 - 1
    cmd4 terminator test
    cmd5 und so
    cmd6 term: ist
    cmd7 term>  in com nein >
    cmd8 .
$/tstScanSqlStmt/ */
    call pipeIni
    call scanWinIni
    call tst t, 'tstScanSqlStmt'
    b = jBuf('select -- /* c1', ' /* c1 */ current/* c2 " '' ',
       ,'c3"', '  c4   */time', 'stamp-- c5', 'from s.1; /* c6 */ ;' ,
       ,';update ";--""''/*";;       del123',
       , 'ete ''*/''''"'' / 3 - 1  -- c7', '/*c8 */   ' ,
       , ';terminator test; ','terminator|; und--  ', 'so| | |',
       , 'term: --#SET TERMINATOR : oder', 'ist: ',
       , 'term> /*--#SET TERMINATOR > oder', ' */ in com nein >:')
    call scanWinOpen scanSqlStmtOpt(scanWinReset(tstJcat, b, 30), ';')
    call scanSqlOpt tstJcat
    do sx=1 until nx = ''
        nx = scanSqlStmt(tstJCat)
        call tstOut t, 'cmd'sx nx
        end
    call scanReadCLose tstJCat
    call tstEnd t
/*
$=/tstScanSqlStmtRdr/
    ### start tst tstScanSqlStmtRdr ###################################
    cmd1 select   current time                 stamp from s.1
    cmd2 update ";--""'/*"
    cmd3 delete '*/''"' / 3 - 1
    cmd4 terminator test
    cmd5 und so
    cmd6 term: ist
    cmd7 term>  in com nein >
$/tstScanSqlStmtRdr/ */
    call tst t, 'tstScanSqlStmtRdr'
    r = jOpen(ScanSqlStmtRdr(b, 30), '<')
    do sx=1 while jRead(r)
        call tstOut t, 'cmd'sx m.r
        end
    call jClose r
    call tstEnd t
    return
endProcedure tstScanSqlStmt

tstScanSql: procedure expose m.
    call scanWinIni
/*
$=/tstScanSqlId/
    ### start tst tstScanSqlId ########################################
    sqlId ABC
    spaceNL
    sqlId AB__345EF
    spaceNL
$/tstScanSqlId/ */
    call tst t, 'tstScanSqlId'
    b = jBuf('abc  --  kommentar', right('ab_', 72), '_345ef-- kom')
    s = scanOpen(scanSqlReset(tstScn, b))
    do sx=1 while \scanEnd(s)
        if scanSqlID(s) then       call tstOut t, 'sqlId' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlDelimited/
    ### start tst tstScanSqlDelimited #################################
    sqlDeId ABC
    spaceNL
    sqlDeId AB_3F
    spaceNL
    sqlDeId abc
    spaceNL
    sqlDeId ab_Ef
    spaceNL
$/tstScanSqlDelimited/ */
    call tst t, 'tstScanSqlDelimited'
    b = jBuf('abc  --  kommentar',,'  -- ',,right('ab_', 72),'3F-- kom',
           , '"abc"  --  ko', ' -- ',,right('"ab_', 72),'Ef"-- kom')
    s = scanOpen(scanSqlReset(tstScn, b))
    do sx=1 while \scanEnd(s)
        if scanSqlDeID(s) then       call tstOut t, 'sqlDeId' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlQualified/
    ### start tst tstScanSqlQualified #################################
    sqlQuId ABC 1 ABC
    sqlQuId AB_3F 1 AB_3F
    sqlQuId abc 1 abc
    sqlQuId ab_Ef 1 ab_Ef
    sqlQuId EINS.Zwei.DREI 3 EINS
    sqlQuId vi er.fu  enf 2 vi er
$/tstScanSqlQualified/ */
    call tst t, 'tstScanSqlQualified'
    b = jBuf('abc  --  kommentar',,'  -- ',,right('ab_', 72),'3F-- kom',
           , '"abc"  --  ko', ' -- ',right('"ab_', 72),'Ef"-- kom',
           , 'eins."Zwei', '" -- com', ' . -- com', '  -- com',
           , 'drei -- ko', '"vi er"."fu  enf   " -- co')
    s = scanOpen(scanSqlReset(tstScn, b))
    do sx=1 while \scanEnd(s)
        if scanSqlQuID(s) then
            call tstOut t, 'sqlQuId' m.s.val m.s.val.0 m.s.val.1
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlNum/
    ### start tst tstScanSqlNum #######################################
    sqlNum 1
    spaceNL
    sqlNum 2..
    spaceNL
    sqlNum .3
    spaceNL
    sqlNum 4.5
    spaceNL
    sqlNum +6
    spaceNL
    sqlNum +7.03
    spaceNL
    sqlNum -8
    spaceNL
    sqlNum -.9
    spaceNL
    sqlNum 1E2
    spaceNL
    sqlNum -2.E-2
    spaceNL
    sqlNum +.3E+3
    spaceNL
$/tstScanSqlNum/ */
    call tst t, 'tstScanSqlNum'
    b = jBuf('1 2. .3 4.5 +6 + --kom', , ' -- com ', , ' 7.03 -8 -  .9',
             '1e2 - 2.e-2 + .3e+3')
    s = scanOpen(scanSqlReset(tstScn, b))
    do sx=1 while \scanEnd(s)
        if scanSqlNum(s) then
            call tstOut t, 'sqlNum' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else                            call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlNumUnit/
    ### start tst tstScanSqlNumUnit ###################################
    sqlNumUnit 1 KB
    spaceNL
    sqlNumUnit .3 MB
    sqlNumUnit .5
    sqlNumUnit +6.E-5 B
    spaceNL
    sqlNumUnit -7
    char *
    spaceNL
    sqlNumUnit -.8
    char T
    char B
    spaceNL
    *** err: scanErr bad unit TB after +9..
    .    e 1: last token Tb scanPosition .
    .    e 2: pos 41 in line 1: 1 kb .3mB.5 +   6.e-5B -7* -.8 TB + 9.T+
    b
    sqlNumUnit +9..
    spaceNL
$/tstScanSqlNumUnit/ */
    call tst t, 'tstScanSqlNumUnit'
    b = jBuf('1 kb .3mB.5 +   6.e-5B -7* -.8 TB + 9.Tb')
    s = scanOpen(scanSqlReset(tstScn, b))
    do sx=1 while \scanEnd(s)
        if scanSqlNumUnit(s, 0, 'B KB MB') then
            call tstOut t, 'sqlNumUnit' m.s.val
        else if scanSpace(s) then call tstOut t, 'spaceNL'
        else if scanChar(s ,1) then call tstOut t, 'char' m.s.tok
        else call scanErr s, 'cannot scan'
        end
    call tstEnd t
/*
$=/tstScanSqlClass/
    ### start tst tstScanSqlClass #####################################
    i a 1 A
    d "bC" 1 bC
    q d.e 2 D.E
    q f." g".h 3 F. g.H
    s 'ij''kl' 3 ij'kl
    s x'f1f2' 3 12
    s X'f3F4F5' 3 345
    .. . 3 .
    n .0 3 .0
    n 123.4 3 123.4
    n 5 3 5
    i g 1 G
$/tstScanSqlClass/ */
    call tst t, 'tstScanSqlClass'
    b = jBuf('a "bC" d.e f." g".h' "'ij''kl' x'f1f2' X'f3F4F5'" ,
            , '. .0 123.4 5 g')
    h = scanOpen(scanSqlReset(tstScn, b))
    do sx=1 while scanSqlClass(h)
        call tstOut t, m.h.sqlClass m.h.tok m.h.val.0 m.h.val
        end
    call tstEnd t
    return
endProcedure tstScanSql

/****** tstData begin *************************************************/
tstData: procedure expose m.
parse arg ty 2 le 'n' nu, l, r, num
    abs = abs(num)
    if nu \== '' then do
        if abs // 5 = 0 then
            return 'null' || (abs % 5 // nu + 1)
        end
    if ty = 'c' then do
        if le = '' then
            le = 8
        le = abs // le + 1
        if r = '' then
            r = '+'
        return left(l || num || r, le, right(r, 1))
        end
    if pos(ty, 'ief') < 1 then
        call err 'bad type' ty
    nn = abs
    if abbrev(num, '-') | abbrev(num, '+') then
        parse var num si 2 nn
    else
        si = ''
    if ty == 'e' then
        ex = 'e' || left('-', abs // 2) || (abs // 15)
    else
        ex = ''
    if le \== '' then
        nn = right(nn, abs // max(1, le - length(si||ex)) + 1, 1)
    if ty \== 'i' & (abs // 4) \= 0 & length(nn) > 1 then
            nn = overlay('.', nn, length(nn) - abs // length(nn))
    return si || nn || ex
endProcedure tstData

tstDataClassFo: procedure expose m.
parse arg flds
    ty = ''
    do fx=1 by 2 to words(flds)
        if word(flds, fx) = '.' then
            ty = ty', v'
        else
            ty = ty', f' word(flds, fx) 'v'
        end
    t = classNew('n* tstData u' substr(ty, 2))
    fo = oNew(m.t.name)
    ff = oFlds(fo)
    do fx=1 to m.ff.0
        f = fo || left('.', m.ff.fx \== '') || m.ff.fx
        m.f = word(flds, 2*fx)
        end
    return fo
endProcedure tstDataClassFo


tstDataClassOut: procedure expose m.
parse arg flds, f, t
    fo = tstDataClassFo(flds)
    ff = oFlds(fo)
    do x=f to t
        o = oCopy(fo)
        do fx=1 to m.ff.0
            f = o || left('.', m.ff.fx \== '') || m.ff.fx
            m.f = tstData(m.f, m.ff.fx, '+'m.ff.fx'+', x)
            end
        call out o
        end
    return
endProcedure tstDataClassOut
/****** tst **********************************************************
      test infrastructure
***********************************************************************/
/*--- migrate all compares to new ones:
        tstCI input compare
        tstCO ouput migrated compares
        tstCIO input and output -------------------------------------*/
tstUtTime: procedure expose m.
    say 'begin' utTime()  sysvar('sysnode')
    do 3000000
       end
    call sleep 1
    say 'end  ' utTime()
return

tstUtc2d: procedure expose m.
/*
$=/tstUtc2d/
    ### start tst tstUtc2d ############################################
    .             ff            255
    .           ffff          65535
    .          10000          65536          65536 = 1 * 16 ** 4
    .          10001          65537
    .         ffffff       16777215
    .        1000000       16777216       16777216 = 1 * 16 ** 6
    .        1000001       16777217
    .        20000FF       33554687
    .      100000000     4294967296     4294967296 = 1 * 16 ** 8
    .      300000000    12884901888    12884901888 = 3 * 16 ** 8
    .      3020000EF    12918456559
$/tstUtc2d/
*/
    numeric digits 33
    call tst t, 'tstUtc2d'
    all = 'ff ffff 10000 10001 ffffff 1000000 1000001 20000FF' ,
           '100000000 300000000 3020000EF'
    do ax = 1 to words(all)
        a = word(all, ax)
        if substr(a, 2) = 0 then
            b = right(left(a, 1) * 16 ** (length(a)-1), 15) ,
              '=' left(a, 1) '* 16 **' (length(a)-1)
        else
            b = ''
        call tstout t, right(a, 15)right(utc2d(x2c(a)), 15)b
        end
    call tstEnd t
    return
endProcedure tstUtc2d

tstCI: procedure expose m.
parse arg m, nm
    m.m.CIO = 0
    signal tstCIwork
tstCIO: procedure expose m.
parse arg m, nm
    m.m.CIO = 1
tstCIwork:
    m.m.name = nm
    m.m.cmp.1 = left('### start tst' nm '', 67, '#')

    do ix=2 to arg()-1
        m.m.cmp.ix = arg(ix+1)
        end
    m.m.cmp.0 = ix-1
    if m.m.CIO then
        call tstCO m
    return

tstCO: procedure expose m.
parse arg m
    call tst2dpSay m.m.name, m'.CMP', 68
    return
/*--- initialise m as tester with name nm
        use inline input nm as compare lines -----------------------*/
tstReset: procedure expose m.
parse arg m, nm
    call tstIni
    m.m.name = nm
    m.m.inIx  = 0
    m.m.out.0 = 0
    m.m.err   = 0
    m.err.count = 0
    m.tst.act = m
    if \ datatype(m.m.trans.0, 'n') then
        m.m.trans.0 = 0
    m.m.trans.old = m.m.trans.0
    return
endProcedure tstReset

tst: procedure expose m.
parse arg m, nm, cmpSt
    call tstReset m, nm
    m.tst.tests = m.tst.tests+1
    if cmpSt == '' then do
        cmpSt = mCut(t'.CMP', 0)
        call tst4dp cmpSt, mapInline(nm)
        end
    m.m.cmp = cmpSt
    m.m.moreOutOk = 0
    call mAdd mCut(m'.IN', 0), 'tst in line 1 eins ,'    ,
                     , 'tst in line 2 zwei ;   ' ,
                     , 'tst in line 3 drei .schluss.'
    call tstOut m, left('### start tst' nm '', 67, '#')
    call errReset 'h', 'return tstErrHandler(ggTxt)'
    m.m.errCleanup = m.err.cleanup
    if m.tst.ini.j \== 1 then do
        m.tst_m = m
/*      call err implement outDest 'i', 'call tstOut' quote(m)', msg'
*/      end
    else do
        drop m.tst_m
        call oMutatName m, 'Tst'
        call oMutatName m'.IN', 'Tst'
        m.m.jReading = 1
        m.m.jWriting = 1
        m.m.jUsers = 0
        m.m.in.jReading = 1
        m.m.in.jWriting = 1
        m.m.in.jUsers = 0
        if m.tst.ini.e \== 1 then do
            m.m.oldJin = m.j.in
            m.m.oldOut = m.j.out
            m.j.in = m'.IN'
            m.j.out = m
            end
        else do
            if m.pipe.0 <> 2 then
                call tstErr m, 'm.pipe.0' m.pipe.0 '<> 2'
            call pipe '+Ff', m , m'.IN'
            end
        end
    return m
endProcedure tst

tstEnd: procedure expose m.
parse arg m, opt opt2
    cmp = m.m.cmp
    m.m.jReading = 0
    m.m.jWriting = 0
    drop m.tst_m
    if m.tst.ini.j == 1 then do
        m.m.jReading = 0
        m.m.jWriting = 0
           if m.tst.ini.e \== 1 then do
            m.j.in = m.m.oldJin
            m.j.out = m.m.oldOut
            end
        else do
            if m.j.in \== m'.IN' | m.j.out \== m then
                call tstErr m, m.j.in '\==' m '|' m.j.out '\==' m
            call pipe '-'
            if m.pipe.0 <> 2 then
                call tstErr m, 'm.pipe.0' m.pipe.0 '<> 2'
            end
        end
    if m.m.err = 0 then
        if m.m.errCleanup \= m.err.cleanup then
            call tstErr m, 'err.Cleanup' m.err.cleanup '<> old',
                        m.m.errCleanup
    if m.m.out.0 \= m.cmp.0 & \ (m.m.moreOutOk,
            &  m.m.out.0 > m.cmp.0) then do
        call tstErr m, 'old' m.cmp.0 'lines \= new' m.m.out.0
        do nx = m.m.out.0 + 1 to min(m.m.out.0+10, m.cmp.0)
            say 'old -  ' m.cmp.nx
            end
        end
    call errReset 'h'
    m.tst.act = ''
    soll = 0
    if opt = 'err' then do
        soll = opt2
        if m.m.err \= soll then
            call err soll 'errors expected, but got' m.m.err
        end
    if m.m.err \= soll then do
        say 'new lines:' (m.m.out.0 - 1)
           call tst2dpSay m.m.name, m'.OUT', 68
        end
    say left('###' m.m.name 'end with' m.m.err 'errors ', 67, '#')

    if 1 & m.m.err \= soll then
        call err 'dying because of' m.m.err 'errors'
    m.m.trans.0 = m.m.trans.old
    return
endProcedure tstEnd

tst2dp: procedure expose m.
parse arg st, dp, ml
    dx = m.dp.0
    do sx=1 to m.st.0
        li = m.st.sx
        cx = 1
        do until cx > length(li)
            c = substr(li, cx, 1)
            ou = left('.', strip(c) == '' | pos(c, '.+') > 0)
            ex = min(length(li),cx+ml-length(ou)-2)
            ou = ou || substr(li, cx, ex+1-cx)
            dx = dx + 1
            c = right(ou, 1)
            if ex < length(li) then
                m.dp.dx = ou || '+'
            else if strip(c) == '' | pos(c, '.+') > 0 then
                m.dp.dx = ou || '.'
            else
                m.dp.dx = ou
            cx = ex +1
            end
        end
    m.dp.0 = dx
    return
endProcedure tst2dp

tst2dpSay: procedure expose m.
parse arg name, st, ml
    say '$=/'name'/'
    call tst2dp st, mCut('TST.TMP', 0), 68
    do nx=1 to m.tst.tmp.0
           say '   ' m.tst.tmp.nx
           end
     say '$/'name'/'
return tst2dpSay

tst4dp: procedure expose m.
parse arg st, dp
    sx = m.st.0
    inData = 0
    data = ''
    do dx=1 to m.dp.0
        li = strip(m.dp.dx)
        if pos(left(li, 1), '.+') > 0 then
         li = substr(li, 2)
        if right(li, 1) == '+' then do
            inData = 1
            data = data || left(li, length(li)-1)
            iterate
            end
        if right(li, 1) == '.' then
            li = left(li, length(li)-1)
        sx = sx + 1
        m.st.sx = repAll(data || li, '$ä', '/*', '$ö', '*/')
        inData = 0
        data = ''
        end
    m.st.0 = sx
    if inData then
        call err 'end inData'
    return
endProcedure tst4dp

/*--- write to test: say lines and compare them ----------------------*/
tstOut: procedure expose m.
parse arg m, arg
    do tx=m.m.trans.0 by -1 to 1
        arg = repAll(arg, word(m.m.trans.tx, 1),
            , subword(m.m.trans.tx, 2))
        end
    call mAdd m'.OUT', arg
    nx = m.m.out.0
    cmp = m.m.cmp
    c = m.cmp.nx
    if nx > m.cmp.0 then do
        if nx = m.cmp.0+1 & \ m.m.moreOutOK then
            call tstErr m, 'more new Lines' nx
        end
    else if c \== arg then do
        do cx=1 to min(length(c), length(arg)) ,
             while substr(c, cx, 1) == substr(arg, cx, 1)
             end
         msg = 'old line' nx '<> new overnext, firstDiff' cx',',
                 'len old' length(c)', new' length(arg)

        if cx > 10 then
            msg = overlay('|', msg, cx-10)
        call tstErr m, msg
        say c
        end
    say arg
    return 0
endProcedure tstOut

tstWrite: procedure expose m.
parse arg m, var
    cl = objClass(var)
    if cl == m.class_N then do
        call tstOut m, 'tstR: @ obj null'
        end
    else if cl == m.class_S then do
        call tstOut m, var
        end
    else if abbrev(var, m.o_escW) then do
        call tstOut m, o2String(var)
        end
    else if cl == m.class_V then do
        call tstOut m, m.var
        end
    else if oKindOf(var, 'JRW') then do
        call tstOut m, 'tstWriteO kindOf JRW jWriteNow begin <<<'
        call jWriteNow m, var
        call tstOut m, 'tstWriteO kindOf JRW jWriteNow end   >>>'
        end
    else if oKindOf(var, 'ORun') then do
        call tstOut m, 'tstWriteO kindOf ORun oRun begin <<<'
        call oRun var
        call tstOut m, 'tstWriteO kindOf ORun oRun end   >>>'
        end
    else do
        do tx=m.m.trans.0 by -1 to 1 ,
                while word(m.m.trans.tx, 1) \== var
            end
        if tx < 1 then
            call mAdd M'.TRANS', var 'tstWriteoV' || (m.m.trans.0+1)
        call classOut , var, 'tstR: '
        end
    return
endProcedure tstWrite

tstRead: procedure expose m.
parse arg mP
    if right(mP, 3) == '.IN' then
       m = left(mP, length(mP)-3)
    else
        call err 'tstRead bad m' mP
    ix = m.m.inIx + 1
    m.m.inIx = ix
    if ix <= m.m.in.0 then do
        call tstOut m, '#jIn' ix'#' m.m.in.ix
        m.mP = m.m.in.ix
        return 1
        end
    call tstOut m, '#jIn eof' ix'#'
    return 0
endProcedure tstRead

tstFilename: procedure expose m.
parse arg suf, opt
    if m.err.os == 'TSO' then do
        dsn = dsn2jcl('~tmp.tst.'suf)
        if opt = 'r' then do
            if sysDsn("'"dsn"'") \== 'DATASET NOT FOUND' then
                call adrTso "delete '"dsn"'"
            call csiOpen 'TST.CSI', dsn'.**'
            do while csiNext('TST.CSI', 'TST.FINA')
                say 'deleting csiNext' m.tst.fina
                call adrTso "delete '"m.tst.fina"'"
                end
            end
        return dsn
        end
    else if m.err.os == 'LINUX' then do
        if abbrev(suf, '/') then
            fn = suf
        else
            fn = .Stream%%new('~/tmp/tst/'suf)%%qualify /* full path */
        cx = lastPos('/', fn)
        if cx > 0 then do
            dir = left(fn, cx-1)
            if \sysIsFileDirectory(dir) then
                call adrSh "mkdir -p" dir
            if \sysIsFileDirectory(dir) then
                call err 'tstFileName could not create dir' dir
            end
        if opt \= 'r' then
            nop
        else if sysIsFile(fn) then
            call sysFileDelete fn
        else if sysIsFileDirectory(fn) then
            call adrSh 'rm -r' fn
        return fn
        end
    else
        call err 'tstFilename does not implement os' m.err.os
endProcedure tstFilename

/*--- say total errors and fail if not zero --------------------------*/
tstTotal: procedure expose m.
    say '######'
    say '######'
    say '######' m.tst.tests 'tests with' ,
                 m.tst.err 'errors in' m.tst.errNames
    say '######'
    say '######'
    if m.tst.err \== 0 then
        call err m.tst.err 'errors total'
    return
endProcedure tstTotal

/*--- test err: message, count it and continue -----------------------*/
tstErr: procedure expose m.
parse arg m, msg
    say '### error' msg
    m.m.err = m.m.err + 1
    m.tst.err = m.tst.err + 1
    nm = m.m.name
    if wordPos(nm, m.tst.errNames) < 1 then
        m.tst.errNames = m.tst.errNames nm
    return 0
endProcedure tstErr

/*--- tstErrHandler: intercept errors --------------------------------*/
tstErrHandler: procedure expose m.
parse arg ggTxt
    m = m.tst.act
    if m == '' then
        call err ggTxt
    m.err.count = m.err.count + 1
    call splitNl err, errMsg(' }'ggTxt)
    call tstOut m.tst.act, '*** err:' m.err.1
    do x=2 to m.err.0
        call tstOut m, '    e' (x-1)':' m.err.x
        end
    return 0
endSubroutine tstErrHandler

tstTrc: procedure expose m.
parse arg msg
    m.tst.trc = m.tst.trc + 1
    say 'tstTrc' m.tst.trc msg
    return m.tst.trc
endProcedure tstTrc

/*--- tstIni: global initialization ----------------------------------*/
tstIni: procedure expose m.
    if m.tst.ini \== 1 then do
        m.tst.ini = 1
        call mapIni
        m.tst.err = 0
        m.tst.trc = 0
        m.tst.errNames = ''
        m.tst.tests = 0
        m.tst.act = ''
        end
    if m.tst.ini.j \== 1 & m.j.ini == 1 then do
        m.tst.ini.j = 1
          call classNew 'n Tst u JRW', 'm',
             , "jRead return tstRead(m)",
             , "jWrite call tstWrite m, line"
        end
    if m.tst.ini.e \== 1 & m.pipe.ini == 1 then do
        m.tst.ini.e = 1
        end
    return
endProcedure tstIni
/* copy tstAll end   **************************************************/
/* copy wshCopy begin ************************************************/
wshIni: procedure expose m.
    call compIni
    call sqlIni
    call scanWinIni
    return
endProcedure wshIni
/* copy wshCopy end   ************************************************/
/* copy db2Util begin ************************************************/
tstDb2Ut: procedure expose m.
/*
$=/tstDb2Ut/
$/tstDb2Ut/
*/
    call pipeIni
    call tst t, 'tstDb2Ut'
    call mAdd mCut(t'.IN', 0), '   template old ,'    ,
                     , 'LOAD DATA INDDN oldDD ' ,
                     , '( cols  )'
call db2UtilPunch 'rep iDsn=DSN.INPUT.UNL'
call tstEnd t
return
endProcedure tstDb2Ut
/* ???????????? achtung nicht fertig |
          Idee: allgemein Punch Umformungs Utility
              aber man müsste wohl auf scan Util umstellen
                  und abstürzen wenn man etwas nicht versteht
          GrundGerüst von cadb2 umgebaut
????????????????? */

db2UtilPunch: procedure expose m.
parse upper arg args
    call scanSrc scanOpt(s), args
    a.rep = 1
    a.tb = ''
    a.trunc = 0
    a.iDD = ''
    a.iDSN = ''
    do while scanKeyValue(scanSkip(s), 1)
        ky = m.s.key
        say '????ky' ky m.s.val
        if wordPos(ky, 'REP TB TRUNC IDD IDSN') < 1 then
            call scanErr s, 'bad key' ky
        a.ky = m.s.val
        end
    if a.iDSN \== '' then do
        if a.iDD == '' then
            a.iDD = 'IDSN'
        call out '  TEMPLATE' a.iDD 'DSN('a.iDsn')'
        end
    do while in() & word(m.in, 1) <> 'LOAD'
        call out m.in
        end
    ll = space(m.in, 1)
    if \ abbrev(ll, 'LOAD DATA ') then
        call err 'bad load line:' m.in
    call out subword(m.in, 1, 2) 'LOG NO'
    if abbrev(ll, 'LOAD DATA INDDN ') then
        call db2UtilPunchInDDn word(ll, 4)
    else if \ abbrev(ll, 'LOAD DATA LOG ') then
        call err 'bad load line' ix':' m.i.ix
    if a.rep then
        call out '    STATISTICS INDEX(ALL) UPDATE ALL'
    call out '    DISCARDS 1'
    call out '    ERRDDN   TERRD'
    call out '    MAPDDN   TMAPD '
    call out '    WORKDDN  (TSYUTD,TSOUTD) '
    call mAdd o, '  SORTDEVT DISK '
    do ix=ix+1 to m.i.0
        if pos('CHAR(', m.i.ix) > 0 then
            call mAdd o, strip(m.i.ix, 't') 'TRUNCATE'
        else if word(m.i.ix, 1) word(m.i.ix, 3) == 'PART INDDN' then
            call mAdd o, m.i.ix,
                       , '  RESUME NO REPLACE COPYDDN(TCOPYD)' ,
                       , '  DISCARDDN TDISC '
        else
            call mAdd o, m.i.ix
        end
    call writeDsn oDsn ':~'iDsn, 'M.O.', , 1
    return
endProcedure db2UtilPunch

db2UtilPunchInDDn:
parse arg inDDn
     if a.iDD == '' then
         ll =  '    INDDN' inDDn
     else
         ll =  '    INDDN' a.iDD
     if a.rep then
         call out ll 'RESUME NO REPLACE COPYDDN(TCOPYD)'
     else
         call out ll 'RESUME YES'
     call out  '    DISCARDDN TDISC'
     return
endSubroutine db2UtilPunchInDDn
/* copy db2Util end   ************************************************/
/* copy time begin ****************************************************
 timestamp format yz34-56-78-hi.mn.st.abcdef
 11.12.14 wk: added lrsn2uniq
 11.05.13 wk: numeric digits transparent: in jeder Procedure drin
              time2jul, tst externalisiert
**********************************************************************/
/*--- timestamp to julian --------------------------------------------*/
time2jul: procedure expose m.
parse arg yyyy '-' mm '-' dd '-'
    if yyyy < 1100 then
        yyyy = 11 || right(yyyy, 2, 0)
        /* date function cannot convert to julian, only from julian
           use b (days since start of time epoch) instead     */
    return right(yyyy, 2) ,
         || right(date('b', yyyy || mm || dd, 's') ,
                - date('b', yyyy-1 || '1231', 's') , 3, 0)
endProcedure time2jul

/*--- current timestamp ---------------------------------------------*/
timestampNow: procedure expose m.
    parse value date('s') time('l') with y 5 m 7 d t
    return y'-'m'-'d'-'translate(t, '.', ':')

/*--- timestamp expand: expand to timestamp of 26 chars -------------*/
timestampExp: procedure expose m.
parse arg tst .
    if length(tst) < m.timeStamp_Len then
        return overlay(tst, m.timeStamp_01)
    else
        return left(tst, timeStamp_Len)
endProcedure tiemstampExp

/*--- timestamp check return '' if correct or error text ------------*/
timestampcheck: procedure expose m.
parse arg tst
    if length(tst) < m.timestamp_d0Len | \ abbrev(m.timestamp_00,
             , translate(tst, '000000000', '123456789')) then
        return 'bad timestamp' tst
    parse arg yyyy '-' mo '-' dd '-' hh '.' mm '.' ss '.' u
    if mo < 1 | mo > 12 then
        return 'bad month in timestamp' tst
    if dd < 1 | dd > 3 || substr('101010110101', mo, 1) then
        return 'bad day in timestamp' tst
    if mo = 2 then
        if dd > date('d', yyyy'0301', 's') - 32 then
            return 'bad day in timestamp' tst
    if hh > 23 & \ abbrev(24 00 00 000000, hh mm ss u) then
        return 'bad hour in timestamp' tst
    if mm > 59 then
        return 'bad minute in timestamp' tst
    if ss > 59 then
        return 'bad second in timestamp' tst
    return ''
endProcedure timestampCheck

/*--- timestamp to days since 1.1.0001 ------------------------------*/
timestamp2days: procedure expose m.
parse arg yyyy '-' mo '-' dd '-' hh '.' mm '.' ss
numeric digits 15
    return date('b', yyyy || mo || dd, 's') ,
                + (((hh * 60) + mm) * 60 + ss) / 86400

/*--- timestamp diff in days ----------------------------------------*/
timestampDiff: procedure expose m.
parse arg t1, t2
numeric digits 15
    return timestamp2days(t1) - timestamp2Days(t2)

/*--- convert 2 digit year to 4 digit -------------------------------*/
timeYear24: procedure expose m.
parse arg s
    y = left(date('S'), 4)
    s4 = left(y, 2)right(s, 2, 0)
    if s4 > y + 30 then
        return (left(y, 2) - 1)substr(s4, 3)
    else if s4 < y - 69 then
        return (left(y, 2) + 1)substr(s4, 3)
    else
        return s4
endProcedure timeYear24

/*--- convert 2 or 4 digit year Y (A=0...T=19) ----------------------*/
timeYear2Y: procedure expose m.
parse arg y
    return substr('ABCDEFGHIJKLMNOPQRST' , ((y + 10) // 20) + 1, 1)

/*--- convert 2 or 4 digit year Y (A=0...T=19) ----------------------*/
timeY2Year: procedure expose m.
parse arg i
    j = pos(i, 'ABCDEFGHIJKLMNOPQRST') - 1
    if j < 0 then
        call err 'timeYearY24 bad input' i
    y = left(date('S'), 4)
    r = y - (y+10) // 20 + j
    if r < y - 15 then
        return r + 20
    else if r > y + 4 then
        return r - 20
    else
        return r
endProcedure timeY2Year

/*--- convert numeric month to M (Jan=B=1, dec=12=M) ----------------*/
timeMonth2M: procedure expose m.
parse arg m
    return substr('BCDEFGHIJKLM', m, 1)

/*--- convert M to numeric Month (Jan=B=1, dec=12=M) ----------------*/
timeM2Month: procedure expose m.
parse arg m
    p = pos(m, 'BCDEFGHIJKLM')
    if p= 0 then
        call err 'bad M month' m
    return right(p, 2, 0)

/*--- convert numeric hour 78 to H8 (A=0..D=3) ----------------------*/
timeHour2H: procedure expose m.
parse arg h
    h = right(h, 2, 0)
    return substr('ABCD', left(h, 1)+1, 1)substr(h, 2)

/*--- convert H8 to numeric Hour 78 (A=0..D=3) ----------------------*/
timeH2Hour: procedure expose m.
parse arg h
    p = pos(left(h, 1), 'ABCD') - 1
    if p < 0 | length(h) \== 2 then
        call err 'bad H hour' h
    return p || substr(h, 2)

/*--- read timeZoneOffset and leapSeconds registers
        and set variables for uniq ----------------------------------*/
timeIni: procedure expose m.
parse arg debug
    numeric digits 25
    /* offsets documented in z/OS Data Areas  Vol.1 */
    cvtOH      = '00000010'          /* cvt control block Address */
    cvtext2O   = x2d('00000560') /* offset  to extension 2    */
    cvtldtoO   = x2d('00000038') /* offset to timezone    */
    cvtlsoO    = x2d('00000050') /* offset to leapSeconds */

    /* CVT CB        address               + extention2   */
    cvtExt2A       = C2D(STORAGE(cvtOH,4)) + cvtext2O
    /* cvtLdto timeZone              address +offset      */
    m.time_Zone    = c2d(STORAGE(d2x(cvtext2A+cvtldtoO, 8), 8))
    m.time_StckUnit = 1e-6 / 256 / 16
    /* cvtLso LeapSecs               address +offset      */
    m.time_Leap    = c2d(STORAGE(d2x(cvtext2A+cvtlsoO, 8), 8))
    m.time_LZT_TAI10_us16 = (m.time_zone - m.time_leap) % 256
    m.time_UQDigits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345678'
                         /* find lrsn of day 0, 0 out last 6 bits  */
    m.time_UQZero = x2d(left(timeTAI102Lrsn( ,
                 '2004-12-31-00.00.22.000000'), 14)) % 64 * 64
    m.timeStamp_00 = '0000-00-00-00.00.00.000000'
    m.timeStamp_01 = '0001-01-01-00.00.00.000000'
    m.timeStamp_99 = '9999-12-31-23.59.59.999999'
    m.timeStamp_len = length(m.timestamp_00)
    m.timeStamp_d0Llen = m.timestamp_len - 7
    m.time_ini = 1
    return
endSubroutine timeIni

/*--- TAI10 timestamp yyyy-mm.... -> stckE value char(16)
         BLSUETID is described in z/OS MVS IPCS Customization -------*/
timeTAI102stckE: procedure expose m.
                        /* timestamp must include microSeconds |||*/
    parse arg year'-'mo'-'da'-'hh'.'mm'.'secs
    tDate = mo'/'da'/'year hh':'mm'.'secs
    ACC=left('', 16, '00'x)
    ADDRESS LINKPGM "BLSUETID TDATE ACC"
    RETURN acc
endProcedure timeTAI102stckE

timeTAI102lrsn: procedure expose m.
parse arg tst
    return c2x(left(timeTAI102StckE(tst), 10))

timeLZT2stckE: procedure expose m.
parse arg tst
    numeric digits 23
    s =timeTAI102StckE(tst)
    return d2c(c2d(left(s, 8)) - m.time_lzt_TAI10_us16,8) || substr(s,9)
endProcedure timeLZT2stckE

timeLZT2lrsn: procedure expose m.
parse arg tst
    return c2x(left(timeLZT2StckE(tst), 10))

/*--- expand lrsn to 20 hexDigits -------------------------------*/
timeLrsnExp: procedure expose m.
parse arg s
    return left(copies('00', length(s) <= 12 & \abbrev(s, 0))s, 20,0)

/*--- expand stcK(e) to 16 Byte ---------------------------------*/
timeStckExp: procedure expose m.
parse arg s
    return left(copies('00'x, length(s) <= 8 & s >> '09'x)s, 16,'00'x)

/*--- conversion from StckE Clock Value to TAI10 Timestamp
        BLSUETOD is described in z/OS MVS IPCS Customization --------*/
timeStckE2TAI10: PROCEDURE expose m.
parse arg stck      /* must be 16 characters ||||| */
  TDATE = left('' , 26)
  ADDRESS LINKPGM "BLSUETOD stck TDATE"
  /* return format   : mo/dd/yyyy hh:mm:ss.uuuuuu */
  /* Timestamp format: yyyy-mm-dd-hh.mm.ss.uuuuuu */
  parse var TDATE mo '/' dd '/' yyyy hh ':' mm ':' secs
RETURN yyyy'-'mo'-'dd'-'hh'.'mm'.'secs
endProcedure timeStckE2TAI10

/*--- conversion from Lrsn Clock Value to TAI10 Timestamp ----------*/
timeLrsn2TAI10:
    return timeStckE2TAI10(x2c(arg(1))'000000000000'x)

/*--- conversion from Lrsn Clock Value to LZT Local Zurich Time -----*/
timeStckE2LZT: procedure expose m.
parse arg s
    numeric digits 23
    return timeStckE2TAI10(d2c(c2d(left(s, 8)) ,
                + m.time_LZT_TAI10_us16, 8) || substr(s, 9))
endProcedure timeStckE2LZT

/*--- conversion from Lrsn Clock Value to LZT Local Zurich Time -----*/
timeLrsn2LZT: procedure expose m.
parse arg lrsn
    return timeStckE2LZT(x2c(lrsn) || '000000000000'x)

/* convert a lrsn to the uniq variable ********************************/
timeLrsn2uniq: procedure expose m.
parse arg lrsn
    /* unique are bits 0:41 of the TodClock value
              minus 31.12.2004 represented
              by base 35 by 'ABC...YZ01..8'
    */
    lrsn = left(timeLrsnExp(lrsn), 14)
    numeric digits 20
    diff = x2d(lrsn) - m.time_UQZero
    if diff < 0 then
        return'< 2005'
    return right(i2q(diff % 64, m.time_UQDigits), 8, 'A')
endProcedure timeLrsn2Uniq

/* convert a uniq variable to lrsn ************************************/
timeUniq2lrsn: procedure expose m.
parse arg uniq
    numeric digits 15
    u1 = q2i(left(uniq, 8, 'A'), m.time_UQDigits) * 64
    lrsn = '00'right(d2x(u1 + m.time_UQZero), 12, 0)'000000'
    return lrsn
endProcedure uniq2lrsn

/*--- translate a number in q-system to decimal
       arg digits givs the digits corresponding to 012.. in the q sysem
       q = length(digits) --------------------------------------------*/
q2i: procedure expose m.
parse arg v, digits
    b = length(digits)
    i = 0
    do x = 1 to length(v)
        q = substr(v, x, 1)
        r = pos(q, digits)
        if r < 1 then
            call err 'bad digit' q 'in' v 'valid digits' digits
        i = i * b + r - 1
        end
    return i
endProcedure q2i
/*--- translate a decimal number to q-system - inverse of q2i --------*/
i2q: procedure expose m.
parse arg i, digits
    if i = 0 then
        return left(digits, 1)
    b = length(digits)
    v = ''
    do while i > 0
        v = substr(digits, 1 + (i // b), 1) || v
        i = i % b
        end
    return v
endProcedure i2q
/* copy time end -----------------------------------------------------*/
/* copy sort begin ****************************************************/
sortComparator: procedure expose m.
parse arg cmp, le, ri
    if cmp == '' then
        cmp = '<<='
    if length(cmp) < 6 then
        m.sort_comparator = 'cmp =' le cmp ri
    else if pos(';', cmp) < 1 then
        m.sort_comparator = "aLe =" le"; aRi =" ri"; cmp =" cmp
    else
        m.sort_comparator = "aLe =" le "; aRi =" ri";" cmp
    return
endProcedure sort

sortWords: procedure expose m.
parse arg wrds, cmp
    if words(wrds) <= 1 then
        return strip(wrds)
    m.sort_ii.0 = words(wrds)
    do sx=1 to m.sort_ii.0
        m.sort_ii.sx = word(wrds, sx)
        end
    call sort sort_ii, sort_oo, cmp
    r = m.sort_oo.1
    do sx=2 to m.sort_oo.0
        r = r m.sort_oo.sx
        end
    return r
endProcedure sortWords

sortWordsQ: procedure expose m.
parse arg wrds, cmp
    call sortComparator cmp, 'word(le, lx)', 'word(ri, rx)'
    return strip(sortWord1(wrds))
endProcedure sortWordsQ

sortWord1: procedure expose m.
parse arg wrds
    if words(wrds) <= 1 then
        return wrds
    h = words(wrds) % 2
    le = sortWord1(subWord(wrds, 1, h))
    ri = sortWord1(subWord(wrds, h+1))
    lx = 1
    rx = 1
    res = ''
    do forever
        interpret m.sort_comparator
        if cmp then do
            res = res word(le, lx)
            if lx >= words(le) then
                return res subword(ri, rx)
            lx = lx + 1
            end
        else do
            res = res word(ri, rx)
            if rx >= words(ri) then
                return res subword(le, lx)
            rx = rx + 1
            end
        end
endProcedure sortWord1

sort: procedure expose m.
parse arg i, o, cmp
    call sortComparator cmp, 'm.l.l0', 'm.r.r0'
    call sort1 i, 1, m.i.0, o, 1, sort_work, 1
    m.o.0 = m.i.0
    return
endProcedure sort

sort1: procedure expose m.
parse arg i, i0, le, o, o0, w, w0
    if le <= 3 then do
        if le = 3 then do
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, w, w0
            call sortMerge i, i0+2, i0+3, w, w0, w0+2, o, o0
            end
        else if le = 2 then
            call sortMerge i, i0, i0+1, i, i0+1, i0+2, o, o0
        else if le = 1 then
            m.o.o0 = m.i.i0
        return
        end
    h = (le + 1) % 2
    call sort1 i, i0,   h,    o, o0+le-h, w, w0
    call sort1 i, i0+h, le-h, w, w0,      o, o0
    call sortMerge o, o0+le-h, o0+le, w, w0, w0+le-h, o, o0
    return
endProcedure sort1

sortMerge: procedure expose m.
parse arg l, l0, le, r, r0, re, o, o0
    do while l0 < le & r0 < re
        interpret m.sort_comparator
        if cmp then do
            m.o.o0 = m.l.l0
            l0 = l0 + 1
            end
        else do
            m.o.o0 = m.r.r0
            r0 = r0 + 1
            end
        o0 = o0 + 1
        end
    do while l0 < le
        m.o.o0 = m.l.l0
        l0 = l0 + 1
        o0 = o0 + 1
        end
    do while r0 < re
        m.o.o0 = m.r.r0
        r0 = r0 + 1
        o0 = o0 + 1
        end
    return
endProcedure sortMerge
/* copy sort end   ****************************************************/
/* copy match begin ***************************************************/
/*--- 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
    if symbol('m.match_m.mask') == 'VAR' then
        interpret m.match_m.mask
    else
        interpret matchGen('MATCH_M.'mask, mask, 'm')
endProcedure match

matchGG: procedure expose m.
parse arg wert, cd, vars
    interpret cd
endProcedure matchGG

matchVars: procedure expose m.
parse arg wert, mask, vars
    if symbol('m.match_v.mask') == 'VAR' then
        interpret m.match_v.mask
    else
        interpret matchGen('MATCH_V.'mask, mask, 'v')
endProcedure match

matchRep: procedure expose m.
parse arg wert, mask, mOut
    vars = 'MATCH_VV'
    mm = mask'\>'mOut
    if symbol('m.match_r.mm') == 'VAR' then
        interpret m.match_r.mm
    else
        interpret matchGen('MATCH_R.'mm, mask, 'r', mOut)
endProcedure matchRep

matchGen: procedure expose m.
parse arg m, mask, opt, mOut
    a = matchScan(match_sM, mask)
    if symbol('m.match_g') \== 'VAR' then
        m.match_g = 0
    if opt \== 'r' then do
        r = matchgenMat(a, opt, 1, m.a.0, 0)
        end
    else do
        m.match_g = m.match_g + 1
        sub = 'MATCH_G'm.match_g
        m.sub = matchgenMat(a, 'v', 1, m.a.0, 0)
        o = matchScan(match_sO, mOut)
        r = matchGenRep(o, m.a.wildC)
        r = 'if matchGG(wert, m.'sub', vars) then return' r';' ,
            'else return "";'
        end
    m.m = r
    return r
endProcedure matchGen

matchScan: procedure expose m.
parse arg a, mask, opt
    s = match_scan
    call scanSrc s, mask
    ax = 0
    vx = 0
    m.a.wildC = ''
    do forever
        if scanUntil(s, '*?&\') then do
            if m.a.ax == 'c' then do
                m.a.ax.val = m.a.ax.val || m.s.tok
                end
            else do
                ax = ax + 1
                m.a.ax = 'c'
                m.a.ax.val = m.s.tok
                end
            end
        else if scanChar(s, 1) then do
            if pos(m.s.tok, '*?') > 0 then do
                ax = ax + 1
                vx = vx + 1
                m.a.ax = m.s.tok
                m.a.ax.ref = vx
                m.a.wildC = m.a.wildC || m.s.tok
                end
            else if m.s.tok == '\' then do
                call scanChar s, 1
                if pos(m.s.tok, '\*?&') < 1 then
                    return scanErr(s, 'bad char after \')
                if abbrev(m.a.ax, 'c') then
                    m.a.ax.val = m.a.ax.val || m.s.tok
                else do
                    ax = ax + 1
                    m.a.ax = 'c'
                    m.a.ax.val = m.s.tok
                    end
                end
            else if m.s.tok == '&' then do
                if opt \== 'r' then
                    call scanErr s, '& in input'
                if \ scanChar(s,1) | pos(m.s.tok, 'ms123456789')<1 then
                    call scanErr s, 'bad & name' m.s.tok
                ax = ax + 1
                m.a.ax = '&'
                m.a.ax.ref = m.s.tok
                end
            else
                call scanErr s, 'bad char 1 after until'
            end
        else
            leave
        end
    m.a.0 = ax
    if vx \== length(m.a.wildC) then
        call scanErr 'vars' m.a.wildC 'mismatches' vx
    return a
endProcedure matchScan

matchGenMat: procedure expose m.
parse arg a, opt, fx, tx, minLL
    ml = 0
    if fx == 1 then do
        do ax=1 to m.a.0
            if m.a.ax == '?' then
               ml = ml + 1
            else if m.a.ax == 'c' then
               ml = ml + length(m.a.ax.val)
            m.a.minLen.ax = ml
            end
        end
    r = ''
    ret1 = ''
    ret1After = ''
    lO = 0
    do fy=fx to tx
        if m.a.fy == 'c' then do
            r = r 'if substr(wert,' (1+lO)
            if fy < m.a.0 then
                r = r',' length(m.a.fy.val)
            r = r') \==' quote(m.a.fy.val, "'") 'then return 0;'
            lO = lO + length(m.a.fy.val)
            end
        else if m.a.fy == '?' then do
            lO = lO + 1
            if opt == 'v' then
                ret1 = ret1 'm.vars.'m.a.fy.ref ,
                        '= substr(wert,' lO', 1);'
            end
        else if m.a.fy == '*' then
            leave
        else
            call err 'bad match ast' a'.'fy m.a.fy
        end
    rO = 0
    do ty=tx by -1 to fy
        if m.a.ty == 'c' then do
            rO = rO + length(m.a.ty.val)
            r = r 'if substr(wert, length(wert) -' (rO - 1)',' ,
                  length(m.a.ty.val)')' ,
                  '\==' quote(m.a.ty.val, "'") 'then return 0;'
            end
        else if m.a.ty == '?' then do
            rO = rO + 1
            if opt == 'v' then
                ret1 = ret1 'm.vars.'m.a.fy.ref ,
                        '= substr(wert, length(wert) -' (rO-1)', 1);'
            end
        else if m.a.ty ==  '*' then
            leave
        else
            call err 'bad match ast' a'.'fy m.a.fy
        end
    if fy > ty then do /* every thing is handled with fix len */
        if fx = tx & abbrev(m.a.fx, 'c') then
            r = 'if wert \==' quote(m.a.fx.val, "'") ,
                               'then return 0;'
        else
            r = 'if length(wert) \==' (lO + rO) 'then return 0;' r
        end
    else do
        myMiLe = m.a.minLen.ty
        if fy > 1 then do
            fq = fy -1
            myMiLe = myMiLe - m.a.minLen.fq
            end
        if minLL < myMiLe then
            r = 'if length(wert) <' myMiLe 'then return 0;' r
        if fy = ty & m.a.fy == '*' then     /* single * */
            ret1  = ret1 'm.vars.'m.a.fy.ref ,
                 '= substr(wert,' (1+lO)', length(wert) -' (lO+rO)');'
        else if fy < ty & abbrev(m.a.fy, '*') ,
                        & abbrev(m.a.ty, '*') then do
                                /* several variable length parts */
            suMiLe = m.a.minLen.ty - m.a.minLen.fy
            m.match_g = m.match_g + 1
            sub = 'MATCH_G'm.match_g
            m.sub = matchGenMat(a, opt, fy+1, ty, suMiLe)
            if rO = 0 then
                subV = 'substr(wert, lx)'
            else do
                r = r 'wSub = left(wert, length(wert) -' rO');'
                subV = 'substr(wSub, lx)'
                end
            r = r 'do lx = length(wert) -' (suMiLe+rO-1) ,
                       'by -1 to' (lO+1)';' ,
                       'if \ matchGG('subV', m.'sub', vars) then' ,
                            'iterate;'
            ret1  = ret1 'm.vars.'m.a.fy.ref '= substr(wert,' (lO+1) ,
                     ||  ', lx -' (lO+1)');'
            ret1After = 'end; return 0;'
            end
        else
            call err 'matchGenMat bad case' a'.'fy m.a.fy a'.'ty m.a.ty
        end
    if opt == 'v' & fx == 1 then do
        if r <> '' then
           r = 'm.vars.0 = -9;' r
        ret1 = ret1 'm.vars.0 =' length(m.a.wildC)';'
        end
    r = r ret1 'return 1;' ret1After
    return r
endProcedure matchGenMat

matchGenRep: procedure expose m.
parse arg o, wildC
    xQ = 0
    xS = 0
    do ox=1 to m.o.0
        if m.o.ox == '?' then do
             xQ = pos('?', wildC, xQ+1)
             if xQ < 1 then
                 call err 'unmatchted ?' ox
             m.o.ox.re2 = xQ
             end
        else if m.o.ox == '*' then do
             xS = pos('*', wildC, xS+1)
             if xS < 1 then
                 call err 'unmatchted *' ox
             m.o.ox.re2 = xS
             end
        else if m.o.ox == '&' & m.o.ox.ref >> '0' then do
            if m.o.ox.ref > length(wildC) then
                 call err '&'m.o.ox.ref 'but wildcards' wildC
            xQ = m.o.ox.ref
            xS = xQ
            m.o.ox.re2 = xQ
            end
        end
    r = ''
    do ox=1 to m.o.0
        if abbrev(m.o.ox, 'c') then
            r = r '||' quote(m.o.ox.val, "'")
        else if m.o.ox == '&' & m.o.ox.re2 == 's' then
            r = r '|| wert'
        else if m.o.ox == '&' & m.o.ox.re2 == 'm' then
            r = r '||' quote(mask, "'")
        else if pos(m.o.ox, '*?&') > 0 then
            r = r '|| m.vars.'m.o.ox.re2
        end
    if r=='' then
        return "''"
    else
        return substr(r, 5)
endProcedure matchGenRep

/* copy match end *****************************************************/
/* copy comp begin *****************************************************
    the shell compiler
         syntax and semantics of the shell language see javaDoc
***********************************************************************/
/***** initialisation *************************************************/
/*--- module initialisation ------------------------------------------*/
compIni: procedure expose m.
    if m.compIni = 1 then
        return
    m.compIni = 1
    call pipeIni
    call scanReadIni
    cc = classNew('n Compiler u')
    call mNewArea 'COMP.AST', '='

    m.comp_chOp   = '.-<@|?%^'
    m.comp_chKind = '.-=#@:%^'
    m.comp_chKiNO = '=:#'
    m.comp_chKiNBOE = '=<#:' /* nonBLock only expression not Primary*/
    call mPut 'COMP_INFO..', "object"
    call mPut 'COMP_INFO.-', "string"
    call mPut 'COMP_INFO.=', "skeleton"
    call mPut 'COMP_INFO.#', "text"
    call mPut 'COMP_INFO.@', "rexxShell"
    call mPut 'COMP_INFO.:', "pureShell"

    m.comp_chDol = '$'
    m.comp_chSpa = ' 'x2c('09')
    call mPut 'COMP_EXTYPE.b', m.comp_chDol'{}'       /* braces */
    call mPut 'COMP_EXTYPE.d', m.comp_chDol            /* data */
    call mPut 'COMP_EXTYPE.s', m.comp_chDol            /* strip */
    call mPut 'COMP_EXTYPE.w', m.comp_chDol||m.comp_chSpa /* word */

    m.comp.idChars = m.ut_alfNum'@_'
    m.comp.wCatC = 'compile'
    m.comp.wCatS = 'do withNew with for forWith ct proc arg table'
    return
endProcedure compIni

/*--- constructor of Compiler ----------------------------------------*/
comp: procedure expose m.
parse arg src
    nn = oNew('Compiler')
    m.nn.cmpRdr = in2File(src)
    return nn
endProcedure comp

/*--- compile and run ------------------------------------------------*/
compRun: procedure expose m.
parse arg spec, inO, ouO, infoA
    cmp = comp(inO)
    r = compile(cmp, spec)
    if infoA \== '' then
        m.infoA = 'run'
    if ouO \== '' then
        call pipe '+F', ouO
    call oRun r
    if ouO \== '' then
        call pipe '-'
    return 0
endProcedure compRun

/*--- compile the source and return an oRunner with the code ---------*/
compile: procedure expose m.
parse arg m, spec
    ki = '@'
    spec = strip(spec, 'l')
    if spec \== '' then
        if pos(left(spec, 1), m.comp_chKind'*') > 0 then do
            ki = left(spec, 1)
            spec = substr(spec, 2)
            end
    call compBegin m, ki, spec
    s = m.m.scan
    res = compileWsh(m)
    if 0 then
        call compAstSay res, 0
    if \ scanEnd(s) & m.m.out == '' then
        return scanErr(s, m.comp_info.ki "expected: compile",
             ki "stopped before end of input")
    call compEnd m
    if res == '' then
        return ''
    return oRunner(compAst2Rx(m, ';', res))
endProcedure compile

compBegin: procedure expose m.
parse arg m, ki, spec
    m.m.scan = m'.scan'
    m.m.out = ''
    m.m.end = ''
    m.m.defKind = ki
    s = m.m.scan
    if m.m.cmpRdr == '' then
        call scanOpt scanSrc(s, spec), , '0123456789'
    else
        call scanReadOpen scanReadReset(scanOpt(s, , '0123456789'),
                          , m.m.cmpRdr), spec' '
    return m
endProcedure compBegin

compEnd: procedure expose m.
parse arg m
    if m.m.cmpRdr \== '' then
        call scanReadClose m.m.scan
    return m
endProcedure compEnd

/*--- compile wsh until eof or unknown syntax ------------------------*/
compileWsh: procedure expose m.
parse arg m
    s = m.m.scan
    res = compAst(m, '¢')
    eOld = m.err.count
    do while m.m.out == '' & \ scanEnd(s)
        one = ''
        if \ scanLit(s, '$#') then do
            oldPos = scanPos(s)
            one = compileOne(m, m.m.defKind)
            if one == '' | m.one.0 = 0 then
                if oldPos == scanPos(s) then
                    leave
            end
        else if pos(scanLook(s, 1), m.comp_chKind'*') > 0 then do
            call scanChar s, 1
            m.m.defKind = m.s.tok
            one = compileOne(m, m.m.defKind)
            end
        else if \ scanName(s) then do
            call scanErr s, 'kind or hook expected after $#'
            end
        else if m.s.tok == 'out' then do
            m.m.out = scanPos(s)
            leave
            end
        else if m.s.tok == 'end' then do
            if m.m.end = '' then
                m.m.end = scanPos(s)
            one = compileOne(m)
            end
        else if m.s.tok == 'version' then do
            call scanSpace s
            vers = 'v41 v42'
            if \ scanWord(s) | wordPos(m.s.tok, vers) < 1 then
                call scanErr s, 'only versions' vers 'are supported'
            call scanNl s, 1
            end
        else do
            say 'interpreting hook' m.s.tok':' strip(scanLook(s))
            interpret 'one = wshHook_'m.s.tok'(m)'
            end
        if m.err.count <> eOld then
            return ''
        if one \== '' then
            call mAdd res, one
        end
    return res
endProcedure compileWsh
/*--- compile or use hook for one part from spec or input -----------*/
compileOne: procedure expose m.
parse arg m, ki, hook
    s = m.m.scan
    m.m.comp_assVars = 0
    call compSpComment m
    if ki == '*' | m.m.end \== '' then do
        do until scanLook(s, 2) == '$#' | scanEnd(s)
            call scanNl s, 1
            end
        return ''
        end
    return compUnit(m, ki, '$#')
endProcedure compileOne

/*--- parse the whole syntax of a unit ------------------------------*/
compUnit: procedure expose m.
parse arg m, ki, stopper
    s = m.m.scan
    if pos(ki, m.comp_chKind) < 1 then
        return scanErr(s, 'bad kind' ki 'in compUnit(...'stopper')')
    else if ki <> '#' then do
        a = compAst(m, '¢')
        do forever
            one = compPipe(m, ki)
            if one \== '' then
                call mAdd a, one
            if \ scanLit(m.m.scan, '$;', '$<>') then
                return a
            end
        end
    else do
        res = compAST(m, '¢')
        call scanChar s
        if verify(m.s.tok, m.comp_chSpa) > 0 then
            call mAdd res, compAstAddOp(m,
                , compAst(m, '=', strip(m.s.tok, 't')), '$')
        do while scanNL(s, 1) & \ abbrev(m.s.src, stopper)
            call mAdd res, compAstAddOp(m,
                , compAst(m, '=', strip(m.s.src, 't')), '$')
            end
        return res
        end
endProcedure compUnit

/*--- compile an expression, type d=data, b=block w=word, s=strip ---*/
compExpr: procedure expose m.
parse arg m, type, ki, textEnd
    s = m.m.scan
    if symbol('m.comp_exType.type') \== 'VAR' then
        call err s, 'bad type' type 'in compExpr'
    if ki == '#' then do
        if textEnd == '' then
            call scanChar(s)
        else if textEnd <= m.s.pos then
            return ''
        else
            call scanChar s, textEnd - m.s.pos
        if type == 's' then
            res = compAst(m, '=', strip(m.s.tok))
        else
            res = compAst(m, '=', , m.s.tok)
        res = compAST(m, '-', , res)
        m.res.containsC = 1
        m.res.containsD = 1
        return res
        end
    else if ki == '%' | ki == '^' then do
        call compSpComment m
        vr = compVar(m, left('c', ki == '^'))
        if vr == '' then
            return ''
        if m.vr.var == 'c' then
            res = compAst(m, 'M')
        else
            res = compAst(m, ki, , compASTAddOp(m, vr, '&'))
        call compSpComment m
        if textEnd == '' | textEnd < m.s.pos then do
            ex = compOpBE(m, '=', 1, , textEnd)
            if ex \== '' then do
                call mAdd res, ex
                call compSpComment m
                end
            end
        m.res.containsC = 1
        m.res.containsD = 1
        return res
        end

    if length(ki) \== 1 | pos(ki, '.-=@') < 1 then
        return scanErr(s, 'bad kind' ki 'in compExpr')
    res = compAST(m, translate(ki, '-', '='))
    m.res.containsC = 0
    astKi = translate(ki, 'os=c', '.-=@')
    laPrim = 0
    gotTxt = 0
    if pos(type, 'sb') > 0 then
        m.res.containsC = compSpComment(m) >= 2
    do forever
        if textEnd \== '' then
            if m.s.pos >= textEnd then
                leave
        if scanVerify(s, m.comp_exType.type, 'm') then do
            if textEnd \== '' then
                if m.s.pos > textEnd then do
                    m.s.tok = left(m.s.tok, length(m.s.tok) ,
                                    + textEnd - m.s.pos)
                    m.s.pos = textEnd
                    end
            one = compAST(m, astKi, m.s.tok)
            if verify(m.s.tok, m.comp_chSpa) > 0 then
                gotTxt = 1
            end
        else do
            old = scanPos(s)
            if \ scanLit(s, m.comp_chDol) then
                leave

            if pos(scanLook(s, 1), '.-') > 0 then
                one = compCheckNN(m, compOpBE(m, , 1, 0),
                   , 'primary block or expression expected')
            else
                one = compPrimary(m)
            if one = '' then do
                call scanBackPos s, old
                leave
                end
            laPrim = m.res.0 + 1
            end
        call mAdd res, one
        if compComment(m) then
            m.res.containsC = 1
        end
    if pos(type, 'bs') > 0 then do
        do rx=m.res.0 by -1 to laPrim+1
            one = m.res.rx
            m.one.text = strip(m.one.text, 't')
            if length(m.one.text) <> 0 then
                leave
            call mFree one
            end
        m.res.0 = rx
        end
    m.res.containsD = laPrim > 0 | gotTxt
    return compAstFree0(res, '')
endProcedure compExpr

/*--- compile a primary and return code ------------------------------*/
compPrimary: procedure expose m.
parse arg m, ops
    s = m.m.scan
    if scanString(s) then
        return compASTAddOp(m, compAST(m, '=', m.s.val), ops)
    r = compVar(m, left('c', right(ops, 1) == '^'))
    if r == '' then
        return ''
    if m.r.var \== 'c' then
         return compASTAddOp(m, compAst(m, '&', m.r.var, r), ops)
    else
         return compASTAddOp(m, compAst(m, 'M'),
                              , left(ops, length(ops)-1))
endProcedure compPrimary

/*--- oPBE ops (primary or block or expression)
       oDef = default Kind, oPre = opPrefix,
       uniq=1 extract unique, uniq='<' prefix <
       withEx <> 0: expression allowed ------------------------------*/
compOpBE: procedure expose m.
parse arg m, oDef, uniq, withEx, textEnd
    s = m.m.scan
    old = scanPos(s)
    op = compOpKind(m, oDef)
    if uniq == '<' & left(op, 1) \== '<' then
        op = left('<', uniq == '<') || op
    if pos(scanLook(s, 1), '/¢') > 0 then do
        if uniq == 1 & length(op) == 1 then
            if op == '.' then
                op = '|.'
            else if op == '=' then
                op = '-='
            else if pos(op, '-@<') > 0 then
                op = op || op
        return compBlock(m, op)
        end
    if compSpComment(m) == 0 ,
        & pos(right(op, 1), m.comp_chKiNBOE) <= 0 then
        return compPrimary(m, op)
    if withEx \== 0 then do
        res = compExpr(m, 's', right(op, 1), textEnd)
        if pos(right(op, 1), m.comp_chKind) < 1 then
            op = left(op, kx-1)
        if res \== '' then
            return compASTAddOp(m, res, op)
        end
    call scanBackPos s, old
    return ''
endProcedure compOPBE

/*--- compile var of ^or % clause ------------------------------------*/
compCallVar: procedure expose m.
parse arg m, ki
    call compSpComment m
    vr = compVar(m, left('c', ki == '^'))
    if vr == '' then
        call scanErr m.m.scan, 'var expected after' ki
    call compSpComment m
    if m.vr.var == 'c' then
        return compAst(m, 'M')
    else
        return compAst(m, ki, , compASTAddOp(m, vr, '&'))
endProcedure compCallVar

/*--- compile a pipe and return code ---------------------------------*/
compPipe: procedure expose m.
parse arg m, ki
    s = m.m.scan
    res = compAST(m, 'P', ' ', '', '')
    do forever
        one = compExprStmts(m, ki)
        if one \== '' then do
            if m.res.0 > 2 then
                call scanErr s, '$| before statements needed'
            call mAdd res, one
            end
        pre = left(m.comp_chDol, scanLit(s, m.comp_chDol))
        if scanLook(s, 2) == '<>' then
            leave
        if scanLit(s, '<') then do
            if m.res.2 == '' then
                m.res.2 = compAst(m, '.')
            else
                call mAdd m.res.2, compAst(m, 'o', ', ')
            call mAdd m.res.2, compOpBE(m, '<', '<')
            m.res.text = m.res.text'f'
            end
        else if scanLit(s, '>>', '>') then do
            if m.res.1 <> '' then
                call scanErr s, 'duplicate output'
            m.res.text = if(m.s.tok == '>', 'F', 'A') ,
                ||substr(m.res.text, 2)
            m.res.1 = compOpBE(m, '<', '<')
            end
        else if scanLit(s, '|') then do
            if m.res.0 < 3 then
                call scanErr s, 'stmts expected before |'
            call compSpNlComment m
            call mAdd res, compCheckNE(m, compExprStmts(m, ki),
                , 'stmts or expressions after | expected')
            end
        else
            leave
        end
    call scanBack s, pre
    if m.res.0 > 3 | m.res.1 \== '' | m.res.2 \== '' then
        return res
    one = if(m.res.0 = 3, m.res.3)
    call mFree res
    return one
endProcedure compPipe

/*--- compile expressions and stmts ---------------------------------*/
compExprStmts: procedure expose m.
parse arg m, ki
    s = m.m.scan
    res = compAst(m, '¢')
    withNew = ''
    nlLe = 0 /* sophisticated logic using left and right NLs*/
    tb = ''
    do forever
        if tb \== '' then do
            fx=0
            fy = m.tb.0
            fL = m.tb.fy
            aa = ''
            do forever
                call compSpComment m
                px = m.s.pos
                do until px < m.ff.end | fx >= m.tb.0
                    fx = fx + 1
                    ff = m.tb.fx
                    end
                if fx > m.tb.0 then do
                    if compExpr(m, 's', m.fL.colKind) == '' then
                        leave
                    call err 'fallout table'
                    end
                e1 = compExpr(m, 's', m.ff.colKind, m.ff.end)
                if e1 == '' then
                    leave
                else if fx > m.tb.0 then
                    call err 'fallout table'
                if m.ff.colOps \== '' then
                    e1 = compAstAddOp(m, e1, m.ff.colOps)
                if aa == '' then
                    aa = compAst(m, '¢')
                call mAdd aa, compAst(m, 'A', ,
                    , compAst(m, '=', m.ff.name), e1)
                end
            if aa \== '' then
                call mAdd res, compAst(m, 'F', 'with',
                    , compAst(m, 'o', "oNew('"m.tb.class"')"),
                    , aa, compAst(m, '*', '$.'))
   /*       px = m.s.pos
            e1 = compExpr(m, 'w', '=')
            if e1 \== '' then do
                aa = compAst(m, '¢')
                fx = 0
                do until e1 == ''
                    do fx=fx+1 to m.tb.0 until px < m.ff.end
                        ff = m.tb.fx
                        end
                    if fx > m.tb.0 then
                        call scanErr s, 'right of all table fields'
                    if m.s.pos <= m.ff.pos then
                        call scanErr s, 'before table field' m.ff.name
                    call mAdd aa, compAst(m, 'A', ,
                        , compAst(m, '=', m.ff.name), e1)
                    call compSpComment m
                    px = m.s.pos
                    e1 = compExpr(m, 'w', '=')
                    end
                call mAdd res, compAst(m, 'F', 'with',
                    , compAst(m, 'o', "oNew('"m.tb.class"')"),
                    , aa, compAst(m, '*', '$.'))
                end
     */     nlRi = scanNL(s)
            end
        else if ki == ':' then do
            call compSpNlComment m, '*'
            nlRi = 0
            end
        else if ki == '@' then do
            call compSpNlComment m
            one = compExpr(m, 's', ki)
            if one == '' then
                nlRi = 0
            else if m.one.0 < 1 then
                call scanErr s, 'assert not empty' m.one.0
            else do
                do forever /* scan all continued rexx lines */
                    nlRi = 1
                    la = m.one.0
                    la = m.one.la
                    if m.la.kind \== 'c' then
                        leave
                    m.la.text = strip(m.la.text, 't')
                    if right(m.la.text, 1) \== ',' then
                        leave
                    m.la.text = strip(left(m.la.text,
                            , length(m.la.text)-1), 't')' '
                    call compSpNlComment m
                    cont = compExpr(m, 's', '@')
                    if cont == '' | m.cont.kind \== m.one.kind then
                        call scanErr s, 'bad rexx continuation'
                    call mAddSt one, cont
                    call mFree cont
                    end
                call mAdd res, one
                end
            end
        else if ki == '%' | ki == '^' then do
            do cc=0 while compSpNlComment(m)
                end
            one = compExpr(m, 's', ki)
            nlRi = one \== ''
            if nlRi then do
                if ki == '^' then
                    one = compAstAddOp(m, one, '$')
                call mAdd res, one
                end
            end
        else do
            do cc=0 while compComment(m)
                end
            one = compExpr(m, 'd', ki)
            nlRi = scanNL(s)
            if one == '' then do
                if nlLe & nlRi & cc < 1 then
                    call mAdd res,compAstAddOp(m,compAst(m,'='), '$')
                end
            else if m.one.containsD | (nlLe & nlRi,
                      & \ (cc > 0 | m.one.containsC)) then do
                call mAdd res, compAstAddOp(m, one, '$')
                end
            else do
                call mFree one
                end
            end
        nlLe = nlRi
        if \ nlRi then do
            one = compStmt(m, ki)
            if one \== '' then do
                call mAdd res, one
                end
            else if scanLit(s, 'table', '$table') then do
                tb = compTable(m, ki)
                end
            else do
                if withNew \== '' then do
                    r = compAst(m, 'F', 'withNew', '', res,
                                      , compAst(m, '*', '$.'))
                    m.r.class = classNew('n* CompTable u' ,
                               substr(m.m.comp_assVars, 3))
                    m.r.1 = compAst(m, 'o', "oNew('"m.r.class"')")
                    res = withNew
                    call mAdd res, r
                    m.m.comp_assVars = assVars
                    end
                if scanLit(s, 'withNew', '$withNew') then do
                    withNew = res
                    assVars = m.m.comp_assVars
                    m.m.comp_assVars = ''
                    res = compAst(m, '¢')
                    end
                else
                    return compAstFree0(res)
                end
            end
        end
endProcedure compExprStmts

/*--- compile a single statement -------------------------------------*/
compStmt: procedure expose m.
parse arg m, ki
    s = m.m.scan
    res = compAss(m)
    if res \== '' then
        return res
    pre = ''
    old = scanPos(s)
    if scanLit(s,m.comp_chDol'$',m.comp_chDol'@',m.comp_chDol,'@') then
        pre = m.s.tok
    if pre == m.comp_chDol'$' then
        return  compAstAddOp(m, compCheckNN(m, compOpBE(m,'=', 1),
            , 'block or expression expected after $$'), '$')
    wCat = compName(m, 'sv')
    fu = m.s.tok

    if right(pre, 1) == '@' & wCat \== 's' then do
        if wCat == 'v' then
            return compAstAddOp(m, compAst(m, '=', fu), '%.&')
        if compSpNlComment(m) then
                return compAstAddOp(m,
                , compCheckNE(m, compExpr(m, 's','@'),
                , 'block/primary/expr after $@'), ';')

        one = compCheckNN(m, compOpBE(m, '@') ,
                 ,  'primary, block or expression')
        if one \== '' then
            return one
        call scanBackPos s, old
        return ''
        end

    if fu == 'arg' then do
        res = compAst(m, 'R')
        do forever
            call compSpComment m
            if scanLit(s, ',') then
                a1 = compAst(m, 'c', ',')
            else do
                gotV = 1
                a1 = compVar(m, 'v')
                end
            if a1 \== '' then
                call mAdd res, a1
            else if gotV == 1 then
                return res
            else
                call scanErr s, 'empty arg'
            end
        end

    if fu == 'ct' then do
        call compSpComment m
        return compAst(m, 'C', , compCheckNN(m, compStmt(m, ki),
            , 'ct statement'));
        end

    if fu == 'do' then do
        call compSpComment m
        pre = compExpr(m, 's', '@')
        res = compAst(m, 'D', , pre)
        p1 = m.pre.1
        if pre \== '' then do
            txt = ''
            do px=1 to m.pre.0
                pC = m.pre.px
                if m.pC.kind \== 'c' then
                    leave
                txt = txt m.pC.text
                cx = pos('=', txt)
                if cx > 0 then do
                    m.res.text = strip(left(txt, cx-1))
                    leave
                    end
                end
            end
        call compSpComment m
        call mAdd res, compCheckNN(m, compStmt(m, ki), 'stmt after do')
        return res
        end

    if wordPos(fu, 'for forWith with') > 0 then do
        res = compAst(m, 'F', fu)
        call compSpComment m
        if fu \== 'with' then do
            b = compVar(m)
            end
        else do
            b = compAss(m) /* ???????????, 'o') */
            if b == '' then
                b = compCheckNE(m, compExpr(m, 's', '.'),
                , "assignment or expression after with")
            end
        call compSpComment m
        st = compCheckNN(m, compStmt(m, ki), "var? statement after" fu)
        if b = '' then do
            b = compBlockName(m, st)
            if b \== '' then
                b = compAst(m, '=', b)
            else if \ abbrev(fu, 'for') then
                call scanErr s, "variable or named block after" fu
            end
        call mAdd res, b, st
        if abbrev(fu, 'with') then do   /* ???????????????
            r1 = m.res.1
            if m.r1.kind == 'A' then do
                a1 = m.r1.1
                if m.a1.var == 'o' then do
                    call mAdd res, compAstAddOp(m, m.r1.2, '$.')

                    m.res.1 = m.r1.2
                    call mFree a1
                    end
                end  ????????????????*/
            end
        return res
        end

    if fu == 'proc' then do
           call compSpComment m
        nm = ''
        if compName(m, 'v') == 'v' then do
            nm = m.s.tok
            call compSpComment m
            end
        st = compCheckNN(m, compStmt(m, ki), 'proc statement')
        if nm == '' then do
            nm = compBlockName(m, st)
            if nm == '' then
                call scanErr s, 'var or namedBlock expected after proc'
            end
        return compAst(m, 'B', '', compAst(m, '=', nm), st)
        end
    call scanBack s, pre || fu
    return ''
endProcedure compStmt

compBlockName: procedure expose m.
parse arg m, a
    a1 = m.a.1
    if m.a.kind == '¢' then
         return m.a.text
    else if m.a.kind == '*' & m.a1.kind == '¢' then
        return m.a1.text
    return ''
endProcedure compBlockName

compVar: procedure expose m.
parse arg m, vk
    if pos('o', vk) > 0 then call err(sdf)/0
    s = m.m.scan
    ty = compName(m, 'v' || vk)
    if ty \== '' then do
        r = compAst(m, '=', m.s.tok)
        m.r.var = ty
        return r
        end
    if \ scanLit(s, '{') then
        return ''
    call scanLit s, '?', '>'
    f = m.s.tok
    r = compCheckNE(m, compExpr(m, 'b', '='), 'var name')
    if \scanLit(s, '}') then
        call scanErr s, 'closing } missing after {'
    m.r.var = f
    return r
endProcedure compVar

compAss: procedure expose m.
parse arg m, vk
    s = m.m.scan
    old = scanPos(s)
    call scanLit s, m.comp_chDol'=', '='
    pr = m.s.tok
    if pr \== '' then
        call compSpComment m
    v = compVar(m, vk)
    if v \== '' then do
        call compSpComment m
        if \ scanLit(s, '=') then do
            call scanBackPos s, old
            return ''
            call scanErr s, '= expected in assignment after' pr 'var'
            ??????????????
            end
        end
    else if pr == '' then
        return ''
    else
        oldInfo = scanInfo(s)
    eb = compCheckNE(m, compOpBE(m, '=', 1),
        , 'block or expression in assignment after' pr)
    if m.eb.kind == '¢' then
        eb = compAstAddOp(m, eb, '-')
    if v == '' then do
        v = compBlockName(m, eb)
        if v == '' then
            call scanEr3 s, 'var or namedBlock expected',
                    'in assignment after' pr, oldInfo
        v = compAst(m, '=', v)
        m.v.var = 'v'
        end
    if m.m.comp_assVars \== 0 then
      if m.v.kind == '=' & m.v.var == 'v' then do
        if words(m.v.text) \= 1 then
            call compAstErr v, 'bad var'
        if m.eb.kind == '*' then
            ki = left(m.eb.text, 1)
        else
            ki = m.eb.kind
        if pos(ki, '-=s') > 0 then
            f = ', f' m.v.text 'v'
        else if pos(ki, '.<@o') > 0 then
            f = ', f' m.v.text 'r'
        else
            call compAstErr eb, 'string or object????'
        if pos(f, m.m.comp_assVars) < 1 then
            m.m.comp_assVars = m.m.comp_assVars || f
        end
    return compAst(m, 'A', , v, eb)
endProcedure compAss

/*--- block deals with the correct kind and operators
      the content is parsed by compUnit ------------------------------*/
compBlock: procedure expose m.
parse arg m, ops
    s = m.m.scan
    if \ scanLit(s, '¢', '/') then
        return ''
    start = m.s.tok
    if ops == '' | pos(right(ops, 1), m.comp_chKind) < 1 then
        return scanErr(s, 'bad kind' ops 'for block')
    ki = right(ops, 1)
    ops = left(ops, length(ops)-1)
    starter = start
    if start == '¢' then
        stopper = m.comp_chDol'!'
    else do
        call scanVerify s, '/', 'm'
        starter = '/'m.s.tok'/'
        stopper = m.comp_chDol || starter
        if \scanLit(s, '/') then
            call scanErr s, 'ending / after stopper' stopper 'expected'
        end
    res = compUnit(m, ki, stopper)
    if \ scanLit(s, stopper, substr(stopper, 2)) then
           call scanErr s, 'ending' stopper 'expected after' starter
    if abbrev(starter, '/') then
        m.res.text = substr(starter, 2, length(starter)-2)
    return compAstAddOp(m, res, ops)
endProcedure compBlock

/*--- compile table body and return table ----------------------------*/
compTable: procedure expose m.
parse arg m, ki
    s = m.m.scan
    call compSpComment m
    if scanNl(s) then
        call compSpComment m
    res = compAst(m, 'T', 'c')
    flds = ''
    pB = 1
    do forever
        opKi = compOpKind(m)
        if compName(m, 'v') \== 'v' then
            if opKi == '' then
                leave
            else
                call scanErr s, 'table col expected after' opKi
        f1 = compAst(m, 'T')
        m.f1.pos = pB
        if opKi == '' then
            opKi = translate(ki, '=', ':')
        m.f1.colKind = right(opKi, 1)
        m.f1.colOps  = left(opKi, length(opKi)-1)
        m.f1.name = m.s.tok
        if pos(left(opKi, 1), '-=#') > 0 then
            flds = flds', f' m.s.tok 'v'
        else
            flds = flds', f' m.s.tok 'r'
        call compSpComment m
        pB = m.s.pos
        m.f1.end = pB
        m.f1.text = 'f blabla' m.f1.name m.f1.pos pB opKi
        call mAdd res, f1
        if scanLit(s, ',') then
            call compSpComment m
        end          /* ?????????????????????????
    do while compName(m, 'v') == 'v'
        f1 = compAst(m, 'T')
        m.f1.end = m.s.pos
        m.f1.pos = m.s.pos - length(m.s.tok)
        m.f1.name = m.s.tok
        m.f1.text = 'f' m.f1.name m.f1.pos m.f1.end
        call mAdd res, f1
        flds = flds', f' m.s.tok 'v'
        call compSpComment m
        end  ???????? */
    if \ scanNl(s) then
        call scanErr s, 'name or nl after table expected'
    if m.res.0 < 1 then
        call scanErr s, 'no names in table'
    m.f1.end = ''
    m.res.class = classNew('n* CompTable u' substr(flds, 3))
    m.res.text = 'c' cl
    return res
endProcedure compTable
/**** lexicals ********************************************************/
/*--- skip a comment. return 0 if there is none ----------------------*/
compComment: procedure expose m.
parse arg m
    s = m.m.scan
    got = 0
    do forever
        if scanLit(s, m.comp_chDol'**') then
            m.s.pos = 1 + length(m.s.src) /* before next nl */
        else if scanLit(s, m.comp_chDol'*+') then
            call scanNL s, 1
        else if scanLit(s, m.comp_chDol'*(') then do
            do forever
                if scanVerify(s, m.comp_chDol, 'm') then iterate
                if scanNL(s) then iterate
                if compComment(m) then iterate
                if \ scanLit(s, m.comp_chDol) then
                    call scanErr s, 'source end in comment'
                if scanLit(s, '*)') then
                    return 1
                if scanLit(s, m.comp_chDol) then iterate
                if scanString(s) then iterate
                end
            end
        else
            return got
        got = 1
        end
endProcedure compComment

/*--- skip spaces and comments ---------------------------------------*/
compSpComment: procedure expose m.
parse arg m, xtra
    s = m.m.scan
    got = 0
    do forever
        if scanVerify(s, m.comp_chSpa) then
            got = bitOr(got, 1)
        else if compComment(m) then
            got = bitOr(got, 2)
        else if xtra == '' then
            return got
        else if \ scanLit(s, xtra) then
            return got
        else do
            got = bitOr(got, 4)
            m.s.pos = 1+length(m.s.src)
            end
        end
endProcedure compSpComment

/*--- skip spaces, NLs and comments ----------------------------------*/
compSpNlComment: procedure expose m.
parse arg m, xtra
    found = 0
    do forever
        if compSpComment(m, xtra) < 1 then
            if \ scanNL(m.m.scan) then
             return found
        found = 1
        end
endProcedure compSpNlComment
/*--- scan a name in one of the categories
        v=var, c=compile, s=stmt ----------------------------------*/
compName: procedure expose m.
parse arg m, cats
    s = m.m.scan
    if \ scanName(s) then
        return ''
    if wordPos(m.s.tok, m.comp.wCatS) > 0 then do
        if pos('s', cats) > 0 then
            return 's'
        end
    else if wordPos(m.s.tok, m.comp.wCatC) > 0 then do
        if pos('c', cats) > 0 then
            return 'c'
        end
    else if pos('v', cats) > 0 then do
        return 'v'
        end
    call scanBack s, m.s.tok
    return ''
endProcedure compName

compOpKind: procedure expose m.
parse arg m, op
    s = m.m.scan
    if scanVerify(s, m.comp_chOp || m.comp_chKiNO) then
        op = m.s.tok
    else if op == '' then
        return ''
    /* ??????? temporary until old syntax vanished ????? */
    x = verify(op, '%^', 'm')
    if x > 0 & x < length(op) then
        call scanErr s, 'old syntax? run not at end'
    if right(op, 1) == '<' then
        op = op'='
    kx = verify(op, m.comp_chKiNO, 'm')
    if kx \== 0 & kx \== length(op) then
        call scanErr s, 'kind' substr(op, kx, 1) 'not after ops'
    if pos(right(op, 1), m.comp_chKind) == 0 then
        call scanErr s, 'no kind after ops' op
    return op
endProcedure compOpKind

compSpNlComment: procedure expose m.
/**** small helper routines ******************************************/
/*--- if va == null then issue an error with msg --------------------*/
compCheckNN: procedure expose m.
parse arg m, va, msg
    if va == '' then
        call scanErr m.m.scan, msg 'expected'
    return va
endProcedure compCheckNN

/*--- return true iff expression is empty ---------------------------*/
compIsEmpty: procedure expose m.
parse arg m, a, block0
    do forever
        if a == '' then
            return 1
        else if m.a.kind == '*' then
            a = m.a.1
        else if m.a.kind \== '¢' then
            return 0
        else if block0 then
            return 0
        else if m.a.0 = 1 then
            a = m.a.1
        else
            return m.a.0 < 1
        end
endProcedure compIsEmpty

/*--- if va == null or empty then issue an error with msg -----------*/
compCheckNE: procedure expose m.
parse arg m, ex, msg
    e1 = left(ex, 1)
    if compIsEmpty(m, ex, 1) then
        call scanErr m.m.scan, msg 'expected'
    return ex
endProcedure compCheckNE

/**** AST = Astract Syntax Tree ****************************************

------- atoms, no children
  =  string constant                 =  string constant
----------- rexx fragments
  c  rexxStatement fragment          x  rexx fragment
  s  string
  o  object
  r  run
  f  file

------- containers
  ¢  block          ==> * c-.        ¢  exprBlock
  @  code = 1 stmt  ==> *            ;  one rexx statement without end ;
  -  string                          -  string
  .  object                          .  object
                                     @  run
                                     <  file
                                     %  callOut
                                     ^  callRet
  *  operand chain  ==> 1            *  operand chain  ==> 1

------- molecules
  &  variable access==> 1 =-
  A  assignment     ==> 2
  B  proc           ==> 2
  C  ct             ==> 1
  D  do             ==> 2
  F  for + with     ==> 2
  P  Pipe           ==> * 1=input 2=output
  R  aRg
  T  Table
  M  compile
  %  RunOut         ==> 1,2 (Run, arguments)
  ^  RunRet         ==> 1,2 (Run, arguments)
  I??  Input        ==> * .

***********************************************************************/
/*--- create a new AST -----------------------------------------------*/
compAST: procedure expose m.
parse arg m, ki, txt
    n = mNew('COMP.AST')
    if length(ki) <> 1 then
        call err 'compAST bad kind' ki / 0
    m.n.kind = ki
    m.n.text = txt
    if pos(ki, '¢@-.*&ABCDFPRTM%^') > 0 then do
        do cx=1 to arg()-3
            m.n.cx = arg(cx+3)
            end
        m.n.0 = cx-1
        if txt \== '' & pos(ki, '&*FPT') < 1 then
            return err('kind' ki 'text='txt'|')/0
        end
    else if pos(ki, '=csorf') > 0  then do
        m.n.0 = 'kind'ki
        end
    else do
        call err "compAst kind '"ki"' not supported"
        1/0
        end
    return n
endProcedure compAST

/*--- free AST if empty ----------------------------------------------*/
compASTFree0: procedure expose m.
parse arg a, ret
    if m.a.0 > 0 then
        return a
    call mFree a
    return ret
endProcedure compAstFree0

/*--- add operandands to an AST -------------------------------------*/
compASTAddOp: procedure expose m.
parse arg m, a, ops
    do while right(ops, 1) == m.a.kind
        ops = left(ops, length(ops)-1)
        end
    if ops == '' then
        return a
    ops = translate(ops, '-', '=')
    if m.a.kind \== '*' then
        return compAst(m, '*', ops, a)
    m.a.text = ops || m.a.text
    return a
endProcedure compAstAddOp

/*--- return the kind of an AST --------------------------------------*/
compASTKind: procedure expose m.
parse arg m, a
    m.m.astOps = ''
    if \ abbrev(a, 'COMP.AST.') then
        return left(a, 1)
    c = a
    do while m.c.kind == 'ops'
        if m.c.text \== '' then
            return left(m.c.text, 1)
        c = m.c.1
        end
    if a == c then
        return '?'
    return compAstKind(m, c)
endProcedure compASTKind
/* ?????????????????????????
compAstAssignedVars: procedure expose m.
parse arg a
    res = ''
    if m.a.kind == 'F' then
        return ''
    if m.a.kind == 'A' then do
        a1 = m.a.1
        if m.a1.kind == '=' & m.a1.var == 'v' then do
            if words(m.a1.text) \= 1 then
                call compAstErr a1 'bad var'
            a2 = m.a.2
            if m.a2.kind == '*' then
                ki = left(m.a2.text, 1)
            else
                ki = m.a2.kind
            if pos(ki, '-=s') > 0 then
                res = ', f' m.a1.text 'v'
            else if pos(ki, '.<@o') > 0 then
                res = ', f' m.a1.text 'r'
            else
                call compAstErr a2, 'string or object????'
            end
        end
    if datatype(m.a.0, 'n') then
        do ax=1 to m.a.0
            res = res || compAstAssignedVars(m.a.ax)
            end
    return res
endProcedure compAstAssignedVars  ?????????????????????????*/

compAstSay: procedure expose m.
parse arg a, lv
    if \ abbrev(a, 'COMP.AST.') then do
        if a \== '' then
            return err('bad ast' a)
        say left('', 19)': * empty ast'
        return
        end
    say lefPad(left('', lv) m.a.kind, 10) ,
        || rigPad(if(dataType(m.a.0, 'n'), m.a.0), 3),
        '@'rigPad(substr(a, 10), 4)':' m.a.text'|'
    if dataType(m.a.0, 'n') then do cx=1 to m.a.0
        call compAstSay m.a.cx, lv+1
        end
    return
endProcedure compAstSay

compAstErr: procedure expose m.
parse arg a, txt
    call errSay txt
    call compAstSay a, 0
    return err(txt)
endProcedure compAstErr

compAstOnlyOut: procedure expose m.
parse arg a, rec
    do ax=1 to m.a.0
        a1 = m.a.ax
        if m.a1.kind == '*' then
            if abbrev(m.a1.text, '$') then
                iterate
        if m.a1.kind = '¢' & rec \== 0 then
            if compAstOnlyOut(a1) then
                iterate
        return 0
        end
    return 1
endProcedure compAstOnlyOut

/*--- return the code for an AST with operand chain trg --------------*/
compCode2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if pos(o1, ';') > 0 then
        return compCode2rx(m, oR, strip(f))
    if pos(o1, '-.<|?@') > 0 then
        return compRun2rx(m, ops, quote(oRunner(f)))

    call err 'compCode2rx bad ops' ops
endProcedure compCode2rx

compCon2rx: procedure expose m.
parse arg m, ops, f, a
    do ox=length(ops) by -1 to 1 while pos(substr(ops,ox,1), '.-')>0
        end
    if substr(ops, ox+1, 1) == '.' then
        f = s2o(f)
    if length(f) < 20 then
        v = quote(f, "'")
    else if a \== '' & m.a.text == f then
        v = 'm.'a'.text'
    else
        v =    'm.'compAst(m, '=', f)'.text'
    if substr(ops, ox+1, 1) == '.' then
        return compObj2rx(m, left(ops, ox), v)
    else
        return compString2rx(m, left(ops, ox), v)
endProcedure compCon2rx

compString2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '-' then
        return compString2rx(m, oR, f)
    if o1 == '.' then
        return compObj2rx(m, oR, 's2o('f')')
    if pos(o1, '$') > 0 tOnlyOut then
        return compCode2rx(m, oR, 'call out' f';')
    if o1 == '&' then do
        o2 = substr('1'ops, length(ops), 1)
        if pos(o2,  '.<^%') < 1 then
            return compString2rx(m, oR, 'vGet('f')')
        else
            return compObj2rx(m, oR, 'vGet('f')')
        end
    if o1 == '<' then
        return compFile2rx(m, oR, 'file('f')')
    call err 'compString2rx bad ops' ops
endProcedure compString2rx

compObj2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '.' then
        return compObj2rx(m, oR, f)
    if o1 == '-' then
        return compString2rx(m, oR, 'o2string('f')')
    if o1 == '$' then
        return compCode2rx(m, oR, 'call out' f';')
    if o1 == '<' then
        return compFile2rx(m, oR, 'o2file('f')')
    if o1 == '@' then
        return compRun2Rx(m, oR, f)
    if pos(o1, ';%^') > 0 then
        return compRun2rx(m, ops, f)
    call err 'compObj2rx bad ops' ops
endProcedure compObj2rx

compRun2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if o1 == '@' then
        return compRun2Rx(m, oR, f)
    if o1 == ';' | o1 == '%' then /*??? immer % verwenden ???*/
        return compCode2Rx(m, oR, 'call oRun' f';')
    if o1 == '^' then
        if pos(right(oR, 1),  '.<^%') < 1 then
            return compString2Rx(m, oR, 'oRun('f')')
        else
            return compObj2Rx(m, oR, 'oRun('f')')
    return compObj2rx(m, ops, f)
endProcedure compRun2rx

compFile2rx: procedure expose m.
parse arg m, ops, f
    if ops == '' then
        return f
    o1 = right(ops, 1)
    oR = left(ops, length(ops)-1)
    if pos(o1, '<.@') > 0 then
        return compFile2rx(m, oR, f)
    if o1 == '|' | o1 == '?' then
        return compObj2Rx(m, oR, 'jSingle('f ||if(o1=='?', ", ''")')')
    return compRun2rx(m, ops, f)
endProcedure compFile2rx

compAst2rx: procedure expose m.
parse arg m, ops, a

    ki = m.a.kind

    if ki == 'c' then
        return compCode2Rx(m, ops, m.a.text)
    if ki == 's' then
        return compString2rx(m, ops, m.a.text)
    if ki == 'o' then
        return compObj2Rx(m, ops, m.a.text)
    if ki == '=' then
        return compCon2Rx(m, ops, m.a.text, a)
    if ki == '*' then
        return compAst2Rx(m, ops || m.a.text, m.a.1)
    o1 = right(ops, 1)
    oR = left(ops, max(0, length(ops)-1))
    if ki == '-' then
        if m.a.0 = 1 then
            return compAst2rx(m, ops, m.a.1)
        else
            return compString2rx(m, ops, compCatRexxAll(m, a,,, ' || '))
    if ki == '.' then
        return compObj2Rx(m, ops, compCatRexxAll(m, a,,, ' || '))
    if ki == '@' then
        return compCode2Rx(m, ops, compCatRexxAll(m, a,,,' || ')';')
    if ki == '¢' then do
        a1 = m.a.1
        if m.a.0 == 1 & m.a1.kind == '¢' then
            return compAst2Rx(m, ops, a1)
        if o1 == '-' then do
            if \ compAstOnlyOut(a) then
                return compAst2Rx(m, ops';', a)
            res = ''
            do ax=1 to m.a.0
                a1 = m.a.ax
                if m.a1.kind == '*' & abbrev(m.a1.text, '$') then
                    b = compAst2rx(m, overlay('-',m.a1.text,1), m.a1.1)
                else if m.a1.kind == '¢' then
                    b = compAst2rx(m, '-', a1)
                else
                    call compAstErr a, 'onlyOut but' ax'='a1 ,
                        'kind='m.a1.kind 'text='m.a1.text
                res = compCatRexx(res, b, ' ')
                end
                    /* ???? only necessary if part of expression ????*/
            return compString2rx(m, oR, '('strip(res)')')
            end
        if o1 == '<' then
            return compAst2Rx(m, ops'@', a)
        if o1 == '.' then
            return compAst2Rx(m, oR'|', a)
        if o1 == '|' | o1 == '?' then
            if m.a.0 = 1 & compAstOnlyOut(a, 0) then do
                a1 = m.a.1
                if m.a1.kind \== '*' | \ abbrev(m.a1.text, '$') then
                    call compAstErr a, 'onlyOut but',
                        '1='a1 'kind='m.a1.kind 'text='m.a1.text
                return compAst2Rx(m, oR'.'substr(m.a1.text, 2), m.a1.1)
                end
            else
                return compFile2Rx(m, ops, compAst2rx(m, '<;', a))
        if pos(o1, '@;') > 0 then do
            if m.a.0 = 1 then  /*???pipe und code können multi stmts */
               return compCode2Rx(m, ops, 'do;' ,
                    compAst2rx(m,';',m.a.1) 'end;')
            res = ''
            do ax=1 to m.a.0
                res = res compAst2rx(m, ';', m.a.ax)
                end
            if res = '' then
                return compCode2Rx(m, ops, 'nop;')
            return compCode2Rx(m, ops, 'do;'res 'end;')
            end
        end
    if ki == '&' then do
        nm = compAst2Rx(m, '-', m.a.1)
        if m.a.text=='' | m.a.text=='v' then
            return compString2rx(m, ops'&', nm)
        else if m.a.text == '?' then
            return compString2rx(m, ops, 'vIsDefined('nm')')
        else if m.a.text == '>' then
            return compString2rx(m, ops, 'vIn('nm')')
        else
            call compAstErr a, 'bad text' m.a.text 'in ast &'
        end
    if ki == '%' | ki == '^' then do
        c1 = compAst2Rx(m, '.', m.a.1)
        if m.a.0 > 1 then
            c1 =  c1',' compAst2Rx(m, '', m.a.2)
        return compRun2Rx(m, ops || ki, c1)
        end
    if ki == 'A' then do /* assignment */
        nm = compAst2Rx(m, '-', m.a.1)
        vl = m.a.2
        if m.vl.kind == '=' | m.vl.kind == '-' ,
            | (m.vl.kind == '*' & right(m.vl.text, 1) == '-') then
            return compCode2Rx(m, ops,
                , 'call vPut' nm',' compAst2Rx(m, '-', vl)';')
        else
            return compCode2Rx(m, ops,
                , 'call vPut' nm',' compAst2Rx(m, '.', vl)';')
        end
    if ki == 'B' then do /* proc */
        call vPut utInter('return' compAst2Rx(m, '-', m.a.1)),
            , oRunner(compAst2Rx(m ,';', m.a.2))
        return ''
        end
    if ki == 'C' then do /* ct */
     call utInter compAst2Rx(m, ';', m.a.1)
        return ''
        end
    if ki == 'D' then do /* do */
        res = 'do' compAst2rx(m, '', m.a.1)';'
        if m.a.text \== '' then
            res = res "call vPut '"m.a.text"'," m.a.text";"
        return compCode2Rx(m, ops, res compAst2Rx(m, ';', m.a.2),
             "end;")
        end
    if ki == 'F' then do /* for... */
        a1 = m.a.1
        st = compAst2Rx(m, ';', m.a.2)
        if abbrev(m.a.text, 'for') then do
            if m.a.1 == '' then
                v = "''"
            else
                v = compAst2Rx(m, '-', m.a.1)
            if m.a.text == 'for' then
                s1 = 'do while vIn('v');'
            else if m.a.text \== 'forWith' then
                call compAstErr a, 'bad for...'
            else
                s1 = 'call vWith "+"; do while vForWith('v');'
            return compCode2Rx(m, ops, s1 st 'end;')
            end
        else if \ abbrev(m.a.text, 'with') then
            call compAstErr a, 'bad with...'
        if m.a1.kind \== 'A' then do
            v = compAst2Rx(m, '.', a1)
            end
         else do
            v = compAst2Rx(m, ,a1)
            if \ abbrev(v, 'call vPut ') | right(v, 1) \==';' then
                call scanErr s, 'bad vPut' v
            v = 'vPut('substr(v, 11, length(v)-11)')'
            end
        ret1 = 'call vWith "+",' v';' st
        if m.a.0 <= 2 then
            return ret1 'call vWith "-";'
        a3 = m.a.3
        if m.a3.kind \== '*' then
            call compAstErr a, 'for/with a.3 not *'
        return ret1 compObj2Rx(m, m.a3.text, "vWith('-')")
        end
    if ki == 'P' then do /* pipe */
        if ((left(m.a.text, 1) == ' ') \== (m.a.1 == '')) ,
         | ((substr(m.a.text, 2) == '') \== (m.a.2 == '')) ,
         | (m.a.0 <= 3 & m.a.text == '') then
            call compAstErr a, 'bad/trivial astPipe'
        res = ''
        do ax=3 to m.a.0
            a1 = ''
            if ax < m.a.0 then /* handle output */
                t1 = 'N'
            else if m.a.1 == '' then
                t1 = 'P'
            else do
                t1 = left(m.a.text, 1)
                a1 = compAst2Rx(m, '.', m.a.1)
                end
            if ax == 3 then do /* handle input */
                t1 = '+'t1 || substr(m.a.text, 2)
                if m.a.2 \== '' then
                    a1 = a1',' compAst2Rx(m, '.', m.a.2)
                end
            else
                t1 = t1'|'
            res = res "call pipe '"t1"'," a1";" ,
                   compAst2Rx(m, ';', m.a.ax)
            end
        return compCode2Rx(m, ops, res "call pipe '-';")
        end
    if ki == 'R' then do /* aRg statement */
        prs = 'parse arg ,'
        pts = ''
        do ax=1 to m.a.0
            a1 = m.a.ax
            if m.a1.kind = 'c' & m.a1.text == ',' then
                prs = prs','
            else do
                prs = prs 'ggAA'ax
                pts = pts'call vPut' compAst2Rx(m, '-', a1)', ggAA'ax';'
                end
            end
        return compCode2rx(m, ops, prs';' pts)
        end
    if ki == 'M' then do
        if m.a.0 = 0 then
            args = ''
        else
            args = ',' compAst2Rx(m, , m.a.1)
        return compRun2rx(m, ops, 'compile(comp(in2Buf())' args')')
        end
    call compAstErr a, 'compAst2rx bad ops' ops 'kind' ki
endProcedure compAst2rx

compCatRexxAll: procedure expose m.
parse arg m, a, ops, mi, sep
    res = ''
    do ax=1 to m.a.0
        res = compCatRexx(res, compAst2rx(m, ops, m.a.ax), mi , sep)
        end
    return strip(res)
endProcedure compCatRexxAll

/*--- cat two rexx parts, avoid strange effects--------------------*/
compCatRexx: procedure expose m.
parse arg le, ri, mi, sep
    if mi \== '' then
        return le || mi || ri
    lr = right(le, 1)
    rl = left(ri, 1)
    if (lr == "'" | lr == '"') then do
        if rl == lr then                /* "a","b" -> "ab" */
            return left(le, length(le)-1) || substr(ri, 2)
        else if  rl == '(' then            /* "a",( -> "a" || ( */
            return le||sep||ri            /* avoid function call    */
        end
    else if pos(lr, m.comp.idChars) > 0 then
        if pos(rl, m.comp.idChars'(') > 0 then
            return le || sep || ri        /* a,b -> a || b */
    return le || ri
endProcedure compCatRexx
/* copy comp end *****************************************************/
/* copy scan     begin ************************************************
     Achtung: inc generiert SB aus scan, Aenderungen nur in scan|
SB = scanBasic: single line, no reader, no newLines, class not needed
    scanSrc(m, source) starts scanning a single line = scanBasic
    scanLook(m,len) : returns next len chars, pos is not moved
    scanChar(m,len) : scans next len chars
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanEnd(m)     : returns whether we reached end of input
    scanLit(m,lit) : scan Literal lit if present or return 0
    scanChar(m,n)  : scan next n characters
    scanName(m)    : scan a name
    ScanNat(m)     : scan a natural number (without sign)
    scanString(m,q): scan a String with quote q. (with doubble = 1)
    scanVerify(m,c,o): verify(...,c,o,...)
    scanKeyValue(m): scan a key = value clause (with spaces)
    scanWord(m,st,uc)  : scan a space delimited word or a string,
                          st=stopper, if u=1 then uppercase non-strings
    scanSpace(m)   : skips over spaces (and nl and comment if \ basic
    scanInfo(m)    : text of current scan location
    scanErr(m, txt): error with current scan location

    m is an address, to store our state
    returns: true if scanned, false otherwise
    if a scan function succeeds, the scan position is moved

         m.m.tok  ==> last token
         m.m.val  ==> last value for scanString/Word
         m.m.pos ==> scan position
         m.m.src ==> scan source
***********************************************************************/
/*--- start basic scannig: set new src, reset outFields -------------*/
scanSrc: procedure expose m.
parse arg m, m.m.src
    m.m.pos = 1
    m.m.tok = ''
    m.m.scanIsBasic = 1
    return m
endProcedure scanSrc

scanBasic: procedure expose m.
parse arg src
    if symbol('m.scan.0') == 'VAR' then
        m.scan.0 = m.scan.0 + 1
    else
        m.scan.0 = 1
    return scanSrc('SCAN.'m.scan.0, src)
endProcedure scanBasic

scanEr3: procedure expose m.
parse arg m, txt, info
    return err('s}'txt'\n'info)

scanErr: procedure expose m.
parse arg m, txt
    if arg() > 2 then
        return err(m,'old interface scanErr('m',' txt',' arg(3)')')
    return scanEr3(m, txt, scanInfo(m))

/*--- scanInfo: text of current scan position for scanErr -----------*/
scanInfo: procedure expose m.
parse arg m
    if m.m.scanIsBasic then
        return scanSBInfo(m)
    else
        interpret objMet(m, 'scanInfo')
endProcedure scanInfo

scanSBInfo: procedure expose m.
parse arg m
    return 'last token' m.m.tok 'scanPosition' ,
        strip(substr(m.m.src, m.m.pos, 40), 't') ,
        || '\npos' m.m.Pos 'in string' strip(m.m.src, 't')

/*--- return the next len characters until end of src ----------------*/
scanLook: procedure expose m.
parse arg m, len
    if len == '' then
        return substr(m.m.src, m.m.pos)
    else
        return substr(m.m.src, m.m.pos,
                     , min(len, 1 + length(m.m.src) - m.m.pos))
endProcedure scanLook

/*--- scan len chararcters, atmost to end of src ---------------------*/
scanChar: procedure expose m.
parse arg m, len
    m.m.tok = scanLook(m, len)
    m.m.pos = m.m.pos + length(m.m.tok)
    return m.m.tok \== ''
endProcedure scanChar

/*--- scan the literal lit ------------------------------------------*/
scanLit: procedure expose m.
parse arg m
    do ax=2 to arg()
        if abbrev(substr(m.m.src, m.m.pos), arg(ax)) then do
            m.m.tok = arg(ax)
            m.m.pos = m.m.pos + length(arg(ax))
            return 1
            end
        end
    m.m.tok = ''
    return 0
endProcedure scanLit

/*--- scan with verify, vOpt is passed to verify ---------------------*/
scanVerify: procedure expose m.
parse arg m, alpha, vOpt, onlyIfMatch
    if arg() > 3 then
        call err 'deimplement onlyIfMatch???'
    if vOpt == '' then   /* empty string does not take default */
        nx = verify(m.m.src, alpha, , m.m.pos)
    else
        nx = verify(m.m.src, alpha, vOpt, m.m.pos)
    if nx = 0 then
        if onlyIfMatch == 1 then
            nx = m.m.pos
        else
            nx = length(m.m.src) + 1
    m.m.tok = substr(m.m.src, m.m.pos, nx - m.m.pos)
    m.m.pos = nx
    return m.m.tok \== ''
endProcedure scanVerify

/*--- scan while in charset ------------------------------------------*/
scanWhile: procedure expose m.
parse arg m, chSet
    return scanVerify(m, chSet, 'n')

/*--- scan until in charset ------------------------------------------*/
scanUntil: procedure expose m.
parse arg m, chSet
    return scanVerify(m, chSet, 'm')

/*--- scan until (and over) string End -------------------------------*/
scanStrEnd: procedure expose m.
parse arg m, sep
    sx = m.m.pos
    bx = sx
    do forever
        ex = pos(sep, m.m.src, sx)
        if ex = 0 then do
            m.m.val = m.m.val || substr(m.m.src, bx)
            return 0
            end
        m.m.val = m.m.val || substr(m.m.src, bx, ex-bx)
        bx = ex + length(sep)
        if \ abbrev(substr(m.m.src, bx), sep) then do
            m.m.tok = m.m.tok || substr(m.m.src, m.m.pos, bx-m.m.pos)
            m.m.pos = bx
            return 1
            end
        sx = bx + length(sep)
        end
endProcedure scanStrEnd

/*--- scan a string with quote char qu ------------------------------*/
scanString: procedure expose m.
parse arg m, prefs
    if prefs = '' then do
        call scanLit m, "'", '"'
        end
    else do
        do px=1 to words(prefs) until scanLit(m, word(prefs, px))
            end
        end
    if m.m.tok == '' then
        return 0
    m.m.val = ''
    if \ scanStrEnd(m, m.m.tok) then
        return scanErr(m, 'ending Apostroph missing')
    return 1
endProcedure scanString

/*--- scan a word and put value into *.val
           a word is either delimited by space or stopper
                     or a string (with single or double quotes -------*/
scanWord: procedure expose m.
parse arg m, stopper, ucWord
    if scanString(m) then
        return 1
    if stopper == '' then
        stopper = ' ''"'
    if \scanUntil(m, stopper) then
        return 0
    if ucWord == 1 then
        m.m.val = translate(m.m.tok)
    else
        m.m.val = m.m.tok
    return 1
endProcedure scanWord

/*--- skip, scan and return word or scanErr -------------------------*/
scanRetWord: procedure expose m.
parse arg m, stopper, ucWord, eWhat
    if scanWord(scanSKip(m), stopper, ucWord) then
        return m.m.val
    else
        return scanErr(m, eWhat 'expected')
endProcedure scanRetWord

/*--- scan a key = word phrase
          put key into m.key and word into m.m.val -------*/
scanKeyValue: procedure expose m.
parse arg m, def, uc
    if \ scanWord(m, ' =''"') then
        return 0
    m.m.key = m.m.val
    if \ scanLit(scanSkip(m), '=') then
        m.m.val = def
    else if \ scanWord(scanSkip(m)) then
        return scanErr(m, 'word expected after' m.m.key '=')
    if uc == 1 then
        upper m.m.key m.m.val
    return 1
endProcedure scanKeyValue

/*--- scan over space, nl, comments ---------------------------------*/
scanSpace: procedure expose m.
parse arg m
   if m.m.scanIsBasic then
       return scanSpaceOnly(m)
   else
       return scanSpNlCo(m)
endProcedure scanSpace

scanSpaceOnly: procedure expose m.
parse arg m
    nx = verify(m.m.src, ' ', , m.m.pos)
    if nx = 0 then
        nx = length(m.m.src) + 1
    m.m.tok = left(' ', nx <> m.m.pos)
    m.m.pos = nx
    return m.m.tok == ' '
endProcedure scanSpaceOnly

/*--- skip over space and return m ----------------------------------*/
scanSkip: procedure expose m.
parse arg m
    call scanSpace m
    return m
endProcedure scanSkip

/*--- return true if at end of src -----------------------------------*/
scanEnd: procedure expose m.
parse arg m
    if m.m.pos <= length(m.m.src) then
        return 0
    else if m.m.scanIsBasic then
        return 1
    else
        return m.m.atEnd
endProcedure scanEnd

/*--- scan a natural number (no sign, decpoint ...) Ignore After -----*/
scanNatIA: procedure expose m.
parse arg m
    return scanVerify(m, '0123456789')

/*--- scan an integer (optional sign, no decpoint ...) Ignore After --*/
scanIntIA: procedure expose m.
parse arg m
    poX = m.m.pos
    call scanLit m, '-', '+'
    if \ scanNatIA(m) then do
        m.m.pos = poX
        return 0
        end
    m.m.tok = substr(m.m.src, poX, m.m.pos - poX)
    return 1
endProcedure scanIntIA

/*--- scanOpt set the valid characters for names, and comments
          it must be called
          before any of the following functions ---------------------*/
scanOpt: procedure expose m.
parse arg m, m.m.scanName1, namePlus, m.m.scanComment
    if m.m.scanName1 == '' then
        m.m.scanName1 = m.ut_alfa
    if namePlus == '' then
        m.m.scanNameR = m.m.scanName1 || '0123456789'
    else
        m.m.scanNameR = m.m.scanName1 || namePlus
    m.m.scanNestCom = 0
    return m
endProcedure scanOpt

/*--- return true if at comment --------------------------------------*/
scanSBCom: procedure expose m.
parse arg m
    m.m.tok = ''
    if m.m.scanComment == '' then
        return 0
    if \ abbrev(substr(m.m.src, m.m.pos), m.m.scanComment) then
        return 0
    m.m.tok = substr(m.m.src, m.m.pos)
    m.m.pos = 1 + length(m.m.src)
    return 1
endProcedure scanSBCom

/*--- scan a Name, first char in *.scanName1, rest in *.scanNameR ----*/
scanName: procedure expose m.
parse arg m
    if pos(substr(m.m.src, m.m.pos, 1),
                 , m.m.scanName1) <= 0 then do
        m.m.tok = ''
        return 0
        end
    return scanVerify(m, m.m.scanNameR)
endProcedure scanName

/*--- check character after a number
          must not be Identifier or plus ----------------------------*/
scanCheckNumAfter: procedure expose m.
parse arg m, res, plus
    if \ res then
        return 0
    if pos(substr(m.m.src, m.m.pos, 1), m.m.scanNameR || plus) > 0 then
        call scanErr m, 'illegal char after number' m.m.tok
    return 1
endProcedure scanCheckNumAfter

/*--- scan a natural number check character after -------------------*/
scanNat: procedure expose m.
parse arg m
    if arg() \== 1 then
        return err('old interface scanNat') / 0
    return scanCheckNumAfter(m, scanNatIA(m), '.')
endProcedure ScanNat

/*--- scan an Integer check character after -------------------------*/
scanInt: procedure expose m.
parse arg m
    if arg() \== 1 then
        return err('old interface scanInt') / 0
    return scanCheckNumAfter(m, scanIntIA(m), '.')
endProcedure ScanInt

/*--- scan a Number check character after ---------------------------*/
scanNum: procedure expose m.
parse arg m
    if arg() \== 1 then
        return err('old interface scanNum') / 0
    return scanCheckNumAfter(m, scanNumIA(m))
endProcedure ScanInt

/*--- scan a number (optional sign, decpoint, exponent) Ignore After-*/
scanNumIA: procedure expose m.
parse arg m
    poX = m.m.pos
    call scanLit m, '-', '+'
    bDP = scanNatIA(m)
    if scanLit(m, '.') then
        aDP = scanNatIA(m)
    else
        aDP = 0
    if \ (aDP | bDP) then do
        m.m.pos = poX
        return 0
        end
    if scanLit(m, 'e', 'E') then
        if \ scanIntIA(m) then
            call scanErr m, 'exponent expected after' ,
                             substr(m.m.src, poX, m.m.pos-poX)
    m.m.tok = substr(m.m.src, poX, m.m.pos-poX)
    m.m.val = translate(m.m.tok)
    return 1
endProcedure scanNumIA

scanType: procedure expose m.
parse arg m, opt
    m.m.tok = ''
    if scanName(m) then
        m.m.type = 'n'
    else if scanNum(m) then
        m.m.type = 0
    else if scanString(m) then
        m.m.type = left(m.m.tok, 1)
    else if scanSpace(m) then
        m.m.type = 's'
    else do
        call scanChar m, 1
        m.m.type = m.m.tok
        end
    return m.m.type
endProcedure scanType

/* copy scan     end *************************************************/
/* copy scanRead begin ***********************************************
Scan: scan an input: with multiple lines
    ==> all of scan

returns: true if scanned, false otherwise
         m.m.tok  ==> last token
         m.m.val  ==> last value for scanString/Word/KeyValue
         m.key    ==> key for scanKeyValue
         m.m.pos ==> scan position
         m.m.src ==> scan source
***********************************************************************/
scanReadIni: procedure expose m.
    if m.scanRead_ini == 1 then
        return
    m.scanRead_ini = 1
    call jIni
    ts = classNew('n ScanRes u f TOK v, f VAL v, f KEY v, f TYPE v')
    call classNew 'n ScanRead u JRW', 'm',
        , 'scanNL return scanReadNL(m, unCond)',
        , 'scanCom  return scanSBCom(m)',
        , 'scanInfo return scanReadInfo(m)',
        , 'scanPos return scanReadPos(m)',
        , "jOpen   call scanReadOpen m, arg(3)" ,
        , "jClose  call scanReadClose m" ,
        , 'isWindow 0',
        , "jRead if scanType(m) == '' then return 0;" ,
                    " m.m = oClaCopy('"ts"', m, ''); return 1"
    call classNew "n EditRead u JRW", "m" ,
        , "jRead  return editRead(m)",
        , "jOpen" ,
        , "jReset m.m.linex = arg - 1"
    call classNew 'n ScanSqlStmtRdr u JRW', 'm',
        , "jReset   call scanSqlStmtRdrReset m, arg, arg2, arg3",
        , "jOpen    call scanOpen m'.SCAN'" ,
        , "jClose   call scanClose m'.SCAN'" ,
        , "jRead    r = scanSqlStmt(m'.SCAN');if r==''then return 0" ,
                         "; m.m = r; return 1"
    return
endProcedure scanReadIni

scanOpen: procedure expose m.
parse arg m
    interpret objMet(m, 'jOpen')
    return m
endProcedure scanOpen

scanClose: procedure expose m.
parse arg m
    interpret objMet(m, 'jClose')
    return m
endProcedure scanOpen

/*--- scan over white space, nl, comments ...-------------------------*/
scanSpNlCo: procedure expose m.
parse arg m
    res = 0
    do while scanSpaceOnly(m) | scanCom(m) | scanNl(m)
        res = 1
        end
    m.m.tok = left(' ', res)
    return res
endProcedure scanSpNlCo

/*--- scan next line -------------------------------------------------*/
scanNL: procedure expose m.
parse arg m, unCond
    interpret objMet(m, 'scanNL')

/*--- scan one comment -----------------------------------------------*/
scanCom: procedure expose m.
parse arg m
    interpret objMet(m, 'scanCom')
/*--- go back the current token --------------------------------------*/
scanBack: procedure expose m.
parse arg m, tok
    if m.m.pos <= length(tok) then
        call scanErr sc, 'cannot back "'tok'" length'
    cx = m.m.pos - length(tok)
    if substr(m.m.src, cx, length(tok)) \== tok then
        return scanErr(m, 'cannot back "'tok'" value')
    m.m.pos = cx
    return
endProcedure scanBack

/*--- return position in simple format -------------------------------*/
scanPos: procedure expose m.
parse arg m
    interpret objMet(m, 'scanPos')
endProcedure scanPos

/*--- set position to position in arg to------------------------------*/
scanBackPos: procedure expose m.
parse arg m, to
    cur = scanPos(m)
    wc = words(cur)
    if wc <> words(to) ,
        | subWord(cur, 1, wc-1) <> subWord(to, 1, wc-1) then
        call scanErr m 'cannot back from' cur 'to' to
    m.m.pos = word(to, wc)
    return
endProcedure scanBackPos

/*--- begin scanning the lines of a reader ---------------------------*/
scanRead: procedure expose m.
parse arg rdr, n1, np, co
    return scanReadReset(scanOpt(oNew('ScanRead'), n1, np, co), rdr)

scanReadReset: procedure expose m.
parse arg m, m.m.rdr
    return oMutatName(m, 'ScanRead')
endProcedure scanReadReset

scanReadOpen: procedure expose m.
parse arg m, line0
    call jOpen m.m.rdr, '<'
    return scanSetPos0(m, (line0 == '') 1, line0)
endProcedure scanReadOpen

scanReadClose: procedure expose m.
parse arg m
    call jClose m.m.rdr
    m.m.atEnd = 'closed'
    return m
endProcedure scanReadClose

/*--- scan over next newLine
        if unCond \== 1 only if we are already at endOfLine
         return true if we scanned a NL ------------------------------*/
scanReadNL: procedure expose m.
parse arg m, unCond
    m.m.tok = ''
    if unCond \== 1 then
        if m.m.pos <= length(m.m.src) then
            return 0
    if m.m.atEnd then
        return 0
    m.m.tok = substr(m.m.src, m.m.pos)
    r = m.m.rdr
    if \ jRead(r) then do
        m.m.atEnd = 1
        m.m.pos = 1 + length(m.m.src)
        return 0
        end
    m.m.src = m.r
    m.m.pos = 1
    m.m.lineX = m.m.lineX + 1
    return 1
endProcedure scanReadNl

/*--- postition scanner to lx px (only with jBuf) -------------------*/
     after rdr is positioned to line before -------------------------*/
scanSetPos: procedure expose m.
parse arg m, lx px
    call jPosBefore m.m.rdr, lx
    return scanSetPos0(m, lx px)

/*--- postition scanner to lx px
     after rdr is positioned to line before -------------------------*/
scanSetPos0: procedure expose m.
parse arg m, lx px, line0
    call scanReset m, line0
    call scanNl m
    m.m.lineX = lx
    m.m.pos = px
    return m
endProcedure scanSetPos0

/*--- reset scanner fields ------------------------------------------*/
scanReset: procedure expose m.
parse arg m, m.m.src
    m.m.pos = 1
    m.m.tok = ''
    m.m.scanIsBasic = 0
    m.m.atEnd = 0
    m.m.lineX = 0
    m.m.val = ''
    m.m.key = ''
    return m
endProcedure

scanTextCom: procedure expose m.
parse arg m, untC, untWrds
    if m.m.scanComment \= '' then
        untC = untC || left(m.m.scanComment, 1)
    if \ m.m.scanNestCom then
        return scanText(m, untC, untWrds)
    else if wordPos('*/', untWrds) > 0 then
        return scanText(m, untC'*/', untWrds)
    res = scanText(m, untC'*/', untWrds '*/')
    if res then
        if scanLook(m, 2) == '*/' then
            call scanErr m, '*/ without preceeding comment start /*'
    return res
endProcedure scanTextCom

scanText: procedure expose m.
parse arg m, untC, untWrds
    res = ''
    do forever
        if scanUntil(m, untC) then do
            res = res || m.m.tok
            if m.m.pos > length(m.m.src) then do
                /* if windowing we need to move the window| */
                if scanNl(m, 0) then
                    if right(res, 1) \==' ' & scanLook(m, 1)\==' ' then
                        res = res' '
                iterate
                end
            end
        m.m.tok = res
        c9 = scanLook(m, 9)
        do sx=1 to words(untWrds)
            if abbrev(c9, word(untWrds, sx)) then
                return 1
            end
        if scanCom(m) | scanNl(m, 0) then do
            if right(res, 1) \== ' ' & scanLook(m, 1) \== ' ' then
                res = res' '
            end
        else if scanString(m) then
            res = res || m.m.tok
        else if scanChar(m, 1) then
            res = res || m.m.tok
        else if scanEnd(m) then do
            m.m.tok = res
            return res \== ''  /* erst hier NACH scanCom,  scanNl */
            end
        else
            call scanErr m, 'bad pos'
        end
endProcedure scanText

scanReadPos: procedure expose m.
parse arg m, msg
    return m.m.lineX m.m.pos
endProcedure scanReadPos

scanReadInfo: procedure expose m.
parse arg m
    msg = 'last token' m.m.tok 'scanPosition' ,
        strip(substr(m.m.src, m.m.pos, 40), 't')
    if scanEnd(m) then
        msg = msg'\natEnd after'
    else
        msg = msg'\npos' m.m.pos 'in'
    return msg 'line' m.m.lineX':' strip(m.m.src, 't')
endProcedure scanReadInfo

/*--- use scan sqlEdit macro --> temporarily here --------------------*/
/*--- read next line from edit data ----------------------------------*/
editRead: procedure expose m.
parse arg m
    m.m.lineX = m.m.lineX + 1
    if adrEdit('(ll) = line' m.m.lineX, 12) \= 0 then
        return 0
    m.m = ll
    return 1
endProcedure editRead
/*--- search loop in edit macro --------------------------------------*/
scanSqlSeekId: procedure expose m.
parse arg m, lx, cmd, opts
    if opts = '' then
        opts = word
                     /* line 1 col 0, otherwise first word is skipped*/
    if adrEdit("cursor =" max(trunc(lx), 1) 0, 12) = 12 then
        return -1
    do while adrEdit("seek" cmd opts, 4) = 0 /* find each command*/
        call adrEdit "(fx) = cursor"
        if m.debug then do
            call adrEdit "(LI) = LINE" fx
            call debug 'scanSqlSeekId after' lx 'found' cmd 'at' fx li
            end
        call jReset m.m.rdr, fx
        call jOpen m, '<'
        m.m.lineX = fx
        do while word(scanPos(m), 1) <= fx & scanSqlClass(m)
            if m.m.sqlClass = 'i' & m.m.val == cmd then
                return fx
            end
        call jClose m
        end
    return -1
endProcedure scanSqlSeekId
/* copy scanRead end **************************************************/
/* copy scanWin begin *************************************************
     scan the the concatenation of the lines of a reader
         any token my be split over several line
         except the end-of-line-comment-token
***********************************************************************/
scanWinIni: procedure expose m.
    if m.scanWin.ini = 1 then
        return
    m.scanWin.ini = 1
    call scanReadIni
    call classNew 'n ScanWin u ScanRead', 'm',
        , "jOpen call scanWinOpen m, arg(3)",
        , "jClose call scanReadClose m",
        , 'scanNL return scanWinNl(m, unCond)',
        , 'scanCom return scanWinCom(m)',
        , 'scanInfo return scanWinInfo(m)',
        , 'scanPos  return scanWinPos(m)',
        , 'isWindow 1'
    return
endProcedure scanWinIni

/*--- instanciate a new window scanner -------------------------------*/
scanWin: procedure expose m.
parse arg rdr, wOpts
    return scanWinReset(scanSqlOpt(oNew('ScanWin')), rdr, wOpts)

/*--- set the reader and window attributes of scanner m -------------*/
scanWinReset: procedure expose m.
parse arg m, m.m.rdr, winOpt
    return scanWinOpt(oMutatName(m, 'ScanWin'), winOpt)

/*--- set the window scanner attributes -----------------------------*/
scanWinOpt: procedure expose m.
parse arg m, cuLe wiFo wiBa
    if pos('@', cuLe) > 0 then
        parse var cuLe cuLe '@' m.m.cutPos
    else
        m.m.cutPos = 1
    cuLe = word(cuLe 72, 1)
    m.m.cutLen = cuLe
    m.m.posMin = word(wiba 3, 1) * cuLe    /* room to go back */
    m.m.posLim = m.m.posMin + 3 * cuLe
    m.m.winTot = m.m.posLim + cuLe * (1 + word(wiFo 5, 1))
    m.m.cutLen = cuLe                      /* fix recLen */
    return m
endProcedure scanWinReset

/*--- open reader and start scanning --------------------------------*/
scanWinOpen: procedure expose m.
parse arg m, line0
    call jOpen m.m.rdr, '<'
    if line0 == '' then
        return scanSetPos0(m, 1 1)
    if length(line0) // m.m.cutLen \== 0 then
        line0 = line0||left('', m.m.cutLen - length(line0)//m.m.cutLen)
    return scanSetPos0(m, (1 - length(line0) % m.m.cutLen) 1, line0)
endProcedure scanWinOpen

/*--- move the source window: cut left side and append at right side
      return number of characters cut at left ------------------------*/
scanWinRead: procedure expose m.
parse arg m
    dlt = 0
    if m.m.atEnd then
        return 0
    if m.m.pos >= m.m.posLim then do     /*  cut left side */
        dlt = m.m.pos - ((m.m.pos-1) // m.m.cutLen + 1 + m.m.posMin)
        call assert 'dlt >= 0 & dlt // m.m.cutLen = 0', 'dlt m.m.cutLen'
        m.m.src = substr(m.m.src, dlt+1)
        m.m.pos = m.m.pos - dlt
        m.m.lineX = m.m.lineX + dlt % m.m.cutLen
        end
    r = m.m.rdr
    do while length(m.m.src) < m.m.winTot /* read and fill to len */
        if \ jRead(r) then do
            m.m.atEnd = 1
            return dlt
            end
        m.m.src = m.m.src || substr(m.r, m.m.cutPos, m.m.cutLen)
        end
    call assert 'length(m.m.src) = m.m.winTot',
              , 'm.m.winTot length(m.m.src) m.m.src'
    return dlt
endProcedure scanWinRead

/*--- return position of next line start -----------------------------*/
scanWinNLPos: procedure expose m.
parse arg m
    return m.m.pos + m.m.cutLen - ((m.m.pos - 1) // m.m.cutLen)

/*--- scan comment ---------------------------------------------------*/
scanWinCom: procedure expose m.
parse arg m
    call scanWinRead m
    if m.m.scanComment \== '' then do
        cl = length(m.m.scanComment)
        if scanLook(m, cl) == m.m.scanComment then do
            np = scanWinNlPos(m)
            if np = m.m.pos then
                np = np +  m.m.cutLen
            if np >= m.m.pos + cl then do
                m.m.tok = substr(m.m.src, m.m.pos, np - m.m.pos)
                m.m.pos = np
                return 1
                end
            end
        end
    if m.m.scanNestCom then
        if scanLit(m, '/*') then do
            tk = substr(m.m.src, m.m.pos, m.m.cutLen + 2)
            call scanTextCom m, , '*/'
            if \ scanLit(m, '*/') then
                 call scanErr m, 'nested comment after /* not finished'
            if pos('*/', tk) < 1 then
                m.m.tok = left(tk, m.m.cutLen - 5)'...*/'
            else
                m.m.tok = left(tk, pos('*/', tk) + 1)
            return 1
            end
    m.m.tok = ''
    return 0
endProcedure scanWinCom


/*--- scan nl --------------------------------------------------------*/
scanWinNL: procedure expose m.
parse arg m, unCond
    call scanWinRead m
    m.m.tok = ''
    if unCond \== 1 then
       return 0
    np = scanWinNLPos(m)
    if np = m.m.pos then
        return 0
    m.m.tok = substr(m.m.pos, np-m.m.pos)
    m.m.pos = np
    return 1
endProcedure scanWinNl

/*--- return current position in input ------------------------------*/
scanWinPos: procedure expose m.
parse arg m
    ps = m.m.pos - 1
    return (m.m.lineX + (ps % m.m.cutLen)) (ps // m.m.cutLen + 1)
endProcedure scanWinPos

/*--- return a description of the current scan position --------------*/
scanWinInfo: procedure expose m.
parse arg m
    p = scanWinPos(m)
    if scanEnd(m) then do
        res = 'atEnd after'
        p = m.m.lineX - 1 + length(m.m.src) % m.m.cutLen
        p = word(p, 1)
        end
    else do
        res = 'pos' word(p, 2) 'in'
        p = word(p, 1)
        end
    return 'last token' m.m.tok 'scanPosition' ,
          strip(left(substr(m.m.src, m.m.pos), 40), 't') ,
        || '\n'res 'line' p':' strip(substr(m.m.src,
          , 1 + (p - m.m.lineX) * m.m.cutLen, m.m.cutLen), 't')
endProcedure scanWinInfo
/* copy scanWin end   *************************************************/
/* copy scanSql begin *************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanSqlReset: procedure expose m.
parse arg m, r, scanWin, stmtOpt
    call scanSqlStmtOpt scanSqlOpt(m), strip(stmtOpt)
    if scanWin \== 0 then
        return scanWinReset(m, r, scanWin)
    else if r \== '' then
        return scanReadReset(m, r)
    else
        return scanSrc(m, m.m.src)
endProcedure scanSqlReset

scanSqlOpt: procedure expose m.
parse arg m
    call scanOpt m, , '0123456789_' , '--'
    m.m.scanNestCom = 1
    return m
endProcedure scanSqlOpt

/*--- scan a sql token put class in m.sqlclass:
      'i': ordinary identifier   e.g. Name
      'd': delimited identifier  e.g. "Delimited"
      'q': qualified identifier  e.g. abc."efg"
      'u': integer units         e.g. 8G
      'n': number                e.g. -234 or .2e3
      's': string                e.g. 'abc''ef'
      '' : at end
         : any other character   e.g. ;
      ----------------------------------------------------------------*/
scanSqlClass: procedure expose m.
parse arg m, retSpace
    m.m.val = ''
    if scanSpNlCo(m) & retSpace = 1 then do
        m.m.sqlClass = 'b'
        return 1
        end
    c2 = scanLook(m ,2)
    if scanLit(m, "'",  "x'", "X'") then do
        if \ scanStrEnd(m, "'") then
            call scanErr m, 'ending apostroph missing'
        m.m.sqlClass = 's'
        if \abbrev(m.m.tok, "'") then
            m.m.val = x2c(m.m.val)
        end
    else if scanSqlQuId(m) then do
        if m.m.val.0 > 1 then
            m.m.sqlClass = 'q'
        else if abbrev(m.m.tok, '"') then
            m.m.sqlClass = 'd'
        else
            m.m.sqlClass = 'i'
        end
    else if scanSqlNumIA(m)  then
        m.m.sqlClass = 'n'
    else if scanChar(m, 1) then
        m.m.sqlClass = m.m.tok
    else if scanEnd(m) then do
        m.m.sqlClass = ''
        return 0
        end
    else
        call scanErr m, 'cannot scan sql'
    return 1
endProcedure scanSqlClass

scanSqlSkipBrackets: procedure expose m.
parse arg m, br
    if br \== '' then
        nop
    else if scanLit(m, '(') then
        br = 1
    else
        return 0
    do while scanSqlClass(m) & m.m.sqlClass \== ';'
        if m.m.sqlClass = '('        then br = br + 1
        else if m.m.sqlClass \== ')' then iterate
        else if br > 1              then br = br - 1
        else                             return 1
        end
    call scanErr m, '; or eof, but' br 'closing ) expected'
endProcedure skipBrackets
/*--- scan an ordinary sql identifier e.g. abc, ef_12 ----------------*/
scanSqlId: procedure expose m.
parse arg m
    if \ scanName(m) then
        return 0
    m.m.val = translate(m.m.tok)
    return 1
endProcedure scanSqlId

/*--- scan a delimited or ordinay sql identifier ---------------------*/
scanSqlDeId: procedure expose m.
parse arg m
    if scanSqlId(m) then
        return 1
    if \ scanString(m, '"') then
        return 0
    m.m.val = strip(m.m.val, 't')
    return 1
endProcedure scanSqlDeId

/*--- scan a qualified sql identifier --------------------------------*/
scanSqlQuId: procedure expose m.
parse arg m
    res = ''
    rto = ''
    do qx=1
        if \ scanSqlDeId(m) then do
            if qx <> 1 then
                call scanErr m, 'id expected after .'
            return 0
            end
        m.m.val.qx = m.m.val
        res = res'.'m.m.val
        rto = rto'.'m.m.tok
        if \ scanLit(scanSkip(m), '.') then
            leave
        call scanSpace m
        end
    m.m.val.0 = qx
    m.m.val = substr(res, 2)
    m.m.tok = substr(rto, 2)
    return 1
endProcedure scanSqlQuId

/*--- scan a sql number, Ignore After -------------------------------*/
scanSqlNumIA: procedure expose m.
parse arg m
    if \ scanLit(m, '+ ', '- ') then
        return scanNumIA(m)
    si = left(m.m.tok, 1)
    if \ scanNumIA(scanSkip(m)) then
        call scanErr m, 'no sqlNum after +-'
    else if abbrev(m.m.tok, '-') | abbrev(m.m.tok, '+') then
        call scanErr m, 'doubble signs at sqlNum'
    m.m.val = si || m.m.val
    return 1
endProcedure scanSqlNumIA

/*--- scan a sql number, check After --------------------------------*/
scanSqlNum: procedure expose m.
parse arg m
    if arg() \== 1 then
        return err('old interface scanSqlNum') / 0
    return scanCheckNumAfter(m, scanSqlNumIA(m))
endProcedure ScanSqlNum

/*--- scan a sql number with a unit which may follow without space ---*/
scanSqlNumUnit: procedure expose m.
parse arg m, both, units
    if \ scanSqlNumIA(m) then
        return 0
    nu = m.m.val
    sp = scanSpace(m)
    if scanSqlId(m) then do
        if units == '' | wordpos(m.m.val, units) > 0 then
            nu = nu m.m.val
        else if both | \ sp then
            call scanErr m, 'bad unit' m.m.val 'after' nu
        else
            call scanBack m, m.m.tok
        end
    else if both then
        call scanErr m, 'no unit after' nu
    else if \sp & pos(scanLook(m, 1), m.m.scanNameR) > 0 then
        call scanErr m, 'bad unit after number' nu
    m.m.val = nu
    return 1
endProcedure scanSqlNumUnit

/*--- find next statement, after scanSqlStmtOpt -----------------------
       m.m.stop contains delimiter, will be changed by
          terminator?; or --#terminator               */
scanSqlStmtOpt: procedure expose m.
parse arg m, m.m.stop
    if m.m.stop == '' then
        m.m.stop = ';'
    return m
endProcedure scanSqlStmtOpt

scanSqlStmt: procedure expose m.
parse arg m
    loop = 0
    res = ''
    fuCo = copies(m.m.scanComment'#SET', m.m.scanComment \== '')
    do lx=1
        if lx > 100 then
            say '????iterating' scanLook(m)
        if m.m.stop == '' then
            scTx = scanTextCom(m, '''"' ,fuCo)
        else
            scTx = scanTextCom(m, '''"'left(m.m.stop,1), m.m.stop fuCo)
        if scTx then do
            res = res || m.m.tok
            if fuCo \== '' then
                if scanLook(m, length(fuCo)) == fuCo then do
                    tx = scanLook(m)
                    ok = word(tx, 2) == 'TERMINATOR' ,
                         & length(word(tx, 3)) == 1
                    if ok then
                       ok = scanCom(m)
                    if ok then do
                        m.m.stop = word(tx, 3)
                        if \ (right(res, 1) == ' ' ,
                                 | scanLook(m, 1) == ' ') then
                            res = res' '
                        end
                    else if scanChar(m, 1) then
                        res = res || m.m.tok
                    else
                        call scanErr m, 'no char, now what?'
                    iterate
                    end
            if m.m.stop \== '' then
                if \ scanLit(m, m.m.stop) then
                    iterate
            end
        res = strip(res)
        if length(res)=11 ,
            & abbrev(translate(res), 'TERMINATOR') then do
            m.m.stop = substr(res, 11, 1)
            res = ''
            end
        else if res \== '' | \ scTx then
            return res
        end
     call scanErr m, 'loop in scanSqlStmt'
endProcedure scanSqlStmt

/*-- return next sqlStmt from rdr ( or string or '' = j.in) ---------*/
scanSqlIn2Stmt: procedure expose m.
parse arg rdr, wOpt
    s = scanSqlIn2Scan(rdr, m'.SCAN_SqlIn2Stmt', wOpt)
    res = scanSqlStmt(scanOpen(s))
    call scanReadClose s
    return res
endProcedure scanSqlIn2Stmt

/*-- reset s as scanSql from rdr ( or string or '' = j.in) ----------*/
scanSqlIn2Scan: procedure expose m.
parse arg m, s, wOpt, sOpt
    interpret objMet(m, 'scanSqlIn2Scan')
endProcedure scanSqlIn2Scan

/*-- create a new scanSqlStmtRdr ------------------------------------*/
scanSqlStmtRdr: procedure expose m.
parse arg rdr, wOpt, sOpt
    return oNew('ScanSqlStmtRdr', rdr, wOpt, sOpt)

/*-- reset a new scanSqlStmtRdr
         must be called from jReset to allow jRead ------------------*/
scanSqlStmtRdrReset: procedure expose m.
parse arg m, rdr, wOpt, sOpt
    call scanSqlIn2Scan rdr, m'.SCAN', wOpt, sOpt
    return oMutatName(m, 'ScanSqlStmtRdr')
endProcedure scanSqlStmtRdrReset
/* copy scanSql end   *************************************************/
/* copy scanUtil begin *************************************************
    scan db2 utility input statements using scan and a reader
**********************************************************************/
/*--- initialize with reader inRdr ----------------------------------*/
scanUtilOpt: procedure expose m.
parse arg m
    call scanSqlOpt m
    m.m.scanNestCom = 0
    m.m.utilBrackets = 0
    m.scanUtil =  'BACKUP CATENFM CATMAINT CHECK' ,
                  'COPY COPYTOCOPY DIAGNOSE EXEC LISTDEF LOAD' ,
                  'MERGECOPY MODIFY OPTIONS QUIESCE REBUILD' ,
                  'RECOVER REORG REPAIR REPORT RESTORE' ,
                  'RUNSTATS STOSPACE TEMPLATE UNLOAD'
    return m
endProcedure scanUtilOpt
/*--- scan next token and put its class in m.sc.utilClass:
      'u' a utility name
      'n' a name
      '"' a quoted name
      "'" an apostroph'd string
      '.' a .
      ',' a ,
      'v' a value
      ''  at end
      ---------------------------------------------------------------*/
scanUtil: procedure expose m.
parse arg sc
    m.sc.utilSpace = scanSpace(sc)
    ty = '?'
    if scanLit(sc, '(') then do
        m.sc.utilBrackets = m.sc.utilBrackets + 1
        end
    else if scanLIT(sc, ')') then do
        m.sc.utilBrackets = m.sc.utilBrackets - 1
        if m.sc.utilBrackets < 0 then
           call scanErr sc, 'unmatched closing bracket )'
        end
    else if scanLit(sc, ',') then do
        end
    else if scanLit(sc, '.') then do
        end
    else if scanString(sc, "'") then do
        end
    else if scanString(sc, '"') then do
        end
    else if scanName(sc) then do
        m.sc.val = translate(m.sc.tok)
        if m.sc.utilBrackets > 0 then
            ty = 'n'
        else if 0 < wordPos(m.sc.val, m.scanUtil) then
            ty = 'u'
        else
            ty = 'n'
        end
    else if scanVerify(sc, ' (),''"', 'm') then do
        ty = 'v'
        m.sc.val = translate(m.sc.tok)
        end
    else if \scanEnd(sc) then do
            call scanErr sc, 'scanUtil stopped before end'
        end
    else do
        ty = ''
        m.sc.val = ''
        end
    if ty == '?' then
        m.sc.utilClass = left(m.sc.tok, 1)
    else
        m.sc.utilClass = ty
    return m.sc.utilClass
endProcedure scanUtil

/*--- scan a value or a bracketed list of values ---------------------*/
scanUtilValue: procedure expose m.
parse arg sc, remApo, nl
    if remApo = '' | rempApo = 0 then
        remApo = "nv"
    else if rempApo = 1 then
        remApo = "nv'"
    if '(' \== scanUtil(sc) then
         return scanUtilValueOne(sc, remApo)
    v = ''
    brx = m.sc.utilBrackets
    oLine = word(scanPos(sc), 1)
    do forever
        call scanUtil sc
        one = scanUtilValueOne(sc, remApo)
        if one == '' then
           call scanErr sc, 'eof in brackets'
        else if brx > m.sc.utilBrackets then
           return v
        nLine = word(scanPos(sc), 1)
        if \ m.sc.utilSpace then
            v = v || one
        else if nl \== '' & oLine <> nLine then
            v = v || nl || one
        else
            v = v' 'one
        oLine = nLine
        end
endProcedure scanUtilValue

scanUtilValueOne: procedure expose m.
parse arg sc, valTy
    if m.sc.utilClass == '' then
        return ''
    else if m.sc.utilClass == 'u' then
        call scanErr sc, 'util in scanUtilValueOne'
    if pos(m.sc.utilClass, valTy) > 0 then
        return m.sc.val
    else
        return m.sc.tok
endProcedure scanUtilValueOne

/*--- skip over nested brackets --------------------------------------*/
scanUtilSkipBrackets: procedure expose m.
parse arg m, br, doCat
    if br \== '' then
        lim = m.m.utilBrackets - br
    else if scanLit(m, '(') then do
        lim = m.m.utilBrackets
        m.m.utilBrackets = lim + 1
        end
    else
        return 0
    doCat = doCat == 1
    res = ''
    do while scanUtil(m) \== ''
        if m.m.utilBrackets <= lim then do
            if doCat then
                m.m.val = res
            return 1
            end
        if doCat then
            res = res m.m.tok
        end
    return scanErr(m, 'eof with' m.m.utilBrackets 'open (')
endProcedure skipBrackets

/*--- analyze a punch file write intoField to stdOut -----------------*/
scanUtilInto: procedure expose m.
parse arg m
    if m.m.utilBrackets \== 0 then
        call scanErr m, 'scanUtilInto with brackets' m.m.utilBrackets
  /*sc = scanUtilReader(m.j.in)
    call jOpen sc, 'r'
 */ do forever
        cl = scanUtil(m)
        if cl == '' then
            return 0
        if cl = 'n' & m.m.tok == 'INTO' then
            leave
        end
    if scanUtil(m) \== 'n' | m.m.tok \== 'TABLE' then
        call scanErr m, 'bad into table '
    if \ scanSqlQuId(scanSkip(m)) then
        call scanErr m, 'table name expected'
    if m.m.utilBrackets \== 0 then
        call scanErr m, 'into table in brackets' m.m.utilBrackets
    m.m.tb = m.m.val
    m.m.part = ''
    m.m.when = ''
    do forever
        cl = scanUtil(m)
        if cl == '' then
            call scanErr m, 'eof after into'
        if cl == 'n' & m.m.tok == 'PART' then do
            if scanUtil(m) == 'v' then
                m.m.part = m.m.val
            else
                call scanErr m, 'bad part'
            end
        else if cl == 'n' & wordPos(m.m.val, 'WHEN WORKDDN') > 0 then do
            call scanUtilSkipBrackets m
            end
        else if cl == '(' then do
           leave
           end
        end
    oX =  m.m.lineX
    oL =  overlay('', m.m.src, 1, m.m.pos-2)
    do while m.m.utilBrackets > 0
        call scanUtil m
        if oX \== m.m.lineX then do
            call out strip(oL, 't')
            oX =  m.m.lineX
            oL =  m.m.src
            end
        end
    call out left(oL, m.m.pos)
 /* call jClose sc
 */ return 1
endProcedure scanUtilInto
/* copy scanUtil end **************************************************/
/* copy pipe begin *****************************************************
***********************************************************************/
pipeIni: procedure expose m.
    if m.pipe.ini == 1 then
        return
    m.pipe.ini = 1
    call catIni
    call mapReset v
    m.v_with.0 = 0
    m.v_withMap = ''
    m.v_with.0.map = ''
    m.pipe.0 = 1
    m.pipe.1.in  = m.j.in
    m.pipe.1.out = m.j.out
    call pipe '+'
    return
endProcedure pipeIni

/*-------------------------------
  +-       push pop frame
  PYNFA    ouput: Parent saY Newcat File, Appendtofile
  psf|     input: parent string file oldOut
  old          --> new
  pipeBegin    --> pipe '+N'
  pipeBeLa f   --> pipe '+F'
  pipeLast     --> pipe 'P|'
  pipeLast f   --> pipe 'F|', f
  pipeEnd      --> pipe '-'
--------------------------------*/
pipe: procedure expose m.
parse arg opts, aO
    ox = 1; oc = substr(opts, ox, 1)
    ax = m.pipe.0
    px = ax -1
    if oc == '-' then do
        if px < 2 then
            call err 'pipe pop empty'
        call jClose m.pipe.ax.out
        call jClose m.pipe.ax.in
        ax = px
        m.pipe.0 = ax
        px = ax-1
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if oc == '+' then do
        px = ax
        ax = ax+ 1
        m.pipe.0 = ax
        m.pipe.ax.in  = jOpen(m.pipe.px.in, '<')
        m.pipe.ax.out = jOpen(m.pipe.px.out, '>')
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    oOut = m.pipe.ax.out
    if pos(oc, 'NYPFA') > 0 then do
        call jClose oOut
        if oc == 'Y' then
            m.pipe.ax.out = jOpen(m.pipe.1.out, '>')
        else if oc == 'P' then
            m.pipe.ax.out = jOpen(m.pipe.px.out, '>')
        else if oc == 'N' then
            m.pipe.ax.out = jOpen(Cat(), '>')
        else if oc == 'F' then
            m.pipe.ax.out = jOpen(o2file(aO), '>')
        else if oc == 'A' then
            m.pipe.ax.out = jOpen(o2file(aO), '>>')
        ox = ox+1; oc = substr(opts, ox, 1)
        end
    if oc \== ' ' then do
        call jClose m.pipe.ax.in
        if substr(opts, ox+1) = '' & oc \== 's' then
            ct = ''
        else
            ct = jOpen(Cat(), '>')
        lx = 3
        do forever
            if oc == 's' then do
                call jWrite ct, arg(lx)
                lx = lx + 1
                end
            else do
                if oc == 'p' then
                    i1 = m.pipe.px.in
                else if oc == '|' then
                    i1 = oOut
                else if oc == 'f' then do
                    i1 = arg(lx)
                    lx = lx + 1
                    end
                else
                    call err 'implement' oc 'in pipe' opts
                if ct \== '' then
                    call jWriteAll ct, o2File(i1)
                end
            ox = ox + 1
            if substr(opts, ox, 1) == ' ' then
                leave
            else if ct == '' then
                call err 'pipe loop but ct empty'
            else
                oc = substr(opts, ox, 1)
            end
        if ct == '' then
            m.pipe.ax.in = jOpen(o2file(i1), '<')
        else
            m.pipe.ax.in = jOpen(jClose(ct), '<')
        if lx > 3 & lx <> arg() + 1 then
            call err 'pipe opts' opts 'but' arg() 'args not' (lx-1)
        end
    m.j.in  = m.pipe.ax.in
    m.j.out = m.pipe.ax.out
    return
endProcedure pipe

/*--- write all from rdr (rsp in) to out, not lazy ----------------*/
pipeWriteNow: procedure expose m.
    parse arg rdr
    call jWriteNow m.j.out, if(rdr == '', m.j.in, rdr)
    return
endProcedure pipeWriteNow

/*--- write all from rdr (rsp in) to out, possibly lazy -----------*/
pipeWriteAll: procedure expose m.
parse arg rdr
    call jWriteAll m.j.out, if(rdr == '', m.j.in, rdr)
    return
endProcedure pipeWriteAll

pipePreSuf: procedure expose m.
parse arg le, ri
    do while in()
        call out le || m.in || ri
        end
    return
endProcedure pipePreSuf

vIsDefined: procedure expose m.
parse arg na
    return   '' \== vAdr(na, 'g')
endProcedure vIsDefined

vWith: procedure expose m.
parse arg fun, o
    if fun == '-' then do
        tBe = m.v_with.0
        tos = tBe - 1
        if tos < 0 then
            call err 'pop empty withStack'
        m.v_with.0 = tos
        m.v_withMap = m.v_with.tos.map
        return m.v_with.tBe.obj
        end
    else if fun \== '+' then
        call err 'bad fun vWith('fun',' o')'
    par = m.v_with.0
    tos = par + 1
    m.v_with.0 = tos
    if symbol('m.v_with.tos.obj') == 'VAR' then
      if objClass(o) == objClass(m.v_with.tos.obj) then do
          m.v_with.tos.obj = o
          m.v_withMap = m.v_with.tos.map
          return
          end
    m.v_with.tos.obj = o
    if par > 0 then
        key = m.v_with.par.classes
    else
        key = ''
    if o \== '' then
        key = strip(key objClass(o))
    m.v_with.tos.classes = key
    if symbol('m.v_withManager.key') == 'VAR' then do
        m.v_with.tos.map = m.v_withManager.key
        m.v_withMap = m.v_withManager.key
        return
        end
    m = mapNew()
    m.v_with.tos.map = m
    m.v_withMap = m
    m.v_withManager.key = m
    do kx=1 to words(key)
        c1 = word(key, kx)
        call vWithAdd m, kx, classMet(c1, 'oFlds')
        call vWithAdd m, kx, classMet(c1, 'stms')
        end
    return
endProcedure vWith

vWithAdd: procedure expose m.
parse arg m, kx, ff
    do fx=1 to m.ff.0
        n1 = m.ff.fx
        dx = pos('.', n1)
        if dx > 1 then
            n1 = left(n1, dx-1)
        else if dx = 1 | n1 = '' then
            iterate
        call mPut m'.'n1, kx
        end
    return
endProcedure vWithAdd

vForWith: procedure expose m.
parse arg var
    call vWith '-'
    if \ vIn(var) then
        return 0
    call vWith '+', m.in
    return 1
endProcedure vForWith

vGet: procedure expose m.
parse arg na
    a = vAdr(na, 'g')
    if a = '' then
        call err 'undefined var' na
    return m.a
endProcedure vGet


vPut: procedure expose m.
parse arg na, val
    a = vAdr(na, 'p')
    m.a = val
    return val
endProcedure vPut

/*--- find the final address
      return f || a with address a and
             f = m -> mapGet(a), o -> obect m.a, s -> string m.a  ---*/
vAdr: procedure expose m.
parse arg na, f
    cx = 0
    cx = verify(na, '&>', 'm')
    if cx > 0 then
        a = left(na, cx-1)
    else do
        a = na
        cx = length(na)+1
        end
    nxt = 0
    do forever
        cy = verify(na, '&>', 'm', cx+1)
        if cy > 0 then
            fld = substr(na, cx+1, cy-cx-1)
        else
            fld = substr(na, cx+1)
        if substr(na, cx, 1) == '>' then do
            if nxt then
                a = vAdrByM(a)
            if fld \== '' then
               a = a'.'fld
            end
        else do
            if nxt then
                a = vAdrByM(a)
            mp = m.v_withMap
            aL = a
            if pos('.', a) > 0 then
                aL = left(a, pos('.', a)-1)
            if mp \== '' & symbol('m.mp.aL') == 'VAR' then do
                wx = m.mp.aL
                a = m.v_with.wx.obj'.'a
                end
            else if cx >= length(na) then
                return mapAdr(v, a, f)
            else
                a = mapAdr(v, a, 'g')
            if fld \== '' then
                a = vAdrByM(a)'.'fld
            end
        if cy < 1 then do
            if f == 'g' then
                if symbol('m.a') \== 'VAR' then
                    return ''
            return a
            end
        cx = cy
        nxt = 1
        end
endProcedure vAdr

vAdrByM:
parse arg axx
    if axx = '' then
        return err('null address at' substr(na, cx) 'in' na)
    if symbol('m.axx') \== 'VAR' then
        return err('undef address' axx 'at' substr(na, cx) 'in' na)
    ayy = m.axx
    if ayy == '' then
          return err('null address at' substr(na, cx) 'in' na)
    return ayy
endProcedure vAdrByM

vIn: procedure expose m.
parse arg na
    if \ in() then
       return 0
    if na \== '' then
       call vPut na, m.in
    return 1
endProcedure vIn

vRead: procedure expose m.    /* old name ????????????? */
parse arg na
    return vIn(na)

vHasKey: procedure expose m.
parse arg na
    return mapHasKey(v, na)

vRemove: procedure expose m.
parse arg na
    return mapRemove(v, na)
/* copy pipe end ******************************************************/
/* copy cat  begin ****************************************************
***********************************************************************/
/*--- create a new cat -----------------------------------------------*/
cat: procedure expose m.
    m = oNew('Cat') /* calls catReset */
    do ax=1 to arg()
        call catWriteAll m, arg(ax)
        end
    return m
endProcedure cat

catReset: procedure expose m.
parse arg m
    m.m.RWs.0 = 0
    m.m.catWr = ''
    m.m.catRd = ''
    m.m.catIx = -9e9
    m.m.catKeepOpen = ''
    return m
endProcedure catReset

catClose: procedure expose m.
parse arg m
    if m.m.catWr \== '' then do
        call jClose m.m.catWr
        call mAdd m'.RWS', m.m.catWr
        m.m.catWr = ''
        end
    if m.m.catRd \== '' then do
        call jClose m.m.catRd
        m.m.catRd = ''
        end
    m.m.catIx = -9e9
    return m
endProcedure catClose

catOpen: procedure expose m.
parse arg m, oo
    if oo == m.j.cRead then do
        m.m.catIx = 0
        call catNextRdr m
        m.m.jReading = 1
        end
    else if oo == m.j.cWri | oo == m.j.cApp then do
        if oo == m.j.cWri then
            m.m.RWs.0 = 0
        m.m.catIx = -9e9
        m.m.jWriting = 1
        end
    else do
        call err 'catOpen('m',' oo') bad opt'
        end
    return m
endProcedure catOpen

/*--- return and open next reader ------------------------------------*/
catNextRdr: procedure expose m.
parse arg m
    if m.m.catRd \== '' then
        call jClose m.m.catRd
    cx = m.m.catIx + 1
    m.m.catIx = cx
    if cx > m.m.RWs.0 then do
        m.m.catRd = ''
        return 0
        end
    m.m.catRd = m.m.RWs.cx
    if cx = word(m.m.catKeepOpen, 1) then
        m.m.catKeepOpen = subWord(m.catKeepOpen, 2)
    else
        call jOpen m.m.catRd , m.j.cRead
    return 1
endProcedure catNextRdr

catRead: procedure expose m.
parse arg m
    do while m.m.catRd \== ''
        cr = m.m.catRd
        if jRead(cr) then do
            m.m = m.cr
            return 1
            end
        call catNextRdr m
        end
    return 0
endProcedure catRead

catWrite: procedure expose m.
parse arg m, line
    if m.m.catWr == '' then
        m.m.catWr = jOpen(jBuf(), m.j.cWri)
    call jWrite m.m.catWr, line
    return
endProcedure catWrite

/*--- write contents of a reader to cat
          or keep it for later reading -------------------------------*/
catWriteAll: procedure expose m.
parse arg m
    if m.m.catWr \== '' then do
        call mAdd m'.RWS', jClose(m.m.catWr)
        m.m.catWr = ''
        end
    do ax=2 by 1 to arg()
        r = o2File(arg(ax))
        call mAdd m'.RWS', r
        if m.r.jReading then do
            m.m.catKeepOpen = m.m.rws.0 m.m.catKeepOpen
            call jOpen r, m.j.cRead
            end
        end
    return
endProcedure catWriteAll

/*--- create a reader/WriteO for an external file --------------------*/
file: procedure expose m.
parse arg str
    return oNew('File', str)
endProcedure file

fileChild: procedure expose m.
parse arg m, name, opt
    interpret objMet(m, 'fileChild')
endProcedure fileChild

fileRm: procedure expose m.
parse arg m
    interpret objMet(m, 'fileRm')
    return
endProcedure fileRm

filePath: procedure expose m.
parse arg m
    interpret objMet(m, 'filePath')
endProcedure filePath

fileIsFile: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsFile')
endProcedure fileIsFile

fileIsDir: procedure expose m.
parse arg m
    interpret objMet(m, 'fileIsDir')
endProcedure fileIsDir

fileMkDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileMkDir')
    return
endProcedure fileRm

fileRmDir: procedure expose m.
parse arg m, opt
    interpret objMet(m, 'fileRmDir')
    return
endProcedure fileRm

/*--- create a reader/WriteO for the filelist of a directory----------*/
fileList: procedure expose m.
parse arg m, opt
    str = oIfStr(m, '')
    if str == '' then
        return oNew('FileList', filePath(m),  opt)
    else
        return oNew('FileList', dsn2Jcl(str),  opt)
endProcedure fileList

catIni: procedure expose m.
    if m.cat.ini == 1 then
        return
    m.cat.ini = 1
    call errIni
    call jIni
    call classNew "n Cat u JRW", "m",
        , "jOpen  call catOpen m, opt",
        , "jReset call catReset m, arg",
        , "jClose call catClose m",
        , "jRead return catRead(m)",
        , "jWrite call catWrite m, line; return",
        , "jWriteAll call catWriteAll m, rdr; return"

    if m.err.os == 'TSO' then
        call fileTsoIni
    else if m.err.os == 'LINUX' then
        call fileLinuxIni
    else
        call err 'file not implemented for os' m.err.os
    return
endProcedure catIni
/* copy cat  end   ****************************************************/
/* copy mail begin ***************************************************/
mailHead: procedure expose m.
parse arg m, subj, rec, snd
    m.m.1  = 'sender='if(snd=='', userid(), snd)
    m.m.2  = 'type=TEXT/HTML'
    m.m.3  = 'to='rec
    m.m.4  = 'subject='subj
    m.m.5  = 'SEND=Y'
    m.m.6  = 'TEXT=<HTML>'
    m.m.7  = 'TEXT=<HEAD>'
    m.m.8  = 'TEXT=</HEAD>'
    m.m.9  = 'TEXT=<BODY>'
    m.m.10 = 'TESTINFO=Y'
    m.m.0 = 10
    return m
endProce4 re mailHead

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

mailSend: procedure expose m.
parse arg m, dsn
    call mAdd m,'INFO=Y' ,
               ,'TEXT=</BODY>' ,
               ,'TEXT=</HTML>'
    call dsnAlloc 'dd(mailIn)' if(dsn<> '', dsn, 'new') '::v4092'
    call writeDD mailIn, 'M.'m'.'
    call tsoClose mailIn
    dsnOs3560 = 'PCL.U0000.P0.'iirz2dsn(sysVar(sysNode)) ,
          ||    'AKT.PERM.@008.LLB'
    call adrTSO "TLIB ADD DATASET('"dsnOs3560"') STEPLIB"
    address LINKMVS 'OS3560'
    if rc <> 0 then
        call err 'call OS3560 failed Rc('rc')'
    call adrTSO "TLIB delete DATASET('"dsnOs3560"') STEPLIB"
    call tsoFree mailIn
    return 0
endProcedure mailSend
/* copy mail end *****************************************************/
/* copy fiLinux begin *************************************************/
/*--- send ggShCmd to shell bash,
        fail if rc <> 0 or not listed in ggRet -----------------------*/
adrSh: procedure expose m.  /* really no need for variables???? */
    parse arg ggShCmd, ggRet
    address 'bash' ggShCmd
    if rc == 0                     then return 0
    else if ggRet == '*'           then return rc
    else if wordPos(rc, ggRet) > 0 then return rc
    else
        call err 'adrSh rc' rc 'for' ggShCmd
endProcedure adrSh

fileLinuxReset: procedure expose m.
parse arg m, nm
    m.m.spec = nm
    if abbrev(nm, '&') then do
        if nm == '&in' then do
            m.m.stream = .input
            m.m.jReading = 1
            end
        else if nm == '&out' then do
            m.m.stream = .output
            m.m.jWriting = 1
            end
        else do
            call err 'bad spec' nm
            end
        end
    else do
        m.m.stream = .Stream%%new(nm)
        m.m.stream%%init(m.m.stream%%qualify)
        end
    return m
endProcedure fileLinuxReset

fileLinuxOpen: procedure expose m.
parse arg m, opt
    if opt == m.j.cRead then do
        res = m.m.stream%%open(read shareread)
        m.m.jReading = 1
        end
    else do
        if opt == m.j.cApp then
            res = m.m.stream%%open(write append)
        else if opt == m.j.cWri then
            res = m.m.stream%%open(write replace)
        else
            call err 'fileLinuxOpen('m',' opt') with bad opt'
        m.m.jWriting = 1
        end
    if res \== 'READY:' then
        call err 'fileLinuxOpen fails' res':' opt ,
        "'"m.m.stream%%qualify"'"
    return m
endProcedure fileLinuxOpen

fileLinuxClose:
parse arg m
    res = m.m.stream%%close
    if res \== 'READY:' then
        call err 'fileLinuxClose' res':' m.m.stream%%qualify
    return m
endProcedure fileLinuxClose

fileLinuxRead: procedure expose m.
parse arg m, var
    res = m.m.stream%%lineIn
    if res == '' then
        if m.m.stream%%state \== 'READY' then
            return 0
    m.var = res
    m.o.o2c.var = m.class_V
    return 1
endProcedure fileLinuxRead

fileLinuxWrite: procedure expose m.
parse arg m, line
    if m.m.stream%%lineOut(line) then
        call err 'fileLinuxWrite'
    return
endProcedure fileLinuxWrite

fileLinuxRmDir: procedure expose m.
parse arg m, opt
    if opt == '' then
        return adrSh('rmdir' m.m.spec)
    else if opt == '-r' then
        return adrSh('rm -r' m.m.spec)
    else
        call err 'bad opt' opt 'in fileLinuxRmDir'
endProcedure fileLInuxRmDir

fileLinuxListReset: procedure expose m.
parse arg m, m.m.spec, o
    if o == 'r' then
        m.m.opt = 'S'
    else if o == '' then
        m.m.opt = ''
    else
        call err 'bad opt' o 'in fileLinuxListReset'
    m.m.rx = 'closed'
    return m
endProcedure fileLinuxListReset

fileLinuxListOpen: procedure expose m.
parse arg m
    if m \== translate(m) then
        call err 'bad m for fileLinuxList:' m
    if m.m.opt == '' then
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST')
    else
        rc = sysFileTree(m.m.spec'/*', 'm.'m'.LIST', m.m.opt)
    if rc \== 0 then
        call err 'rc' rc 'in sysFileTree('m.m.spec', m.'m'.LIST)'
    m.m.rx = 0
    m.m.jReading = 1
    return m
endProcedure fileLinuxListOpen

fileLinuxListRead: procedure expose m.
parse arg m, var
    x = m.m.rx + 1
    if x > m.m.list.0 then
        return 0
    m.var = substr(m.m.list.x, 43)
    m.m.rx = x
    call oMutate var, m.class_V
    return 1
endProcedure fileLinuxListRead

fileLinuxIni: procedure expose m.
    if m.fileLinux.ini == 1 then
        return
    m.fileLinux.ini = 1
    m.file.sep = '/'
    call jIni
    call classNew "n File u JRW", "m",
        , "jReset call fileLinuxReset m, arg",
        , "jOpen  call fileLinuxOpen m, opt",
        , "jClose call fileLinuxClose m",
        , "jRead return fileLinuxRead(m, var)",
        , "jWrite call fileLinuxWrite m, line",
        , "filePath return m.m.stream%%qualify",
        , "fileIsFile return sysIsFile(m.m.stream%%qualify)" ,
        , "fileIsDir return sysIsFileDirectory(m.m.stream%%qualify)" ,
        , "fileChild return file(m.m.stream%%qualify'/'name)",
        , "fileRm return adrSh(m.m.spec)",
        , "fileMkDir return adrSh('mkdir' m.m.stream%%qualify)" ,
        , "fileRmDir return fileLinuxRmDir(m, opt)"
    call classNew "n FileList u JRW", "m",
        , "jReset call fileLinuxListReset m, arg, arg2",
        , "jOpen  call fileLinuxListOpen m, opt",
        , "jClose m.m.rx = 'closed'",
        , "jRead return fileLinuxListRead(m, var)"
    return
endProcedure fileLinuxIni
/* copy fiLinux end   *************************************************/
/* copy fileTso begin *************************************************/
fileTsoReset: procedure expose m.
parse arg m, sp
    m.m.readIx = 'c'
    if symbol('m.m.defDD') \== 'VAR' then do
        m.m.defDD = 'CAT*'
        m.fileTso.buf = m.fileTso.buf + 1
        m.m.buf = 'FILETSO.BUF'm.fileTso.buf
        m.m.spec = sp
        end
    if sp \== '' then do
        m.m.spec = dsnSpec(sp)
        rr = translate(subword(m.m.spec, 4))
        m.m.stripT = \ (pos(':V', rr) > 0 | pos('RECFM(V', rr) > 0)
        end
    return m
endProcedure fileTsoReset

fileTsoOpen: procedure expose m.
parse arg m, opt
    buf = m.m.buf
    if opt == m.j.cRead then do
        aa = dsnAlloc(m.m.spec, 'SHR', m.m.defDD)
        aDD = word(aa, 1)
        aDsn = m.tso_dsn.aDD
        if aDsn <> '' then
            if pos('(', aDsn) > 0 & pos('/', aDsn) < 1 then
                if sysDsn("'"m.tso_dsn.aDD"'") <> 'OK' then
                    call err 'cannot read' m.tso_dsn.aDD':',
                               sysDsn("'"m.tso_dsn.aDD"'")
        call tsoOpen word(aa, 1), 'R'
        m.m.jReading = 1
        m.buf.0 = -1
        m.m.readIx = 0
        end
    else do
        if opt == m.j.cApp then
            aa = dsnAlloc(m.m.spec, 'MOD', m.m.defDD)
        else if opt == m.j.cWri then
            aa = dsnAlloc(m.m.spec, 'OLD', m.m.defDD)
        else
            call err 'fileTsoOpen('m',' opt') with bad opt'
        call tsoOpen word(aa, 1), 'W'
        m.m.jWriting = 1
        m.buf.0 = 0
        m.m.readIx = 'w'
        end
    parse var aa m.m.dd m.m.free
    m.m.dsn = mGet('TSO_DSN.'m.m.dd)
    return m
endProcedure fileTsoOpen

fileTsoClose: procedure expose m.
parse arg m
    buf = m.m.buf
    if m.m.readIx \== 'c' then do
        if m.m.readIx == 'w' & m.buf.0 > 0 then
            call writeDD m.m.dd, 'M.'BUF'.', , m.m.tso_truncOk == 1
        call tsoClose m.m.dd
        call tsoFree  m.m.free
        end
    m.buf.0 = 'closed'
    m.m.readIx = 'c'
    m.m.free  = ''
    m.m.dd    = ''
    return m
endProcedure fileTsoClose

fileTsoRead: procedure expose m.
parse arg m
    ix = m.m.readIx + 1
    buf = m.m.buf
    if ix > m.buf.0 then do
        res = readDD(m.m.dd, 'M.'buf'.')
        if \ res then
            return 0
        ix = 1
        end
    m.m.readIx = ix
    m.m = m.buf.ix
 /* call oMutate var, m.class_V  ?????????? */
    return 1
endProcedure fileTsoRead

fileTsoWrite: procedure expose m.
parse arg m, var
    buf = m.m.buf
    ix = m.buf.0 + 1
    m.buf.0 = ix
    if m.m.stripT then
        m.buf.ix = strip(var, 't')
    else
        m.buf.ix = var
    if ix > 99 then do
        call writeDD m.m.dd, 'M.'buf'.', , m.m.tso_truncOk == 1
        m.buf.0 = 0
        end
    return
endProcedure fileTsoWrite

fileTsoWriteO: procedure expose m.
parse arg m, var
    if objClass(var, m.class_V) == m.class_V then do
        call fileTsoWrite m, m.var
        return
        end
    call err 'fileTsoWriteO('m',' var') cannot write objects of class',
                              objClass(var)
endProcedure fileTsoWriteO

fSub: procedure expose m.
    return file('.sysout(T) writer(intRdr)')
endProcedure fSub

fEdit: procedure expose m.
parse arg spec, vw
    if spec == '' then
        spec = 'new ::f'
    else if abbrev(spec, '::') then
        spec = 'new' spec
    else if abbrev(spec, ':') then
        spec = 'new' ':'spec
    f  = oNew('FileEdit', spec)
    m.f.editArgs = vw
    return f
endProcedure fEdit

fileTsoEditOpen: procedure expose m.
parse arg m, opt
    call fileTsoOpen m, opt
    m.m.maxL = tsoDSIMaxl(m.m.dd)
    return m
endProcedure fileTsoEditOpen

fileTsoEditClose: procedure expose m.
parse arg m
    dsn = m.m.dsn
    parse var m.m.editArgs eTy eAr
    upper eTy
    if abbrev('VIEW', eTy) then
        eTy = 'view'
    else do
        if \ abbrev('EDIT', eTy) then
            eAr = m.m.editArgs
        eTy = 'edit'
        end
                    /* parm uses a variable not text ||||*/
    cx = pos('PARM(', translate(eAr))
    cy = pos(')', eAr, cx+5)
    if cx > 0 & cy > cx then do
        macrParm = substr(eAr, cx+5, cy-cx-5)
        eAr = left(eAr, cx+4)'macrParm'substr(eAr, cy)
        end
    if dsn \== '' then do
        call fileTsoClose m
        call adrIsp eTy "dataset('"dsn"')" eAr, 4
        return
        end
    fr = m.m.free
    dd = m.m.dd
    m.m.free = ''
    call fileTsoClose m
    call adrIsp "LMINIT DATAID(lmmId) ddName("dd") ENQ(SHRW)"
    eRc = adrIsp(eTy "dataid("lmmId")" eAr, '*')
    lRc = adrIsp("LMFree DATAID("lmmId")", '*')
    call tsoFree fr
    if (eRc \== 0 & eRc \== 4) | lRc \== 0 then
        call err eTy eAr 'rc' eRc', lmFree rc' lRc
    return
endProcedure fileTsoEditClose

fileTsoIni: procedure expose m.
    if m.fileTso.ini == 1 then
        return
    m.fileTso.ini = 1
    m.file.sep = '.'
    m.fileTso.buf = 0
    call jIni
    um = "call err 'for tso undefined method'"
    call classNew "n File u JRW", "m",
        , "jOpen  call fileTsoOpen m, opt",
        , "jReset call fileTsoReset m, arg",
        , "jClose call fileTsoClose m",
        , "jRead return fileTsoRead(m, var)",
        , "jWrite call fileTsoWrite m, line",
        , "filePath return word(m.m.spec, 1)"           ,
        , "fileIsFile" um "'fileIsFile'"      ,
        , "fileIsDir   return 1"              ,
        , "fileChild   return file(word(m.m.spec, 1)'.'name opt)",
        , "fileRm"     um "'fileRm'"          ,
        , "fileMkDir"  ,
        , "fileRmDir"  um "'fileRmDir'"
 /*     , "filePath return m.m.stream%%qualify",
        , "fileIsFile return sysIsFile(m.m.stream%%qualify)" ,
        , "fileIsDir return sysIsFileDirectory(m.m.stream%%qualify)" ,
        , "fileChild return file(m.m.stream%%qualify'/'name)",
        , "fileRm return adrSh(m.m.spec)",
        , "fileMkDir return adrSh('mkdir' m.m.stream%%qualify)" ,
        , "fileRmDir return fileLinuxRmDir(m, opt)" */
    call classNew "n FileList u JRW", "m",
        , "jReset if arg2 == 'r' then m.m.dsnMask=arg'.**';",
                                "else m.m.dsnMask=arg'.*';",
        , "jOpen  call csiOpen m, m.m.dsnMask",
        , "jClose" ,
        , "jRead return csiNext(m, m)"
    call classNew "n FileEdit u File, f MAXL v", "m",
        , "jOpen  call fileTsoEditOpen  m, opt",
        , "jWrite call fileTsoWrite m, o2Text(line, m.m.maxL)",
        , "jClose call fileTsoEditClose m"
    return
endProcedure fileTsoIni
/* copy fileTso end   *************************************************/
/* copy mat begin *****************************************************/
sqrt: procedure expose m.
parse arg n
    if n < 2 then
        return n
     k = 1
     g = n
     do while k+1 < g
         m = (g + k) % 2
         if m * m <= n then
             k = m
         else
             g = m
         end
     return k
endProcedure sqrt

isPrime: procedure expose m.
parse arg n
    if n < 2 then
        return 0
    if n // 2 = 0 then
        return n = 2
    do q=3 by 2 to sqrt(n)
        if n // q = 0 then
            return 0
        end
    return 1
endProcedure isPrime

nxPrime: procedure expose m.
parse arg n
    do i = n + (\ (n // 2)) by 2
        if isPrime(i) then
            return i
        end
endProcedure nxPrime

permut: procedure expose m.
parse arg m, p
    m.m.1 = 1
    do i=2 while p > 0
        j = i - (p // i)
        m.m.i = m.m.j
        m.m.j = i
        p = p % i
        end
    m.m.0 = i-1
    return i-1
endProcedure permut
/* copy mat end   *****************************************************/
/* copy sqlDiv begin **************************************************/
/*--- generate the format m for a sql cx as specified in sp
          use the information from the sqlDa -------------------------*/
sqlFTabReset: procedure expose m.
parse arg ff, c, maxCh, maxBlo, maxDe
    return sqlFTabOpts(fTabReset(ff, , , '-'), c, maxCh, maxBlo, maxDe)

sqlFTabOpts: procedure expose m.
parse arg ff, cx, m.ff.maxChar, m.ff.blobMax, m.ff.maxDec
    if m.ff.maxChar == '' then
        m.ff.maxChar = 32
    if m.ff.blobMax == '' then
        m.ff.blobMax = 200
    bf = '%-'max(m.ff.blobMax, 4)'C'
    m.ff.flds = ''
    m.ff.sqlX = cx
    m.ff.sqlOthers = 0
    m.ff.sql2fmt.384 = '%-10C' /* date    */
    m.ff.sql2fmt.388 = '%-8C'  /* time    */
    m.ff.sql2fmt.392 = '%-26C' /* timestamp */
    m.ff.sql2fmt.400 = 'c'     /* graphic string */
    m.ff.sql2fmt.404 = bf      /* BLOB           */
    m.ff.sql2fmt.408 = bf      /* CLOB           */
    m.ff.sql2fmt.412 = bf      /* DBCLOB         */
    m.ff.sql2fmt.448 = 'c'     /* varchar        */
    m.ff.sql2fmt.452 = 'c'     /* char           */
    m.ff.sql2fmt.452 = 'c'     /* long varchar   */
    m.ff.sql2fmt.460 = 'c'     /* null term. string */
    m.ff.sql2fmt.464 = 'c'     /* graphic varchar   */
    m.ff.sql2fmt.468 = 'c'     /* graphic char      */
    m.ff.sql2fmt.472 = 'c'     /* long graphic varchar   */
    m.ff.sql2fmt.480 = '%-7e'  /* float                  */
    m.ff.sql2fmt.484 = 'd'     /* packed decimal         */
    m.ff.sql2fmt.492 = '%20i'  /* bigInt                 */
    m.ff.sql2fmt.496 = '%11i'  /* int                    */
    m.ff.sql2fmt.500 = '%6i'   /* smallInt               */
    m.ff.sql2fmt.904 = '%-34H' /* rowID 17 Byte Binary   */
    return ff
endProcedure sqlFTabOpts
/*--- set a defaultFormat for type tx in fTab ff ---------------------*/
sqlFTabDef: procedure expose m.
parse arg ff, tx, m.ff.sql2fmt.tx
return ff

sqlFTabAdd: procedure expose m.
parse arg m, c1 aDone, f1, l1
    if symbol('m.m.set.c1') == 'VAR' then do
        sx = m.m.set.c1
        if word(m.m.set.sx, 1) == c1 & sx <= m.m.set.0 then do
            parse var m.m.set.sx c1 aDone
            f1 = m.m.set.sx.fmt
            l1 = m.m.set.sx.labelTi
            end
        end
    cx = m.m.sqlX
    f2x = classMet(sqlFetchClass(cx), 'f2x')
    if symbol('m.f2x.c1') \== 'VAR' then
        call err 'colName not found' c1
    kx = m.f2x.c1
    t1 = m.sql.cx.d.kx.sqlName
    if l1 == '' then
        l1 = t1
    if f1 == '' then do
        ty = m.sql.cx.d.kx.sqlType
        le = m.sql.cx.d.kx.sqlLen
        withNulls = ty // 2
        ty = ty - withNulls
        if symbol('m.m.sql2fmt.ty') <> 'VAR' then
            call err 'sqlType' ty 'col' c1 'not supported'
        f1 = m.m.sql2fmt.ty
        if f1 == 'c' then
            f1 = '%-'min(le, m.m.maxChar)'C'
        else if f1 == 'd' then do
            trace ?r
            pr =  le % 256
            de =  le // 256
            f1 = '%'pr'.'de'i'
            end
        if \ abbrev(f1, '%') then
            call err 'sqlType' ty 'col' c1 'bad format' f1
        end
    call fTabAddRCT m, c1 aDone, f1, t1, l1
    ox = m.m.0
    m.m.ox.tit.0 = max(arg()-3, 1)
    do tx=2 to m.m.ox.tit.0
        m.m.ox.tit.tx = arg(tx+3)
        end
    return m
endProcedure sqlFTabAdd

sqlFTabOthers: procedure expose m.
parse arg m, doNot
    cx = m.m.sqlX
    ff = m.sql.cx.fetchFlds
    m.m.sqlOthers = 1
    do kx=1 to m.sql.cx.d.sqlD
        c1 = word(ff, kx)
        wx = wordPos(c1, m.m.cols)
        if (wx < 1 | m.m.wx.done \== 1) & wordPos(c1, doNot) < 1 then
            call sqlFTabAdd m, c1
        end
    return m
endProcedure sqlFTabOthers

sqlFTab: procedure expose m.
parse arg m
    call fTabBegin m
    do while sqlRxFetch(m.m.sqlX, 'sqlFTab')
        call out f(m.m.fmt, 'sqlFTab')
        end
    return fTabEnd(m)
endProcedure sqlFTab

sqlFTabCol: procedure expose m.
parse arg m
    if pos('c', m.m.generated) < 1 then
        call fTabColGen m
    do rx=1 while sqlRxFetch(m.m.sqlX, 'sqlFTab')
        call out left('--- row' rx '',  80, '-')
        call fTabCol m, 'sqlFTab'
        end
    call out left('--- end of' (rx-1) 'rows ', 80, '-')
    return
endProcedure sqlFTabCol

sqlCatTb: procedure expose m.
parse arg ty gOnly, wh, ord, fTab, paPlus
    tb = tkrTable(, ty)
    if gOnly == 1 then
        edFun = ''
    else
        edFun = tkrTable(, ty, 'e')
    cx = 1
    ft = 'ft'm.tb.alias
    call sqlFTabOpts FTabReset(ft, 'c 1', '1 c', '-'),
                     ,cx , 12, if(fTab, , 2000)
    call sqlFTabDef      ft, 492, '%7e'
    call FTabSet         ft, 'CONTOKEN'  , '%-16H'
    call FTabSet         ft, 'DCONTOKEN'  , '%-16H'
    call FTabSet         ft, 'DBNAME'    , '%-8C', 'db'
    call FTabSet         ft, 'DSNAME'    , '%-44C'
    call FTabSet         ft, 'DSNUM'     , '%5i'
    call FTabSet         ft, 'PARTITION' ,'%5i' , 'part'
    call FTabSet         ft, 'PIT_RBA'   , '%-12H'
    call FTabSet         ft, 'RBA1'      , '%-12H'
    call FTabSet         ft, 'RBA2'      , '%-12H'
    call FTabSet         ft, 'START_RBA' ,'%-12H'
    call FTabSet         ft, 'TSNAME'    , '%-8C', 'ts'
    call FTabSet         ft, 'VERSION'   , '%-28C'
    if edFun \== '' then do
        interpret 'sq =' edFun'(ft, tb, wh, ord)'
        end
    else do
        cl = sqlColList(m.tb.table, m.ft.blobMax)
        sq = 'select' cl tkrTable( , tb, 'f') wh ,
             'order by' if(ord=='', m.tb.order, ord)
        call sqlQuery cx, sq
        call sqlFTabOthers ft
        call sqlCatTbVl ft, tb
        end
    if fTab then
        call sqlFTab ft
    else
        call sqlFTabCol ft
    call sqlClose cx
    call sqlCatTbTrailer space(m.TKR.path paPlus, 1), sq
    return 0
endProcedure sqlCatTb

sqlCatTbVlsep:
    return '+++'

sqlCatTbVl: procedure expose m.
parse arg ft, tb, sep
    if sep == '' then
        sep = sqlCatTbVLsep()
    if m.tb.vlKey == '' then
        return
    ky = m.tb.vlKey
    ff = ''
    tt = ''
    do kx=1 to m.ky.0
        tt = tt || sep || m.ky.kx.col
        ff = ff || sep'@'m.ky.kx.col'%S'
        end
    call fTabAddRCT ft, substr(tt,length(sep)+1) ,
          , substr(ff,length(sep)+1)
    return
endProcedure sqlCatTbVl

sqlCatTbTrailer: procedure expose m.
parse arg pa, sq
    ox = lastPos(' order by ', sq)
    if ox < 1 then
        call err 'order by not found in' sq
    ord = substr(sq, ox+10)
    sq = left(sq, ox-1)
    sqUp = translate(sq)
    call out ''
    call out 'dbSys:' m.sql_dbSys
    call out 'path:' pa
    int = ''
    iNx = '  '
    br = ''
    cx = 1
    stops = '(select from where'
    do while cx < length(sq)
        nx = -1
        do sx=1 to words(stops)
            n2 = pos(word(stops, sx), sq, cx+1)
            if n2 > cx & (nx < 1 | n2 < nx) then
                nx = n2
            end
        if nx < 0 then
            leave
        call out int || substr(sq, cx, nx-cx)
        int = iNx
        if substr(sq, nx, 3) = '(se' then do
            iNx = iNx'  '
            br = left(br, length(int))')'
            end
        cx = nx
        end
    ll =  strip(substr(sq, cx))
    bq = strip(br)
    do while bq <> ''
        if right(bq, 1) \== ')' | right(ll, 1) \== ')' then
           call err 'missing ) bq:' bq', ll:' ll
        ll = strip(left(ll, length(ll) - 1))
        bq = strip(left(bq, length(bq) - 1))
        end
    call out int || ll
    if br <> '' then
        call out br
    if ord <> '' then
        call out '  order by' ord
    return
endProcedure sqlCatTbTrailer

sqlCatIxKeys: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select ikK.colSeq, ikK.colName, ikK.ordering, ikK.period' ,
             ', ik.creator, ik.name, ik.tbCreator, ik.tbName, ikC.*'  ,
          tkrTable(, tb ,'f') wh,
          'order by' if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, CREATOR, '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME   , '%-16C','index'
    call sqlFTabAdd      ft, colSeq  , '%5i',  'coSeq'
    call sqlFTabAdd      ft, colName, '%-16C', 'column'
    call sqlFTabAdd      ft, ordering
    call sqlFTabAdd      ft, period
    call sqlFTabAdd      ft, COLNO
    call sqlFTabAdd      ft, COLTYPE
    call sqlFTabAdd      ft, LENGTH
    call sqlFTabAdd      ft, SCALE
    call sqlFTabAdd      ft, NULLS
    call sqlFTabOthers ft, 'COL9 COL10 COL11 COL47'
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatIxKeys

sqlCatIXStats: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select *' tkrTable( , tb, 'f') wh ,
         'order by' if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, CREATOR, '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME   ,       , 'index'
    call sqlFTabAdd      ft, INSTANCE   , '%1i' , 'i'
    call sqlFTabAdd      ft, PARTITION , , 'part'
    call sqlFTabOthers ft
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatIXStats

sqlCatTables: procedure expose m.
parse arg ft, tb, wh, ord
    al = m.tb.alias
    sq = 'select' al'.*, tsX.type tsType, tsX.partitions',
            ', tsX.pgSize, tsX.dsSize' ,
            ', timestamp(rba1 || x''0000'') rba1Tst' ,
            ', timestamp(rba2 || x''0000'') rba2Tst' ,
          'from' m.tb.table 'left join sysibm.sysTablespace tsX',
            'on' al'.dbName = tsx.dbName and' al'.tsName = tsX.name',
            'where' m.tb.cond wh ,
            'order by'  if(ord == '', m.tb.order, ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, creator   , '%-8C', 'creator'
    call sqlFTabAdd      ft, NAME      , '%-24C', 'table'
    call sqlFTabAdd      ft, type
    call sqlFTabAdd      ft, dbNAME    , '%-8C', 'db'
    call sqlFTabAdd      ft, tsNAME    , '%-8C', 'ts'
    call sqlFTabAdd      ft, tsType
    call sqlFTabAdd      ft, partitions,       , 'parts'
    call sqlFTabAdd      ft, pgSize
    call sqlFTabAdd      ft, dsSize
    call sqlFTabOthers ft, 'RBA1 RBA1TST RBA2 RBA2TST'
    call sqlFTabAdd      ft, rba1      , '%-12H'
    call sqlFTabAdd      ft, rba1Tst   ,       , 'rba1Timestamp:GMT'
    call sqlFTabAdd      ft, rba2      , '%-12H'
    call sqlFTabAdd      ft, rba2Tst   ,       , 'rba2Timestamp:GMT'
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatTables

sqlCatTSStats: procedure expose m.
parse arg ft, tb, wh, ord
    sq = 'select' m.tb.alias'.*' ,
           tkrTable( , tb, 'f') wh ,
           'order by' if(ord == '', m.tb.order , ord)
    call sqlQuery m.ft.sqlX, sq
    call sqlFTabAdd      ft, DBNAME, '%-8C', 'db'
    call sqlFTabAdd      ft, NAME   , '%-8C', 'ts'
    call sqlFTabAdd      ft, INSTANCE   , '%1i' , 'i'
    call sqlFTabAdd      ft, PARTITION , , 'part'
    call sqlFTabAdd      ft, NACTIVE   , , 'nActive'
    call sqlFTabAdd      ft, NPAGES    , , 'nPages'
    call sqlFTabAdd      ft, SPACE       , , 'spaceKB'
    call sqlFTabAdd      ft, TOTALROWS   , , 'totRows'
    call sqlFTabAdd      ft, DATASIZE         , , 'dataSz'
    call sqlFTabAdd      ft, LOADRLASTTIME    , , 'loadRLasttime'
    call sqlFTabAdd      ft, REORGLASTTIME    , , 'reorgLasttime'
    call sqlFTabAdd      ft, REORGINSERTS     , , 'inserts'
    call sqlFTabAdd      ft, REORGDELETES     , , 'deletes'
    call sqlFTabAdd      ft, REORGUPDATES     , , 'updates'
    call sqlFTabAdd      ft, REORGUNCLUSTINS  , , 'unClIns'
    call sqlFTabAdd      ft, REORGDISORGLOB   , , 'disorgL'
    call sqlFTabAdd      ft, REORGMASSDELETE  , , 'massDel'
    call sqlFTabAdd      ft, REORGNEARINDREF  , , 'nearInd'
    call sqlFTabAdd      ft, REORGFARINDREF   , , 'farInd'
    call sqlFTabAdd      ft, REORGCLUSTERSENS , , 'cluSens'
    call sqlFTabAdd      ft, REORGSCANACCESS  , , 'scanAcc'
    call sqlFTabAdd      ft, REORGHASHACCESS  , , 'hashAcc'
    call sqlFTabAdd      ft, STATSLASTTIME    , , 'statsLasttime'
    call sqlFTabAdd      ft, STATSINSERTS     , , 'inserts'
    call sqlFTabAdd      ft, STATSDELETES     , , 'deletes'
    call sqlFTabAdd      ft, STATSUPDATES     , , 'updates'
    call sqlFTabAdd      ft, STATSMASSDELETE  , , 'massDel'
    call sqlFTabAdd      ft, COPYLASTTIME     , , 'copyLasttime'
    call sqlFTabAdd      ft, COPYUPDATETIME   , , 'copyUpdatetime'
    call sqlFTabAdd      ft, COPYUPDATELRSN   , '%-12H', 'updateLRSN'
    call sqlFTabAdd      ft, COPYUPDATEDPAGES , , 'updaPgs'
    call sqlFTabAdd      ft, COPYCHANGES      , , 'changes'
    call sqlFTabOthers ft
    call sqlCatTbVl ft, tb
    return sq
endProcedure sqlCatTSStats

sql4obj: procedure expose m.
parse arg m, tb
    call out 'insert into' tb '--' className(objClass(m))
    line = ''
    ff = oFlds(m)
    pr = '   ('
    do fx=1 to m.ff.0
        call sql4ObjOut m.ff.fx
        end
    call sql4ObjOut , 1
    call out '   ) values '
    pr = '   ('
    do fx=1 to m.ff.0
        f1 = m || left('.', m.ff.fx  \== '')m.ff.fx
        v = m.f1   /* no strip T, gives errors in RCM profile | */
        if dataType(v, n) then
            call sql4ObjOut v
        else do qx=1 until v == ''
            vx = verify(v, m.ut_alfPrint)
            if vx = 0 then do
                l1 = min(60, length(v))
                w = quote(left(v, l1), "'")
                end
            else if vx > 29 then do
                l1 = min(60, vx-1)
                w = quote(left(v, l1), "'")
                end
            else do
                l1 = min(29, length(v))
                w = 'x'quote(c2x(left(v, l1)), "'")
                end
            if qx == 1 then
                call sql4ObjOut w
            else do
                if qx = 2 then
                    call sql4ObjOut , 1
                call out '   ||' w
                end
            v = substr(v, l1+1)
            end
        end
    call sql4ObjOut , 1
    call out '   ) ; '
    return
endProcedure
sql4objOut:
parse arg t1, force
    if (force == 1 & line \== '') | length(line t1) > 65 then do
        call out pr  substr(line, 3)
        pr = '   ,'
        line = ''
        end
    if force \== 1 then
        line = line',' t1
    return
endProcedure sql4objOut
/*--- -dis db interface ---------------------------------------------*/
/*--- do one -dis db... and insert it into stem  --------------------*/
sqlDisDb: procedure expose m.
parse upper arg o, cc
    do cx=1
        mid = strip(left(m.cc.cx, 10))
        if words(mid) > 1 then
            call err 'bad msgId' mid 'line:' m.cc.cx
        if mid == '' | wordPos(mid, 'DSNT360I DSNT361I DSNT365I') ,
                > 0 then
            iterate
        if mid == 'DSN9022I' then
            if cx = m.cc.0 then
                return m.o.0
            else
                call err 'not at end' cx':' m.cc.cx
        if mid \== 'DSNT362I' then
            call err 'DSNT362I expected not line:' m.cc.cx
        dx = pos('DATABASE =', m.cc.cx)
        sx = pos('STATUS ='  , m.cc.cx)
        if dx < 1 | sx <= dx then
            call err 'bad DSNT362I line' cx':' m.cc.cx
        db = word(substr(m.cc.cx, dx+10), 1)
        sta = strip(substr(m.cc.cx, sx+8))
        call sqlDisDbAdd o, db, ,0, 0, 'DB', sta
        do cx=cx+1 while abbrev(m.cc.cx, '   ')
            end
        if abbrev(m.cc.cx, 'DSNT397I ') then do
            cx = cx + 1
            if \ abbrev(space(m.cc.cx, 1),
                 , 'NAME TYPE PART STATUS ') then
                call err 'NAME TYPE PART STATUS mismatch' cx m.cc.cx
            txNa = pos('NAME', m.cc.cx)
            txTy = pos('TYPE', m.cc.cx)
            txPa = pos('PART', m.cc.cx)
            txSt = pos('STAT', m.cc.cx)
            txEn = verify(m.cc.cx, ' ', 'n', txSt+6)
            if 0 then say 'title' txNa txTy txPa txSt txEn cx m.cc.cx
            cx=cx+1
            do forever
                do while abbrev(m.cc.cx, '----')
                    cx = cx + 1
                    end
                if abbrev(m.cc.cx, '*') then
                    leave
                parse var m.cc.cx sp =(txTy)  ty . =(txPa)  paFr . ,
                                       =(txSt) sta   =(txEn)
                sp = strip(sp)
                if words(sp) \= 1 | wordPos(ty, 'TS IX') < 0 then
                    call err 'bad name or type' cx':'m.cc.cx
                if paFr == '' | paFr == 'L*' then
                    paFr = 0
                else if abbrev(paFr, 'D') | abbrev(paFr, 'L') then
                    paFr = substr(paFr, 2)
                if \ datatype(paFr, 'n') then
                    call err 'part not numeric' cx':'m.cc.cx
                paTo = paFr
                cw = cx
                cx = cx + 1
                if abbrev(m.cc.cx, '    -THRU ') then do
                    parse var m.cc.cx =(txPa)  paTo . =(txSt)
                    if \ datatype(paTo, 'n') then
                        call err '-thru part not numeric' cx':'m.cc.cx
                    cx = cx + 1
                    end
                call sqlDisDbAdd o, db, sp, paFr, paTo, ty, sta
                end
            end
        if m.cc.cx = '******** NO SPACES FOUND' then
            cx = cx + 1
        if abbrev(m.cc.cx, '******* DISPLAY OF DATABASE ') ,
          & word(m.cc.cx,5) == db then
            if word(m.cc.cx,6) == 'ENDED' then
                iterate
            else if word(m.cc.cx,6) == 'TERMINATED' then
                call err 'db display overflow' cx':' m.cc.cx
        call err 'database' db 'ENDED mismatch' cx':' m.cc.cx
        end
endProcedure sqlDbDis

/*--- insert one tuple into tDbState ---------------------------------*/
sqlDisDbAdd: procedure expose m.
    if arg(7) == '' | arg(7) == 'RW' then
         return
parse arg o
    m.o.0 = m.o.0 + 1
    q = o'.'m.o.0
parse arg , m.q.db, m.q.sp, m.q.paFr, m.q.paTo, m.q.ty, m.q.sta
 /* say added q m.q.db'.'m.q.sp':'m.q.paFr'-'m.q.paTo m.q.ty':'m.q.sta*/
    ky = m.q.db'.'m.q.sp
    if symbol('m.o.ky') \== 'VAR' then
        m.o.ky = m.o.0
    return
endProceedure sqlDisDbAdd

/*--- get index in o for db sp part ----------------------------------*/
sqlDisDbIndex: procedure expose m.
parse arg st, d, s, pa
     if symbol('m.st.d.s') \== 'VAR' then
         return 0
     ix = m.st.d.s
     if ix > m.st.0 | d \== m.st.ix.db | s \== m.st.ix.sp then
         return 0
     if pa == '' then
         return ix
     do ix=ix to m.st.0 while d == m.st.ix.db & s == m.st.ix.sp
         if pa < m.st.ix.paFr then
             return 0
         else if pa <= m.st.ix.paTo then
             return ix
         end
     return 0
endProcedure sqlDisDbIndex

/*--- dsn Command, return true if continuation needed ----------------*/
sqlDsnCont: procedure expose m.
parse arg cc, ssid, cmd
    say '???dsnCont' cmd
    cont = sqlDsn(cc, ssid, cmd, 12) <> 0
    if cont then do
        cz = m.cc.0
        cy = cz - 1
        if \ abbrev(m.cc.cy, DSNT311I) ,
                | \ abbrev(m.cc.cz, 'DSN9023I') then
            call err 'sqlDsn rc=12 for' cmd 'out='cz ,
                     '\n'cy'='m.cc.cy'\n'cz'='m.cc.cz
        m.cc.0 = cz-2
        end
    return cont
endProcedure sqlDsnCont
/* copy sqlDiv end   **************************************************/
/* copy db2Cat begin **************************************************/
catTbLastCol: procedure expose m.
parse upper arg cr, tb
    return sql2one( ,
          "select strip(char(colcount)) || ' ' || strip(c.name) one"  ,
              "from sysibm.sysTables t left join sysibm.sysColumns c" ,
                  "on c.tbCreator = t.creator and c.tbName = t.name"  ,
                       "and c.colNo = t.colCount"                     ,
               "where t.creator = '"cr"' and t.name = '"tb"'",,,, 'r')
endProcedure catTbLastCol

catTbCols: procedure expose m.
parse upper arg cr, tb
    if sql2St("select strip(name) name "     ,
          "from sysibm.sysColumns " ,
          "where tbcreator = '"cr"' and tbname='"tb"'",
          "order by colNo", ggSt) < 1 then
        return ''
    res = m.ggst.1.name
    do cx=2 to m.ggst.0
        res = res m.ggst.cx.name
        end
    return res
endProcedure catTbCols

catTbColsTrunc: procedure expose m.
parse upper arg cr, tb, maxL
    if sql2St("select strip(name) name, colType, length, length2"     ,
          "from sysibm.sysColumns " ,
          "where tbcreator = '"cr"' and tbname='"tb"'",
          "order by colNo", ggSt) < 1 then
        return ''
    res = ''
    do cx=1 to m.ggst.0
        ty = m.ggSt.cx.colType
        if pos('LOB', ty) > 0 then
            res = res', substr('m.ggSt.cx.name', 1,' ,
                 min(maxL, m.ggSt.cx.length2)') 'm.ggSt.cx.name
        else if pos('CHAR', ty) > 0 & m.ggSt.cx.length > maxL then
            res = res', substr('m.ggSt.cx.name', 1,' maxL')',
                 m.ggSt.cx.name
        else
            res = res',' m.ggSt.cx.name
        end
    return substr(res, 3)
endProcedure catTbColsTrunc

catIxKeys: procedure expose m.
parse upper arg cr, ix
    sql = "select colSeq sq, colName col, ordering ord"       ,
              "from sysibm.sysKeys"                                   ,
               "where ixCreator = '"cr"' and ixName = '"ix"'" ,
               "order by colSeq"
    call sqlQuery 1, sql
    res = ''
    drop d
    do kx=1 while sqlFetch(1, d)
        if m.d.sq \= kx then
            call err 'expected' kx 'but got colSeq' m.d.sq ,
                     'in index' cr'.'ix'.'m.d.col
        res = res || strip(m.d.col) || translate(m.d.ord, '<>?', 'ADR')
        end
    call sqlClose 1
    return res
endProcedure catIxKeys

catColCom: procedure expose m.
parse upper arg fCr, fTb, tCr, tTb
    sql = "select t.name, t.colType, t.nulls, t.""DEFAULT"""        ,
                    ", coalesce(f.nulls, 'new')"                    ,
              "from sysibm.sysColumns t"                            ,
                "left join sysibm.sysColumns f"                     ,
                  "on f.tbCreator = '"fCr"' and f.tbName = '"fTb"'" ,
                    "and f.name = t.name"                           ,
              "where t.tbCreator = '"tCr"' and t.tbName = '"tTb"'"  ,
              "order by t.colNo"
    call sqlQuery 1, sql, 'na ty nu de nn'
    pr = ' '
    do kx=1 while sqlFetch(1)
        /* say kx m..na m..ty m..nu m..de 'nn' m..nn */
        if pos('CHAR', m..ty) > 0 then
            dv = "''"
        else if pos('INT' ,m..ty) > 0 ,
                | wordPos(m..ty, 'REAL FLOAT') > 0 then
            dv = 0
        else if m..ty == 'TIMESTMP' then
            dv = '0001-01-01-00.00.00'
        else if pos('LOB', m..ty) > 0 then
            dv = m..ty"('')"
        else
            dv = '???'
        if m..nu = 'Y' then
            dv = 'case when 1=0 then' dv 'else null end'
        r = '???'
        if m..ty = 'ROWID' then do
            r = '--'
            end
        else if m..nn == 'new' then do
            if m..de = 'Y' then
                r = '--'
            else if m..nu == 'N' then
                r = dv
            else
                r = 'case when 1=0 then' dv 'else null end'
            end
        else do
            if m..nu = 'Y' | (m..nu = m..nn) then
                r = ''
            else
                r = 'coalesce('m..na',' dv')'
            end
        if abbrev(r, '--') then do
            r = ' ' r
            end
        else do
            r = pr r
            pr = ','
            end
        if pos('???', r) > 0 then
            call err 'no default for type' m..ty 'in' tCr'.'tTb'.'m..na
        call out r m..na
        end
    call sqlClose 1
    return
endProcedure catColCom
/* copy db2Cat end   **************************************************/
/* copy sqlO   begin ***************************************************
    sql interface  mit  o und j Anbindung
***********************************************************************/
sqlIni: procedure expose m.
    if m.sql_ini == 1 then
        return
    m.sql_ini = 1
    call sqlRxIni
    call jIni
    call fTabIni
    call scanWinIni
    m.sqlO.cursors  = left('', 200)
    m.sql_rdrClass = classNew('n SqlRdr u JRW', 'm',
        , "jReset m.m.src = arg; m.m.type = arg2;",
        , "jOpen  call sqlRdrOpen m, opt",
        , "jClose call sqlRdrClose m",
        , "jRead return sqlRdrRead(m)")
    call classNew 'n SqlResRdr u JRW', 'm',
        , "jReset m.m.cursor = arg; m.m.type = arg2;",
        , "jOpen  call sqlRdrO2 m" ,
        , "jClose call sqlClose m.m.cursor" ,
        , "jRead return sqlRdrRead(m)"
    call classNew 'n SqlRxConnection u', 'm',
        , "sqlQuery  return sqlRxQuery(cx, src, feVa, 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, feVa, 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, feVa, retOK)",
        , "sqlFetch  return sqlCsmFetch(cx, dst, retOk)",
        , "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, feVa, retOk)",
        , "sqlFetch  return sqlCsmFetch(m.cx.cursor, dst, retOk)",
        , "sqlClose  return 0",
        , "sqlUpdate call err 'implement sqlCsmUpdate'"   ,
        , "sqlCall   call err 'implement sqlCsmCall'"
/*  call classNew 'n SqlExecuteRdr u JRW', 'm',
        , "jReset    call sqlExecuteRdrReset(m, arg, arg2)" ,
        , "jOpen     call sqlExecuteRdrOpen(m)" ,
        , "jClose    call sqlExecuteRdrClose(m)" ,
        , "jRead     call sqlExecuteRdrRead(m)"  ???????? */
    return 0
endProcedure sqlIni

/*--- connect to DB2 dsnRexx or csm ----------------------------------*/
sqlConnect: procedure expose m.
parse upper arg sys, retOk
    call sqlIni
    if sys == '' then
        sys = sqlDefaultSys()
    if pos('/', sys) <= 0 then do
        call  sqlRxConnect sys
        m.sql_connClass = class4Name('SqlRxConnection')
        end
    else do
        parse var sys m.sql_csmHost '/' m.sql_dbSys
        m.sql_connClass = class4Name('SqlCsmConnection')
        end
    return 0
endProcedure sqlConnect

/*--- disconnect from db2 --------------------------------------------*/
sqlDisconnect: procedure expose m.
parse arg retOk
    if m.sql_csmHost == '' then
        call sqlRxDisconnect
    else
        m.sql_csmHost = ''
    m.sql_dbSys = ''
    m.sql_connClass = 'sql not connected'
    return 0
endProcedure sqlDisonnect

/*--- execute a query from sql, with one resultset -------------------*/
sqlQuery: procedure expose m.
parse arg cx, src, feVa, retOk
    interpret classMet(m.sql_connClass, 'sqlQuery')
endProcedue sqlQuery
/*--- fetch next row to m.dst.* at end return false ------------------*/
sqlFetch: procedure expose m.
parse arg cx, dst, retOk
    interpret classMet(m.sql_connClass, 'sqlFetch')
endProcedue sqlFetch
/*--- close cursor 'c'cx ---------------------------------------------*/
sqlClose: procedure expose m.
parse arg cx, retOk
    interpret classMet(m.sql_connClass, 'sqlClose')
endProcedue sqlClose
/*-- execute an sql with no resultset, but possibly outParms ---------*/
sqlUpdate: procedure expose m.
parse arg cx, src, retOk
    interpret classMet(m.sql_connClass, 'sqlUpdate')
endProcedue sqlUpdate

/*-- execute an sql call with outParms and multiple resultSets -------*/
sqlCall: procedure expose m.
parse arg cx, src, retOk
    interpret classMet(m.sql_connClass, 'sqlCall')
endProcedure sqlCall

/*--- return a free cursor -------------------------------------------*/
sqlGetCursor: procedure expose m.
parse arg rng
    if rng == '' then
        return sqlGetCursorRng(rng, 10, 48)
    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

/*** execute sql's in a stream (separated by ;)
       opt: 'o' ==> write objects, otherwise fTabAuto
            'sql72' ==> spufi formatting (window 72) else linebreaks */
sqlStmts: procedure expose m.
parse arg sqlSrc, retOk, opt
   return sqlsOut(scanSqlStmtRdr(sqlSrc, opt), retOk, 'a')
   return
endProcedure sqlStmts

/*--- execute the given sql plus a commit
         until no more rows are updated -----------------------------*/
sqlUpdComLoop: procedure expose m.
parse arg src, retOk
    crs = sqlGetCursor()
    upds = 0
    if retOk == '' then
        retOk = 100
    do coms=0
        cd = sqlExecute(crs, src, retOk)
        if m.sql.crs.updateCount < 1 then do
            call sqlFreeCursor(crs)
            return sqlMsgLine( , upds, src, coms 'commits')
            end
        upds = upds + m.sql.crs.updateCount
        call sqlCommit
        if coms // 20 = 19 then
            say sqlMsgLine(time(), upds, src, (coms+1) 'commits')
        end
endProcedure sqlUpdComLoop

/*--- sql call statement ---------------------------------------------*/
sqlStmtCall: procedure expose m.
parse arg src, retOk, opt
    s = scanSqlReset(scanSrc(sqlstmtcall, 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.ut_alfDot) 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 fTabAuto 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 jRead(rdr)
         a = m.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

/*--- prepare and open cursor
      generate type and fetchList ------------------------------------*/
sqlRdr: procedure expose m.
parse arg src, type
     return oNew('SqlRdr', scanSqlIn2Stmt(src), type)
endProcedure sqlRdr

sqlRdrOpen: procedure expose m.
parse arg m, opt
    if opt\== m.j.cRead then
        call err 'opt not' m.j.cRead 'sqlRdrOpen('m',' opt')'
    cx = sqlGetCursor()
    m.m.cursor = cx
    if words(m.m.type) <> 1 | abbrev(m.m.type, ' ') then do
        m.sql.cx.fetchClass = ''
        call sqlQuery m.m.cursor, m.m.src, m.m.type
        m.m.type = sqlFetchClass(cx)
        end
    else do
        m.m.type = class4name(m.m.type)
        call sqlQuery m.m.cursor, m.m.src, mCat(classFlds(m.m.type),' ')
        m.sql.cx.fetchClass = m.m.type
        end
    call sqlRdrO2 m
    return
endProcedure sqlRdrOpen

sqlQuery2Rdr: procedure expose m.
parse arg cx
    r = jReset(oMutate('SQL_RDR.'cx, 'SqlResRdr'), cx)
    m.r.type = sqlFetchClass(cx)
    return r
endProcedure

sqlFetchClass: procedure expose m.
parse arg cx, force
     if m.sql.cx.fetchClass == '' | force == 1 then
          m.sql.cx.fetchClass = classNew('n* Sql u f%v' ,
              m.sql.cx.fetchFlds)
     return m.sql.cx.fetchClass
endProcedure sqlFetchClass

sqlRdrO2: procedure expose m.
parse arg m
    cx  = m.m.cursor
    if m.m.type \== m.sql.cx.fetchClass | m.m.Type == '' then
        call err 'type' m.m.type '\==' m.sql.cx.fetchClass 'fetchClass'
    m.m.rowCount = 0
    m.sql_lastRdr = m
    return
endProcedure sqlRdrO2

/*--- close sql Cursor -----------------------------------------------*/
sqlRdrClose: procedure expose m.
parse arg m, v
    call sqlClose m.m.cursor
    call sqlFreeCursor m.m.cursor
    m.m.cursor = ''
    return m
endProcedure sqlRdrClose

/*--- read next from cursor, return as object ------------------------*/
sqlRdrRead: procedure expose m.
parse arg m
    v = oNew(m.m.type)
    if \ sqlFetch(m.m.cursor, v) then do
        call mFree v
        return 0
        end
    m.m.rowCount = m.m.rowCount + 1
    m.m = v
    return 1
endProcedure sqlRdrRead

/*--- return sqlFTab for this (open) rdr -----------------------------*/
sqlRdrFtabReset: procedure expose m.
parse arg m, q, maxChar, blobMax, maxDec
    if m == '' then
        m = m.sql_lastRdr
    if \ dataType(m.m.cursor, 'n') then
        call err 'sqlRdrFTabReset('m') but cursor empty'
    return sqlFTabReset(q, m.m.cursor, maxChar, blobMax, maxDec)
endProcedure sqlRdrFTabReset

/*--- output sql as table --------------------------------------------*/
sql2tab: procedure expose m.
parse arg tBef, tAft, maxChar, blobMax, maxDec
    cx = sqlGetCursor()
    call sqlQuery cx, in2str(,' ')
    t = sqlFTabReset('SQL.'cx'.fTab', cx,
            , tBef, tAft, maxChar, blobMax, maxDec)
    call sqlFTab sqlFTabOthers(t)
    call sqlClose cx
    call sqlFreeCursor cx
    return
endProcedure sql2tab

/*--- select and write all to stdOut ---------------------------------*/
sqlSel: procedure expose m.
parse arg src, type
     s = sqlRdr(src, type)
     call pipeWriteAll s
     return m.s.rowCount
endProcedure sqlSel

/*--- result of each sql read from rdr to out
           oo='a' autoTab, 't'=sqlTab, 'o'=object, 'c'=csv ----------*/
sqlsOut: procedure expose m.
parse arg rdr, retOk, oo
    m.sql_errRet = 0
    if oo == '' then
        oo = 'a'
    cx = sqlGetCursor()
    r = jOpen(in2file(rdr), '<')

    do while jRead(r)
        sqlC = sqlExecute(cx, m.r, retOk)
        if m.sql_errRet then
            leave
        if m.sql.cx.resultSet == '' then do
             call outNl(m.sql_HaHi ,
                     || sqlMsgLine(sqlC, m.sql.cx.updateCount, m.r))
           end
        else if oo == 'o' then do
            call pipeWriteAll sqlQuery2Rdr(cx)
            end
        else if oo == 'a' | oo == 't' then do
            sqR = sqlQuery2Rdr(cx)
            ft = sqlfTabOpts(fTabReset('sqls2AutoFT', 'c 1'), cx)
            if oo == 't' then do
                call sqlFTabOthers(ft)
                end
            else do
                bf = in2Buf(sqR)
                if m.sql_errRet then
                    leave
                call sqlFTabDetect ft, bf'.BUF'
                call fTab ft, bf
                call out sqlMsgLine(m.sqR.rowCount 'rows fetched',
                                   , , m.r)
                end
            end
        else
            call err 'bad outputOption' oo
        end
    call jClose r
    if m.sql_errRet then do
   /*   call out 'sqlsOut terminating because of sql error' */
        call sqlClose cx, '*'
        say 'sqlsOut terminating because of sql error'
        end
    call sqlFreeCursor cx
    return \ m.sql_errRet
endProcedure sqlsOut

/*-- execute and put result to m -------------------------------------*/
sqlExecuteRes: procedure expose m.
parse arg m, cx, m.m.sql, retOk  ?????
    m.m.sqlCode = sqlExecute(cx, m.m.sql, retOk) + deimplement
    m.m.sqlMsg = m.sql_HaHi || sqlMsgLine(m.m.sqlCode,
                             , m.sql.cx.updateCount, m.m.sql)
endProcedure sqlExecuteRes
/*--- execute stmts with options -------------------------------------*/
sqlExecuteRdrReset: procedure expose m.
parse arg rdr, wOpt, m.m.retOk
    if abbrev(wOpt, '-sql') then  + deimplement  ??????????????????
        wOpt = substr(wOpt, 5)
    call scanSqlReset m'.SCAN', rdr, wOpt, ';'
    return m
endProcedure sqlExecuteRdrReset

sqlExecuteRdrOpen: procedure expose m.
parse arg m
    call scanOpt m'.SCAN'  + deimplement  ??????????????????
    m.m.cursor = sqlGetCursor()
    return m
endProcedure sqlExecuteRdrOpen

sqlExecuteRdrClose: procedure expose m.
parse arg m
    call scanOpt m'.SCAN'    + deimplement  ??????????????????
    call sqlFreeCursor m.m.cursor
    drop m.m.cursor
    return m
endProcedure sqlExecuteRdrClose

sqlExecuteRdrRead: procedure expose m.
parse arg m, var
    src = scanSqlStmt(m'.SCAN') + deimplement  ??????????????????
    if src == '' then
        return 0
    call sqlExecuteRes m, m.m.cursor, src, m.m.retOk
    m.var = m.m.cursor
    return 1
endProcedure sqlExecuteRdrRead

/* copy sqlO   end   **************************************************/
/* copy sqlCsm begin **************************************************/
/*--- send an sql to csm and handle sqlCode --------------------------*/
sqlCsmExe:
parse arg ggSqlStmt, ggRetOk
    sql_HOST =  m.sql_csmhost
    SQL_DB2SSID = m.sql_dbSys
    sql_query = ggSqlStmt
    address tso "CSMAPPC START PGM(CSMASQL)"
    if \ (rc = 0 |  rc = 4) then
        return 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\nsql =' ggSqlStmt
        else
            call errSay 'w}'sqlMsg(sqlCA2rx(sqlCa))
    return sqlCode
endProcedure sqlCsmExe

/*--- execute a query from sql, with one resultset -------------------*/
sqlCsmQuery: procedure expose m.
parse arg cx, sqlSrc, feVa, retOk, dst
    res = sqlCsmExe(sqlSrc, 100 retOk)
    if res < 0 then
        return res
    if dst == '' then
        dst = 'SQL.'cx'.CSMDATA'
    m.dst.0 = 0
    m.dst.laIx = 0
    st = 'SQL.'cx'.COL'
    if abbrev(feVa, '?') | abbrev(feVa, ':') then do
        return err('implement sqlCmsQuery fetchVars ? or :' feVa)
        end
    else if feVa <> '' then do
        vv = feVa
        end
    else do
        vv = ''
        do kx=1 to sqlD
            vv = sqlNiceVarsAdd(vv, SQLDA_REXXNAME.kx)
            end
        end
    m.sql.cx.fetchFlds = vv
    if sqlD <> words(vv) then
        return err('sqlCsmQuery sqlD' sqlD '<>' words(vv) 'for' vv)
    do kx=1 to sqlD
        rxNa = SQLDA_REXXNAME.kx
        cn = word(vv, kx)
        do rx=1 to sqlRow#
            if substr(sqlIndicator.rx, kx ,1) == 'ff'x then
                m.dst.rx.cn = m.sqlNull
            else
                m.dst.rx.cn = value(rxNa'.'rx)
            end
        end
    m.dst.0 = sqlRow#
    m.sql_lastRdr  = 'cms' cx
    return 0
endProcedure sqlCsmQuery

sqlCsmFetch: procedure expose m.
parse arg cx, dst
    src = 'SQL.'cx'.CSMDATA'
    rx = m.src.laIx + 1
    if rx > m.src.0 then
        return 0
    m.src.laIx = rx
    ff = m.sql.cx.fetchFlds
    do kx = 1 to words(ff)
        c = word(ff, kx)
        m.dst.c = m.src.rx.c
        end
    return 1
endProcedure sqlCsmFetch
/* copy sqlCsm end   **************************************************/
/* copy sqlRx  begin ***************************************************
       Achtung: inc generiert sql aus sqlRx, Aenderungen nur in sqlRx|
    sql interface
***********************************************************************/
/*--- initialize sqlRx -----------------------------------------------*/
sqlRxIni: procedure expose m.
    if m.sqlRx_ini == 1 then
        return
    m.sqlRx_ini = 1
    call utIni
    m.sqlNull = '---'
    m.sqlInd = 'sqlInd'
    m.sql_defCurs= 49
    m.sqlCAMsg = 0
    m.sqlSuMsg = 2
    m.sql_dbSys = ''
    m.sql_csmhost = ''
    isInProd = wordPos(sysvar(sysNode), 'RZ2') > 0
    m.sql_retOk   = 'dne' copies('rod', \ isInProd)
    return 0
endProcedure sqlRxIni

/*--- get default db2System ------------------------------------------*/
sqlDefaultSys: procedure expose m.
parse arg
    if sysvar(sysnode) == 'RZ1' then
        return 'DBAF'
    else if sysvar(sysnode) == 'RZ4' then
        return 'DP4G'
    else if sysvar(sysnode) == 'RZX' then
        return 'DX0G'
    else
        call err 'no default subsys for' sysvar(sysnode)
endProcedure sqlDefaultSys

/*--- 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
    if sys == '' then
        sys = sqlDefaultSys()
    m.sql_dbSys = sys
    ggSqlStmt =  'connect' sys
    address dsnRexx ggSqlStmt
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, ggRetOk, ggSqlStmt)
    return sqlCode
endProcedure sqlRxConnect

/*--- diconnect from db2 ---------------------------------------------*/
sqlRxDisconnect: procedure expose m.
parse arg retOk
    if m.sql_dbSys == '' then
        return 0
    ggSqlStmt =  'disconnect'
    m.sql_dbSys = ''
    m.sql_csmHost = ''
    address dsnRexx ggSqlStmt
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, retOk, ggSqlStmt)
    return sqlCode
endProcedure sqlRxDisconnect

/*--- reset sql cursor 'c'cx fields ----------------------------------*/
sqlReset: procedure expose m.
parse arg cx
     m.sql.cx.updateCount = ''
     m.sql.cx.resultSet   = ''
     m.sql.cx.d.sqlD = 'noSqlDA'
     m.sql.cx.i.sqlD = 'noDescInp'
     m.sql.cx.fetchVars = ''
     m.sql.cx.fetchFlds = ''
     m.sql.cx.fetchClass = ''
     m.sql.cx.type  = ''
     return
endProcedue sqlReset

/*--- execute a query from sql, with one resultset -------------------*/
sqlRxQuery: procedure expose m.
parse arg cx, src, feVa, retOk
     call sqlReset cx
     s1 = ''
     if pos(left(feVa, 1), '?:') < 1 then
         s1 = 'into :M.SQL.'cx'.D'
     res = sqlExec('prepare s'cx s1 'from :src', retOk)
     if res < 0 then
         return res
     call sqlRxFetchVars cx, feVa
     call sqlExec 'declare c'cx 'cursor for s'cx
     res = sqlExec('open c'cx, retOk)
     if res < 0 then
         return res
     m.sql.cx.updateCount = sqlErrd.3
     m.sql.cx.resultSet = cx
     return res
endProcedure sqlRxQuery

/*--- prepare a query from sql, with one resultset -------------------*/
sqlQueryPrepare: procedure expose m.
parse arg cx, src, feVa, retOk
     call sqlReset cx
     s1 = ''
     if feVa == '' | feVa = 'd' then
         s1 = 'into :M.SQL.'cx'.D'
     res = sqlExec('prepare s'cx s1 'from :src', retOk)
     if res < 0 then
         return res
     call sqlRxFetchVars cx, feVa
     call sqlExec 'declare c'cx 'cursor for s'cx
     return sqlExec('describe input s'cx 'into :M.SQL.'cx'.I')
endProcedure sqlQueryPrepare

sqlQueryExecute: procedure expose m.
parse arg cx retOk
    do ix=1 to arg()-1
        call sqlDASet cx , 'I', ix, arg(ix+1)
        end
     res = sqlExec('open c'cx 'using descriptor :M.SQL.'cx'.I', retOk)
     if res < 0 then
         return res
     m.sql.cx.updateCount = sqlErrd.3
     m.sql.cx.resultSet = cx
     return res
endProcedure sqlQueryExecute

/*--- fetch next row to m.dst.* at end return false ------------------*/
sqlRxFetch: procedure expose m.
parse arg cx, dst, retOk
    fetCode = sqlExec('fetch c'cx 'into' m.sql.cx.fetchVars, 100 retOk)
    if fetCode == 100 then
        return 0
    if fetCode < 0 then
        return fetCode
    interpret m.sql.cx.fetchCode
    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 sqlExec('execute immediate :src', retOk)
        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, retOk)
        end
    if fun == 'DECLARE'  then do
        if 'GLOBAL' == translate(word(substr(src, bx), 2)) then
            return sqlExec('execute immediate :src', retOk)
        end
    res = sqlExec(src, retOk)
    if wordPos(fun, 'DELETE INSERT UPDATE') > 0 then
        m.sql.cx.updateCount = sqlErrd.3
    return res
endProcedure sqlRxUpdate

/*-- prepare an update -----------------------------------------------*/
sqlUpdatePrepare: procedure expose m.
parse arg cx, src, retOk
    res = sqlExec('prepare s'cx 'from :src', retOk)
    return sqlExec('describe input s'cx 'into :M.SQL.'cx'.I')
endProcedure sqlUpdatePrepare

/*-- execute a prepared update with the given arguments --------------*/
sqlUpdateExecute: procedure expose m.
parse arg cx retOk
    do ix=1 to arg()-1
        call sqlDASet cx , 'I', ix, arg(ix+1)
        end
    res = sqlExec('execute s'cx 'using descriptor :M.SQL.'cx'.I',
                  , retOk)
    m.sql.cx.updateCount = sqlErrd.3
    return res
endProcedure sqlUpdateExecute

/*-- execute a query, update or call ---------------------------------*/
sqlExecute: procedure expose m.
parse arg cx, src, retOk
    f = translate(word(src, 1))
    bx = pos('(', f)
    if bx > 0 then
        f = left(f, max(1, bx-1))
    m.sql.cx.fun = f
    if f == 'SELECT' | f == 'WITH' | f == '(' 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

/*-- fetch all rows to stem and close --------------------------------*/
sqlFetch2St: procedure expose m.
parse arg cx, dst, retOK
    do sx=1 while sqlFetch(cx, dst'.'sx)
       end
    res = sx-1
    m.dst.0 = sx-1
    call sqlClose cx
    return m.dst.0
endProcedure sqlFetch2St

/*-- execute a query, copy result to stem ----------------------------*/
sql2St: procedure expose m.
parse arg src, dst, feVa, retOK
    cx = m.sql_defCurs
    res = sqlQuery(cx, src, feVa, retOk)
    return sqlFetch2St(cx, dst, retOk)
endProcedure sql2St

/*-- return first column of the only row and close -------------------*/
sqlFetch2One: procedure expose m.
parse arg cx, dst, retNone
    f1 = sqlFetch(cx, dst)
    if f1 then
        f2 = sqlFetch(cx, dst'.2')
    call sqlClose cx
    if \ f1 then
        if retNone \== '' then
            return substr(retNone, 2)
        else
            call err 'sqlFetch2One: no row returned'
    else if f2 then
        call err 'sqlFetch2One: more than 1 row'
    if m.sql.cx.fetchFlds == '' then do
        c1 = substr(word(m.sql.cx.fetchVars, 1), 2)
        res = value(c1)
        return res
        end
    c1 = word(m.sql.cx.fetchFlds, 1)
    return m.dst.c1
endProcedure sqlFetch2One

/*-- execute a query and return first column of the only row
           if > 1 row fail, if 0 rows return arg(3) or fail ----------*/
sql2One: procedure expose m.
parse arg src, dst, feVa, retOk, retNone
    cx = m.sql_defCurs
    call sqlQuery cx, src, feVa, retOk
    return sqlFetch2One(cx, dst, retNone)
endProcedure sql2One

/*--- describe table and return sqlDA --------------------------------*/
sqlDescribeTable: procedure expose m.
parse upper arg tb, force
    if force == 1 | \ datatype(m.sql.table.tb.sqlD, 'n') then
         call sqlExec 'describe table :tb into :M.SQL.TABLE.'tb
    return 'SQL.TABLE.'tb
endProcedure sqlDescribeTable

/*--- return select column list for table tb
      omit Blobs (blobMax < 0) or substr(blob, 1, blobMax) ----------*/
sqlColList: procedure expose m.
parse arg tb al, blobMax
    sd = sqlDescribeTable(tb)
    bs = ''
    lst = ''
    if al \== '' & right(al, 1) \== '.' then
        al = al'.'
    do sx=1 to m.sd.sqld
        if wordPos(m.sd.sx.sqlType, '404 405 408 409 412 413') < 1 then
            lst = lst',' al || m.sd.sx.sqlName
        else do
            bs = bs m.sd.sx.sqlName
            if blobMax >= 0 then
                lst = lst', length('al || m.sd.sx.sqlName')' ,
                                          m.sd.sx.sqlName'Len' ,
                     || ', substr('al  || m.sd.sx.sqlName ,
                     || ', 1,' blobMax')' m.sd.sx.sqlName
            end
        end
    m.sd.colList = substr(lst, 3)
    m.sd.blobs = strip(bs)
    return substr(lst, 3)
endProcedure sqlColList

/*--- use describe output to generate column names,
        ''         use names from ca (rexxified)
        nms+       use names, check ca for null values
        ?('?'?nm)+ use names, check for null if preceeded by ?
        :...       use string as is
                fetchVariables and sqlNull names ---------------------*/
sqlRxFetchVars: procedure expose m.
parse arg cx, src, cd
    st = 'SQL.'cx'.COL'
    if abbrev(src, ':') then do
        m.sql.cx.fetchVars = src
        m.sql.cx.fetchCode = cd
        m.sql.cx.fetchFlds = ''
        return
        end
    m.sql.cx.fetchVars = ''
    if abbrev(src, '?') then do
        call err  implement + rxFetchVars ?????? /*
        r = substr(src, 2)
        do wx=1 to words(src)
            cn = word(src, wx)
            if abbrev(cn, '?') then
                call sqlRexxAddVar substr(cn, 2), 0, 1
            else
                call sqlRexxAddVar cn, 0, 0
            end                              ????????????? */
        end
    else if src <> '' then do
        ff = src
        end
    else do
        ff = ''
        do kx=1 to m.sql.cx.d.sqlD
             ff = sqlNiceVarsAdd(ff, m.sql.cx.d.kx.sqlName)
             end
        end
    m.sql.cx.fetchFlds = ff
    if m.sql.cx.d.sqlD <> words(ff) then
        call err 'sqlRxFetchVars sqlD='m.sql.cx.d.sqlD ,
                '<>' words(ff) 'fields of' ff
    sNu = ''
    sFe = ''
    do kx=1 to m.sql.cx.d.sqlD
        nm = word(ff, kx)
        sFe = sFe', :m.dst.'nm
        if m.sql.cx.d.kx.sqlType // 2 then do
            sFe = sFe' :m.dst.'nm'.sqlInd'
            sNu = sNu 'if m.dst.'nm'.sqlInd < 0 then' ,
                   'm.dst.'nm '= m.sqlNull;'
            end
        end
    m.sql.cx.fetchVars = substr(sFe, 3)
    m.sql.cx.fetchCode = sNu cd
    return
endProcedure sqlRxFetchVars
/* ????????????
sqlRexxAddVar:
parse arg nm, nicify, hasNulls
     nm = sqlAddVar(st, nm, nicify)
     if \ hasNulls then
          vrs = vrs', :m.dst.'nm
     else do
         vrs = vrs', :m.dst.'nm ':m.dst.'nm'.sqlInd'
         sNu = sNu 'if m.dst.'nm'.sqlInd < 0 then' ,
                    'm.dst.'nm '= m.sqlNull;'
         end
    return
endSubroutine sqlRexxAddVar   ?????? */

sqlNiceVarsAdd: procedure expose m.
parse arg old, nx rest
    upper nx
    cx = verifId(nx)
    if cx > 0 then /* avoid bad characters for classNew| */
        nx = left(nx, cx-1)
    if nx <> '' & wordPos(nx, old) < 1 0 then
        return old nx
    else
        return old  'COL' || (words(old) + 1)
endProcedure sqlNiceVarsAdd

/*--- 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

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

/*--- execute sql thru the dsnRexx interface -------------------------*/
sqlExec: /* no procedure, to keep variables sql... */
    parse arg ggSqlStmt, ggRetOk
    m.sql_HaHi = ''
    address dsnRexx 'execSql' ggSqlStmt
    /* say 'sqlCode' sqlCode 'rc' rc 'for' ggSqlStmt ggNo */
    if rc = 0 then
        return 0
    interpret sqlErrorHandler(rc, ggRetOk, ggSqlStmt)
    return sqlCode
endProcedure sqlExec

sqlExecMsg: procedure expose m.
parse arg sql
    sc = sqlExec(sql, '*')
    return sqlMsgLine(sc, , sql)

sqlErrorHandler: procedure expose m. ,
                   sqlCode sqlErrMc sqlErrP sqlErrD. sqlWarn. sqlState
parse arg drC, retOk, verb rest
    if drC == 0 then
        return 'return 0'
    if pos('-', retOK) < 1 then
        retOK = retOk m.sql_retOk
    if wordPos(drC, '1 -1') < 1 then do
        eMsg = "'dsnRexx rc="drC"' sqlmsg()"
        end
    else if pos('*', retOK) > 0 | wordPos(sqlCode, retOK) > 0 then do
        if sqlCode < 0 & pos('say', retOK) > 0 then
            return "call outNl errMsg(' }'sqlMsg())"
        else
            return ''
        end
    else do
        upper verb
        if verb == 'DROP' then do
            if (sqlCode == -204 | sqlCode == -458) ,
                           & wordPos('dne', retok) > 0 then
                return 'return' sqlCode
            if sqlCode = -672 & wordPos('rod', retok) > 0 then do
                hahi = m.sql_HaHi || sqlMsgLine(sqlCode,
                                   , 'tb='sqlErrMc ,verb rest)'\n'
                haHi = haHi || sqlExecMsg('alter table' SqlErrMc ,
                        'drop restrict on drop')
                call sqlExec verb rest
                m.sql_HaHi = hahi
                return ''
                end
            end
        if drC < 0 then
            eMsg = "sqlmsg()"
        else if (sqlCode<>0 | sqlWarn.0 ^==' ') & pos('w',retOK)<1 then
            return "call outNl errMsg(' }'sqlMsg()); return" sqlCode
        else
            return ''
        end
    if wordPos('rb', retok) > 0 then
        eMsg = eMsg " || '\n"sqlExecMsg('rollback')"'"
    if wordPos('ret', retok) < 1 then
        return "call err" eMsg
    m.sql_errRet = 1
    return 'call outNl' eMsg
endProcedure sqlErrorHandler

sqlMsgLine: procedure expose m. sqlErrD.
parse arg res, cnt, src, plus
    verb = translate(word(src, 1))
    if datatype(res, 'n') then
        res = 'sqlCode' res
    if cnt \== '' then do
        res = res',' cnt
        vx = wordPos(translate(word(src,1)), 'DELETE INSERT UPDATE')
        if datatype(cnt, 'n') then
            if vx > 0 then
               res = res 'rows' word('deleted inserted updated', vx)
            else if cnt <> 0 then
                res = res 'rows updated'
        end
    if plus \== '' then
        res = res',' plus
    if abbrev(res, ', ') then
        res = substr(res, 3)
    if src \== '' then do
        ll = 75 - length(res)
        aa = strip(src)
        if length(aa) > ll then
            aa = space(aa, 1)
        if length(aa) > ll then
           aa = left(aa,  ll-3)'...'
        res = res':' aa
        end
    return res
endProcedure sqlMsgLine

/*--- 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

/*--- 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
    ggFrom = 'ggSqlStmt'
    ggW1 = translate(word(ggSqlStmt, 1))
    ggW2 = translate(word(ggSqlStmt, 2))
    if ggW1 == 'PREPARE' then
        ggFrom = sqlHostVarFind(ggSt, 'FROM')
    else if ggW1 ggW2 == 'EXECUTE IMMEDIATE' then
        ggFrom = sqlHostVarFind(ggSt, 1)
    ggPos = 0
    if datatype(sqlErrd.5, 'n') & sqlErrd.5 > 0 then do
        ggPos = sqlErrd.5
        ggRes = ggRes || sqlMsgSrcPos(value(ggFrom), sqlErrd.5)
        end
    if ggFrom == 'ggSqlStmt' then do
        ggRes = ggRes'\nsql =' sqlShorten(ggSqlStmt, 2000, ggPos)
        end
    else do
        ggRes = ggRes'\nsql =' sqlShorten(value(ggFrom), 2000, ggPos)
        ggRes = ggRes'\nstmt =' sqlShorten(ggSqlStmt, 2000)
        end
    ggPref = '\nwith'
    do ggXX=1 to m.ggSt.0
        if ggFrom = m.ggVa.ggXX then
            iterate
        ggRes = ggRes || ggPref m.ggBe.ggXX ':'m.ggVa.ggXX ,
                      '=' sqlShorten(value(m.ggVa.ggXX), 210)
        ggPref = '\n    '
        end
    if m.sqlSuMsg == 1 | (m.sqlSuMsg == 2 & m.sql_csmhost \== '') then
        ggRes = ggRes'\nsubsys =' m.sql_dbSys ,
             || ', host =' m.sql_csmhost
    return  ggRes
endSubroutine sqlMsg

sqlShorten: procedure expose m.
parse arg txt, maxL, pos
    if length(txt) <= maxL then
        return txt
    if \ datatype(pos, 'n') | pos < 1 then
        pos = 1
    ex = pos + min(60, maxL%7)
    if ex <= maxL - 4 then
        return left(txt, maxL-4) '...'
    if ex >= length(txt) then
        return left(txt, 67) '...\n'substr(txt, length(txt)-maxL+72)
    else
        return left(txt, 67) '...\n'substr(txt, ex-maxL+76, maxL-75) ,
                       '...'
endProcedure sqlShorten
/*--- 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 nop
    else if rc = 4 then say 'warn linkPgm dsnTiar rc' rc 'sqlCa' ca
    else                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: procedure expose m. ,
                   sqlCode sqlErrMc sqlErrP sqlErrD. sqlWarn. sqlState
    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 = 1
    do cnt
        cx = pos(':', src, cx) + 1
        if cx < 2 then
           leave
        if pos(substr(src, cx, 1), m.ut_rxN1) > 0 then
            iterate
        ex = verify(src, m.ut_rxDot, 'n', cx)
        if ex < 1 then
            m.st.var.sx = substr(src, cx)
        else
            m.st.var.sx = substr(src, cx, ex - cx)
        if m.st.var.sx == '' | length(m.st.var.sx) > 100 then
            iterate
                       /* 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.ut_alfa) > 0
            end
        if bB < bE & bB >= 0 then
            m.st.bef.sx = substr(src, bB+1, bE-bB)
        else
            m.st.bef.sx = ''
        sx = sx + 1
        end
    m.st.0 = sx-1
    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 sqlRx  end   **************************************************/
/* copy dsnList begin **************************************************
     csi interface: see dfs managing catalogs chapt. 11
         returncode/reason see message IDC3009I
**********************************************************************/
/*--- specify dsn mask and fields to start a csi catalog search --------
      arguments:
          m       objectPointer
          dsnMask specifies the dsns with wildcards:
              %  1 character
              *  0 - n character in one level
              ** 0 - n levels
          fields a (space separated) list of field names -------------*/
csiOpen: procedure expose m.
parse arg m, dsnMask, fields
    m.m.fld.0 = words(fields)
    ffix = d2c(m.m.fld.0, 2)
    do x=1 to m.m.fld.0
        m.m.fld.x = translate(word(fields, x))
        ffix = ffix || left(m.m.fld.x, 8)
        end
    if dsnMask \== '' & right(dsnMask, 1) \== ' ' ,
          & pos('*', dsnMask) < 1 & length(dsnMask) < 42 then
        dsnMask = dsnMask'.**'
    m.m.filt = left(dsnMask, 149) ,
             || left('Y', 3) ,        /* resume offset 149      */
             || ffix                  /* csiNumEn offset 152    */

    WORKLEN = 1024 * 64
    m.m.work = D2C(WORKLEN,4) || COPIES('00'X,WORKLEN-4)
    m.m.pos = workLen + 1
    return
endProcedure csiOpen

/*--- put the next dsn into m.o and m.o.* (for other fields)
      return 1 if next dsn exists 0 otherwise ------------------------*/
csiNext: procedure expose m.
parse arg m, o
    usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET AMOUNT OF WORK AREA USED */
    px = m.m.pos
    do forever
        if px > usedL then do
            if substr(m.m.filt, 150, 1) \== 'Y' then do
                m.m.pos = px
                m.o = ''
                return 0
                end
            reason = left('', 4)
            ADDRESS LINKPGM 'IGGCSI00  reason m.'m'.filt  m.'m'.work'
            if rc == 0 & substr(reason, 3, 2) == '0000'x then
                nop
            else if rc == 4 & substr(reason, 3, 2) == '0464'x then
                say 'data set entry with error'
            else
                call err 'call csi returns' rc,
                             'rc' c2d(substr(reason, 4,1)),
                             'reason' c2d(substr(reason, 3,1)),
                             'module' substr(reason, 1,2)
            usedL = C2D(SUBSTR(m.m.work,9,4)) /* GET WORK AREA USED */
            numFD = C2D(SUBSTR(m.m.work,13,2)) /* no flds + 1 */
            if numFd <> m.m.fld.0 + 1 then
                call err 'csiNumFd' numFd 'not' m.m.fld.0 '+' 1
            px = 15
            iterate
            end
        eType =  substr(m.m.work, px+1, 1)
        m.o = strip(substr(m.m.work, px+2, 44), 't')
        flag = substr(m.m.work, px, 1)
        /* say 'eType' eType m.o 'flag' c2x(flag) */
        if eType == '0' then do
            if flag \== '00'x & flag \== '40'x then
                call err 'flag' c2x(flag) 'for catalog' m.o
            px = px + 50    /* length of catalog entry  */
            iterate
            end
        else do
            if \ abbrev(x2b(c2x(flag)), '101') then
                call err 'call csi entry flag' x2b(c2x(flag)),
                             'rc' c2d(substr(m.m.work, px+49,1)),
                             'reason' c2d(substr(m.m.work, px+48,1)),
                             'module' substr(m.m.work, px+46, 2),
                             'for entry' m.o,
                             'see qw IDC3009I'
            py = px + 46
            tl = c2d(substr(m.m.work, py, 2))
            pl = py + 4
            pf = py + m.m.fld.0 * 2 + 4
            do fx = 1 to m.m.fld.0
                fi = m.m.fld.fx
                fl = c2d(substr(m.m.work, pl, 2))
                m.o.fi = substr(m.m.work, pf, fl)
                if fi = 'MGMTCLAS' then
                    m.o.fi = substr(m.o.fi, 3, c2d(left(m.o.fi ,2)))
                else if wordPos(fi, 'COMUDSIZ NOBYTTRK') > 0 then
                    m.o.fi = utc2d(m.o.fi)
                pf = pf + fl
                pl = pl + 2
                end
            if py + tl <> pf then
                call err 'length mismatch for entry' m.o
            m.m.pos = pf
            return 1
            end
        end
endProcedure csiNext
/*--- if dsn is arcived return 'arcive'
      if dsn is tape return 'tape'
      otherwise return managment class ------------------------------*/
csiArcTape: procedure expose m.
parse arg vo, cl, dt, dsn
        if vo = '' then
            say err '||| no volume for dsn' dsn
        else if vo = 'ARCIVE' then
            res = 'arcive'
        else if cl <> '' then
            res = cl
        else if abbrev(vo, 'SHR') then
            res = 'SHR'
        else
            res = 'tape'
        if   res = 'arcive' then
            return res
      /*if   abbrev(res, 'ar') \= abbrev(dt, '00'x) , */
        if   abbrev(res, 'ta') \= abbrev(c2x(left(dt, 1)), '7') ,
           | (left(res, 1) >= 'A') \= abbrev(dt, '30'x) then
           say '||| mismatch cl' cl 'vo' vo 'dt' c2x(dt) 'dsn' dsn
        return res
endProcedure csiArcTape
/*--- mbrList with  listDS -----------------------------------------*/
mbrList: procedure expose m.
parse arg m, pds
    msk = strip(dsnGetMbr(pds))
    if msk == '*' then
        msk = ''
    parse value dsnCsmSys(dsnSetMbr(pds)) with sys '/' dsn
    if sys == '*' then do
        call adrTso listDS "'"dsn"'" members
        oy = m.tso_trap.0 + 99
        mFound = 0
        mx = 0
        do ox=1 to m.tso_trap.0
            if mFound then do
                if msk \== '' then
                    if \ match(strip(m.tso_trap.ox), msk) then
                        iterate
                mx = mx +1
                m.m.mx = strip(m.tso_trap.ox)
                end
            else
                mFound = m.tso_trap.ox == '--MEMBERS--'
            end
        end
    else do
        if msk <> '' then
            msk = 'member('translate(msk, '%', '?')')'
        mbr_name.0 = -99
        call adrCsm "mbrList system("sys") dataset('"dsn"')" msk ,
                "index(' ') short"
        do mx=1 to mbr_name.0
            m.m.mx = strip(mbr_name.mx)
            end
        mx = mbr_name.0
        end
    m.m.0 = mx
    return mx
endProcedure mbrList
/* copy dsnList end   ************************************************/
/* copy csm begin ******************************************************
    interface to csm,
        it is integrate with adrTso, eg. dsnAlloc , 'RZ3/..' uses csm
***********************************************************************/
/*--- execute a single csmExec command ------------------------------*/
adrCsm:
    if wordPos(translate(word(arg(1), 1)), 'COPY MBRLIST') > 0 then
       ggTO = ''
    else if symbol('m.csm_timeOut') == 'VAR' then
        ggTO = 'timeout('m.csm_timeOut')'
    else
        ggTO = 'timeout(30)'
    ggStart = time()
    if adrTso('csmExec' arg(1) ggTO, '*') == 0 then
        return 0
    if pos('CSMXC43E NO CONNECTION TO S', m.tso_trap) > 0 ,
         | pos('CSMXC36E TIMEOUT LIMIT EXCEEDED DURING LOGON TO' ,
            , m.tso_trap) > 0 then
               /* CSMXC43E NO CONNECTION TO SYSPLEX:RR2 AVAILABLE
                  CSMXC36E TIMEOUT LIMIT EXCEEDED DURING LOGON TO SYS */
        m.csm_err = 'noConn'
    else if pos('IKJ56225I', m.tso_trap) > 0             ,
               & ( pos('ALREADY IN USE', m.tso_trap) > 0 ,
                 | pos('CATED TO ANOTH', m.tso_trap) > 0) then
               /* 5 IKJ56225I DATA SET A540769.TMP.TEXT ALREADY IN USE,
                  6 IKJ56225I DATA SET IS ALLOCATED TO ANOTHER JOB */
        m.csm_err = 'inUse'
    else
        m.csm_err = ''
    if pos('*', arg(2)) <= 0 & wordPos(m.tso_rc, arg(2)) <= 0 then
        call err strip('csmExec' m.csm_err) 'rc='m.tso_rc ,
            '\nstmt='subWord(m.tso_stmt, 2) m.tso_trap ,
            '\nend of csmExec, time='ggStart '-' time()
    return m.tso_rc
endProcedure adrCsm

csmDel: procedure expose m.
parse arg rz, dsn
    if dsnGetMbr(dsn) == '' then do
        call adrCsm "allocate system("rz") dataset('"dsn"')" ,
                         "disp(del) ddname(del1)"
        call adrTso 'free dd(del1)'
        end
    else do
        rr = adrCsm("mDelete system("rz") dataset('"dsnSetMbr(dsn)"')",
                          "member("dsnGetMbr(dsn)")", 8)
        if rr <> 0 then
            if pos('CSMEX77E Member:', m.tso_trap) < 1 ,
              | pos(' not found', m.tso_trap) < 1 then
                  call err 'rc='rr 'csm mDelete' rz'/'dsn':'m.tso_trap
        end
    return
endProcedure csmDel
/*--- copy members / datasets
      Vorlage: csrxUtil ----------------------------------------------*/
csmCopy: procedure expose m.
parse arg aFr, aTo, retOk
    frDD = tsoDD('csmFr*', 'a')
    frMbr = dsnGetMbr(aFr)
    if frMbr == '*' then
        fr = dsnSetMbr(aFr)
    else
        fr = aFr
    call csmAlloc fr frDD 'shr'
    toDD = tsoDD('csmTo*', 'a')
    toMbr = dsnGetMbr(aTo)
    if toMbr\== '=' then
        to = aTo
    else
        to = dsnSteMbr(aTo, frMbr)
    call csmAlloc to toDD 'shr ::D'frDD
    if frMbr \== '' & toMbr == '' & m.tso_dsOrg.toDD == 'PO' then do
        call adrTso 'free dd('toDD')'
        to = dsnSetMbr(aTo, frMbr)
        call csmAlloc to toDD 'shr'
        end
    inDD = tsoDD('csmIn*', 'a')
    if frMbr == '' & m.tso_dsOrg.frDD == 'PO' then do
        call tsoAlloc '-' inDD 'NEW ::F'
        call adrCsm "mbrList ddName("frDD") index(' ') short"
        do ix=1 to mbr_mem#
            i.ix = ' S M='mbr_name.ix
            end
        call writeDD inDD, 'I.', mbr_mem#
        call tsoCLose inDD
        end
    else do
        call adrTso 'alloc dd('inDD') dummy'
        end
    outDD = word(dsnAlloc('dd(csmO*) new ::V137'), 1)
    cmdu = 'CSMUTIL CSM,COPYREPLACE,DD(,,,,'inDD','outDD ,
               ||  ',,'frDD','toDD'),MARC(0)'
    cRc = adrTso(cmdU, '*')
    if cRc <> 0 then do
        call readDD outDD, o.
        call tsoClose outDD
        say 'rc='cRc',' o.0 'outputlines for' cmdU
        do ox=1 to o.0
            say o.ox
            end
        end
    call tsoFree frDD toDD inDD outDD
    if cRc <> 0 then
        call err 'csmCopy rc='cRc
    return cRc
endProcedure csmCopy

csmAlloc: procedure expose m.
parse arg dsn dd disp rest ':' nn, retRc
    sys = ''
    a2 = ''
    upper dd disp
    m.tso_dsn.dd = dsnCsmSys(dsn)
    parse var m.tso_dsn.dd sys '/' dsn
    if disp = '' then
        disp = 'shr'
    else if words(disp) = 2 then
        disp = word(disp, 2)
    a1 = "SYSTEM("sys") DDNAME("dd")"
    if dsn == 'INTRDR' then do
        a1 = a1 'sysout(T) writer(intRdr)'
        end
    else do
        if dsn <> '' then do
            a1 = a1 "DATASET('"dsnSetMbr(dsn)"')"
            mbr = dsnGetMbr(dsn)
            if mbr <> '' then
                a1 = a1 'MEMBER('mbr')'
            end
        if abbrev(disp, 'SYSOUT(') then
            a1 = a1 disp
        else
            a1 = a1 "DISP("disp")"
        end
    nAtts = wordPos(disp, 'NEW MOD CAT') > 0 & nn \== ''
    if nAtts then
        rest = dsnCreateAtts('-'dsn , nn) rest
    cx = pos(' UCOUNT(', ' 'translate(rest))
    if cx > 0 then do
         rest = left(rest, cx) || 'nitCnt(' || substr(rest, cx+7)
         end
    cx = pos(' RECFM(', ' 'translate(rest))
    if cx > 0 then do
        cy = pos(')', rest, cx)
        rest = left(rest, cx+5) || space(substr(rest,cx+6,cy-cx-6), 0) ,
                                || substr(rest,cy)
        end
    cx = pos(' CYLINDERS ', ' 'translate(rest)' ')
    if cx > 0 then do
        rest = delStr(rest, cx+8, 1)
        end
    cx = pos(' CYL ', ' 'translate(rest)' ')
    if cx > 0 then do
        rest = insert('inder', rest, cx+2)
        end
    if retRc <> '' | nAtts | nn == '' then do
        alRc = adrCsm('allocate' a1 rest, retRc)
        m.tso_dsorg.dd = subsys_dsOrg
        return alRc
        end
    alRc = adrCsm('allocate' a1 rest, '*')
    m.tso_dsorg.dd = subsys_dsOrg
    if alRc = 0 then
        return 0
    say 'csmAlloc rc' alRc 'for' a1 rest '...trying to create'
    call csmAlloc m.tso_dsn.dd dd 'CAT' rest ':'nn
    call adrTso 'free  dd('dd')'
    return adrCsm('allocate' a1 rest)
endProcedure csmAlloc

dsnCsmSys: 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 dsnCsmSys

csmLikeAtts: procedure expose m.
parse upper arg rz '/' dsn
    lc = adrCsm('dslist system('rz') dsnMask('dsnSetMbr(dsn)')')
    if stemsize <> 1 then
        call err 'csmLikeAtts stemSize='stemsize 'for dsn='dsn
    if abbrev(dsOrg.1, 'PO') then
        r = 'dsorg(po) dsnType(library)'
    else if abbrev(dsOrg.1, 'PS-') then
        r = 'dsorg(PS)'
    else
        r = 'dsorg('dsOrg.1')'
    r = r 'mgmtClas('mgmtClas.1')'                       ,
       /* 'dataClas('dataClas.1')'   */                  ,
          'recFM('strip(translate('1 2 3', recFm.1, '123'))')'  ,
          'lRecl('lRecl.1')'                         ,
          'space('tracksused.1','  tracks.1') tracks'
    /*    if \ datatype(tracksused.1, 'n') then do
              if \ datatype(tracks.1, 'n') then
                  r = r 'space('tracks.1',' tracks.1')'
              if \ datatype(tracks.1, 'n') then
                  tracks.1 = tracksUsed.1   */

    return r
endProcedure csmLikeAtts
/*--- execute a rexx (under tso) in another rz
           here we use rexx TPSYSIKJ which was written for
           jcl procedure RM@IKJ01
arguments
rz   which rz to run rexx
proc the (remote) procedure library to use
opt  options
cmd  the tso command to execute
----------------------------------------------------------------------*/
/*--- execute a rexx (under tso) in another rz
          directly (without TPSYSIKJ) --------------------------------*/
csmExRx: procedure expose m.
parse arg rz, proc, opt, cmd
    pStem = opt
    if pStem = '' then
        pStem ='CSMEXRX'               /* split tso cmd in linews */

    do cx=1 to (length(cmd)-1) % 68
        cmd.cx = substr(cmd, 68*cx-67,68)'-'
        end
    cmd.cx = substr(cmd, 68*cx-67)
    cmd.0 = cx
    timeout = 11
    if 0 then do
        call adrTso  'free ed(rmtSys)'  ,'*'
        call tsoFree tsoDD(rmtsPrt, 'a')
        call adrTso  'free dd(rmtsIn)','*'
        call adrTso  'free dd(sysproc)' ,'*'
        end
                                       /* alloc necessary dd */
    call dsnAlloc rz"/"proc "dd(rmSyPro) rmtDDn(sysProc)"
    call dsnAlloc rz"/tmp.tsin new dd(rmTsIn) rmtDdn(sysTsIn) ::f "
    call tsoOpen rmTsIn, 'w'           /* write tso cmd */
    call writeDD rmTsIn, cmd.
    call tsoClose rmtsin
    call dsnAlloc rz"/tmp.prt new dd(rmtsprt) rmtDdn(sysTsPrt)",
                    "::f133"
    call dsnAlloc rz"/tmp.rmt new dd(rmtSys) timeout("timeout")"
                                       /* now, run tso remote */
    call adrtso "csmappc start pgm(csmexec)" ,
           "parm('select tsocmd(''csmappc allocate plu(*.rmtSys)" ,
                 "tpname(sysikj) dealloc '')')",
           "timeout("timeOut")", '*'
    if rc <> 0 | appc_rc <> 0 then do  /* handle csm error */
        ee = 'csm tso exec rc='rc 'appc_rc='appc_rc
        say ee
        say '  rexx rz='rz 'proc='proc 'opt='opt
        say '  cmd='cmd
        call csmappcRcSay ggTsoCmd
        call readDD 'rmTsPrt', 'M.'pStem'.'
        call tsoClose rmtsPrt
        say m.pStem.0 'tso output lines'
        do px=1 to m.pStem.0
            say ' ' strip(m.pStem.px, 't')
            end
        call err ee
        end
    if opt <> '' then do               /* copy output to stem */
        call readDD 'rmTsPrt', 'M.'pStem'.'
        call tsoClose rmtsPrt
        end
    call tsoFree rmSyPro rmtsPrt rmtSys rmtsIn
    return
 endProcedure csmExRx

/*--- sys the re and result variables from csmAppcRc -----------------*/
csmappcRcSay: procedure expose appc_rc appc_reason appc_msg. ,
           appc_state_c appc_state_f
 parse arg cmd
     say 'rc='appc_rc 'reason='appc_reason ,
         'state_c='appc_state_c appc_state_f
     say '  for' cmd
     do ix=1 to appc_msg.0
         say ' ' appc_msg.ix
         end
     return appc_rc
 endProcedure csmappcRcSay

/*--- execute a single csmAppc command ------------------------------*/
csmAppc:
    return adrTso('csmAppc' arg(1), arg(2))
endProcedure csmAppc
/* copy csm end *******************************************************/
/* copy timing begin *************************************************/
timing: procedure expose m.
parse arg typ, c2, txt
    e1 = time('E')
    c1 = strip(sysvar('syscpu'))
    s1 = sysvar('syssrv')
    if typ == '' then
        return strip(f('%c ela=%5i cpu=%8.3i su=%9i' ,
            , time(), e1, c1, s1) txt)
    if symbol('m.timing_ela') \== 'VAR' then
        call err 'timing('typ',' c2',' txt') ohne ini'
    if symbol('m.timing.typ.ela') \== 'VAR' then do
        m.timing.typ.ela = 0
        m.timing.typ.cpu = 0
        m.timing.typ.su  = 0
        m.timing.typ.cnt = 0
        m.timing.typ.cn2 = 0
        if symbol('m.timing_types') == 'VAR' then
            m.timing_types = m.timing_types typ
        else
            m.timing_types = typ
        if symbol('m.timing_say') \== 'VAR' then
            m.timing_say = 0
        end
    m.timing.typ.ela = m.timing.typ.ela + e1 - m.timing_ela
    m.timing.typ.cpu = m.timing.typ.cpu + c1 - m.timing_cpu
    m.timing.typ.su  = m.timing.typ.su  + s1 - m.timing_su
    m.timing.typ.cnt = m.timing.typ.cnt + 1
    if c2 \== '' then
       m.timing.typ.cn2 = m.timing.typ.cn2 + c2
    m.timing_ela = e1
    m.timing_cpu = c1
    m.timing_su  = s1
    if m.timing_say then
            say left(typ, 10)right(m.timing.typ.cn2, 10) ,
                'ela='m.timing.typ.ela ,
                'cpu='m.timing.typ.cpu 'su='m.timing.typ.su txt
    return
endProcedure timing

timingSummary: procedure expose m.
    say 'timing summary' time()
    do tx = 1 to words(m.timing_types)
        typ = word(m.timing_types, tx)
        say left(typ, 10)right(m.timing.typ.cnt,  7)       ,
                      || right(m.timing.typ.cn2,  7)       ,
                         'cpu='right(m.timing.typ.cpu, 10) ,
                         'su='right(m.timing.typ.su, 10)
        end
    return
endProcedure timingSummary
/* copy timing end   *************************************************/
/* copy ii begin ********* Installation Info *************************/
iiini: procedure expose m.
    if m.ii_ini == 1 then
        return
    m.ii_ini = 1
    m.ii_ds.org = ORG.U0009.B0106.KLEM43
    m.ii_ds.db2 = DSN.DB2
    m.ii_rz = ''
    i = 'RZ0 0 T S0 RZ1 1 A S1'  ,
        'RZX X X X2 RZY Y Y Y2 RZZ Z Z Z2'  ,
        'RQ2 Q Q Q2 RR2 R R R2 RZ2 2 B S2 RZ4 4 D S4'
    do while i <> ''
        parse var i rz m.ii_rz2c.rz m.ii_rz2plex.rz sys i
        if rz <> RZ0 & rz <> RZ1 then
            m.ii_rz = strip(m.ii_rz rz)
        m.ii_rz2Sys.rz  = sys
        m.ii_sys2rz.sys = rz
        end
    i = 'DBTF T DTF DVTB V DTB DBOC C DOC DBTC B DBT DBIA A DIA' ,
        'DE0G E DE0 DEVG M DEV DPXG I DPX DX0G X DX0' ,
        'DPYG Y DPY DPZG N DPZ' ,
        'DBOF F DOF DVBP P DBP DP2G Q DP2 DBOL O DOL DP4G U DP4'
    do while i <> ''
        parse var i db m.ii_db2c.db mbr i
        m.ii_mbr2db.mbr = db
        m.ii_db2Elar.db = wordPos(db, 'DVTB DVBP DEVG') > 0
        end
    m.ii_rz2db.rz0 = 'DBTC DBIA'
    m.ii_rz2db.rz1 = 'DBTF DVTB DBOC'
    m.ii_rz2db.rzx = 'DE0G DEVG DX0G DPXG'
    m.ii_rz2db.rzy = 'DE0G DEVG DPYG'
    m.ii_rz2db.rzz = 'DE0G DEVG DPZG'
    m.ii_rz2db.rq2 = 'DBOF DVBP DP2G'
    m.ii_rz2db.rr2 = 'DBOF DVBP DP2G'
    m.ii_rz2db.rz2 = 'DBOF DVBP DP2G'
    m.ii_rz2db.rz4 = 'DBOL DP4G'
    i = ''
    do rx=1 to words(m.ii_rz)
        rz = word(m.ii_rz, rx)
        i = i repAll(' 'space(m.ii_rz2db.rz, 1), ' ', ' 'rz'/')
        end
    m.ii_rzDb = space(i, 1)
    return
endProcedure iiIni

iiDS: procedure expose m.
parse upper arg nm
    return iiLazy(ii_ds, nm, 'ds')

iiMbr2DbSys: procedure expose m.
parse upper arg mbr
    return iiLazy(ii_mbr2db, left(mbr, 3), 'member')

iiRz2C: procedure expose m.
parse upper arg rz
    return iiLazy(ii_rz2c, rz, 'rz')

iiRz2P: procedure expose m.
parse upper arg rz
    return iiLazy(ii_rz2plex, rz, 'rz')

iiRz2Dsn: procedure expose m.
parse upper arg rz
    return overlay('Z', rz, 2)

iiDBSys2C: procedure expose m.
parse upper arg db
    return iiLazy(ii_db2c, db, 'dbSys')

iiSys2RZ: procedure expose m.
parse upper arg sys
    return iiLazy(ii_sys2rz, left(sys, 2), 'sys')

iiLazy: procedure expose m.
parse arg st, key, txt
    if symbol('m.st.key') == 'VAR' then
        return m.st.key
    if m.ii_ini == 1 then
       return err('no' txt'='key 'in ii' st)
    call iiIni
    return iiLazy(st, key, txt)
endProcedure iiLazy

iiVPut:procedure expose m.
parse upper arg rz '/' db .
    call vPut 'rz', rz
    call vPut 'rzC', iiRz2C(rz)
    call vPut 'rzP', iiRz2P(rz)
    call vPut 'rzD', iiRz2Dsn(rz)
    call vPut 'dbSys', db
    call vPut 'dbSysC', iidbSys2C(db)
    call vPut 'dbSysElar', iiLazy(ii_db2Elar, db)
    return 1
endProcedure iiVPut

iiIxVPut:procedure expose m.
parse arg ix
    if ix > words(m.ii_rzDb) then
        return 0
    else
        return iiVPut(word(m.ii_rzDb, ix))
endProcedure iiIxVPut
/* copy ii end   ********* Installation Info *************************/
/* 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 tsoOpen grp, 'R'
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 tsoClose 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 m.tso_stmt, ggRet
    call outtrap m.tso_trap.
    address tso m.tso_stmt
    m.tso_rc = rc
    call outtrap off
    if m.tso_rc == 0 then
        return 0
    m.tso_trap = ''
    do ggXx=1 to min(7, m.tso_trap.0)
        m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
        end
    if m.tso_trap.0 > 7 then do
        if m.tso_trap.0 > 14 then
            m.tso_trap = m.tso_trap'\n............'
        do ggXx=max(8, m.tso_trap.0-6) to m.tso_trap.0
            m.tso_trap = m.tso_trap'\n'strip(m.tso_trap.ggXx)
            end
        end
    if ggRet <> '*' & wordPos(m.tso_rc, ggRet) <= 0 then
        call err 'adrTso rc=' m.tso_rc 'stmt='m.tso_stmt m.tso_trap
    return m.tso_rc
endSubroutine 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 lib '(' . , mbr .
     bx = pos('(', dsn)
     if mbr = '' then
         return strip(lib)
     else
         return strip(lib)'('mbr')'
endProcedure dsnSetMbr

dsnGetMbr: procedure expose m.
parse arg dsn '(' mbr ')'
     if mbr = '' then
         return arg(2)
     else
         return strip(mbr)
endProcedure dsnGetMbr

dsnCsmSys: 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 dsnCsmSys
/**********************************************************************
    io: read or write a dataset with the following callsequences:
        read:  tsoOpen...'R', readDD*,  tsoClose
        write: tsoOpen...'W', writeDD*, tsoClose

        readDD returns true if data read, false at eof
        do not forget that open is mandatory to write empty file|
***********************************************************************/

/*--- open dd for read (rw='R') or write (rw='W') --------------------*/
tsoOpen: procedure expose m.
parse upper arg dd, rw
    return adrTso('execio' 0 'disk'RW dd '(open)')
return /* end tsoOpen */

/*--- close dd -----------------------------------------------------*/
tsoClose: procedure expose m.
    parse upper arg dd
    return adrTso('execio 0 diskR' dd '(finis)')
endProcedure tsoClose

/*--- 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 */

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

/*--- 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
    parse value dsnAlloc('dd('m.m.dd')' m.m.dsn) with m.m.dd m.m.free
    call tsoOpen m.m.dd, 'R'
    return m
endProcedure readDDNxBegin

/*--- return the stem of the next line, or '' at end -----------------*/
readNx: procedure expose m.
parse arg m
    m.m.cx = m.m.cx + 1
    if m.m.cx <= m.m.0 then
        return m'.'m.m.cx
    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 stem of the curr line, '' at end --------------------*/
readNxCur: procedure expose m.
parse arg m
    if m.m.cx <= m.m.0 then
        return m'.'m.m.cx
    else
        return ''
endProcedure readNxCur

/*--- 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 tsoClose m.m.dd
    call tsoFree m.m.free
    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' | w == 'CAT' then
            di = di 'CAT'
        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 pos('(', w) > 0 then
            leave
        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.tso_trap.1 = ''
        m.tso_trap.2 = ''
        m.tso_trap.3 = ''
        res = dsnAlloc(spec, pDi, pDD, '*')
        if \ datatype(res, 'n') then
            return res
        msg = m.tso_trap.1'\n'm.tso_trap.2'\n'm.tso_trap.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 dd == '-' & pDD \== '' then
        dd = pDD
    if dd == '-' then
        dd = 'DD*'
    dd = tsoDD(dd, 'a')
    m.tso_dsn.dd = ''
    if na == '-' & di == '-' & rest = '' then
        return dd
    if di = '-' then
        if pDi == '' then
            di = 'SHR'
        else
            di = pDi
    if pos('(', na) < 1 then
        nop
    else if di = 'MOD' then
        call err 'disp mod for' na
    else
        di = 'SHR'
    if lastPos('/', na, 6) > 0 then
        rx = csmAlloc(na dd di rest, retRc)
    else
        rx = tsoAlloc(na dd di rest, retRc)
    if rx = 0 then
        return dd dd
    else
        return rx
endProcedure dsnAlloc

/*--- find a free dd with prefix dd ----------------------------------*/
tsoDD: procedure expose m.
parse upper arg dd, f, noErr
    if symbol('m.tso_ddAll') \== 'VAR' then do
        call errIni
        m.tso_ddAll = ''
        end
    if f == '-' then do
        ax = wordPos(dd, m.tso_ddAll)
        if ax > 0 then
            m.tso_ddAll = delWord(m.tso_ddAll, ax, 1)
        else if noErr \== 1 then
            call err 'tsoDD dd' dd 'not used' m.tso_ddAll
        end
    else if f <> 'A' then
        call err 'tsoDD bad fun' f
    else do
        if right(dd, 1) = '*' then do
            d0 = left(dd, length(dd)-1) || m.err.screen
            dd = d0
            do dx=1 while wordPos(dd, m.tso_ddAll) > 0
                dd = d0 || dx
                end
            end
        else if pos('?', dd) > 0 then
            dd = repAll(dd, '?', m.err.screen)
        if wordPos(dd, m.tso_ddAll) < 1 then
            m.tso_ddAll = strip(m.tso_ddAll dd)
        m.tso_dsn.dd = ''
        m.tso_dsOrg.dd = ''
        end
    return dd
endProcedure tsoDD

tsoAlloc: procedure expose m.
parse arg na dd disp rest ':' nn, retRc
    dd = translate(dd)
    c = 'alloc dd('dd')' disp
    if na == '-' then
        m.tso_dsn.dd = ''
    else if na \== 'INTRDR' then do
        c = c "DSN('"na"')"
        m.tso_dsn.dd = na
        end
    else do
        c = c "sysout(*) writer(intRdr)"
        m.tso_dsn.dd = '*intRdr'
        end
    if wordPos(disp, 'NEW MOD') > 0 & nn \== '' then
        c = c dsnCreateAtts(,nn)
    if adrTso(c rest, '*') = 0 then
        return 0
    if pos('IKJ56246I', m.tso_trap) > 0 then
        if pos('TED, FILE IN USE', m.tso_trap) > 0 then do
     /* IKJ56246I DATA SET A540769.WK.REXX NOT ALLOCATED, FILE IN USE */
        say 'tsoAlloc dd' dd 'already in use:' substr(m.tso_trap, 3)
        say '.... trying to free'
        call tsoFree dd, 1
        say '.... retrying to allocate' c rest
        if adrTso(c rest, '*') = 0 then
            return 0
        end
    if nn \= '' & wordPos(disp, 'OLD SHR') > 0 ,
          & pos('IKJ56228I', m.tso_trap) > 0 ,
          & pos(' NOT IN CATALOG ', m.tso_trap) > 0 then do
       /* IKJ56228I DATA SET A540769.XY.REXX NOT IN CATALOG OR CATAL*/
        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
    if pos('*', retRc) < 1 & wordPos(m.tso_rc, retRc) < 1 then
        call err 'tsoAlloc rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
    call sayNl 'tsoAlloc rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
    return m.tso_rc
endProcedure tsoAlloc

dsnExists: procedure expose m.
parse upper arg aDsn
    parse value dsnCsmSys(aDsn) with rz '/' dsn
    if rz == '*' then
        return sysDsn("'"dsn"'") == 'OK'
    else if dsnGetMbr(dsn) == '' then do
        lc = adrCsm('dslist system('rz') dsnMask('dsn') short', 4)
        if stemsize = 0 | stemSize = 1 then
            return stemSize
        call err 'csmExists stemSize='stemsize 'for dsn='aDsn
        end
    else do
        cc = adrCsm("mbrList system("rz") dataset('"dsnSetMbr(dsn)"')",
                    "member("dsnGetMbr(dsn)") index(' ') short", 8)
        if cc <> 0 then do
            if pos(' NOT IN CATALOG\', m.tso_trap) > 0 ,
              & pos('CSMSV29E DATA SET ', m.tso_trap) > 0 then
                return 0
            return err('error in csm mbrList' aDsn m.tso_trap)
            end
        if mbr_name.0 == 0 | mbr_name.0 == 1 then
            return mbr_name.0
        call err 'csmExists mbr_mem#='mbr_name.0 'for dsn='aDsn
        end
endProcedure dsnExists

dsnLikeAtts: procedure expose m.
parse upper arg dsn, isFile
     if isFile then do
         ddDsn = m.tso_dsn.dsn
         if lastPos('/', m.tso_dsn.dsn, 4) < 1 then
              return tsoLikeAtts(dsn, 1)
         dsn = m.tso_dsn.dsn
         end
     sx = lastPos('/', dsn, 4)
     if sx < 1 then
         return tsoLikeAtts(dsn, 0)
     else if abbrev(dsn, '*/') | abbrev(dsn, sysVar('SYSNODE')'/') then
         return tsoLikeAtts(substr(dsn, sx+1), 0)
     else
         return csmLikeAtts(dsn)
endProcedure dsnLikeAtts

tsoLikeAtts: procedure expose m.
parse arg dsn, isFile
    rc = listDsi("'"dsn"'" copies('FILE', isFile) "SMSINFO")
    if rc = 0 then
        r = ''
    else if rc = 4 & sysReason = 19 then do
        r = 'UCOUNT(30)' /* 7.8.13 corrected attribute */
        say 'creating' dsn 'with multi volume' mv
        end
    else if rc ^= 0 then
        call err 'listDsi rc' rc 'reason' sysReason,
                             sysMsgLvl1 sysMsgLvl2
    if right(sysDsSms, 7) == 'LIBRARY' | abbrev(sysDsSms, 'PDS') ,
                      | sysDsOrg = 'PO' then
         r = 'dsOrg(PO) DSNTYPE(LIBRARY)' r
    else
         r = "dsOrg("sysDSorg")" r
    if sysUnits = 'TRACK' then
        sysUnits = 'TRACKS'
    return r "MGMTCLAS("sysMgmtClass")",
        "DATACLAS("sysDataClass")" ,
        "RECFM("strip(translate('1 2 3', ' 'sysREcFM, '123'))")",
        "LRECL("SYSLRECL")",
        "SPACE("sysPrimary"," sysSeconds")" ,
            sysUnits || left('S', sysUnits == 'TRACK')
    /*  "blksize("sysBLkSIZE")" removed 3.4.13: let sms do the magic */
endProcedure tsoLikeAtts

tsoFree: procedure expose m.
parse arg ddList, tryClose
    do dx=1 to words(ddList)
        dd = word(ddList, dx)
        if adrTso('free dd('dd')', '*') <> 0 then do
            if pos('IKJ56861I', m.tso_trap) > 0 & tryClose == 1 then
              if pos('NOT FREED, DATA SET IS OPEN', m.tso_trap),
                    > 0 then do
                  /* IKJ56861I  FILE A1 NOT FREED, DATA SET IS OPEN */
                say 'dataset open:' substr(m.tso_trap, 3)
                say '.... trying to close'
                if adrTso('execio 0 diskR' dd '(finis)', '*') = 0 then
                   call adrTso 'free dd('dd')', '*'
                end
            if m.tso_rc \== 0 then
                call sayNl 'rc='m.tso_rc 'tsoStmt='m.tso_stmt m.tso_trap
            end
        call tsoDD dd, '-', 1
        end
    return
endProcedure tsoFree

dsnCreateAtts: procedure expose m.
parse arg dsn, atts
    res = ''
    if dsn \== '' & \ abbrev(dsn, '-') then
        res = "dataset('"dsnSetMbr(dsn)"')"
    if abbrev(atts, ':') then do
        parse var atts a1 atts
        rl = substr(a1, 3)
        if abbrev(a1, ':F') then do
            if rl = '' then
                rl = 80
            res =  res "recfm("space(f b)") lrecl("rl")"
            end
        else if abbrev(a1, ':V') then do
            if rl = '' then
                rl = 32756
            res =  res "recfm("space(v b)") lrecl("rl")"
            end
        else if abbrev(a1, ':L') then
            res = res dsnLikeAtts(rl, 0)
        else if abbrev(a1, ':D') then
            res = res dsnLikeAtts(rl, 1)
        else
            call err 'csnCreateAtt bad :' a1
        end
    aU = ' 'translate(atts)
    hasOrg = pos(' DSORG(', aU) > 0 | pos(' DSNTYPE(', aU) > 0
    hasMbr = pos('(', dsn) > 0
    if hasMbr & \ hasOrg then
        atts = atts 'dsorg(po) dsntype(library)'
    if hasOrg | hasMbr then do
        ww = DSORG DSNTYPE
        do wx=1 to words(ww)
            do forever
                cx = pos(' 'word(ww, wx)'(', ' 'translate(res))
                if cx == 0 then
                    leave
                cy = pos(')', res, cx)
                res = delstr(res, cx, cy+1-cx)
                end
            end
        end
    res = res atts
    aU = ' 'translate(res)
    if pos(' MGMTCLAS(', aU) < 1 then
        res = res 'mgmtclas(COM#A091)'
    if pos(' SPACE(', aU) < 1 then
        res = res 'space(1, 50) cylinders'
    return res
endProcedure dsnCreateAtts
/*--- check if a dataset is archive ------------------------------------
          returns 'ok'                    if dataset on disk
                  'not'                   if dataset is not catalogued
                  'arc'                   if dataset archived
                  listDsi errorMsg        otherwise ------------------*/
dsnArc: procedure expose m.
parse upper arg dsn
    lc = listDsi("'"strip(dsn)"' noRecall")
    if lc = 0 then
        return 'ok'
    else if lc=4 & sysReason = 19 then  /* multiple volumes */
        return 'ok'
    else if lc=16 & sysReason = 5 then
        return 'notCat'
    else if lc=16 & sysReason = 9 then
        return 'arc'
    else
        return 'listDsi cc='lc', sysReason='sysReason ,
                          'm2='sysMsgLvl2', m1='sysMsgLvl1
endProcedure dsnArc
/*--- 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)'
    call tsoFree word(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)'
    call tsoFree word(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
    parse value dsnAlloc(frSpec, 'SHR', 'FRDD') with frDD frFr
    parse value dsnAlloc(toSpec, 'OLD', 'TODD') with toDD toFr
    call tsoOpen frDD, 'R'
    call tsoOpen toDD, 'W'
    cnt = 0
    do while readDD(frDD, r.)
        call writeDD toDD, r.
        cnt = cnt + r.0
        end
    call tsoClose frDD
    call tsoClose toDD
    call tsoFree frFr toFr
    if ggSay == 1 | m.debug == 1 then
       say cnt 'records copied from' frSpec 'to' to toSpec
    return
endSubroutine writeDsn
tsoDsiMaxl:
    rc = listDsi(arg(1) 'FILE')
    if rc ^= 0 then
        call err 'listDsi rc' rc 'reason' sysReason,
                             sysMsgLvl1 sysMsgLvl2
    return SYSLRECL  - 4 * abbrev(sysRecFm, 'V')
endSubroutine tsoDsiMaxL
/* copy adrTso end ****************************************************/
/* copy csv begin *****************************************************/
csvIni: procedure expose m.
    if m.csv.ini == 1 then
        return
    m.csv.ini = 1
    call jIni
    call classNew "n CsvRdr u JRW, f RDR r", "m",
        , "jReset m.m.rdr = arg",
        , "jOpen call csvRdrOpen m, opt",
        , "jClose call jClose m.m.rdr; call oMutatName m, 'CsvRdr'"
    call classNew "n CsvRdrR u CsvRdr", "m",
        , "jRead return csvRdrRead(m)"
    call classNew "n CsvWrt u JRW, f RDR r", "m",
        , "jReset m.m.rdr = arg",
        , "jOpen call csvWrtOpen m, opt",
        , "jClose call jClose m.m.rdr; call oMutatName m, 'CsvWrt'"
    call classNew "n CsvWrtR u CsvWrt", "m",
        , "jRead return csvWrtRead(m)"
    return
endProcedure csvIni

/*--- create a new csvRdr --------------------------------------------*/
csvRdr: procedure expose m.
parse arg rdr
    return jReset(oNew('CsvRdr'), rdr)
endProcedure csvRdr

/*--- open csvRdr: read first line and create dataClass --------------*/
csvRdrOpen: procedure expose m.
parse arg m
    call jOpen m.m.rdr, '<'
    mr = m.m.rdr
    if jRead(mr) then do
        ff = 'f' repAll(m.mr, ',', ' v, f ') 'v'
        m.m.class = classNew("n* CsvF u" ff)
        end
    call oMutatName m, 'CsvRdrR'
    return
endProcedure csvRdrOpen

/*--- read next line and return derived object -----------------------*/
csvRdrRead: procedure expose m.
parse arg m
    mr = m.m.rdr
    do until m.mr <> ''
        if \ jRead(mr) then
            return 0
        end
    var = oNew(m.m.class)
    ff = classMet(m.m.class, 'oFlds')
    s = m'.SCAN'
    call scanSrc s, m.mr
    do fx=1
        f1 = m.ff.fx
        if scanString(s, '"') then
            m.var.f1 = m.s.val
        else do
            call scanUntil s, ','
            m.var.f1 = m.s.tok
            end
        if scanEnd(s) then
            leave
        if \ scanLit(s, ',') then
            call scanErr s, ',' expected
        end
    if fx <> m.ff.0 then
        call scanerr s, 'csv cla' m.ff.0 'fields but' cx 'in line'
    m.m = var
    return 1
endProcedure csvRdrRead

/*--- create a new csvRdr --------------------------------------------*/
csvWrt: procedure expose m.
parse arg rdr
    return jReset(oNew('CsvWrt'), rdr)
endProcedure csvWrt

/*--- open csvRdr: read first line and create dataClass --------------*/
csvWrtOpen: procedure expose m.
parse arg m
    call jOpen m.m.rdr, '<'
    m.m.class = ''
    m.m.o1    = ''
    call oMutatName m, 'CsvWrtR'
    return
endProcedure csvWrtOpen

/*--- read next line and return derived object -----------------------*/
csvWrtRead: procedure expose m.
parse arg m
    mr = m.m.rdr
    if m.m.o1 \== '' then do
        i1 = m.m.o1
        m.m.o1 = ''
        end
    else if jRead(mr) then
         i1 = m.mr
    else
        return 0
    if m.m.class == '' then do
        m.m.class = objClass(i1)
        m.m.o1 = i1
        t = ''
        ff = oFlds(i1)
        do fx=1 to m.ff.0
            t = t','m.ff.fx
            end
        m.m = substr(t, 2)
        return 1
        end
    else do
        m.m = csv4Obj(i1, oFlds(i1), 0)
        return 1
        end
endProcedure csvWrtRead

csv4obj: procedure expose m.
parse arg o, ff, hasNull, oNull
    res = ''
    do fx=1 to m.ff.0
        of1 = o || left('.', m.ff.fx \== '') || m.ff.fx
        v1 = m.of1
        if hasNull & v1 = oNull then
            res = res','
        else if v1 = '' then
            res = res',""'
        else if pos(',', v1) > 0 | pos('"', v1) > 0 then
            res = res','quote(v1, '"')
        else
            res = res','v1
        end
    return substr(res, 2)
endProcedure csv4obj
/* copy csv end   *****************************************************/
/* copy j begin *******************************************************
    the j framework
         jReset
         jOpen
         jClose
         jRead
         jWrite
***********************************************************************/
jRead: procedure expose m.
parse arg m
if arg() > 1 then call err '???  old interface'  / 0
    met = objMet(m, 'jRead')
    if m.m.jReading then
        interpret met
    else
        return err('jRead('m') but not opened r')
endProcedure jRead

jReadVar: procedure expose m.
parse arg m, var
if arg() > 2 then call err '???  old interface' / 0
    if \ jRead(m) then
        return 0
    m.var = m.m
    return 1
endProcedure jReadVar

jReadObRe: procedure expose m.
parse arg m
if arg() > 1 then call err '???  old interface' / 0
    if jRead(m) then
        return m.m
    else
        return ''
endProcedure jReadObRe

jReadO: procedure expose m.
parse arg m
if arg() > 1 then call err '???  old interface'  /0
    return jRead(m)
endProcedure jReadO

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

jPosBefore: procedure expose m.
parse arg m, lx
    interpret objMet(m, 'jPosBefore')
    return m
endProcedure jPosBefore

jWriteO: procedure expose m.
parse arg m, var
    call jWrite m, var
    return
endProcedure jWriteO

jWriteAll: procedure expose m.
parse arg m, rdr
    rdr = o2file(rdr)
    met = objMet(m, 'jWriteAll')
    if \ m.m.jWriting then
        return err('jWriteAll('m',' rdr') but not opened w')
    interpret met
    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)
        call jWrite m, m.rdr
        end
    call jClose rdr
    return
endProcedure jWriteNow

jWriteNowImplO: procedure expose m.
parse arg m, rdr
    call jOpen rdr, m.j.cRead
    do while jRead(rdr)
        call jWrite m, m.rdr
        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')')
    m.m.jReading = 0
    m.m.jWriting = 0
    m.m.jUsers = 0
    interpret objMet(m, 'jReset')
    return m
endProcedure jReset

jReset0: 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')')
    m.m.jReading = 0
    m.m.jWriting = 0
    m.m.jUsers = 0
    return m
endProcedure jReset

jOpen: procedure expose m.
parse arg m, opt
    met = objMet(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 met
            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 met
            m.m.jWriting = 1
            end
        end
    m.m.jUsers = oUsers + 1
    return m
endProcedure jOpen

jClose: procedure expose m.
parse arg m
    met = objMet(m, 'jClose')
    oUsers = m.m.jUsers
    if oUsers = 1 then do
        interpret met
        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 do
        call err '-sql in jCatLines'
        end
    f2 = fmt'%#?a%c%#? %#?e%# %& %&a'
    call jOpen m, m.j.cRead
    if \ jRead(m) then do
        call jClose m
        return f(f2'%##e')
        end
    res = f(f2'%##a', m.m)
    do while jRead(m)
        res = res || f(f2, m.m)
        end
    call jClose m
    return res
endProcedure jCatLines


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"
    c1 = classNew('n JRW u ORun, f JREADING v, f JWRITING v', 'm',
        , "new ?r m = jReset0(?new2); ?jReset; return m" ,
        , "jRead" am "jRead('m')'" ,
        , "jWrite" am "jWrite('m',' line')'" ,
        , "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")
    c2 = classNew('n JRWDeleg u JRW', 'm',
        , "new ?r return jReset(?new1, arg)",
        , "jRead md = m.m.deleg; if \ jRead(md) then return 0;" ,
                         "m.m = m.md; return 1",
        , "jWrite  call jWrite(m.m.deleg, line)" ,
        , "jWriteAll call jWriteAll m.m.deleg, rdr",
        , "jWriteNow call jWriteNow m.m.deleg, rdr",
        , "jReset    if arg \== '' then m.m.deleg = arg;",
                                   "else call jReset m.m.deleg;",
        , "jOpen     call jOpen m.m.deleg,' opt; return m" ,
        , "jClose    call jClose m.m.deleg; return m" )
    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 o2Text(line, 157)" ,
        , "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  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)
    m.j.out = jOpen(oNew('JSay'), '>')
    m.j.errRead  = "return err('jRead('m') but not opened r')"
    m.j.errWrite = "return err('jWrite('m',' line') but not opened w')"
    call classNew "n JBuf u JRW, f BUF s r", "m",
        , "jOpen call jBufOpen m, opt",
        , "jClose call oMutatName m, 'JBuf'",
        , "jReset call jBufReset m, arg",
        , "jRead return jBufRead(m)",
        , "jWrite call jBufWrite m, line",
        , "jPosBefore m.m.readIx = m.m.readIx+0;m.m.readIx = lx-1"
    call classNew "n JbufText u JBuf, f MAXL v ", "m",
        , "jReset call jBufReset m, arg; m.m.maxl = word(arg 80, 1)",
        , "jWrite call jBufWrite m, o2Text(line, m.m.maxl)"
    return
endProcedure jIni

/*--- return a JRW from rdr or in ------------------------------------*/
in2File: procedure expose m.
    parse arg m
    interpret objMet(m, 'in2File')
    return err('in2File did not return')
endProcedure in2File
      /* jstr is part of out interface --> in2Str */
in2Str: procedure expose m.
    parse arg m, fmt
    interpret objMet(m, 'in2Str')
    return err('in2Str did not return')
endProcedure in2Str

in2Buf: procedure expose m.
parse arg m
    interpret objMet(m, 'in2Buf')
    return err('in2Buf did not return')
endProcedure in2Buf

in: procedure expose m.
    if arg() > 0 then call err '??? old interface'
    r = m.j.in
    m.in_ret = jRead(r)
    m.in = m.r
    return m.in_ret
endProcedure in

inVar: procedure expose m.
parse arg var
    return jReadVar(m.j.in, var)
endProcedure inVar

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

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

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

/*--- out (part of) the lines of a stem ----------------------------*/
outSt: procedure expose m.
parse arg st, fx, tx
    do lx=word(fx 1, 1) to word(tx m.st.0, 1)
        call out m.st.lx
        end
    return st
endProcedure outSt

outX: procedure expose m.
parse arg line
    if symbol('m.tst_m') \== 'VAR' then
        call jWrite m.j.out, line
    else
        call tstOut m.tst_m, line
    return 0
endProcedure out

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

JRWDeleg: procedure expose m.
parse arg arg
    return oNew('JRWDeleg', arg)
endProcedure JRWDeleg

/*--- 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
    return m
endProcedure jBuf
/*--- jBuf: buffer read or write (supports datataypes) ---------------*/
jbufText: procedure expose m.
    m = oNew('JbufText') /* calls jBufReset */
    do ax=1 to arg()
        m.m.buf.ax = o2text(arg(ax))
        end
    m.m.buf.0 = ax-1
    return m
endProcedure jbufText

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
    return m
endProcedure jBufReset

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

jBufWrite: procedure expose m.
parse arg m, line
    call mAdd m'.BUF', line
    return
endProcedure jBufWrite

jBufWriteStem: procedure expose m.
parse arg m, st
    ax = m.m.buf.0
    do sx=1 to m.st.0
        ax = ax + 1
        m.m.buf.ax = m.st.sx
        end
    m.m.buf.0 = ax
    return m
endProcedure jBufWriteStem

jBufRead: procedure expose m.
parse arg m
    nx = m.m.readIx + 1
    if nx > m.m.buf.0 then
        return 0
    m.m.readIx = nx
    m.m = m.m.BUF.nx
    return 1
endProcedure jBufRead

jBufCopy:
parse arg rdr
    b = jOpen(jBuf(), m.j.cWri)
    call jWriteNow b, rdr
    return jClose(b)
endProcedure jBufCopy

jSingle: procedure expose m.
parse arg m
    call jOpen m, '<'
    one = jRead(m)
    two = jRead(m)
    call jClose m
    if \ one then
        if arg() < 2 then
            call err 'empty file in jSingle('m')'
        else
            return arg(2)
    if two then
        call err '2 or more recs in jSingle('m')'
    return m.m
endProcedure jSingle
/* copy j end *********************************************************/
/* copy o begin ******************************************************
    an object is register for a class in o2c
    a class has a list of parents in cParent
    a class has a methodTable cMet with lazy initialization
             a method generator
        otherwise an existing method is simply copied
***********************************************************************/
oIni: procedure expose m.
    if m.o_ini == 1 then
        return
    m.o_ini = 1
    call classIni
    call classNew 'n= root u', 'm',
           , "new    ?l" ,
           , "new1   ?l" ,
           , "new2   ?l" ,
           , "oClear ?l" ,
           , "oCopy  ?l"
    return
endProcedure oIni

oMetLazy: procedure expose m.
parse arg cl, met, trg rest
    if met == 'new' then
        return 'return' classMet(cl, 'new2')
    if met == 'new1' then do
        call mNewArea cl, 'O.'substr(cl,7)
        return  "oMutate(mNew('"cl"'), '"cl"')"
        end
    if met == 'new2' then do
        call classMet cl, 'oClear'
        return "classClear('"cl"'," classMet(cl, 'new1')")"
        end
    if met == 'oFlds' then do
        m.cl.flds.0 = 0
        m.cl.flds_self = 0
        m.cl.stms.0 = 0
        m.cl.stms_self = 0
        call classFldAdd cl, cl
        return cl'.FLDS'
        end
    call classMet cl, 'oFlds'
    if wordPos(met, 'f2c f2x stms s2c') > 0 then do
        if met == 'f2x' then
            call mInverse cl'.FLDS', cl'.F2X'
        return cl'.'translate(met)
        end
    if met == 'oClear' then do
        do fx=1 to m.cl.flds.0
            f1 = m.cl.flds.fx
            m.cl.flds_null.fx = if(m.cl.f2c.f1==m.class_W,
                            , m.o_escW, '')
            end
        m.cl.flds_null.0 = m.cl.flds.0
        return "return classClear('"cl"', m)"
        end
    if met == 'oCopy' then do
        if cl == m.class_N | cl == m.class_S | cl == m.class_W then
            return 'return m'
        do sx=1 to m.cl.stms.0
            s1 = m.cl.stms.sx
            call classMet m.cl.s2c.s1, 'oCopy'
            end
        call classMet cl, 'new'
        return "if t=='' then t = mNew('"cl"');" ,
               "call oMutate t, '"cl"';" ,
               "return classCopy('"cl"', m, t)"
        end
    call err 'bad method in oMetLazy('cl',' met')'
endProcedure oMetLazy

classInheritsOf: procedure expose m.
parse arg cl, sup
    cl = class4name(cl)
    sup = class4name(sup)
    if m.cl.inheritsOf \== 1 then do
        m.cl.inheritsOf = 1
        call classInheritsOfAdd cl, cl'.INHERITSOF'
        end
    return m.cl.inheritsOf.sup == 1
endProcedure classInheritsOf

classInheritsOfAdd: procedure expose m.
parse arg cl, trg, pa
    pa = classCycle(cl, pa)
    m.trg.cl = 1
    call assert "m.cl == 'u'"
    do cx=1 to m.cl.0
        c1 = m.cl.cx
        if m.c1 == 'u' then
            call classInheritsOfAdd c1, trg, pa
        end
    return
endProcedure classInheritsOf

classClear: procedure expose m.
parse arg cl, m
    if m.cl.flds_self then
        m.m = m.cl.flds_null.1
    do fx=1+m.cl.flds_self to m.cl.flds.0
        f1 = m.cl.flds.fx
        m.m.f1 = m.cl.flds_null.fx
        end
    if m.cl.stms_self then
        m.m.0 = 0
    do sx=1+m.cl.stms_self to m.cl.stms.0
        s1 = m.cl.stms.sx
        m.m.s1.0 = 0
        end
    return m
endProcedure classClear

classCopy: procedure expose m.
parse arg cl, m, t
    if m.cl.flds_self then
        m.t = m.m
    do fx=1+m.cl.flds_self to m.cl.flds.0
        f1 = m.cl.flds.fx
        m.t.f1 = m.m.f1
        end
    if m.cl.stms_self then
        call classCopyStem m.cl.s2c., m, t
    do sx=1+m.cl.stms_self to m.cl.stms.0
        s1 = m.cl.stms.sx
        call classCopyStem m.cl.s2c.s1, m'.'s1, t'.'s1
        end
    return t
endProcedure classCopy

classCopyStem: procedure expose m.
parse arg cl, m, t
    m.t.0 = m.m.0
    do sx=1 to m.t.0
        call classCopy cl, m'.'sx, t'.'sx
        end
    return 0
endProcedure classCopyStem

/*--- return true if src is a rexxVariable a, m.a.c etc. -------------*/
rxIsVar: procedure expose m.
parse arg src
    if pos(left(src, 1), m.ut_rxN1) > 0 then
        return 0
    else
        return verify(src, m.ut_rxId) = 0
endProcedure rxIsVar

/*--- return true if src is a rexxConstant rerpresenting its value ---*/
rxIsConst: procedure expose m.
parse arg src, vars                                                   c
    if \ rxIsVar(src) then
        return 0
    srU = translate(src)
    if srU \== src then
        return 0
    srU = '.'srU'.'
    if pos('.GG', srU) > 0 then
        return 0
    if vars == '' then
        return 1
    upper vars
    do vx=1 to words(vars)
        if pos('.'word(vars, vx)'.', vars) > 0 then
            return 0
        end
    return 1
endProcedure rxIsConst

/*--- return rexx code m.cc or mGet('cc') ----------------------------*/
rxMGet: procedure expose m.
parse arg v1, cc, vars
    if cc == '' then
        return 'm.'v1
    else if rxIsConst(cc, vars) then
        return 'm.'v1'.'cc
    else
        return 'mGet('v1 || quote('.'cc, "'")')'
endProcedure rxMGet

/*--- print object ---------------------------------------------------*/
objOut: procedure expose m.
parse m, pr, p1
    return classOutDone(m.class_O, m, pr, p1)

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

/*--- 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 outX(p1'done :'className(t) '@'a)
    done.t.a = 1
    if t = m.class_O then do
        if a == '' then
            return outX(p1'obj null')
        t = objClass(a)
        if t = m.class_N | t = m.class_S then
            return outX(p1'obj has no class @'m.a)
        else
            return classOutDone(t, a, pr, p1'isA')
        end

    if t == m.class_V then
        return outX(p1'=' m.a)
    if t == m.class_W == 'w' then
        return outX(p1'}' substr(a, 2))
    if m.t == 'f' then
        return classOutDone(m.t.1, a'.'m.t.name, pr, p1'.'m.t.name)
    if m.t == 'r' then do
        if m.a == '' then
            return outX(p1'refTo :'className(m.t.1) '@null@')
        else
            return classOutDone(m.t.1, 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_V
        call outX 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 outX p1'stem' m.a.0
        do ux=1 to m.a.0
            call classOutDone m.t.1, 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.1, 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

/*--- create an an object of the class className
        and call it's new method ------------------------------------*/
oNew: procedure expose m.
parse arg cl, arg, arg2, arg3
    interpret classMet(class4name(cl), 'new')
endProcedure oNew

/*--- return the class of object obj ---------------------------------*/
objClass: procedure expose m.
parse arg m
    if arg() > 1 then
        return err('old objClass') / 0
    if symbol('m.o.o2c.m') == 'VAR' then
        return m.o.o2c.m
    else if abbrev(m, m.o_escW) then
        return m.class_w
    else if m \== '' then
        return m.class_S
    else
        return m.class_N
endProcedure objClass

oKindOf: procedure expose m.
parse arg obj, sup
    cl = objClass(obj)
    return classInheritsOf(cl, sup)
endProcedure oKindOf

/*--- return the code of method met of object m -----------------------*/
objMet: procedure expose m.
parse arg m, met
    if symbol('m.o.o2c.m') == 'VAR' then
        cl = m.o.o2c.m
    else if abbrev(m, m.o_escW) then
        cl = m.class_w
    else if m \== '' then
        cl = m.class_S
    else
        cl = m.class_N
    if symbol('m.cl.method.met') == 'VAR' then
        return m.cl.method.met
    else
        return classMet(cl, met)    /* will do lazy initialisation */
endProcedure objMet

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

oClear: procedure expose m.
parse arg m
    interpret objMet(m, 'oClear')
    return m
endProcedure oClear

/*--- copy object m of class cl to t --------------------------------*/
oClaCopy: procedure expose m.
parse arg cl, m, t
    interpret "drop cl;" classMet(cl, 'oCopy')
endProcedure oClaCopy

/*--- copy object m to t / create a new object if t=='' -------------*/
oCopy: procedure expose m.
parse arg m, t
    interpret objMet(m, 'oCopy')
endProcedure oCopy

/*--- return a new instance of a subclass of Run
        with code code in method oRun -------------------------------*/
oRunner: procedure expose m.
parse arg code
    return oNew(classNew('n* ORun u ORun, m oRun' code))
endProcedure oRunner

/*--- 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 of object m No Procedure:
        ??? optimize: class only run ???
         use from caller unprotected---------------------------------*/
oRunNP: procedure expose m.
    interpret objMet(arg(1), 'oRun')
    return
endProcedure oRunNP

/*--- run method oRun and return output in new JBuf ------------------*/
oRun2File: procedure expose m.
parse arg rn
    b = jBuf()
    call pipe '+F' , b
    call oRun rn
    call pipe '-'
    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 arg() = 1 then
        fmt = ' '
    interpret objMet(m, 'o2String')
    return err('o2String did not return')
endProcedure o2String

/*--- return a short string representation of an object -------------*/
o2Text: procedure expose m.
parse arg m, maxL, fmt
    if maxL == '' then
        maxL = 75
    interpret objMet(m, 'o2Text')
endProcedure o2Text

o2TextFlds: procedure expose m.
parse arg m, cl, maxL
    r = m'=¢'
    do fx=1 to m.cl.flds.0 while length(r) <= maxL
        f1 = m.cl.flds.fx
        c1 = m.cl.f2c.f1
        if c1 = m.class_V then
            op = '='
        else if m.c1 == 'r' then
            op = '=>'
        else
            op = '=?'c1'?'
        r = r || left(' ', fx > 1) || m.cl.flds.fx || op
        if m.cl.flds.fx == '' then
            r = r || strip(m.m)
        else
            r = r || strip(mGet(m'.'m.cl.flds.fx))
        end
    if length(r) < maxL then
        return r'!'
    else
        return left(r, maxL-3)'...'
endProcedure o2TextFlds

o2TextMet: procedure expose m.
parse arg cl, met
    m1 = classMet(cl, 'o2String', '-')
    if m1 \== '-' then do
        if translate(word(m1, 1)) \== 'RETURN' then
            call err 'o2TextMet' className(cl)'#o2String return?:' m1
        return '__r = strip('subword(m1, 2)', "t");',
             'if length(__r) <= maxL then return __r;' ,
             'else return left(__r, maxL-3)"..."'
        end
    call classMet cl, 'oFlds'
    return 'return o2TextFlds(m, '''cl''', maxL)'
endProcedure o2TextMet
/*--- cast a String to an object -----------------------------------*/
s2o: procedure expose m.
parse arg str
    return m.o_escW || str
endProcedure s2o

oIfStr: procedure expose m.
parse arg m
    if length(m) > 200 then
        return m
    cl = objClass(m)
    if cl == m.class_N | cl == m.class_S then
        return m
    else if cl = m.class_V then
        return = m.m
    else if cl == m.class_W 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 address (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' (cu (',' cu)*)?
    cu = ce | c1* '%' c1* '%'? name+      (same type for each name)

    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
    m.o_escW = '!'
    call mapIni
    call timeIni
    m.class.0 = 0
    call mapReset class_n2c  /* name to class */
    m.class_V = classNew('n v u', 'm',
          , "o2String return m.m",
          , "o2File return file(m.m)")
    m.class_W = classNew('n w u', 'm' ,
          , "o2String return substr(m, 2)" ,
          , "o2File return file(substr(m,2))")
    m.class_O = classNew('n o u')
    m.class_R = classNew('r')

    m.class_C = classNew('n class u')
    call classNew 'n= class u v' ,
          , 'c u u f NAME v',           /* union or class */
          , 'c f u f NAME v',           /* field          */
          , 'c s u' ,                   /* stem           */
          , 'c c u f NAME v',           /* choice         */
          , 'c r u' ,                   /* reference      */
          , 'c m u f NAME v, f MET  v'  /* method         */
    call mAdd m.class_C, classNew('s r class')
    m.class_root = classNew('n root u', 'm',
          , "f2c    ?l" ,
          , "f2x    ?l" ,
          , "oFlds  ?l" ,
          , "o2Text ?o2textMet",
          , "s2c    ?l" ,
          , "stms   ?l" ,
          , "in2Str ?r return ?o2String" ,
          , "in2File ?r return ?o2File" ,
          , "in2Buf ?r return jBufCopy(?o2File)" ,
          , "scanSqlIn2Scan ?r" ,
                  "return scanSqlReset(s, ?in2File, wOpt, sOpt)")
    m.class_S = classNew('n String u', 'm',
          , 'in2Str return m' ,
          , 'in2File return jBuf(m)',
          , 'in2Buf return jBuf(m)',
          , 'o2String return m',
          , "scanSqlIn2Scan ?r if wOpt == '' then wOpt = 0;" ,
                  "return scanSqlReset(s, ?in2File, wOpt, sOpt)")
    m.class_N = classNew('n Null u', 'm',
          , 'in2Str return o2String(m.j.in, fmt)',
          , 'in2File return m.j.in',
          , 'in2Buf return jBufCopy(m.j.in)')
    call classNew 'n ORun u', 'm',
          , 'oRun call err "call of abstract method oRun"',
          , 'o2File return oRun2File(m)',
          , 'o2String return jCatLines(oRun2File(m), fmt)',
          , 'o2Text   ?r return m"=¢?:!"'
    return
endProcedure classIni

classNe1: procedure expose m.
parse arg n, ty, nm, refs, io
    ky = ty','nm','space(refs, 1)','strip(io)
    if ty == 'f' & abbrev('=', nm) then do
        if words(refs) = 1 & io == '' then
            return strip(refs)
        else
            call err 'bad field name:' ky
        end
    if n then
        if symbol('m.class_k2c.ky') == 'VAR' then
            return m.class_k2c.ky
    m.class.0 = m.class.0 + 1
    n = 'CLASS.'m.class.0
    call mapAdd class_n2c, n, n
    m.n = ty
    m.n.name = nm
    m.n.met = strip(io)
    m.n.0 = words(refs)
    do rx=1 to m.n.0
        m.n.rx = mapGet(class_n2c, word(refs, rx))
        end
    if right(nm, 1) == '*' then
        nm = left(nm, length(nm)-1)substr(n, 7)
    if length(ty) \== 1 | pos(ty, 'ufscrm') < 1 then
        call err 'bad type' ty': classNe1('ky')' /0
    else if nm == '' & pos(ty, 'm') > 0 then
        call err 'empty name: classNe1('ky')'
    else if nm \== '' & ty \== 'c' & verifId(nm) > 0 then
        call err 'bad name' nm': classNe1('ky')'
    else if nm \= '' & pos(ty, 'rs') > 0 then
        call err 'name for type' ty': classNe1('ky')'
    else if (pos(ty, 'fcsr') > 0 & m.n.0 \== 1) ,
          | (    ty == 'm' & m.n.0 \== 0) then
        call err m.n.0 'bad ref count in classNe1('ky')'
    return n
endProcedure classNe1

classNew: procedure expose m.
parse arg clEx 1 ty rest
    n = ''
    nm = ''
    io = ''
    refs = ''
    if wordPos(ty, 'n n? n* n=') > 0 then do
        nmTy = right(ty, 1)
        parse var rest nm ty rest
        if nmTy = '=' then do
            if \ mapHasKey(class_n2c, nm) then
                call err 'class' nm 'not defined: classNew('clEx')'
            n = mapGet(class_n2c, nm)
            end
        if ty \== 'u' then
            call err 'class name' nm 'without u: classNew('clEx')'
        if nmTy == '?' then do
            if mapHasKey(class_n2c, nm) then
                return mapGet(class_n2c, nm)
            end
        else if nmTy == '*' & arg() == 1 then do
            if mapHasKey(class_n2c, clEx) then
                return mapGet(class_n2c, clEx)
            end
        end
    else do
        nmTy = ''
        if arg() == 1 then
            if mapHasKey(class_n2c, clEx) then
                return mapGet(class_n2c, clEx)
        if length(ty) <> 1 | pos(ty, 'ufscmr') < 1 then
            return err('bad type' ty': classNew('clEx')')
        if pos(ty, 'fcm') > 0 then
            parse var rest nm rest
        if ty == 'm' then
            io = rest
        else if pos(ty, 'fsc') > 0 | (ty == 'r' & rest \== '') then
            refs = classNew(strip(rest))
        else if ty == 'r' then
            refs = m.class_O
        end
    if ty == 'u' then do
        lx = 0
        do while lx < length(rest)
            t1 = word(substr(rest, lx+1), 1)
            cx = pos(',', rest, lx+1)
            if cx <= lx | t1 == 'm' then
                cx = length(rest)+1
            one = strip(substr(rest, lx+1, cx-lx-1))
            lx=cx
            if pos('%', word(one, 1)) < 1 then
                refs = refs classNew(one)
            else do
                parse value translate(word(one, 1), ' ', '-') ,
                      with wBe '%' wAf '%' ww
                do wx=2 to words(one)
                    refs = refs classNew(wBe word(one, wx) wAf)
                    end
                end
            end
        pref = ''
        do ax=2 to arg()
            if length(arg(ax)) == 1 & arg(ax) \== ' ' then
                pref = arg(ax)' '
            else
                refs = refs classNew(pref || arg(ax))
            end
        end
    if nmTy == '=' then do
        if m.n \== ty | ty \== 'u' then
            call err 'n= mismatch'
        do ux=1 to words(refs)
            call mAdd n, word(refs, ux)
            end
        end
    else if nmTy == '*' then
        n = classNe1(0, ty, nm'*', refs, io)
    else
        n = classNe1(nmTy == '', ty, nm, refs, io)
    if arg() == 1 then
        call mapAdd class_n2c, clEx, n
    if nmTy == '*' & m.n.name == nm'*' then
        m.n.name = nm || substr(n, 6)
    if nmTy \== '' & nmTy \== '=' then
       call mapAdd class_n2c, m.n.name, n
    return n
endProcedure classNew

/*--- return the name of a class if it exists otherwise the class ---*/
className: procedure expose m.
parse arg cl
    if \ mapHasKey(class_n2c, cl) then
        return 'notAClass:' cl
    c2 = mapGet(class_n2c, cl)
    if m.c2 = 'u' & m.c2.name \= '' then
        return m.c2.name
    else
        return cl
endProcedure className

/*--- 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

/*--- mutate object m to the class cl -------------------------------*/
oMutate: procedure expose m.
parse arg m, cl
    m.o.o2c.m = cl
    return m
endProcedure oMutate

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

/*--- find the code for a met in a class ---------------------------*/
classMet: procedure expose m.
parse arg cl, met
    if symbol('m.cl.method.met') == 'VAR' then
        return m.cl.method.met
    if symbol('m.cl.metLazy.met') == 'VAR' then do
                                     /* build lazy method */
        m.cl.method.met = "call err 'building lazy method" cl"#"me"'"
        parse var m.cl.metLazy.met m1 mR
        if m1 == '?r' then
            m.cl.method.met = classMetRec(cl, met, mR)
        else if m1 == '?l' then
            m.cl.method.met = oMetLazy(cl, met, mR)
        else
            interpret 'm.cl.method.met =' substr(m1,2)'(cl,met,mR)'
        return m.cl.method.met
        end
    if symbol('m.class_n2c.cl') \== 'VAR' then
        call err 'no class classMet('cl',' met')'
    if cl \== m.class_n2c.cl then
        return classMet(m.class_n2c.cl, met)
    if m.cl.methods \== 1 then do    /* already generated?          */
        m.cl.methods = 1             /* generate methods from class */
        if m.cl == 'u' then
            call classMetGen cl, cl'.'method, cl'.'metLazy
        call classMetGen m.class_root, cl'.'method, cl'.'metLazy
        return classMet(cl, met, arg(3))
        end
    if arg(3) == '' then
        return err('no method' met 'in class' className(cl))
    else
        return arg(3)
endProcedure classMet
/*--- generate all methods for a class recursively (if not already done)
          lazy methods are only put to metLazy -----------------------*/
classMetGen: procedure expose m.
parse arg aC, trg, lazy, pa
    pa = classCycle(aC, pa)
    if m.aC \== 'u' then
        call err 'cl not u:' m.aC aC
    do cx=1 to m.aC.0
        cl = m.aC.cx
        if pos(m.cl, 'ufscr') > 0 then
            iterate
        if m.cl \== 'm' then
            call err 'bad cla' cl m.cl
        m1 = m.cl.name
        if symbol('m.trg.m1')=='VAR' | symbol('m.lazy.m1')=='VAR' then
            nop
        else if \ abbrev(m.cl.met, '?') then
            m.trg.m1 = m.cl.met
        else
            m.lazy.m1 = m.cl.met
        end
    do cx=1 to m.aC.0
        cl = m.aC.cx
        if m.cl \== 'u' then
            iterate
        call classmetGen cl, trg, lazy, pa
        end
    return
endProcedure classmetGen

classMetRec: procedure expose m.
parse arg cl, met, rest
    gen = ''
    rx = 1
    do forever
        ry = pos('?', rest, rx)
        if ry == 0 then
            return gen || substr(rest, rx)
        gen = gen || substr(rest, rx, ry-rx)
        if substr(rest, ry+1, 1) == ':' then do
            gen = gen || className(cl)
            rx = ry+2
            end
        else if substr(rest, ry+1, 1) == '#' then do
            gen = gen || met
            rx = ry+2
            end
        else do
            rx = verify(rest, m.ut_alfid, 'n', ry+1)
            if rx = 0 then
                rx = length(rest)+1
            else if rx <= ry+1 then
                call err 'classMetRec bad ?clause' substr(rest, x)
            rr = classMet(cl, substr(rest, ry+1, rx-ry-1))
            if word(rr, 1) = 'return' then
                rr = subword(rr,2)
            gen = gen || rr
            end
        end
endProcedure classMetRec

classCycle: procedure expose m.
parse arg cl, pa
    if wordPos(cl, pa) < 1 then
        return pa cl
    call err classCycle cl pa / 0
endProcedure classCycle

classMetLazy: procedure expose m.
parse arg cl, meNm, trg, m1 mRest
     m.trg.meNm = "call err 'building lazy method" cl"#"meNm"'"
     if m1 == '?r' then
         m.trg.meNm = classMetRec(cl, meNm, mRest)
     else if l1 == '?l' then
         call oMetLazy(cl, meNm, trg, mest)
     else
         interpret 'm.cl.method.met =' + substr(l1,2)'(cl,met,lRest)'
     return m.cl.method.met
classFlds: procedure expose m.
parse arg cl
    return classMet(cl, 'oFlds')
endProcedure classFlds

/*--- add the the fields of class cl to stem f ----------------------*/
classFldAdd: procedure expose m.
parse arg f, cl, nm, pa
    pa = classCycle(cl, pa)
    if cl == m.class_V | cl == m.class_W | cl == m.class_O ,
             | m.cl == 'r' then
             return classFldAdd1(f'.FLDS', f'.F2C', cl, nm,
                  , if(cl=m.class_W, m.o_escW, ''))
    if m.cl = 's' then do
        if m.cl.1 == '' then
            call err 'stem null class'
        return classFldAdd1(f'.STMS', f'.S2C', m.cl.1, nm, 0)
        end
    if m.cl = 'f' then
        return classFldAdd(f, m.cl.1, nm ,
          || left('.', m.cl.name \== '' & nm \== '') || m.cl.name, pa)
    do tx=1 to m.cl.0
        call classFldAdd f, m.cl.tx, nm, pa
        end
    return 0
endProcedure classFldAdd

classFldAdd1: procedure expose m.
parse arg fa, f2, cl, nm, null
    if symbol('m.f2.nm') == 'VAR' then
        if m.f2.nm == cl then
            return 0
        else
            return err('f2 mismatch f2='f2 Nm 'cl='cl 'old='m.f2.nm)
    m.f2.nm = cl
    if nm == '' then do
        call mMove fa, 1, 2
        m.fa.1 = ''
        call mPut fa'_SELF', 1
        end
    else do
        call mAdd fa, nm
        end
    return 0
endProcedure classFldAdd1
/* copy class 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.ut_alfDot, '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 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 = mapAdr(a, ky, 'a')
    if vv == '' then
        return err('duplicate in mapAdd('a',' ky',' val')')
    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 = mapAdr(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 mapAdr(a, ky, 'g') \== ''
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 =  mapAdr(a, ky, 'g')
    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 = mapAdr(a, ky, 'g')
    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 = 247 - 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 = mapAdr(a, ky, 'g')
            if adr \== '' then do
                ha = left(adr, length(adr) - 2)
                do i = 1 to m.ha.0
                     vv = ha'v'i
                     drop m.ha.i m.vv
                     end
                 drop m.ha.0
                 end
            end
        end
    m.st.0 = 0
    return a
endProcedure mapClear

/*--- return the value pointer for a key, '' if non existing
    f = 'g' return address if exists otherwise ''
        'p' return address if exists otherwise newly added address
        'a' return ''      if exists otherwise newly added address ---*/
mapAdr: procedure expose m.
parse arg a, ky, f
    if length(ky) + length(a) < 247 then do
        res = a'.'ky
        if symbol('m.res') == 'VAR' then
            return copies(res, f \== 'a')
        else if f == 'g' then
            return ''
        end
    else do
        len = length(ky)
        q = len % 2
        ha = a'.'len || left(ky, 80) || substr(ky,
            , len % 2 - 40, 85-length(len)-length(a)) || right(ky, 80)
        if symbol('M.ha.0') == 'VAR' then do
            do i=1 to m.ha.0
                if m.ha.i == ky then
                    return copies(ha'v'i, f \== 'a')
                end
            end
        else do
            i = 1
            end
        if f == 'g' then
            return ''
        if i > 9 then
            call err 'overflow long key' y 'in map' a 'hash' ha'.K.'i
        m.ha.0 = i
        m.ha.i = ky
        res = ha'v'i
        end
    if m.map_keys.a \== '' then
        call mAdd m.map_keys.a, ky
    return res
endProcedure mapAdr

/* copy map 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>.** and m.<mbr>_**: every rexx Module (copy) should only
               allocate these addresses to avoid address conficts
               with <mbr> the name of therexx 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
    ax = m.m_area.0 + 1
    m.m_area.0 = ax
    m.m_area.ax = nm
    if adr == '=' then
        adr = nm
    else if adr == '' then
        adr = 'M.'ax
    if symbol('m.m_2a.nm') == 'VAR' then
        call err 'area name' nm 'already used'
    if symbol('m.m_2a.adr') == 'VAR' then
        call err 'adr' adr 'for area' nm 'already used'
    m.m_2a.adr = adr
    m.m_2a.nm  = adr
    m.adr.0 = 0
    m.m_free.adr.0 = 0
    return nm
endProcedure mNewArea

mNew: procedure expose m. ggArea
parse arg name
    if symbol('m.m_2a.name') \== 'VAR' then
        call err 'area' name 'does not exists'
    adr = m.m_2a.name
    if m.m_free.adr.0 > 0 then do
        fx = m.m_free.adr.0
        m.m_free.adr.0 = fx-1
        return m.m_free.adr.fx
        end
    m.adr.0 = m.adr.0 + 1
    return adr'.'m.adr.0
endProcedure mNew

mFree: procedure expose m.
parse arg m
    adr = left(m, lastPos('.', m)-1)
    fx = m.m_free.adr.0 + 1
    m.m_free.adr.0  = fx
    m.m_free.adr.fx = m
    return ''
endProcedure mFree
/*--- iterate over all allocate elements of an area ------------------*/
mIterBegin: procedure expose m.
parse arg nm
    return m.m_2a.nm'.0'
endProcedure mIterBegin

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

/*--- get m.a --------------------------------------------------------*/
mGet: procedure expose m.
parse arg a
    return m.a
endProcedure mGet

/*--- put value v into m.a -------------------------------------------*/
mPut: procedure expose m.
parse arg a, v
    m.a = v
    return v
endProcedure mPut

/*--- 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

/*--- create the inverse map of a stem -------------------------------*/
mInverse: procedure expose m.
parse arg a, i
    do x=1 to m.a.0
        v = m.a.x
        m.i.v = x
        end
    return m.a.0
endProcedure inverse

/*--- add to m.dst.* a (sub)sequence of m.src.* ----------------------*/
mAddSt: procedure expose m.
    parse arg dst, src, fx , tx
    dx = m.dst.0
    if fx == '' then
        fx = 1
    if tx == '' then
        tx = m.src.0
    do sx = fx to tx
        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 with separator ---------------------------*/
mCat: procedure expose m.
parse arg st, sep
    if m.st.0 < 1 then
        return ''
    res = m.st.1
    do sx=2 to m.st.0
        res = res || sep || m.st.sx
        end
    return res
endProcedure mCat

mIni: procedure expose m.
    if m.m_ini == 1 then
        return
    m.m_ini = 1
    call utIni
    m.m_area.0 = 0
    call mNewArea
    return
endProcedure mIni

/* copy m end *********************************************************/
/* copy fTab begin *****************************************************
    output Modes: t = tableMode 1 line per object
                  c = colMode   1 line per column/field of object

    lifeCycle fTab           sql

        fTabReset            sqlFTabReset
        fTabAdd    *         sqlFTabAdd *
                             sqlFTabOthers ?
        fTabGenerate
        fTabBegin                                      header lines
        fTab1 * / tTabCol *
        fTabEnd                                        trailer lines
***********************************************************************/
fTabIni: procedure expose m.
    if m.fTab_ini == 1 then
        return
    m.fTab_ini = 1
    call classIni
    m.fTab_class = classNew("n FTab u ORun, m oRun call fTab m")
    return
endProcedure fTabIni

fTabReset: procedure expose m.
parse arg m, m.m.titBef, m.m.titAft, m.m.opt
    call fTabIni
    if m.m.titBef == '' & m.m.titaft == '' then do
        m.m.titBef = 'c 1'
        m.m.titAft = '1 c'
        end
    if m.m.titBef == '-' then
        m.m.titBef = ''
    if m.m.titAft == '-' then
        m.m.titAft = ''
    m.m.generated = ''
    m.m.0 = 0
    m.m.len = 0
    m.m.cols = ''
    m.m.sqlOthers = 1
    m.m.set.0 = 0
    return oMutate(m, m.fTab_class)
endProcedure fTabReset

/* add a piece to title tx at current pos */
fTabAddTit: procedure expose m.
parse arg m, cx, tx, t1
    m.m.generated = ''
    if ty < m.m.cx.tit.0 then do
        do xx=m.m.cx.tit.0+1 to tx-1
            m.m.cx.tit.xx = ''
            end
        m.m.cx.tit.0 = tx
        end
    m.m.cx.tit.tx = t1
    return m
endProcedure fTabAddTit

/*--- set the infos for one column -----------------------------------*/
fTabSet: procedure expose m.
parse arg m, c1 aDone, f1, l1
    sx = m.m.set.0 + 1
    m.m.set.0 = sx
    m.m.set.sx = c1 aDone
    m.m.set.sx.fmt = f1
    m.m.set.sx.labelCy = l1
    m.m.set.sx.labelTi = c1
    m.m.set.c1 = sx
    return
endProcedure fTabSet

fTabAdd: procedure expose m.     /* old interface, new is ...RCT */
parse arg m, c1Done, f1, l1
    call fTabAddRCT m, c1Done, f1, , l1
    ox = m.m.0
    m.m.ox.tit.0 = max(arg()-3, 1)
    do tx=2 to m.m.ox.tit.0
        m.m.ox.tit.tx = arg(tx+3)
        end
    return
endProcedure fTabAdd

fTabAddRCT: procedure expose m.
parse arg m, rxNm aDone, f1, cyNm, tiNm
    cx = m.m.0 + 1
    m.m.generated = ''
    m.m.0 = cx
    m.m.cx.tit.0 = max(arg()-4, 1)
    m.m.cx.tit.1 = ''
    do tx=2 to m.m.cx.tit.0
        m.m.cx.tit.tx = arg(tx+4)
        end
    r1 = rxNm
    if rxNm == '' then
        r1 = '='
    else if rxNm == '=' then
        rxNm = ''
    m.m.cols = m.m.cols r1
    if words(m.m.cols) <> cx then
        call err 'mismatch of column number' cx 'col' rxNm / 0
    if length(aDone) > 1 | wordPos('<'aDone'>', '<> <0> <1>') < 1 then
        call err 'bad done' length(aDone) '<'aDone'> after rxNm' rxNm
    m.m.cx.col = rxNm
    m.m.cx.done = aDone \== 0
    if cyNm == '' then
        m.m.cx.labelCy = r1
    else
        m.m.cx.labelCy = cyNm
    if tiNm == '' then
        m.m.cx.labelTi = m.m.cx.labelCy
    else
        m.m.cx.labelTi = tiNm
    px = pos('%', f1)
    ax = pos('@', f1)
    if px < 1 | (ax > 0 & ax < px) then
        m.m.cx.fmt = f1
    else
        m.m.cx.fmt = left(f1, px-1)'@.'rxNm || substr(f1, px)
    return m
endProcedure fTabAddRCT

fTabGenerate: procedure expose m.
parse arg m, sep
    f = ''
    tLen = 0
    m.m.tit.0 = words(m.m.titBef m.m.titAft) + 5
    do tx=1 to m.m.tit.0
        m.m.tit.tx = ''
        end
    do kx=1 to m.m.0
       rxNm = m.m.kx.col
       call mPut 'F_TEMP'left('.', rxNm\=='')rxNm, m.m.kx.labelTi
       t1 = f(m.m.kx.fmt, 'F_TEMP')
       m.m.kx.len = length(t1)
       if pos(strip(t1), m.m.kx.labelTi) < 1 then
           t1 = left(left('', max(0, verify(t1, ' ') -1)) ,
                 || m.m.kx.labelTi, length(t1))
       m.m.kx.tit.1 = t1
       if kx = 1 then do
           f = m.m.kx.fmt
           end
       else do
           tLen = tLen + length(sep)
           f = f || sep || m.m.kx.fmt
           end
       m.m.kx.start = tLen+1
       do tx=1 to m.m.kx.tit.0
           if m.m.kx.tit.tx \== '' then
               if tx > 1 | pos('-', m.m.opt) < 1 then
                   m.m.tit.tx = left(m.m.tit.tx, tLen) ,
                       || strip(m.m.kx.tit.tx, 't')
               else if \ abbrev(m.m.kx.tit.tx, ' ') then
                   m.m.tit.tx = left(m.m.tit.tx, tLen, '-') ,
                       || strip(m.m.kx.tit.tx, 't')
               else
                   m.m.tit.tx = left(m.m.tit.tx, tLen, '-') ,
                          || right(strip(m.m.kx.tit.tx),
                                , length(m.m.kx.tit.tx), '-')
           end
       tLen = tLen + m.m.kx.len
       end
    m.m.len = tLen
    if pos('-', m.m.opt) > 0 then
        m.m.tit.1 = left(m.m.tit.1, tLen +3, '-')
    m.m.fmt = fCache('%.', f)

    cSta = m.m.tit.0+3          /* compute cycle titles */
    cycs = ''
    cyEq = 1
    do cEnd=cSta until kx > m.m.0
        cycs = cycs cEnd
        cx = cSta
        firstRound = 1
        do kx=1 to m.m.0
            if firstRound then
                m.m.tit.cx =  left('', m.m.kx.start-1)m.m.kx.labelCy
            else if length(m.m.tit.cx) <= m.m.kx.start - 2 then
                m.m.tit.cx = left(m.m.tit.cx, m.m.kx.start - 1) ,
                             || m.m.kx.labelCy
            else
                leave
            if cyEq then
               cyEq = translate(m.m.kx.labelCy) ,
                    = translate(m.m.kx.labelTi)
            cx = cx + 1
            if cx > cEnd then do
                cx = cSta
                firstRound = 0
                end
            end
        end
    m.m.cycles = strip(cycs)
    if cyEq & words(cycs) <=  1 then
        m.m.cycles = ''
    m.m.generated = m.m.generated't'
    return
endProcedure fTabGenerate

fTabColGen: procedure expose m.
parse arg m
    do kx=1 to m.m.0
        t = m.m.kx.labelTI
        l = if(m.m.kx.labelCy == t, , m.m.kx.labelCy)
        f = lefPad(lefPad(strip(l), 10) t, 29)
        if length(f) > 29 then
           if length(l || t) < 29 then
               f = l || left('', 29 - length(l || t))t
           else
               f = lefPad(strip(l t), 29)
        g = strip(m.m.kx.fmt)
        o = right(g, 1)
        if pos(o, 'dief') > 0 then
            f = f '@'m.m.kx.col'%12e @'m.m.kx.col'%18c'
        else if o = 'C' then
            f = f left(g, length(g)-1)'c'
        else
            f = f g
        m.m.kx.colFmt = f
        end
    m.m.generated = m.m.generated'c'
    return
endProcedure fTabColGen

fTab: procedure expose m.
parse arg m, rdr
    call fTabBegin m
    call fAll m.m.fmt, rdr
    return fTabEnd(m)
endProcedure fTab

fTabCol: procedure expose m.
parse arg m, i
    do cx=1 to m.m.0
        call out f(m.m.cx.colFmt, i)
        end
    return 0
endProcedure fTabCol

fTabBegin: procedure expose m.
parse arg m
    if pos('t', m.m.generated) < 1 then
        call fTabGenerate m, ' '
    return fTabTitles(m, m.m.titBef)
endProcedure fTabBegin

fTabEnd: procedure expose m.
parse arg m
    return fTabTitles(m, m.m.titAft)

fTabTitles: procedure expose m.
parse arg m, list
    list = repAll(list, 'c', m.m.cycles)
    do tx=1 to words(list)
        t1 = word(list, tx)
        call out m.m.tit.t1
        end
    return m
endProcedure fTabTitles

/*--- auto tables detect format from data ---------------------------*/
/*--- detect fmt from a rdr and write it formatted to stdOut --------*/
fTabAuto: procedure expose m.
parse arg m, rdr, wiTi
    if m == '' then
        m = fTabReset(f_auto, 1)
    i = in2Buf(rdr)
    if m.i.buf.0 <= 0 then
        return m
    call fTabDetect m, i'.BUF', wiTi
    return fTab(m, i)
endProcedure fTabAuto

/*--- generate format for all fields of a stem of objects -----------*/
fTabDetect: procedure expose m.
parse arg m, b
    ff = oFlds(m.b.1)
    do fx=1 to m.ff.0
        call fTabAddDetect m, m.ff.fx, b
        end
    return
endProcedure fTabDetect

/*--- generate format for all fields of a stem of objects -----------*/
sqlfTabDetect: procedure expose m.
parse arg m, b
    cx = m.m.sqlX
    ff = m.sql.cx.fetchFlds
    do fx=1 to words(ff)
        call fTabAddDetect m, word(ff, fx), b, m.sql.cx.d.fx.sqlName
        end
    return
endProcedure fTabDetect

/*--- detect format for one field in stem st ------------------------*/
fTabAddDetect: procedure expose m.
parse arg m, c1 aDone, st, cyNm, tiNm
    lMa = -1
    rMa = -1
    bMa = -1
    aDiv = 0
    nMi =  9e999
    nMa = -9e999
    eMi =  9e999
    eMa = -9e999
    eDa = 2
    dMa = -9e999
    suf = left('.', c1 \== '')c1
    do sx=1 to m.st.0
        v = mGet(m.st.sx || suf)
        lMa = max(lMa, length(strip(v, 't')))
        rMa = max(rMa, length(strip(v, 'l')))
        bMa = max(bMa, length(strip(v, 'b')))
        if \ dataType(v, 'n') then do
            if length(v) > 100 then
                aDiv = 99
            else if aDiv <=3 then
                if aDiv.v \== 1 then do
                    aDiv.v = 1
                    aDiv = aDiv + 1
                    end
            iterate
            end
        v = strip(v)
        nMi = min(nMi, v)
        nMa = max(nMa, v)
        ex = verify(v, 'eEfF', 'm')
        if ex > 0 then do
            eMa = max(eMa, substr(v, ex+1))
            eMi = min(eMi, substr(v, ex+1))
            v = left(v, ex-1)
            do while pos(left(v,1), '+-0') > 0
                v = substr(v, 2)
                end
            eDa = max(eDa, length(v) - (pos('.', v) > 0))
            end
        dx = pos('.', v)
        if dx > 0 then do
            do while right(v, 1) == 0
                v = left(v, length(v)-1)
                end
            dMa = max(dMa, length(v)-dx)
            end
        end
    if nMi > nMa | aDiv > 3 then
        newFo = '-'max(1, (lMa+0))'C'
    else if eMi <= eMa then do
        newFo = ' ' || (eDa+max(length(eMa), length(eMi))+3) ,
             || '.'||(eDa-1)'e'
        end
    else do
        be = max(length(trunc(nMi)), length(trunc(nMa)))
        if dMa <= 0 then
            newFo = max(be, bMa)'I'
        else
            newFo = max(be+1+dMa, bMa)'.'dMa'I'
        end
    call fTabAddRCT m, c1 aDone, '%'newFo, cyNm, tiNm
  /*  say c1 '????==> %'newFo */
   return newFo
endProcedure fTabAddDetect

/*--- format with units seconds ==> 3d13 oder 5.43e6 ==> 5m43 --------*/
fTime: procedure expose m.
?????????????? use f(%kd) ????????????????
fDec: procedure expose m.
?????????????? use f(%kd) ????????????????

fUnits: procedure expose m.
parse arg v, scale, aLen, aPrec, plus
    if \ dataType(v, 'n') then do
        f1 = fUnitsF1(v, scale, aLen, length(plus), length(plus), aPrec)
        return right(v, m.f1.len)
        end
    if v >= 0 then
        sign = plus
    else
        sign = '-'

    v = abs(v)  /* always get rid also of sign of -0 | */
    f1 = fUnitsF1(v, scale, aLen, length(plus), length(sign), aPrec)

    do forever
        w = format(v * m.f1.fact, , m.f1.prec)
        if pos('E-', w) > 0 then
            w = format(0, , m.f1.prec)
        if w < m.f1.lim2 then do
            if m.f1.kind == 'r' then
                x = sign || w || m.f1.unit
            else if m.f1.kind == 'm' then
                x = sign || (w % m.f1.mod) || m.f1.unit ,
                    || right(w // m.f1.mod, m.f1.len2, 0)
            else
                call err 'bad kind' m.f1.kind 'in f1' f1
            if length(x) <= m.f1.len then
                return right(x, m.f1.len)
            end
        if m.f1.next == '' then
            return left(sign, m.f1.len, '+')
        f1 = m.f1.next
        end
endProcedure fUnits

fUnitsF1: procedure expose m.
parse arg v, scale, len, pLen, sLen, aPrec
    slp = 'F_Unit.'scale'.'len'.'pLen'.'sLen'.'aPrec
    if symbol('m.slp.0') \== 'VAR' then do
        sc = 'F_Unit.'scale
        if symbol('m.sc.0') \== 'VAR' then do
            call fUnitsF1Ini1
            if symbol('m.sc.0') \== 'VAR' then
                call err 'bad scale' sc
            end

        if scale = 'd' | scale = 'b' then do
            if aPrec == '' then
                aPrec = 0
            if len = '' then
                len = aPrec + (aPrec >= 0) + 4 + pLen
            dLen = len - sLen
            l2 = '1e' || (dLen - aPrec - (aPrec > 0))
            call fUnitsF1I0 slp, 'nn', 'nn', , , , len
            do x=m.sc.min to m.sc.0
                si = fUnitsF1I0(slp, x, m.sc.x.kind, m.sc.x.unit,
                          , m.sc.x.fact, l2, len)
                m.si.lim1 = m.si.lim2 / m.si.fact
                m.si.prec = aPrec
                m.si.next = slp'.' || (x+1)
                end
            if aPrec > 0 then do
                y = x-1
                si = fUnitsF1I0(slp, x, m.sc.y.kind, m.sc.y.unit,
                          , m.sc.y.fact, ('1e' || dLen), len)
                m.si.lim1 = m.si.lim2 / m.si.fact
                m.si.prec = 0
                end
            end
        else if scale = 't' then do
            if len = '' then
                len = 5 + pLen
            dLen = len - sLen
            call fUnitsF1I0 slp, 'nn', 'nn', , , , len
            do x=m.sc.min to m.sc.0
                si = fUnitsF1I0(slp, x, m.sc.x.kind, m.sc.x.unit,
                          , m.sc.x.fact, m.sc.x.lim2, len ,
                          , m.sc.x.mod, m.sc.x.len2)
                if x = m.sc.0 - 1 then
                    m.si.lim2 = '24e' || (dLen-3)
                else if x = m.sc.0 then
                    m.si.lim2 = '1e' || (dLen-1)
                m.si.lim1 = m.si.lim2 / m.si.fact
                m.si.prec = 0
                m.si.next = slp'.' || (x+1)
                end
            end
        else
            call err implement
        x = m.slp.0
        m.slp.x.next = ''
        end
    if \ datatype(v, 'n') then
        return slp'.nn'
    do q=11 to m.slp.0-1 while v >= m.slp.q.lim1
        end
    if q = 11 & v <> trunc(v) then do
        do q=10 by -1 to m.slp.min+1 while v < m.slp.q.lim1
            end
        q = q + 1
        end
    return slp'.'q
endProcedure fUnitsF1

fUnitsF1Ini1: procedure expose m.
      /*  0    5   10    5   20 */
    iso = '    afpnum kMGTPE   '
    sc = 'F_Unit.d'
    call fUnitsF1i0 sc, 11, 'r', ' ',   1
    f = 1
    do x=1 to 6
        f = f * 1000
        call fUnitsF1i0 sc, 11+x, 'r', substr(iso, 11+x, 1), 1/f
        call fUnitsF1i0 sc, 11-x, 'r', substr(iso, 11-x, 1), f
        end
    sc = 'F_Unit.b'
    f = 1
    do x=11 to 17
        call fUnitsF1i0 sc, x, 'r', substr(iso, x, 1), 1/f
        f = f * 1024
        end
    sc = 'F_Unit.t'
    call fUnitsF1i0 sc, 11, 'm', 's', 100,   6000, , 100, 2
    call fUnitsF1i0 sc, 12, 'm', 'm',   1,   3600, ,  60, 2
    call fUnitsF1i0 sc, 13, 'm', 'h', 1/60,  1440, ,  60, 2
    call fUnitsF1i0 sc, 14, 'm', 'd', 1/3600,    , ,  24, 2
    call fUnitsF1i0 sc, 15, 'r', 'd', 1/3600/24
    return
endProcedure fUnitsF1Ini0

fUnitsF1I0: procedure expose m.
parse arg sc, ix
    si = sc'.'ix
parse arg , , m.si.kind, m.si.unit, m.si.fact,
                , m.si.lim2, m.si.len,
                , m.si.mod, m.si.len2
    if \ datatype(ix, 'n') then
        return si
    if symbol('m.sc.0') \== 'VAR' then do
        m.sc.0   = ix
        m.sc.min = ix
        end
    else do
        m.sc.0   = max(ix, m.sc.0)
        m.sc.min = min(ix, m.sc.min)
        end
    return si
endProcedure fUnitsF1I0
/* copy fTab end   ****************************************************/
/* copy f begin *******************************************************/
/*--- format with the given format ggA1, ggA2, etc. -----------------*/
f: procedure expose m.
parse arg ggFmt, ggA1, ggA2
    if symbol('M.f_gen.ggFmt') \== 'VAR' then
        call fCache ggFmt, ggFmt
    interpret m.f_gen.ggFmt
endProcedure f

fImm: procedure expose m.
parse arg ggFmt, ggA1
    interpret m.ggFmt
endProcedure fImm

fCacheNew: procedure expose m.
    if symbol('m.f_gen0') == 'VAR' then
        m.f_gen0 = m.f_gen0 + 1
    else
        m.f_gen0 = 1
    return '%.'m.f_gen0
endProcedure fCacheNew
/*--- compile format fmt put in the cache with address a ------------*/
fCache: procedure expose m.
parse arg a, fmt
    if a == '%.' then
        a = fCacheNew()
    else if symbol('M.f_gen.a') == 'VAR' then
        return a
    cy = -2
    nm = ' '
    gen = ' '
    opt = 0
    do forever        /* split preprocesser clauses */
        cx = cy+3
        cy = pos('%#', fmt, cx)
        if cy < 1 then
            act = substr(fmt, cx)
        else
            act = substr(fmt, cx, cy-cx)
        do ax=1
            ay = pos('%&', act)
            if ay < 1 then
                leave
            ct = substr(act, ay+2, 1)
            if symbol('f.ct') \== 'VAR' then
                call err 'undefined %&'ct 'in format' fmt
            act = left(act, ay-1) || f.ct || substr(act, ay+3)
            if ax > 100 then
                say 'fGen' fmt nm '==>' act 'actPos' substr(fmt, cx)
            end
        if cy <> 1 & (\ opt | symbol('f.nm') \== 'VAR') then
            f.nm = act
        if cy < 1 | length(fmt) <= cy+1 then
            leave
        nm = substr(fmt, cy+2, 1)
        opt =  nm == '?'
        if pos(nm, '?;#') > 0 then do
            if nm == '#' then do
               if length(fmt) <> cy+3 then
                   call err 'fCache bad %##'nm 'in' fmt
               else if a == fmt then
                   a = left(a, cy-1)
               leave
               end
            cy = cy+1
            nm = substr(fmt, cy+2, 1)
            if nm == ';' then do
               gen = nm
               iterate
               end
            end
        if pos(nm, m.ut_alfa' ') < 1 then
            call err 'fCache bad name %#'nm 'in' fmt
        if pos(nm, gen) < 1 then
            gen = gen || nm
        end
    if symbol('m.f_s_0') \== 'VAR' | m.f_s_0 == 0 then
        m.f_s_0 = 1
    else do
        m.f_s_0 = m.f_s_0 + 1
        f_s = 'F_S_'m.f_s_0
        end
    do cx=1 to length(gen)
        nm = substr(gen, cx, 1)
        act = f.nm
        a2 = a
        if nm == ' ' then
            a2 = a
        else
            a2 = a'%##'nm
        call scanSrc f_s, act
        m.f_gen.a2 = fGen(f_s)
        if \ scanEnd(f_s) then
            call scanErr f_s, "bad specifier '"m.f_s.tok"'"
        end
    m.f_s_0 = m.f_s_0 - 1
    return a
endProcedure fCache

/*--------------------------------------------------------------------
 Format generator    should be compatible with fPrint|
 <<<< + extension of fPrint, - in fPrint but not implemented

   %%  %@ the escaped char
   ('@' argN? '.'? field)?'%' flags? width? ('.' precision)? specifier

 specifier: is the most significant one and defines the type

 - c  Character rigPad or lefPad, prec ==> substr(..., prec)
 -  C Cut %-nC = left(v,n), %nC = right(v,n) %n.mC = substr(m, n)
 - hH Characters in hex
 - iI Signed decimal integer (padded or cut)
 - eE Scientific notation (mantissa/exponent) using e character 3.92e+2
 - S  Strip (both)
 - txy time date formatting from format x to format y see fTstGen
 - kx  units Formatting x = t=time d=decimal b=binary (k) B=binary (kB)
 Flags:
 - -  Left-justify within the given field width; Right is default
 - +  print '+' before non negative numbers
 -' ' print ' ' before non negative numbers
 - /  cut to length

 preprocessor implemented in fCache
%#v   before contents of variable v (1 alfa or 1 space),
      stored at address%##v
%#?v  define variable v if not yet defined
%#;   restart of variables to generate
%&v   use of previously defined variable v
----------------------------------------------------------------------*/
fGen: procedure expose m.
parse arg f_s
    ax = 0
    cd = ''
    cp = ''
    do forever
        txt = fText(f_s)
        if txt \== '' then
            cd = cd '||' quote(txt, "'")
        if scanEnd(f_s) then
            leave
        if \ scanLit(f_s, '@') then do
            ax = ax + 1
            af = ''
            hasDot = 0
            end
        else do
            if scanWhile(f_s, '0123456789') then
                ax = m.f_s.tok
            else if ax < 1 then
                ax = 1
            hasDot = scanLit(f_s, '.')
            af = fText(f_s)
            end
        if \ scanLit(f_s, '%') then
            call scanErr f_s, 'missing %'
        call scanWhile f_s, '-+ /'
        flags = m.f_s.tok
        if scanWhile(f_s, '0123456789') then
            len = m.f_s.tok
        else
            len = ''
        if \ scanLit(f_s, '.') then
            prec  = ''
        else do
            call scanWhile f_s, '0123456789'
            prec = m.f_s.tok
            end
        call scanChar f_s, 1
        sp = m.f_s.tok
        if ax < 3 | ass.ax == 1 then
            aa = 'ggA'ax
        else do
            aa = 'arg(' || (ax+1) || ')'
            if af \== '' then do
                 cp = cp 'ggA'ax '=' aa';'
                 aa = 'ggA'ax
                 ass.ax = 1
                 end
            end
        if af \== '' | hasDot then
            aa = rxMGet(aa, af)
        if sp == 'c' then do
            if prec \== '' then
                aa = 'substr('aa',' prec')'
            if len == '' then
                cd = cd '||' aa
            else if pos('-', flags) > 0 then
                cd = cd '|| lefPad('aa',' len')'
            else
                cd = cd '|| rigPad('aa',' len')'
            end
        else if sp == 'C' then do
            if prec \== '' then do
                cd = cd '|| substr('aa',' prec
                if len == '' then
                    cd = cd')'
                else
                    cd = cd',' len')'
                end
            else if len == '' then
                cd = cd '||' aa
            else if pos('-', flags) > 0 then
                cd = cd '|| left('aa',' len')'
            else
                cd = cd '|| right('aa',' len')'
            end
        else if sp == 'H' then
            cd = cd "|| fH("aa"," len',' (pos('-', flags) > 0)')'
        else if sp == 'h' then
            cd = cd "|| translate(fH("aa", '"siL"'), 'abcdef','ABCDEF')"
        else if sp == 'i' then
            cd = cd "|| fI("aa"," len", '"flags"'," word(prec 0, 1)")"
        else if sp == 'I' then
            cd = cd "|| fI("aa"," len", '/"flags"'," word(prec 0, 1)")"
        else if sp == 'E' | sp == 'e' then do
            if len == '' then
                len = 8
            if prec = '' then
                prec = len - 6
            cd = cd "|| fE("aa"," len"," prec", '"sp"', '"flags"')"
            end
        else if sp = 'S' then
            cd = cd '|| strip('aa')'
        else if sp = 't' then do
            call scanChar f_s, 2
            cd = cd '||' fTstGen(m.f_s.tok, aa)
            end
        else if sp = 'k' then do
            call scanChar f_s, 1
            if pos(m.f_s.tok, 'tdbBiI') < 1 then
                call scanErr f_s, "bad unit type" m.f_s.tok
            if pos('+', flags) > 0 then
                pl = ", '+'"
            else if pos(' ', flags) > 0 then
                pl = ", ' '"
            else
                pl = ''
            cd = cd "|| fUnits("aa", '"m.f_s.tok"'," len"," prec pl")"
            end
  /*    else if sp = '(' then do
            if af == '' | flags \== '' | len \== 0 | prec \== '' then
                call scanErr f_s, "bad call shoud be @sub%("
            interpret "cRes = fGen"af"(f_s, ax)"
            cd = cd '||' cRes
            if \ scanLit(f_s, '%)') then
                if \ scanEnd(f_s) then
                    call scanErr f_s, '%) to end call' af 'expected'
            end     */
        else do
            call scanBack f_s, '%'sp
            leave
            end
        end
    if cd \== '' then
        return strip(cp 'return' substr(cd, 5))
    else
        return "return ''"
endProcedure fGen

fText: procedure expose m.
parse arg f_s
    res = ''
    do forever
        if scanUntil(f_s, '@%') then
            res = res || m.f_s.tok
        if \ scanLit(f_s, '%%', '%@') then
            return res
        res = res || substr(m.f_s.tok, 2)
        end
endProcedure fText

fAll: procedure expose m.
parse arg fmt, rdr
    i = jOpen(in2File(rdr), '<')
    do while jRead(i)
        call out f(fmt, m.i)
        end
    call jClose i
    return
endProcedure fAll

/*--- format character2hex (if not sql null) -------------------------*/
fH: procedure expose m.
parse arg v, l, leftJ
    if v \== m.sqlNull then
        v = c2x(v)
    if length(v) > l then
        return v
    else if leftJ \== 1 then
        return right(v, l)
    else
        return left(v, l)
endProcedure fH

/*--- format integer or fixPoint Decimal -----------------------------*/
fI: procedure expose m.
parse arg v, l, flags, d
    if \ datatype(v, 'n') then
        return fRigLeft(strip(v), l, flags)
    v = format(v, , d, 0)
    if pos('+', flags) > 0 then
        if \ abbrev(v, '-') then
            v = '+'v
    if length(v) > l then
        if pos('/', flags) > 0 then
            return left('', l, '*')
        else
            return v
    return fRigLefPad(v, l, flags)
endProcedure fI

/*--- format with exponent l=total output len
                           d=number of digits after . in mantissa
                           c=exponent character
                           flags: - to ouput text left justified
    differences: exponent is always printed but without +
                 overflow ==> expand exponent, *****, 0e-999 ---------*/
fE: procedure expose m.
parse arg v, l, d, c, flags
    if \ datatype(v, 'n') then
        return fRigLeft(v, l, flags)
    if pos(' ', flags) < 1 then
        if v >=  0 then
            if pos('+', flags) > 0 then
                return '+'substr(fE(v, l, d, c, ' 'flags), 2)
            else
                return substr(fE(v, l+1, d+1, c, ' 'flags), 2)
    x = format(v, 2, d, 7, 0)
    m = 2 + d + (d>0)
    call assert "length(x) == m+9", 'm x length(x)'
    if substr(x, m+1) = '' then
        return left(x, m)c || left('', l-m-1, 0)
    call assert "substr(x, m+1, 1) == 'E'","exponenent in x not at m"
    y = verify(x, '0', 'n',  m+3)
    call assert 'y>0'
    if substr(x, m+1, 2) == 'E+' then do
        if m+10-y <= l-m-1 then
             return left(x,m)c || right(x, l-m-1)
        z = l - 4 - (m+10-y)
        end
    else if substr(x, m+1, 2) == 'E-' then do
        if m+10-y <= l-m-2 then
             return left(x,m)c'-'right(x, l-m-2)
        z = l - 5 - (m+10-y)
        end
    else
        call err 'bad x' x
    if z >= -1 & max(0, z) < d then
        return fE(v, l, max(0, z), c, flags)
    else if substr(x, m+1, 2) == 'E-' then
        return left(x,1)'0'c'-'left('', l-4, 9)
    else
        return left('', l, '*')
endProcedure fE

/*--- right or left with truncation ----------------------------------*/
fRigLeft: procedure expose m
parse arg s, len, flags
    if length(s) = len then
        return s
    else if pos('-', flags) > 0 | length(s) > len then
        return left(s, len)
    else
        return right(s, len)
endProcedure fRigLefPad

/*--- right or left pad without truncation --------------------------*/
fRigLefPad: procedure expose m
parse arg s, len, flags
    if pos('-', flags) > 0 then
        if length(strip(s, 't')) >= len then
            return strip(s, 't')
        else
            return left(s, len)
    else
        if length(strip(s, 'l')) >= len then
            return strip(s, 'l')
        else
            return right(s, len)
endProcedure fRigLefPad

/*--- generate timestamp formats: from format c to format d ----------*/
fTstGen: procedure expose m.
parse arg c 2 d, s
             /* special L = LRSN in Hex
                        l = lrsn (6 or 10 Byte) */

    if c == 'L' then
        return fTstGen('S'd, 'timeLRSN2LZT('s')')
    if c == 'l' then
        return fTstGen('S'd, 'timeLRSN2LZT(c2x('s'))')
    cd = c || d
    if symbol('m.f_tstFo.c') \== 'VAR' ,
         | symbol('m.f_tstFo.d') \== 'VAR' then do
         if m.f_tstIni == 1 then
             call err "bad timestamp from or to format '"cd"'"
        m.f_tstIni = 1
        a = 'F_TSTFO.'
                      /* Y: year  A = 2010 ...
                         M: month B=Januar ...,
                         H: hour  A=0 B=10 C=20 D=30 */
        m.f_tst_N0 =    'yz345678 hi:mn:st'
        m.f_tst_N  =    'yz345678 hi:mn:st.abcdef'
        m.f_tst_S0 =    'yz34-56-78-hi.mn.st'
        m.f_tst_S  =    'yz34-56-78-hi.mn.st.abcdef'
        call mPut a'S',  m.f_tst_S
        call mPut a's',  m.f_tst_S0
        call mPut a' ',  m.f_tst_S0
        call mPut a'D', 'yz345678'
        call mPut a'd',   '345678'
        call mPut a't',            'hi.mn.st'
        call mPut a'T',            'hi:mn:st.abcdef'
        call mPut a'E', '78.56.yz34'
        call mPut a'e', '78.56.34'
        call mPut a'Y',      'YM78'
        call mPut a'M',    'M78himns'
        call mPut a'A',    'A8himnst'
        call mPut a'H',           'Himnst'
        call mPut a'n',  m.f_tst_N0
        call mPut a'N',  m.f_tst_N
        call mPut a'j', 'jjjjj' /* julian date 34jjj        */
        call mPut a'J', 'JJJJJJ' /* day since 1.1.00: 6 digits     */
        call mPut a'l', copies('l', 10) /* LRSN out 10 Byte, input var*/
        call mPut a'L', copies('L', 20) /* LRSN in hex */
        call mPut a'u', 'uuuuuuuu' /* Unique */
        return fTstGen(cd, s)
        end

    if c == ' ' then do
        if pos(d, 'SN') > 0 then
            return fTstgFi(m.f_tst_N, m.f_tstFo.d,
                 , "date('S') time('L')")
        else if pos(d, 'sMAn ') > 0 then
            return fTstgFi(m.f_tst_N0, m.f_tstFo.d,
                 , "date('S') time()")
        else if pos(d, 'DdEeY') > 0 then
            return fTstgFi(mGet('F_TSTFO.D'), m.f_tstFo.d, "date('S')")
        else if pos(d, 'tH') > 0 then
            return fTstgFi(mGet('F_TSTFO.t'), , m.f_tstFo.d, "time()")
        else if pos(d, 'T') > 0 then
            return fTstgFi(mGet('F_TSTFO.T'), m.f_tstFo.d, "time('L')")
        else
            call err 'fTstGen implement d='d
        end
    return fTstgFi(m.f_tstFo.c, m.f_tstFo.d, s)
endProcedure fTstGen

fTstgFi: procedure expose m.
parse arg f, d, s
    code = fTstgFF(f, d, s)
    if pos('$', code) == lastPos('$', code) ,
              | verify(s, '(). ', 'm') < 1 then
        return repAll(code, '$', s)
    a = fCacheNew()
    m.f_gen.a = 'return' repAll(s, '$', 'ggA1')
    return "fImm('F_GEN."a"'," s")"
endProcedure fTstFi

fTstgFF: procedure expose m.
parse arg f, t, s
    if verify(f, 'lLjJu', 'm') > 0 then do
        if f == 'l' then do
            if t == 'l' then
                return 'timeLrsn10('s')'
            else if t == 'L' then
                return 'c2x(timeLrsn10('s'))'
            else if verify(t, 'lL', 'm') = 0 then
                return fTstFi(m.fTst_fo.S, t, 'timeLrsn2LZT('s')')
            end
        call err 'fTstgFF implement' f 'to' t
        end

    if symbol('m.F_TSTSCAN') == VAR then
        m.f_tstScan = m.f_tstScan + 1
    else
        m.f_tstScan = 1
    a = f_tstScan || m.f_tstScan
    call scanSrc a, t
    cd = ''
    toNull = 'imnstabcdef78'
    if verify(f, 'hH', 'm') = 0 then
        toNull = toNull'hH'
    if verify(f, 'M56', 'm') = 0 then
        toNull = toNull'M56'
    if verify(f, 'yz34Y', 'm') = 0 then
        toNull = toNull'yz34Y'
    do while \ scanEnd(a)
        c1 = ''
        do forever
            if scanVerify(a, f' .:-', 'n') then do
                c1 = c1 || m.a.tok
                end
            else if pos(scanLook(a, 1), toNull) > 0 then do
                call scanChar a, 1
                c1 = c1 || translate(m.a.tok, '00000000000010A?010001?',
                                            , 'imnstabcdef78hHM56yz34Y')
                end
            else do
                if c1 == '' then
                    nop
                else if c1 == f then
                    c1 = s
                else if pos(c1, f) > 0 then
                    c1 = "substr("s"," pos(c1, f)"," length(c1)")"
                else
                    c1 = "translate('"c1"'," s", '"f"')"
                leave
                end
            end
        if c1 \== '' then do
            end
        else if scanVerify(a, 'yz34Y', 'n') then do
            t1 = m.a.tok
            if pos('yz34', f) > 0 then
                c1 = "substr("s "," pos('yz34', f)", 4)"
            else if pos('34', f) > 0 then
                c1 = "timeYear24(substr("s "," pos('34', f)", 2))"
            else if pos('Y', f) > 0 then
                c1 = "timeY2Year(substr("s "," pos('Y', f)", 1))"
            if t1 = '34' then
                c1 = "substr("c1", 3)"
            else if t1 = 'Y' then
                c1 = "timeYear2Y("c1")"
            end
        else if scanVerify(a, '56M', 'n') then do
            if m.a.tok == '56' & pos('M', f) > 0 then
                c1 = "timeM2Month(substr("s"," pos('M', f)", 1))"
            else if m.a.tok == 'M' & pos('56', f) > 0 then
                c1 = "timeMonth2M(substr("s"," pos('56', f)", 2))"
            end
        else if scanVerify(a, 'hiH', 'n') then do
            if m.a.tok == 'hi' & pos('Hi', f) > 0 then
                c1 = "timeH2Hour(substr("s"," pos('Hi', f)", 2))"
            else if m.a.tok == 'Hi' & pos('hi', f) > 0 then
                c1 = "timeHour2H(substr("s"," pos('hi', f)", 2))"
            end
        else if scanLit(a, 'jjjjj') then do
            c1 = "time2jul(" fTstGFF(f, 'yz34-56-78', s)")"
            end
        else if scanLit(a, 'JJJJJJ') then do
            c1 = "date('B'," fTstGFF(f, 'yz345678', s)", 's')"
            end
        else if scanLit(a, copies('l', 10), copies('L', 20),
                                          , 'uuuuuuuu') then do
            c1 = "timeLZT2LRSN("fTstGFF(f, m.f_tst_S, s)")"
            if abbrev(m.a.tok, 'l') then
                c1 = "x2c("c1")"
            else if abbrev(m.a.tok, 'u') then
                c1 = "timeLrsn2Uniq("c1")"
            end
        else do
            call scanChar a, 1
            c1 = "'implement "m.a.tok"'"
         /* call err 'implement' */
            end
        if c1 == '' then
            call scanErr a, 'fTstGFF no conversion from' f
        cd = cd "||" c1
        end
    m.f_tstScan = m.f_tstScan - 1
    if cd == '' then
        return "''"
    else
        return substr(cd, 5)
endProcedure fTstGFF

fWords: procedure expose m.
parse arg fmt, wrds
    f2 = fmt'%#?a%c%#? %#?e%# %& %&a'
    if wrds = '' then
        return f(f2'%##e')
    res = f(f2'%##a', word(wrds, 1))
    do wx=2 to words(wrds)
        res = res || f(f2, word(wrds, wx))
        end
    return res
endProcedure fWords

fCat: procedure expose m.
parse arg fmt, st
    return fCatFT(fmt, st, 1, m.st.0)

fCatFT: procedure expose m.
parse arg fmt, st, fx, tx
    f2 = fmt'%#?a%c%#? %#?e%# %& %&a'
    if tx < fx then
        return f(f2'%##e')
    res = f(f2'%##a', m.st.fx)
    do sx=fx+1 to tx
        res = res || f(f2, m.st.sx)
        end
    return res
endProcedure fCatFT

/* copy f end   *******************************************************/
/* copy err begin *** errorhandling, messages, help    ****************/
errIni: procedure expose m.
    if m.err.ini == 1 then
        return
    m.err.ini     = 1
    m.err.handler = ''
    m.err.cleanup = ';'
    m.err.opt     = ''
    parse source m.err.os .
    m.err.ispf    = 0
    m.err.screen  = 0
    if m.err.os \== 'LINUX' then do
        address tso 'profile MsgId'   /* brauchen wir in tsoAlloc| */
        if sysVar('sysISPF') = 'ACTIVE' then do
            m.err.ispf = 1
            address ispExec 'vget zScreen shared'
            m.err.screen = zScreen
            end
        end
    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 & m.err.ispf then
        address ispExec '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 stackHistory
    if ggOpt == '' & m.err.handler \== '' then
        interpret m.err.handler
    call errSay ggTxt
    call errCleanup
    if ggOpt == '' then
        ggOpt = m.err.opt
    upper ggOpt
    if pos('T', ggOpt) > 0  then do
        trace ?r
        say 'trace ?r in err'
        end
    if pos('H', ggOpt) > 0 & m.err_cat == 'f' then
        x = show + stackHistory + by + bad + arithmetic + conversion
    call errSay ' }errorhandler exiting with exit(12)'
    exit errSetRc(12)
endSubroutine err

/*--- set rc for ispf: -------------------------------------------------
    if a cmd is run by ispStart, its RC is ignored,
         but ISPF passes the value of the shared variable zIspfRc
         back as return code
----------------------------------------------------------------------*/
errSetRc: procedure expose m.
parse arg zIspfRc
    if m.err.ispf then
        address ispExec vput 'zIspfRc' shared
    return zIspfRc
endProcedure errSetRc

/*--- error routine: user message cleanup exit -----------------------*/
errAddCleanup: procedure expose m.
parse arg code
    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
    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
    errCleanup = m.err.cleanup
    if errCleanup <> ';' then do
        m.err.cleanup = ';'
        say 'err cleanup begin' errCleanup
        interpret errCleanup
        say 'err cleanup end' errCleanup
        end
    if symbol('m.tso_ddAll') == 'VAR' then
        call tsoFree m.tso_ddAll, 1
    return
endProcedure errCleanup

/*--- say an errorMessage msg with pref and linesplits --------------*/
errSay: procedure expose m.
parse arg msg
    return sayNl(errMsg(msg))

/*--- prefix an errormessage with pref,
          split it into lines at \n to stem m.err -------------------*/
errMsg: procedure expose m.
parse arg msg
    m.err_cat = 'f'
    do while substr(msg, 2, 1) == '}'
        parse var msg m.err_cat '}' msg
        end
   if m.err_cat == ' ' | m.err_cat == 'o' then
        return msg
   pTxt = ',fatal error,error,input error,scanErr,warning,onlyMsg,'
   px = pos(','m.err_cat, pTxt)
   if px < 1 then do
       px = 1
       m.err_cat = 'f'
       end
   pre = substr(pTxt, px+1, pos(',', pTxt, px+2)-px-1)
   if m.err_cat == 's' then
       return pre msg
   parse source . . s3 .              /* current rexx */
   return pre 'in' s3':' msg
endProcedure errMsg

splitNL: procedure expose m.
parse arg st, msg
    bx = 1
    do lx=1 to 20
        ex = pos('\n', msg, bx)
        if ex < bx then
            leave
        m.st.lx = substr(msg, bx, ex-bx)
        bx = ex+2
        end
    m.st.lx = substr(msg, bx)
    m.st.0 = lx
    return st
endProcedure splitNL

outNL: procedure expose m.
parse arg msg
    bx = 1
    do lx=1 to 20
        ex = pos('\n', msg, bx)
        if ex < bx then
            leave
        call out substr(msg, bx, ex-bx)
        bx = ex+2
        end
    call out substr(msg, bx)
    return
endProcedure outNL

/*--- say (part of) the lines of a stem ----------------------------*/
sayNl: procedure expose m.
parse arg msg
    bx = 1
    do lx=1 to 20
        ex = pos('\n', msg, bx)
        if ex < bx then
            leave
        say strip(substr(msg, bx, ex-bx), 't')
        bx = ex+2
        end
    say strip(substr(msg, bx), 't')
    return 0
endProcedure sayNl

/*--- say a trace message if m.trace is set --------------------------*/
debug: procedure expose m.
parse arg msg
    if m.debug == 1 then
        say 'debug:' msg
    return
endProcedure debug

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

/*--- assert that the passed rexx expression evaluates to true -------*/
assert:
    interpret 'if ('arg(1)') == 1 then return 1'
    interpret 'call err' quote('assert failed' arg(1) ':' arg(2) '==>'),
                        arg(2)
endProcedure assert

/*--- abend with Message after displaying help -----------------------*/
errHelp: procedure expose m.
parse arg msg
    call errSay 'i}'msg
    call help 0
    call err 'i}'msg
endProcedure errHelp

/*--- display the first comment block of the source as help ----------*/
help: procedure expose m.
parse arg doClear
    if doClear \== 0 then
        address tso 'clear'
    parse source . . s3 .
    say right(' help for rexx' s3, 72, '*')
    do lx=1 while pos('/*', sourceLine(lx)) < 1
        if lx > 10 then
            return err('initial commentblock not found for help')
        end
    doInc = 1
    ho = m.err.helpOpt
    do lx=lx+1 to sourceline() while pos('*/', sourceline(lx)) = 0
        li = strip(sourceLine(lx), 't')
        cx = lastPos('{', li)
        if cx > 0 then do
            if length(ho) = 1 then
                doInc = cx = length(li) | pos(ho, li, cx+1) > 0
            li = left(li, cx-1)
            end
        if doInc then
            say li
        end
    say right(' end help for rexx' s3, 72, '*')
    return 4
endProcedure help

/* copy err end   *****************************************************/
/* copy ut begin  *****************************************************/
utIni: procedure expose m.
    if m.ut_ini == 1 then
        return
    m.ut_ini = 1
    m.ut_digits = '0123456789'
                /* 012345678901234567890123456789 */
    m.ut_alfLC  = 'abcdefghijklmnopqrstuvwxyz'
    m.ut_alfUC  = translate(m.ut_alfLc)
    m.ut_Alfa   = m.ut_alfLc || m.ut_alfUC
    m.ut_alfNum = m.ut_alfa || m.ut_digits
    m.ut_alfDot = m.ut_alfNum || '.'
    m.ut_alfId  = m.ut_alfNum'_' /* avoid rexx allowed @ # $ ¬ . | ? */
    m.ut_alfIdN1 = m.ut_digits    /* not as first character */
    m.ut_rxId   = m.ut_AlfNum'@#$?' /* charset puff mit ¬*/
    m.ut_rxDot  = '.'m.ut_rxId
    m.ut_rxN1   = '.0123456789'
    m.ut_alfPrint = m.ut_alfNum'+-*/=()¢!{}<> .:,;?|''"%&#@$£\_'
    m.ut_base64 = m.ut_alfUC || m.ut_alfLc || m.ut_digits'+-'
    return
endProcedure utIni
/*--- 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 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 ------------------------------*/
utTime: procedure expose m.
    return time() 'ela='time('E') 'cpu='sysvar('syscpu'),
            'su='sysvar('syssrv')

/*--- 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

/*--- left without truncation ----------------------------------------*/
lefPad: procedure expose m
parse arg s, len
    if length(strip(s, 't')) >= len then
        return strip(s, 't')
    return left(s, len)
endProcedure lefPad

/*--- right without truncation ---------------------------------------*/
rigPad: procedure expose m
parse arg s, len
    if length(strip(s, 'l')) >= len then
        return strip(s, 'l')
    return right(s, len)
endProcedure rigPad

/*--- 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

/*--- translate to lowercase -----------------------------------------*/
ut2lc: procedure expose m.
parse arg s
    return translate(s, m.ut_alfLc, m.ut_alfUc)

/*--- verify an id ---------------------------------------------------*/
verifId: procedure expose m.
    parse arg src, extra
    if pos(left(src, 1), m.ut_alfIdN1) > 0 then
        return 1
    else
        return verify(src, m.ut_alfId || extra, 'n')

/*--- 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
    do ax = 2 by 2 to arg()
        src = repAl2(src, src, arg(ax), arg(ax+1))
        end
    return src
endProcedure repAll

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

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

utc2d: procedure expose m.
parse arg ch
    cx = length(ch) // 3
    if cx = 0 then
        cx = 3
    res = c2d(left(ch, cx))
    do cx=cx+1 by 3 to length(ch)
        res = res * 16777216 + c2d(substr(ch, cx, 3))
        end
    return res

utInter: procedure expose m.
    interpret arg(1)
    return
endProcedure utInter
/* copy ut end ********************************************************/       6