zOs/REXX/TXO

/* rexx ****************************************************************
     tx: testDriver
     as editMacro: tx fun
     from tso:     tx pdsMbr fun
     fun =  empty  execute unprocessed statements
            r      clear process flags and execute from beginning
            c      clear process flags
   version v2 with wsh from 8.6.11
***********************************************************************/
call errReset 'hI'
call wshIni
parse arg oArgs
    args = oArgs
    if 0 then
       oArgs = 'L DSN.MAREC.DBZF.D090702.T175332.JOB101(STAALL)' ,
           '001 YMRCO001  rebu wa'
    m.dbConn = ''
    m.tx.iniRun = 0
    m.tx.isMacro = oArgs == '' & sysVar('sysISPF') = 'ACTIVE'
    if m.tx.isMacro then
        m.tx.isMacro = adrEdit('macro (oArgs)', '*') == 0
    if m.tx.isMacro then do
        call adrEdit '(pds) = dataset'
        call adrEdit '(mbr) = member'
        parse var oArgs o1 o2
        if length(o1) > 8 then do
            m.tx.isMacro = 0

            end
        else if length(o1) > 2 then do
            args = pds'('o1')' o2
            m.tx.isMacro = 0
            end
        else do
            if mbr == '' then
                call err 'edit a pds member not' pds
            args = pds'('mbr')' oArgs
            do sx=1
                call adrEdit '(cha) = data_changed'
                if sx > 3 then
                    call err 'cannot save member'
                if cha = 'NO' then
                    leave
                say '...saving member' pds'('mbr')'
                call adrEdit 'save', '*'
                end
            end
        end
    if args = '' | pos('?', args) > 0 then
        exit help()
    parse var args dsn fun opts
    dsn = dsn2jcl(dsn)
    call envPut 'dsn', dsn
    call envPut 'pds', dsnSetMbr(dsn)
    mbr = dsnGetMbr(dsn)
    if mbr = '' | length(mbr) > 7 then
        call errHelp 'first arg word not a pds with member <=7:' args
    call envPut 'mbr', mbr
    call envPut 'mpr', if(length(mbr) <= 5, mbr, left(mbr, 5))
    call envPut 'ini', dsnSetMbr(dsn, 'INI')
    call envPut 'gen', ''
    if abbrev(fun, '-') then do
        opts = substr(fun, 2) opts
        fun = ''
        end
    call readDsn dsn, 'M.TX.INP.'
    m.tx.save = 0
    lx = m.tx.inp.0
    if fun = '' then do
        call txCont opts
        end
    else if fun = 'c' then do
        call txReset tx'.'inp, opts
        end
    else if fun = 'r' then do
        call txReset tx'.'inp, opts
        call txSave
        call readDsn dsn, 'M.TX.INP.'
        call txCont opts
        end
    else
        call errHelp 'bad fun' fun 'in args' oArgs
    call txSave
    call dbConn
    exit
dbConn: procedure expose m.
parse arg sub
    if m.dbConn = sub then
        return
    if m.dbConn \== '' then
        call sqlDisconnect
    if sub \== '' then
        call sqlConnect sub
    m.dbConn = sub
    say 'connected to' sub
    return
endProcedure dbConn

sqlProc: procedure expose m.
parse arg inp, pJ72
    say sqlProc 'j72' pJ72
    call sqlStmtsOpt inp, if(pJ72==1, 's') 100
    return
endProcedure sqlProc

txCmpRun: procedure expose m.
parse arg ki, inpDsn, outDsn
say 'txCmpRun' inpDsn '->' outDsn
    call compRun ki, file(inpDsn), file(outDsn)
say 'txCmpRun -> ended'
    return
endProcedure txCmpRun
/*--- remove all history information from testcase,
        so it will restart from scratch next time --------------------*/
txReset: procedure expose m.
parse arg i
    z = 0
    do y=1 to m.i.0
        if pos(firstNE(m.i.y), '-+') > 0 then
            iterate
        z = z + 1
        m.i.z = m.i.y
        end
    m.tx.save = z \= m.i.0
    m.i.0 = z
    return
endProcedure txReset

/*--- save testcase member if necessary ------------------------------*/
txSave: procedure expose m.
     if m.tx.save = 0 then
         return
     if m.tx.save = 1 then do
         if \ m.tx.isMacro then do
             call writeDsn envGet('dsn'), 'M.TX.INP.', , 1
             return
             end
         call adrEdit 'del .zf .zl'
         do y=1 to m.tx.inp.0
             li = m.tx.inp.y
             call adrEdit 'line_after  .zl = (li)'
             end
         call adrEdit 'save'
         return
         end
     if m.tx.save = 2 then do
         ox = 0
         ix = 0
         if \ m.tx.isMacro then do
             do y=1 to m.tx.aft.0
                 li = m.tx.aft.y
                 if verify(strip(li), '0123456789') = 0 then do
                     ax = strip(li)
                     do while ix < ax
                         ox = ox + 1
                         ix = ix + 1
                         oo.ox = m.tx.inp.ix
                         end
                     end
                 else do
                     ox = ox + 1
                     oo.ox = li
                     end
                 end
             do ix = ix + 1 to m.tx.inp.0
                 ox = ox + 1
                 oo.ox = m.tx.inp.ix
                 end
             call writeDsn envGet('dsn'), 'OO.', ox, 1
             return
             end
         added = 0
         do y=1 to m.tx.aft.0
             li = m.tx.aft.y
             if verify(strip(li), '0123456789') = 0 then
                 ax = strip(li)
             else do
                 call adrEdit 'line_after ' (added+ax) '= (li)'
                 added = added + 1
                 end
             end
         call adrEdit 'save'
         call adrEdit 'save'
         return
         end
    call err 'implement save' m.tx.save
endProcedure txSave

/*--- return first non Space (Empty) char from str, '' if all spaces -*/
firstNE: procedure expose m.
parse arg str
    c1 = verify(str, ' ')
    if c1 > 0 then
        return substr(str, c1, 1)
    return ''
endProcedure firstNE

/*--- continue testcase
          maximal  cnt steps,
          until testcase has to wait or is at end --------------------*/
txCont: procedure expose m.
parse arg cnt
    fx = txNextFun(1)
    if fx < 1 then
        return
    m.tx.save = 2
    m.tx.aft.0 = 0
    do until fx < 1
        call mAdd 'TX.AFT', fx
        parse var m.tx.inp.fx fun opts
        code = 'txFun'fun'('quote(strip(opts))')'
        say 'code' code
        m.tx.outSta = 0
        interpret 'res =' code
        say 'res' res 'outSta' m.tx.outSta 'from' code
        if m.tx.outSta = 2 then
            return
        if m.tx.outsta \== 1 then
            call err 'bad outSta' m.tx.outSta 'after' code
        fx = txNextFun(fx+1)
        end
    return
endProcedure txCont



/*--- continue testcase ----------------------------------------------*/
txNextFun: procedure expose m.
parse arg firstLi
    i = 'TX.INP'
    nf = 0
    do y=firstLi to m.i.0
        d.y = ''
        w1 = word(m.i.y, 1)
        if w1 = '' | abbrev(w1, '*') > 0 then
            iterate
        if abbrev(w1, '=') | abbrev(w1, '-=') then do
            d.y = substr(m.i.y, pos('=', m.i.y))
            iterate
            end
        if abbrev(w1, '-') then
            iterate
        if \ abbrev(w1, '+') then do
            if nf = 0 then
                nf = y
            d.y = 'ini'                /* run ini here to ensure
                                        same sequence with assignments*/
            end
        else do
            nf = 0
            parse upper var m.i.y '+' sta rest
            say 'sta <'sta'>' rest
            if wordPos(sta, 'RUN WAIT') > 0 then
                return 0
            end
        end
    do y=firstLi to nf       /* redo ini and assignments */
         if d.y == '' then
             iterate
         if d.y == 'ini' then do
            if \ m.tx.iniRun then do
                call compRun '@', file(envGet('ini')), , 1
                m.tx.iniRun = 1
                end
            end
         else if abbrev(d.y, '=') then do
            e2 = pos('=', m.i.y, 2)
            if e2 < 2 then
                call err 'bad assignment line' y':' d.y
            call envPut strip(substr(m.i.y, 2, e2-2)),
                      , strip(substr(m.i.y, e2+1))
            end
        else
            call err 'bad d.'y d.y
        end
    return nf
endProcedure txNextFun

/*--- output a status line -------------------------------------------*/
txOutSta: procedure expose m.
parse arg op fun, rest
    fun = strip(fun)
    if op == '+' then do
         m.tx.outSta = max(m.tx.outSta,
             , 1 + (wordPos(translate(fun), 'RUN WAIT') > 0) )
        end
    else if op == '=' then do
        if words(fun) \== 1 then
            call err 'bad var name' fun 'in txOutSta('op fun',' rest')'
        call envPut fun, strip(rest)
        op = '-='
        fun = fun '='
        end
    else if op \== '-' then
        call err 'bad op' op 'in txOutSta('op fun',' rest')'
    call mAdd 'TX.AFT', op fun strip(rest)
    say 'outSta' m.tx.outSta 'after' op fun strip(rest)
    return
endProcedure txOutSta

/*--- do nothing and continue-----------------------------------------*/
txFunNop: procedure expose m.
parse arg opts
    if envHasKey('nopCount') then
        old = envGet('nopCount')
    else
        old = 0
    call txOutSta '= nopCount', old+1
    call txOutSta '+ ok', 'nop'
    call txOutSta '- nop', 'opts =' opts
    call txOutSta '- nop', 'opts =' opts
    return 1
endProcedure txFunNop

/*--- Manual action required -----------------------------------------*/
txFunManual: procedure expose m.
parse arg opts
    call txOutSta '+ wait', opts
    say 'manual <'opts'>'
    return 1
endProcedure txFunManual

/*--- creDb: sql creates, date etc. ----------------------------------*/
txFunCreDb: procedure expose m.
parse arg dst pha
    say 'txFunCreDb' dst pha 'ddl' envGet('ddl')
    if wordPos(dst, 'src trg') < 1 then
        call err 'creDb bad dest should be src or trg not' dst
    if pha = ''  | verify(pha, '0123456789') > 0 then
        call err 'creDb not natural number but' pha
    call envPut 'phase'  , strip(pha)
    call envPut 'env'    , dst
    call envPut 'subsys' , envGet(dst'Subsys' )
    call envPut 'db'     , envGet(dst'Db'     )
    call envPut 'creator', envGet(dst'Creator')
    gen = envGet('gen')
    if gen \== '' then
        gen = gen'('envGet('mpr')left(dst, 1)pha') ::f'
    call compRun '=', file(envGet('ddl')), file(gen), 1
 /* call adrIsp "view dataset('"gen"')"
 */ call dbConn envGet('subsys')
    m.sq.ignore.drop = '-204'
    j72 = 0
    if envHasKey('j72') then
        j72 = envGet('j72')
    call sqlProc file(gen), j72
    call txOutSta  '+ ok', 'creDb' gen
    return 1
endProcedure txCreDb

/* copy wsh ab hier ???????*/
/* rexx ****************************************************************
  wsh: walter's rexx shell
  interfaces:
      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://chsa4212/host/db2wiki/pmwiki.php?n=Main.Wsh
      syntax: http://chsa4212/host/db2wiki/pmwiki.php?n=Main.WshSyn
--- history ------------------------------------------------------------------
10. 2.12 w.keller div catTb* und eLong
 ********/ /*** end of help ********************************************
 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'
    m.wsh.version = 2.2
    parse arg spec
    if spec = '?' then
        return help('wsh version' m.wsh.version)
    os = errOS()
    isEdit = 0
    if spec = '' & os == 'TSO' then do    /* z/OS edit macro */
        if sysvar('sysISPF') = 'ACTIVE' then
            isEdit = adrEdit('macro (spec) NOPROCESS', '*') == 0
        if isEdit then do
            if spec = '?' then
                return help('version' m.wsh.version)
            call adrEdit '(d) = dataset'
            call adrEdit '(m) = member'
            m.editDsn = dsnSetMbr(d, m)
            if spec = '' & m.editDsn = 'A540769.WK.REXX(WSH)' then
                spec = 't'
            end
        end
    call scanIni
    f1 = spec
    rest = ''
    if pos(verify(f1, m.scan.alfNum), '1 2') > 0 then
        parse var spec f1 2 rest
    u1 = translate(f1)
    if u1 = 'T' then
        return wshTst(rest)
    else if u1 = 'I' then
        return wshInter(rest)
    else if u1 = 'S' then
        spec = '$#@ call sqlStmtsOpt $.$sqlIn,' quote(rest) '$#sqlIn#='
    call wshIni
    inp = ''
    out = ''
    if 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 = s2o('-wsh')
            useOut = listDsi('OUT FILE')
            if \ (useOut = 16 & sysReason = 2) then
                out = s2o('-out')
            end
        end
    else if os == 'LINUX' then do
        inp = s2o('&in')
        out = s2o('&out')
        end
    else
        call err 'implemnt wsh for os' os
    m.wshInfo = 'compile'
    call compRun spec, inp, out, wshInfo
    if isEdit then
        call wshEditEnd
exit 0
/*--- actual test case ----------------------------------------------*/
wshIni: procedure expose m.
    call compIni
    call sqlOIni
    call scanWinIni
    return
endProcedure wshIni

wshTst: procedure expose m.
parse arg rest
    if rest = '' then do /* default */
        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
    return 0
endProcedure wshTst

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

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 errReset 'h'
    oldOut = outDst(jOpen(oNew('JStem', mCut(ggStem, 1)), '>'))
    call errSay ggTxt
    call outDst oldOut
    isScan = 0
    if wordPos("pos", m.ggStem.4) > 0 ,
        & pos(" in line ", m.ggStem.4) > 0 then do
        parse var m.ggStem.4 "pos " pos .  " in line " lin":"
        if pos = '' then do
            parse var m.ggStem.4 " line " lin":"
            pos = 0
            end
        isScan = lin \== ''
        end
    m.ggStem.1 = '***' m.wshInfo 'error ***'
    if m.wshInfo=='compile' & isScan then do
        do sx=1 to m.ggStem.0
            call out m.ggStem.sx
            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
        end
    else do
        if m.wsh.editOut \== '' then do
            do sx=1 to m.ggStem.0
                call jWrite m.wsh.editOut, m.ggStem.sx
                end
            lab = wshEditInsLinSt(m.wsh.editDst, 0, ,
                , m.wsh.editOut'.BUF')
            call wshEditInsLinSt m.wsh.editDst, m.wsh.editHdr,
                , msgline, ggStem
            call wshEditLocate max(1, m.wsh.editDst-7)
            end
        else do
            do sx=1 to m.ggStem.0
                say m.ggStem.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

catTbLastCol: procedure expose m.
parse upper arg cr, tb
    sql = "select strip(char(colcount)) || ' ' || strip(c.name) "     ,
              "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"'"
    if sqlPreAllCl(1, sql, ggSt, ':m.ggLC') = 1 then
        return m.ggLc
    else if m.ggSt.0 = 0 then
        return ''
    else
        call err m.st.0 'rows in catTbLastCol for' cr'.'tb
endProcedur catTbLastCol

catTbCols: procedure expose m.
parse upper arg cr, tb
    sql = "select strip(name) "     ,
              "from sysibm.sysColumns " ,
              "where tbcreator = '"cr"' and tbname = '"tb"'"
    if sqlPreAllCl(1, sql, ggSt, ':m.ggSt.sx') < 1 then
        return ''
    res = m.ggst.1
    do cx=2 to m.ggst.0
        res =  res m.ggst.cx
        end
    return res
endProcedur catTbCols

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 sqlPreOpen 1, sql
    res = ''
    do kx=1 while sqlFetchInto(1, ':sq, :col, :ord')
        if sq \= kx then
            call err 'expected' kx 'but got colSeq' sq ,
                     'in index' cr'.'ix'.'col
        res = res || strip(col) || translate(ord, '<>?', 'ADR')
        end
    call sqlClose 1
    return res
endProcedur 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 sqlPreOpen 1, sql
    pr = ' '
    do kx=1 while sqlFetchInto(1, ':na, :ty, :nu, :de, :nn')
        /* say kx na ty nu de 'nn' nn */
        if pos('CHAR', ty) > 0 then
            dv = "''"
        else if pos('INT' ,ty) > 0 | wordPos(ty, 'REAL FLOAT') > 0 then
            dv = 0
        else if ty == 'TIMESTMP' then
            dv = '0001-01-01-00.00.00'
        else if pos('LOB', ty) > 0 then
            dv = ty"('')"
        else
            dv = '???'
        if nu = 'Y' then
            dv = 'case when 1=0 then' dv 'else null end'
        r = '???'
        if ty = 'ROWID' then do
            r = '--'
            end
        else if nn == 'new' then do
            if de = 'Y' then
                r = '--'
            else if nu == 'N' then
                r = dv
            else
                r = 'case when 1=0 then' dv 'else null end'
            end
        else do
            if nu = 'Y' | (nu = nn) then
                r = ''
            else
                r = 'coalesce('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' ty 'in' tCr'.'tTb'.'na
        call out r na
        end
    call sqlClose 1
    return
endProcedure catColCom

/*** 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.scan.alfLC)
        c1 = substr(m.scan.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.
    call tstBase
    call tstComp
    call tstDiv
    if errOS() = 'TSO' then
        call tstZos
    call tstTut0
    return 0
endProcedure tstAll

/* copx tstZos begin **************************************************/
tstZOs:
    call tstTime
    call sqlIni
    call tstSql
    call tstSqlB
    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   **************************************************/
/* 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

tstTime: procedure
/*         Winterzeit dez 2011
$=/tstTime/
    ### start tst tstTime #############################################
    Lrsn2Lzt(C5E963363741) 2010-05-01-11.34.56.789008
    Achtung: output ist abhaengig von Winter/Sommerzeit und LeapSecs
    timeZone 3600.00000 leapSecs 24.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.56.789008
    gmt2Lrsn(2011-03-31-14.35.01.234567) C78D87B86E38
    lzt2Lrsn(2011-03-31-14.35.01.234567) C78D7A661758
    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/
           Sommerzeit Jun 2011
$=/tstTimeSom/
    ### start tst tstTime #############################################
    Lrsn2Lzt(C5E963363741) 2010-05-01-12.34.56.789008 <<<<<
    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.56.789008  <<<<<
    gmt2Lrsn(2011-03-31-14.35.01.234567) C78D87B86E38
    lzt2Lrsn(2011-03-31-14.35.01.234567) C78D6CFCDD18 <<<<<
    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 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 sqlConDis
    call jIni
/*
$=/tstSql/
    ### start tst tstSql ##############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: stmt =  execSql prepare s7 from :src
    .    e 2: with from :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"
    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 tstEnd t
    return
endProcedure tstSql


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 sqlConDis
    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 sqlPreOpen cx
     do qx=1 while sqlFetch(cx, 'dest'qx'.fet', 'n')
         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 tstEnd t
     return
endProcedure tstSqlB

tstSqlO: procedure expose m.
/*
$=/tstSqlO/
    ### start tst tstSqlO #############################################
    *** err: SQLCODE = -204: A540769.SYSDUMMY IS AN UNDEFINED NAME
    .    e 1: stmt =  execSql 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 sqlOConnect
    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

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 sqlOConnect
    call tst t, "tstSqlO1"
    sq = 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 sq, m.j.cRead
    do while assNN('ABC', jReadO(sq))
        if m.sq.rowCount = 1 then
            call mAdd t.trans, className(m.sq.type)  '<tstSqlO1Type>'
        call outO abc
        end
    call jClose sq
    call out '--- writeAll'
    call pipeWriteAll sq
    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 sqlOConnect
    call tst t, "tstSqlO2"
    call pipeBegin
    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
    call sqlSel
    call pipeLast
    call fmtFTab abc
    call pipeEnd
    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 sqlOIni
    call tst t, "tstSqlS1"
    call sqlConnect dbaf
    s1 = fileSingle( ,
        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, fileSingle( ,
        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: stmt =  execSql execute immediate :ggSrc
    .    e 3: with immediate :ggSrc = set current schema = 'sysibm'
    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 sqlOConnect
    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 7<<<
    .    e 5: stmt =  execSql blabla .
    sqlCode -104: blabla
    sqlCode 0: set current schema=  sysIbm
    C
    1
    1 rows fetched: select count(*) "c" from sysDummy1 with  /* comm */+
    . 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 sqlOConnect
    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, , '-c72'
    call mAdd mCut(t'.IN', 0), 'set current -- sdf', 'schema = s100447;'
    call sqlStmts
    call tstEnd t
    return
endProcedure tstSqlStmts
/* 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 tstCompStmtA
    call tstCompDir
    call tstCompObj
    call tstCompORun
    call tstCompDataIO
    call tstCompPipe
    call tstCompRedir
    call tstCompComp
    call tstCompSyntax
    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)), '+')
    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'

/*
$=/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/'
     return
endProcedure tstCompStmt

tstCompStmtA: procedure expose m.
    call pipeIni

/*
$=/tstCompStmtAssAtt/
    ### start tst tstCompStmtAssAtt ###################################
    compile @, 19 lines: call tstCompStmtAA "begin", "tstAssAtt"
    run without input
    begin    tstAssAtt F1=F1val1   F2=         F3=         FR=
    gugus1
    ass1     tstAssAtt F1=F1val1   F2=F2ass1   F3=F3ass1   FR=
    ass2     tstAssAtt F1=F1val1   F2=F2ass1   F3=F3ass1   FR=<oAAR2>
    ass2     tstAssAr2 F1=FRF1ass2 F2=         F3=         FR=
    gugus3
    ass3     tstAssAtt F1=F1val1   F2=F2ass3   F3=F3ass1   FR=<oAAR2>
    ass3     tstAssAr2 F1=FRF1ass2 F2=FrF2ass3 F3=         FR=<oAAR3>
    ass3     tstAssAr3 F1=r2F1as3  F2=r2F2as3  F3=         FR=
    *** err: no field falsch in class tstAssAtt in EnvPut(falsch, +
             falsch, 1)
$/tstCompStmtAssAtt/

*/
    call classNew 'n? tstAssAtt u f F1 v, f F2 v,' ,
                'f F3 v, f FR r tstAssAtt'
    call envPutO 'tstAssAtt', mNew('tstAssAtt')
    call envPut 'tstAssAtt.F1', 'F1val1'
    call tstComp1 '@ tstCompStmtAssAtt',
        , 'call tstCompStmtAA "begin", "tstAssAtt"',
        , '$=tstAssAtt=:[F2=F2ass1  $$gugus1',
        ,               'F3=F3ass1',
        ,               ']',
        , 'call tstCompStmtAA "ass1", "tstAssAtt"',
        , '$=tstAssAtt.FR.F1 = FRF1ass2',
        , '$=tstAssAr2 =. ${tstAssAtt.FR}',
        , 'call mAdd T.trans, $.$tstAssAr2 "<oAAR2>"',
        , 'call tstCompStmtAA "ass2", "tstAssAtt"',
          ';call tstCompStmtAA "ass2", "tstAssAr2"',
        , '$=tstAssAtt=:[F2=F2ass3  $$gugus3',
        ,               ':/FR/ F2= FrF2ass3',
        ,                  'FR=:[F1=r2F1as3',
        ,                       'F2=r2F2as3',
        ,     '  *  blabla $$ sdf',
        ,                        ']',
        ,               '/FR/    ]',
        , '$=tstAssAr3 =. ${tstAssAtt.FR.FR}',
        , 'call mAdd T.trans, $.$tstAssAr3 "<oAAR3>";',
          'call tstCompStmtAA "ass3", "tstAssAtt";',
          'call tstCompStmtAA "ass3", "tstAssAr2";',
          'call tstCompStmtAA "ass3", "tstAssAr3"',
        , '$=tstAssAtt=:[falsch=falsch$]'
/*
$=/tstCompStmtAsSuTy/
    ### start tst tstCompStmtAsSuTy ###################################
    compile @, 4 lines: call tstCompStmtA2 "begin", "tstAsSuTy"
    run without input
    begin    tstAsSuTy G1=G1ini1  .
    _..GT    tstAsSuTy F1=GTF1ini1 F2=         F3=         FR=
    as2      tstAsSuTy G1=G1ini1  .
    _..GT    tstAsSuTy F1=GtF1ass2 F2=F2ass2   F3=         FR=
$/tstCompStmtAsSuTy/
*/
    call classNew 'n? tstAsSuTy u f G1 v, f GT tstAssAtt'
    call envPutO 'tstAsSuTy', mNew('tstAsSuTy')
    call envPut 'tstAsSuTy.G1', 'G1ini1'
    call envPut 'tstAsSuTy.GT.F1', 'GTF1ini1'
    call tstComp1 '@ tstCompStmtAsSuTy',
        , 'call tstCompStmtA2 "begin", "tstAsSuTy"',
        , '$=tstAsSuTy.GT =:[F1= GtF1ass2',
        ,         'F2= F2ass2 $]',
        , 'call tstCompStmtA2 "as2", "tstAsSuTy"'
/*
$=/tstCompStmtAssSt/
    ### start tst tstCompStmtAssSt ####################################
    compile @, 13 lines: .
    run without input
    *** err: bad stem index 1>0 @ <oASt>.HS class <clSt??> in EnvPut(ts+
    tAssSt.HS.1.F1, HS.1.ini0, )
    begin    tstAssSt  H1=H1ini1   HS.0=1       .
    _..1     tstAssSt. F1=HS.1.ini F2=         F3=         FR=
    ass2     tstAssSt  H1=H1ass2   HS.0=1       .
    _..1     tstAssSt. F1=         F2=hs+f2as2 F3=hs+f3as2 FR=
    ass3     tstAssSt  H1=H1ass3   HS.0=3       .
    _..1     tstAssSt. F1=         F2=hs+f2as3 F3=         FR=
    _..2     tstAssSt. F1=         F2=         F3=         FR=
    _..3     tstAssSt. F1=         F2=         F3=hs+f3as3 FR=
$/tstCompStmtAssSt/
*/
    cl = classNew('n? tstAssSt u f H1 v, f HS s tstAssAtt')
    cl = m.cl.2
    m.tstCl = m.cl.class
    call envPutO 'tstAssSt', mNew('tstAssSt')
    call oClear envGetO('tstAssSt')'.HS.1', class4Name('tstAssAtt')
    call envPut 'tstAssSt.H1', 'H1ini1'
    call tstComp1 '@ tstCompStmtAssSt', '',
        , "call mAdd t.trans, $.$tstAssSt '<oASt>'",
               ", m.tstCl '<clSt??>'",
          ";call envPut 'tstAssSt.HS.1.F1', 'HS.1.ini0'",
          ";call envPut 'tstAssSt.HS.0', 1",
          ";call envPut 'tstAssSt.HS.1.F1', 'HS.1.ini1'",
        , 'call tstCompStmtSt "begin", "tstAssSt"',
        , '$=tstAssSt =:[H1= H1ass2',
        ,      'HS =<:[F2=hs+f2as2',
        ,          'F3=hs+f3as2$] ]' ,
        , 'call tstCompStmtSt "ass2", "tstAssSt"',
        , '$=tstAssSt =:[H1= H1ass3',
        ,      'HS =<:[F2=hs+f2as3',
        ,          '; ; F3=hs+f3as3',
        ,            ' ] ]' ,
        , 'call tstCompStmtSt "ass3", "tstAssSt"',
        , ''
/*
$=/tstCompStmtAssSR/
    ### start tst tstCompStmtAssSR ####################################
    compile @, 13 lines: .
    run without input
    *** err: bad stem index 1>0 @ <oASR>.HS class <clSR??> in EnvPut(ts+
    tAssSR.HS.1.F1, HS.1.ini0, )
    begin    tstAssSR  H1=H1ini1   HS.0=1       .
    _..1     tstAssSR. F1=HS.1.ini F2=         F3=         FR=
    ass2     tstAssSR  H1=H1ass2   HS.0=1       .
    _..1     tstAssSR. F1=         F2=hs+f2as2 F3=hs+f3as2 FR=
    ass3     tstAssSR  H1=H1ass3   HS.0=3       .
    _..1     tstAssSR. F1=         F2=hs+f2as3 F3=         FR=
    _..2     tstAssSR. F1=         F2=         F3=         FR=
    _..3     tstAssSR. F1=         F2=         F3=hs+f3as3 FR=
$/tstCompStmtAssSR/
*/
    cl = classNew('n? tstAssSR u f H1 v, f HS s r tstAssAtt')
    cl = m.cl.2
    m.tstCl = m.cl.class
    call envPutO 'tstAssSR', mNew('tstAssSR')
    call oClear envGetO('tstAssSR')'.HS.1', class4Name('tstAssAtt')

    call envPut 'tstAssSR.H1', 'H1ini1'
    call tstComp1 '@ tstCompStmtAssSR', '',
        , "call mAdd t.trans, $.$tstAssSR '<oASR>'",
               ", m.tstCl '<clSR??>'",
          ";call envPut 'tstAssSR.HS.1.F1', 'HS.1.ini0'",
          ";call envPut 'tstAssSR.HS.0', 1",
          ";call envPutO 'tstAssSR.HS.1', ''",
          ";call envPut 'tstAssSR.HS.1.F1', 'HS.1.ini1'",
        , 'call tstCompStmtSt "begin", "tstAssSR"',
        , '$=tstAssSR =:[H1= H1ass2',
        ,      'HS =<<:[F2=hs+f2as2',
        ,          'F3=hs+f3as2$] ]' ,
        , ';call tstCompStmtSt "ass2", "tstAssSR"',
        , '$=tstAssSR =:[H1= H1ass3',
        ,      'HS =<:[F2=hs+f2as3',
        ,          '; ; F3=hs+f3as3',
        ,            ' ] ]' ,
        , 'call tstCompStmtSt "ass3", "tstAssSR"',
        , ''
/*
$=/tstCompStmtassTb/
    ### start tst tstCompStmtassTb ####################################
    compile @, 19 lines: .
    run without input
    *** err: bad stem index 1>0 @ <oASt>.HS class <clSt??> in EnvPut(ts+
    tAssSt.HS.1.F1, HS.1.ini0, )
    begin    tstAssSt  H1=H1ini1   HS.0=1       .
    _..1     tstAssSt. F1=HS.1.ini F2=         F3=         FR=
    tstR: @tstWriteoV4 isA :<assCla H1>
    tstR:  .H1 = H1ass2
    ass2     tstAssSt  H1=H1ini1   HS.0=2       .
    _..1     tstAssSt. F1=         F2=hs+f2as2 F3=hs+f3as2 FR=
    _..2     tstAssSt. F1=         F2=h3+f2as2 F3=h3+f3as2 FR=
    ass3     tstAssSt  H1=H1ass3   HS.0=3       .
    _..1     tstAssSt. F1=         F2=f2as3    F3=         FR=
    _..2     tstAssSt. F1=         F2=         F3=         FR=
    _..3     tstAssSt. F1=         F2=         F3=f3as3    FR=
$/tstCompStmtassTb/
*/
    cl = classNew('n? tstAssSt u f H1 v, f HS s tstAssAtt')
    cl = m.cl.2
    m.tstCl = m.cl.class
    call envPutO 'tstAssSt', mNew('tstAssSt')
    call oClear envGetO('tstAssSt')'.HS.1', class4Name('tstAssAtt')
    call envPut 'tstAssSt.H1', 'H1ini1'
    call tstComp1 '@ tstCompStmtassTb', '',
        , "call mAdd t.trans, $.$tstAssSt '<oASt>'",
               ", m.tstCl '<clSt??>'",
          ";call envPut 'tstAssSt.HS.1.F1', 'HS.1.ini0'",
          ";call envPut 'tstAssSt.HS.0', 1",
          ";call envPut 'tstAssSt.HS.1.F1', 'HS.1.ini1'",
        , 'call tstCompStmtSt "begin", "tstAssSt"',
        , '$=tstAssSt =:[ $@|[  H1  ',
        , '                      H1ass2  ',
        , "$@{call mAdd 'T.TRANS', className(objClass(envWithObj()))",
               "'<assCla H1>'} $]",
        ,      'HS =<|[  $*(...',
        ,       '..$*)  F2      F3   ',
        ,        '   hs+f2as2     hs+f3as2  ' ,
        ,        '  *   kommentaerliiii    ' ,
        ,        '                          ' ,
        ,        '   h3+f2as2    h3+f3as22222$] ]' ,
        , 'call tstCompStmtSt "ass2", "tstAssSt"',
          '$=tstAssSt =:[H1= H1ass3',
        ,      'HS =<|[F2       F3',
        ,      '        f2as3' ,
        ,      '  ',
        ,      '                 $""',
        ,      '            f3as3 $] ]' ,
        , 'call tstCompStmtSt "ass3", "tstAssSt"'
/*
$=/tstCompStmtassInp/
    ### start tst tstCompStmtassInp ###################################
    compile @, 11 lines: .
    run without input
    tstR: @tstWriteoV2 isA :<cla123>
    tstR:  .eins = l1v1
    tstR:  .zwei = l1v2
    tstR:  .drei = l1v3
    tstR: @tstWriteoV3 isA :<cla123>
    tstR:  .eins = l2v1
    tstR:  .zwei = l2v2
    tstR:  .drei = l21v3
    *** err: undefined variable oo in envGetO(oo)
    oo before 0
    oo nachher <oo>
    tstR: @tstWriteoV5 isA :<cla123>
    tstR:  .eins = o1v1
    tstR:  .zwei = o1v2
    tstR:  .drei = o1v3
$/tstCompStmtassInp/
*/
    call envRemove 'oo'
    call tstComp1 '@ tstCompStmtassInp', '',
        , "$@|[eins    zwei  drei  ",
        , " l1v1    l1v2   l1v3",
        , "$@{call mAdd 'T.TRANS', className(objClass(envWithObj()))",
                  "'<cla123>'}" ,
        , "      l2v1   l2v2   l21v3",
        , "]",
        , "$$ oo before $.$oo",
        , "$; $>.$oo $@|[eins zwei drei",
        , "            o1v1  o1v2   o1v3 $]",
        , "$; call mAdd 'T.TRANS', $.$oo '<oo>'",
        , "$; $$ oo nachher $.$oo $@$oo"
    return
endProcedure tstCompStmtA

tstCompStmtAA: procedure expose m.
parse arg ggTxt, ggN
    call out left(ggTxt,8) left(ggN, 9),
         'F1='left(envGet(ggN'.F1'), 8),
         'F2='left(envGet(ggN'.F2'), 8),
         'F3='left(envGet(ggN'.F3'), 8),
         'FR='envGetO(ggN'.FR')
    return
endSubroutine

tstCompStmtA2: procedure expose m.
parse arg ggTxt, ggN
    call out left(ggTxt,8) left(ggN, 9),
         'G1='left(envGet(ggN'.G1'), 8)
    call tstCompStmtAA '_..GT', ggN'.GT'
    return
endSubroutine

tstCompStmtSt: procedure expose m.
parse arg ggTxt, ggN
    call out left(ggTxt,8) left(ggN, 9),
         'H1='left(envGet(ggN'.H1'), 8),
         'HS.0='left(envGet(ggN'.HS.0'), 8)
    do sx=1 to envGet(ggN'.HS.0')
        call tstCompStmtAA '_..'sx, ggN'.HS.'sx
        end
    return
endSubroutine tstCompStmtSt

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 17 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 assignment expected after $=
    .    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 assignment expected after $=
    .    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 assignment expected after $=
    .    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 block or expr 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 block or expr expected after $@ expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 4 in line 1: $@=
$/tstCompSynRun2/ */
    call tstComp1 '@ tstCompSynRun2 +', '$@='

/*
$=/tstCompSynRun3/
    ### start tst tstCompSynRun3 ######################################
    compile @, 1 lines: $@|
    *** err: scanErr block or expr expected after $@ expected
    .    e 1: last token  scanPosition .
    .    e 2: pos 4 in line 1: $@|
    *** err: scanErr comp2code bad fr | to | for @|| .
    .    e 1: last token  scanPosition .
    .    e 2: pos 4 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 = 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 with $$ 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 envPutO '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 envPut '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 =, 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: @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

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 $"$@$#-"
  $@pi2()
  $#pi2#-
  $'zeile drei nach $@$#- v1='v1
  vierte und letzte Zeile
$/tstCompDirPiSrc/ */
/*
$=/tstCompDirPi/
    ### start tst tstCompDirPi ########################################
    compile call pipePreSuf '<','>' $=v1=eiPi $<.$pi $#pi#=, 6 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 $<.$pi $#pi#="
    return
endProcedure tstCompDir

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 fmtFTab abc
$/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 sqlOConnect
    call tstComp2 'tstCompSql', '@'

    return
endProcedure tstCompFile
tstTut0: procedure expose m.
/*
$=/tstTut01Src/
$#=
$*+>.fSub()                               Kommentar
$*+>~tmp.jcl(t)                           Kommentar
$*+@=[                                    Kommentar
$=subsys=DBAF
$=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                                              20120306 09:58:54
$/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='DBAF,A540769C.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DBAF.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=DBAF
$=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='DBAF,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DBAF.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='DBAF,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DBAF.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=DBAF
$@|[
      db         ts
      DGDB9998   A976
      DA540769   A977
 ]
$** $| call fmtFTab
$**    $#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='DBAF,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DBAF.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='DBAF,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DBAF.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=DBAF
$=db=DA540769
call sqlConnect $subsys
$@=[  select dbName  db , name  ts
          from sysibm.sysTablespace
          where dbName = '$db' and name < 'A978'
          order by name desc
          fetch first 2 rows only
$]
$| call sqlSel
$** $| call fmtFTab
$** $#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 , 36 lines: $#@
    run without input
    //A5407691 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA977A    EXEC PGM=DSNUTILB,
    //             PARM='DBAF,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DBAF.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
    //A5407692 JOB (CP00,KE50),'DB2 REO',
    //         MSGCLASS=T,TIME=1440,
    //         NOTIFY=&SYSUID,REGION=0M
    //CA976A    EXEC PGM=DSNUTILB,
    //             PARM='DBAF,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=DBAF.DBAA.LISTDEF(TEMPL),DISP=SHR
    //SYSIN     DD *
    LISTDEF C#LIST
    INCLUDE TABLESPACE DA540769.A976A*   PARTLEVEL
    OPTIONS EVENT(ITEMERROR, SKIP)
    COPY LIST C#LIST COPYDDN(TCOPYD)
    PARALLEL
    SHRLEVEL CHANGE
$/tstTut04/
$=/tstTut05Src/
$#@
$=subsys = dbaf
$=lst=<:[
    db = DGDB9998
    ts =<|[
             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=. mNew(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='dbaf,A5407691.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=dbaf.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='dbaf,A5407692.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=dbaf.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='dbaf,A5407693.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=dbaf.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='dbaf,A5407694.FULCOPL'
    //SYSPRINT  DD SYSOUT=*
    //UTPRINT   DD SYSOUT=*
    //SYSUDUMP  DD SYSOUT=*
    //SYSTEMPL  DD DSN=dbaf.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 dbtf
$@|[  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 fmtFtab
$|
$=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=(DBTF,'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 dbaf
$#out                                              20101231 11:56:23
$/tstTut07Src/
$=/tstTut07/
$=/tstTut07/
    ### start tst tstTut07 ############################################
    compile , 46 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=(DBTF,'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=(DBTF,'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=(DBTF,'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 sqlOIni
    call sqlDisconnect '*'
    call tstComp2 'tstTut01'
    call tstComp2 'tstTut02'
    call tstComp2 'tstTut03'
    call tstComp2 'tstTut04'
    call tstComp2 'tstTut05'
    call tstComp2 'tstTut07'
    return
endProcedure tstTut0
/* copx tstComp end   *************************************************/
/* copx tstBase begin **************************************************
     test the basic classes
***********************************************************************/
tstBase: procedure expose m.
    call tstTstSay
    call oIni
    call tstM
    call tstMCat
    call tstMap
    call tstMapVia
    call tstClass
    call tstClass2
    call tstO
    call tstOGet
    call jIni
    call tstJSay
    call tstJ
    call tstJ2
    call catIni
    call tstCat
    call pipeIni
    CALL TstEnv
    CALL TstEnvCat
    call tstPipe
    call tstEnvVars
    call tstEnvWith
    call tstTotal
    call tstPipeLazy
    call tstEnvClass
    call tstFile
    call tstFileList
    call tstF
    call tstFmt
    call tstFmtUnits
    call tstTotal
    call scanIni
    call tstScan
    call ScanReadIni
    call tstScanRead
    call tstScanUtilInto
    call tstScanWin
    call tstScanSQL
    call tstScanSqlStmt
    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

tstM: procedure expose m.
/*
$=/tstMa/
    ### start tst tstMa ###############################################
    mNew() 1=newM1 2=newM2
    mNew(tst...) 2=nZwei new 3=nDrei old free fEins nEins new 4=nVier n+
    ew
    iter nDrei old free fEins nEins new
    iter nZwei new
    iter nVier 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, ,
        , "if symbol('m.m') \== 'VAR' then m.m = arg(2) 'new';" ,
                                      "else m.m = arg(2) 'old' m.m",
        , "m.m = 'free' arg(2) m.m"
    t1 = mNew('tst'm1, 'nEins')
    t2 = mNew('tst'm1, 'nZwei')
    call mFree t1, 'fEins'
    t3 = mNew('tst'm1, 'nDrei')
    t4 = mNew('tst'm1, 'nVier')
    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, %+Q)                  =;
    mCat(0, %+Q1)                 =;
    mCat(0, %s11%+Q2222)          =;
    mCat(0, 1%s2%+Q3)             =;
    mCat(0, 1%s2@%s333%+Q4)       =;
    mCat(0, 1%s2@%s3@%s4%+Q5)     =;
    mCat(1, %+Q)                  =eins;
    mCat(1, %+Q1)                 =eins;
    mCat(1, %s11%+Q2222)          =eins11;
    mCat(1, 1%s2%+Q3)             =1eins2;
    mCat(1, 1%s2@%s333%+Q4)       =1eins2eins333;
    mCat(1, 1%s2@%s3@%s4%+Q5)     =1eins2eins3eins4;
    mCat(2, %+Q)                  =einszwei;
    mCat(2, %+Q1)                 =eins1zwei;
    mCat(2, %s11%+Q2222)          =eins112222zwei11;
    mCat(2, 1%s2%+Q3)             =1eins231zwei2;
    mCat(2, 1%s2@%s333%+Q4)       =1eins2eins33341zwei2zwei333;
    mCat(2, 1%s2@%s3@%s4%+Q5)     =1eins2eins3eins451zwei2zwei3zwei4;
    mCat(3, %+Q)                  =einszweidrei;
    mCat(3, %+Q1)                 =eins1zwei1drei;
    mCat(3, %s11%+Q2222)          =eins112222zwei112222drei11;
    mCat(3, 1%s2%+Q3)             =1eins231zwei231drei2;
    mCat(3, 1%s2@%s333%+Q4)       =1eins2eins33341zwei2zwei33341drei2dr+
    ei333;
    mCat(3, 1%s2@%s3@%s4%+Q5)     =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, '%+Q'
         call tstMCat1 qx, '%+Q1'
         call tstMCat1 qx, '%s11%+Q2222'
         call tstMCat1 qx, '1%s2%+Q3'
         call tstMCat1 qx, '1%s2@%s333%+Q4'
         call tstMCat1 qx, '1%s2@%s3@%s4%+Q5'
         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.86 :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 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 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 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 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 ################################################
    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 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
$/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>'
    call tstOut t, 'FLDS of' e mCat(oFlds(e), '%+Q, ')
    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), '%+Q, ')
    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')), '%+Q, ')

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

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

tstOGet: procedure expose m.
/*
$=/tstOGet/
    ### start tst tstOGet #############################################
    class.NAME= class
    class.NAME= class : w
    class|    = u
    *** err: bad stem index 91>7 @ CLASS.4 class class in oGet(CLASS.4,+
    . 91)
    class.91  = 0
    class.1   = CLASS.1 |= u
    class.2   = CLASS.5 |= c
$/tstOGet/ */
    call oIni
    call tst t, 'tstOGet'
    cc = m.class.class
    call tstOut t, 'class.NAME=' oGet(cc, 'NAME')
    o = oGetO(cc, 'NAME')
    call tstOut t, 'class.NAME=' o2String(o) ':' className(objClass(o))
    call tstOut t, 'class|    =' oGet(cc, '|')
    call tstOut t, 'class.91  =' className(oGet(cc, 91))
    call tstOut t, 'class.1   =' oGetO(cc, '1') '|=' oGet(cc, '1||')
    call tstOut t, 'class.2   =' className(oGetO(cc, '2')) ,
            '|=' oGet(cc, '2||')
    call tstEnd t
/*
$=/tstOGet2/
    ### start tst tstOGet2 ############################################
    tstOGet1            get1 w
    tstOGet1.f1         get1.f1 v
    tstOGet1.f2         get1.f2 w
    tstOGet1.F3|        get1.f3 v
    tstOGet1.f3.fEins   get1.f3.fEins v
    tstOGet1.f3.fZwei   get1.f3.fZwei w
    tstOGet1.f3%fDrei   ]get1.f3.fDrei w
    tstOGet1.f3.fDrei   get1.f3.fDrei w
    tstOGet1.f3%1       get1.f3.fDrei.1 w
    tstOGet1.f3.2       TSTOGET1
    tstOGet1.f3.2|f1    get1.f1 v
    tstOGet1.f3.2|f3.2|f2 get1.f2 w
    *** err: bad stem index 4>3 @ TSTOGET1.F3 class TstOGet0 in oGet(TS+
    TOGET1, F3.4)
    tstOGet1.f3.4       0
    tstOGet1.f3.3       get1.f3.fDrei.3 w
    *** err: bad stem index 3>3A @ TSTOGET1.F3 class TstOGet0 in oGet(T+
    STOGET1, F3.3)
    tstOGet1.f3.2       0
$/tstOGet2/

 */
    c0 = classNew('n? TstOGet0 u f FEINS v,f FZWEI w,f FDREI r,v,' ,
            's r TstOGet0')
    cl = classNew('n? TstOGet u r, f F1 v, f F2 r, f F3 TstOGet0')
    call oMutate tstOGet1, cl
    m.tstOGet1    = s2o('get1 w')
    m.tstOGet1.f1 = 'get1.f1 v'
    m.tstOGet1.f2 = s2o('get1.f2 w')
    m.tstOGet1.f3 = 'get1.f3 v'
    m.tstOGet1.f3.fEins = 'get1.f3.fEins v'
    m.tstOGet1.f3.fZwei = s2o('get1.f3.fZwei w')
    m.tstOGet1.f3.fDrei = s2o('get1.f3.fDrei w')
    m.tstOGet1.f3.0 = 3
    m.tstOGet1.f3.1 = s2o('get1.f3.fDrei.1 w')
    m.tstOGet1.f3.2 = tstOGet1
    m.tstOGet1.f3.3 = s2o('get1.f3.fDrei.3 w')

    call tst t, 'tstOGet2'
    call tstOut t, 'tstOGet1           ' oGet(tstOGet1,   )
    call tstOut t, 'tstOGet1.f1        ' oGet(tstOGet1, f1)
    call tstOut t, 'tstOGet1.f2        ' oGet(tstOGet1, f2)
    call tstOut t, 'tstOGet1.F3|       ' oGet(tstOGet1, 'F3|')
    call tstOut t, 'tstOGet1.f3.fEins  ' oGet(tstOGet1, f3.fEins)
    call tstOut t, 'tstOGet1.f3.fZwei  ' oGet(tstOGet1, f3.fZwei)
    call tstOut t, 'tstOGet1.f3%fDrei  ' oGetO(tstOGet1, 'F3%FDREI')
    call tstOut t, 'tstOGet1.f3.fDrei  ' oGet(tstOGet1, f3.fDrei)
    call tstOut t, 'tstOGet1.f3%1      ' oGet(tstOGet1, 'F3%1')
    call tstOut t, 'tstOGet1.f3.2      ' oGetO(tstOGet1, 'F3.2')
    call tstOut t, 'tstOGet1.f3.2|f1   ' oGet(tstOGet1, 'F3.2|F1')
    call tstOut t, 'tstOGet1.f3.2|f3.2|f2' ,
                                oGet(tstOGet1, 'F3.2|F3.2|F2')
    call tstOut t, 'tstOGet1.f3.4      ' oGet(tstOGet1, 'F3.4')
    call tstOut t, 'tstOGet1.f3.3      ' oGet(tstOGet1, 'F3.3')
    m.tstOGet1.f3.0 = 3a
    call tstOut t, 'tstOGet1.f3.2      ' oGet(tstOGet1, 'F3.3')
    call tstEnd t
/*
$=/tstOPut3/
    ### start tst tstOPut3 ############################################
    tstOGet1.f1         get1.f1 v
    tstOGet1.f1   aPut1 f1.put1
    tstOGet1.f2   aPut2 f2.put2
    tstOGet1.f3.fEins  p3 f3.fEins,p3
    tstOGet1.f3%0       3A
    tstOGet1.f3%0    =4 4
    tstOGet1.f3.4.feins val f3.4|feins
$/tstOPut3/
 */
    call tst t, 'tstOPut3'
    call tstOut t, 'tstOGet1.f1        ' oGet(tstOGet1, f1)
    call oPut tstOget1, f1, 'f1.put1'
    call tstOut t, 'tstOGet1.f1   aPut1' oGet(tstOGet1, f1)
    call oPut tstOget1, f2, 'f2.put2'
    call tstOut t, 'tstOGet1.f2   aPut2' oGet(tstOGet1, f2)
     call oPut tstOget1, f3.fEins, 'f3.fEins,p3'
    call tstOut t, 'tstOGet1.f3.fEins  p3' oGet(tstOGet1, f3.fEins)
    call tstOut t, 'tstOGet1.f3%0      ' oGet(tstOGet1, 'F3%0')
     call oPut tstOget1, f3.0, 4
    call tstOut t, 'tstOGet1.f3%0    =4' oGet(tstOGet1, 'F3%0')
    call oPutO tstOget1, 'F3.4', ''
    call oPut tstOget1, 'F3.4|FEINS', 'val f3.4|feins'
    call tstOut t, 'tstOGet1.f3.4.feins'    ,
          oGet(tstOGet1, 'F3.4|FEINS')
    call tstEnd t
    return
endProcedure tstOGet

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, 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, 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 pipeBeLa m.j.cRead 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 m.j.cRead 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 m.j.cRead b0, m.j.cRead b1, m.j.cRead b2,
             , m.j.cRead 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 m.j.cRead c1
    call pipeWriteNow
    call pipeEnd
    call pipeBeLa m.j.cRead 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
    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')
    call pipeBeLa '>' envGetO('theBuf', '-b')
    call out 'one to theBur'
    call out 'two to theBuf'
    call pipeEnd
    call pipeBeLa m.j.cRead envGetO('theBuf')
    call pipeWriteNow
    call pipeEnd
    call tstEnd t
    return
endProcedure tstEnvVars

tstEnvWith: procedure expose m.
/*
$=/tstEW2/
    ### start tst tstEW2 ##############################################
    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    ]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 variable F1 in envGet(F1)
    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    ]get1.f3.fDrei w
    F3%FDREI|   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 variable F1 in envGet(F1)
    po-1 F1     0
$/tstEW2/  */
    call pipeIni
    c0 = classNew('n? TstEW0 u f FEINS v,f FZWEI w, f FDREI r,v,s r')
    cl = classNew('n? TstEW u r TstEW0, f F1 v, f F2 r, f F3 TstEW0')
    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 envPutO 'tstK1', tstEW1

    call tst t, 'tstEW2'
    call tstOut t, 'tstK1|           ' envGet('tstK1|')
    call tstOut t, 'tstK1%f1         ' envGet('tstK1%F1')
    call tstOut t, 'tstK1.f2         ' envGet('tstK1.F2')
    call tstOut t, 'tstK1%F3         ' envGet('tstK1%F3|')
    call tstOut t, 'ttstK1.F3.FEINS  ' envGet('tstK1.F3.FEINS')
    call tstOut t, 'tstK1%F3%FZWEI   ' envGet('tstK1%F3%FZWEI')
    call tstOut t, 'tstK1.F3.FDREI   ' envGetO('tstK1.F3.FDREI')
    call tstOut t, 'tstK1%F3%FDREI|  ' envGet('tstK1%F3%FDREI')
    call tstOut t, 'tstK1.F3.1       ' envGet('tstK1.F3.1')
    call tstOut t, 'tstK1%F3%2       ' envGetO('tstK1%F3%2')
    call tstOut t, 'tstK1.F3.2|F1    ' envGet('tstK1.F3.2|F1')
    call tstOut t, 'tstK1%F3%2|F3.2|F2' ,
                                envGet('tstK1%F3%2|F3%2|F2')
    call tstOut t, 'F1         ' envGet('F1')
    call envPushWith tstEW1
    call tstOut t, 'F1         ' envGet('F1')
    call tstOut t, 'f2         ' envGet('F2')
    call tstOut t, 'F3         ' envGet('F3|')
    call tstOut t, 'F3.FEINS   ' envGet('F3.FEINS')
    call tstOut t, 'F3.FZWEI   ' envGet('F3.FZWEI')
    call tstOut t, 'F3%FDREI   ' envGetO('F3%FDREI')
    call tstOut t, 'F3%FDREI|  ' envGet('F3%FDREI|')
    call tstOut t, 'F3%1       ' envGet('F3%1')
    call tstOut t, 'pu1 F1     ' envGet('F1')
    call envPushWith tstEW2
    call tstOut t, 'pu2 F1     ' envGet('F1')
    call envPopWith
    call tstOut t, 'po-2 F1    ' envGet('F1')

    call envPopWith
    call tstOut t, 'po-1 F1    ' envGet('F1')
    call tstEnd t
/*
$=/tstEW3/
    ### start tst tstEW3 ##############################################
    .          s c3.F1          = v(c3.f1)
    *** err: no reference @ <c3>.F1 class CLASS.1 in envGet(c3.F1.FEINS+
    )
    .          s c3.F1.FEINS    = 0
    .          s c3.F3.FEINS    = .
    .          s c3.F3.FEINS    = val(c3.F3.FEINS)
    *** err: no field FEINS @ <c3> class TstEW in envGet(c3.FEINS)
    .          s c3.FEINS       = 0
    *** err: null @ <c3> class TstEW in envGet(c3|FEINS)
    .          s c3|FEINS       = 0
    aft Put   s c3|FEINS       = val(c3|FEINS)
    Push c3   s F3.FEINS       = val(c3.F3.FEINS)
    *** err: no field FEINS aftPuP= pushPut(F3 @ <c3>.F3 class TstEW0 i+
    n envGet(F3.FEINS aftPuP= pushPut(F3.FEINS))
    .          s F3.FEINS aftPuP= 0
    push c4   s F1             = v(c4.f1)
    put f2    s F2             = put(f2)
    *** err: no field F222 in class TstEW in EnvPut(F222, f222 stopped,+
    . 1)
    put ..    s F3.FEINS       = put(f3.fEins)
    popW c4   s F1             = v(c3.f1)
    *** err: undefined variable F1 in envGet(F1)
    popW c3   s F1             = 0
    .          s F222           = f222 pop stop
$/tstEW3/
*/
    call tst t, 'tstEW3'
    c3 = mNew('TstEW')
    call mAdd t.trans, c3 '<c3>'
    m.c3.f1 = 'v(c3.f1)'
    call envPutO 'c3', c3
    call tstEnvSG , 'c3.F1'
    call tstEnvSG , 'c3.F1.FEINS'
    call tstEnvSG , 'c3.F3.FEINS'
    call envPut 'c3.F3.FEINS', 'val(c3.F3.FEINS)'
    call tstEnvSG , 'c3.F3.FEINS'
    call tstEnvSG , 'c3.FEINS'
    call tstEnvSG , 'c3|FEINS'
    call envPut 'c3|FEINS', 'val(c3|FEINS)'
    call tstEnvSG 'aft Put', 'c3|FEINS'
    call envPushWith c3
    call tstEnvSG 'Push c3', 'F3.FEINS'
    call envPut 'F3.FEINS', 'pushPut(F3.FEINS)'
    call tstEnvSG , 'F3.FEINS aftPuP=' envGet('F3.FEINS')

    c4 = mNew('TstEW')
    call mAdd t.trans, c4 '<c4>'
    m.c4.f1 = 'v(c4.f1)'
    call envPut f222, 'f222 no stop'
    call envPushWith c4
    call tstEnvSG 'push c4', f1
    call envPut f2, 'put(f2)'
    call tstEnvSG 'put f2', f2
    call envPut f222, 'f222 stopped', 1
    call envPut f3.fEins, 'put(f3.fEins)'
    call tstEnvSG 'put .. ', f3.fEins
    call envPopWith
    call tstEnvSG 'popW c4', f1
    call envPopWith
    call envPut f222, 'f222 pop stop'
    call tstEnvSG 'popW c3', f1
    call tstEnvSG          , f222
    call tstEnd t

/*
$=/tstEW4/
    ### start tst tstEW4 ##############################################
    tstO4 S.0 0 R.0 0 class TstEW4
    *** err: no field FZWEI in class  in EnvPut(FZWEI, v 1.fZwei, 1)
    1 fEins   s FEINS          = v 1.fEins
    1 fZwei   s FZWEI          = .
    2 fEins   s FEINS          = .
    2 fZwei   s FZWEI          = v 2.fZwei
    v 1.fEins .# 1 vor
    v 1.fEins .# 2 nach withNext e
    *** err: undefined variable FEINS in envGet(FEINS)
    ? fEins   s FEINS          = 0
    1 fEins   s FEINS          = v 1|fEins
    1 fZwei   s FZWEI          = .
    2 fEins   s FEINS          = .
    2 fZwei   s FZWEI          = v 2.fZwei
    v 1|fEins .# 2
$/tstEW4/
*/
    c4 = classNew('n? TstEW4 u f S s TstEW0, f R s r TstEW0')
    o4 = mReset('tstO4', 'TstEW4')
    call tst t, 'tstEW4'
    call tstout t, o4 'S.0' m.o4.s.0 'R.0' m.o4.r.0 ,
        'class' className(objClass(o4))
    call envPushWith o4'.S', m.c4.f2c.s, 'asM'
    call envPut fZwei, 'v 1.fZwei', 1
    call envWithNext 'b'
    call envPut feins, 'v 1.fEins', 1
    call tstEnvSG '1 fEins ', fEins
    call tstEnvSG '1 fZwei ', fZwei
    m.o4.s.2.feins = 'vorher'
    m.o4.s.2.fZwei = s2o('vorher')
    call envWithNext
    call envPut fZwei, 'v 2.fZwei', 1
    call tstEnvSG '2 fEins ', fEins
    call tstEnvSG '2 fZwei ', fZwei
    call tstOut t, m.o4.s.1.fEins '.#' m.o4.s.0 'vor'
    call envWithNext 'e'
    call tstOut t, m.o4.s.1.fEins '.#' m.o4.s.0 'nach withNext e'
    call envPopWith
    call tstEnvSG '? fEins ', fEins
    call envPushWith o4'.R', m.c4.f2c.r, 'asM'
    call envWithNext 'b'
    call envPut fEins, 'v 1|fEins', 1
    call tstEnvSG '1 fEins ', fEins
    call tstEnvSG '1 fZwei ', fZwei
    call envWithNext
    call envPut fZwei, 'v 2.fZwei', 1
    call tstEnvSG '2 fEins ', fEins
    call tstEnvSG '2 fZwei ', fZwei
    call envWithNext 'e'
    call envPopWith
    o41r = m.o4.r.1
    call tstOut t, m.o41r.fEins '.#' m.o4.r.0
    call tstEnd t

    return
endProcedure tstEnvWith

tstEnvSG: procedure expose m. t
parse arg txt, nm
    call tstOut t, left(txt,10)'s' left(nm, 15)'=' envGet(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
            ty = classNew('n TstPipeLazyBuf u JBuf', 'm',
            , 'jOpen call tstOut "T", "bufOpen" opt;',
                'call jOpen oCast(m, "JBuf"), opt',
            , 'jClose call tstOut "T", "bufClose";',
                'call 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 = class4Name('TstPipeLazyRdr', '')
        if ty == '' then
            ty = classNew('n TstPipeLazyRdr u JRW', 'm',
            , 'jOpen call tstOut "T", "RdrOpen" opt;m.m.jReading=1',
            , '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 pipeBegin
     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: @<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 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 m.j.cRead s2o(tstPdsMbr(pd2, 'eins')), m.j.cRead b,
                    ,m.j.cRead jBuf(),
                    ,m.j.cRead s2o(tstPdsMbr(pd2, 'zwei')),
                    ,m.j.cRead s2o(tstPdsMbr(pds, 'wr0')),
                    ,m.j.cRead 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>>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 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

tstF: procedure expose m.
/*
$=/tstF/
    ### start tst tstF ################################################
    f(1\s23%s345%s67\%8, eins,  zwei ) =1 23eins345 zwei 67%8;
    f(1\S23%s345%S67\%8, eins,  zwei ) =1\S23eins345zwei67%8;
    f(1\s23%s345%s67\%8, eins,  zwei ) =1 23eins345 zwei 67%8;
    f(1\s23%s345%S67\%8, eins,  zwei ) =1 23eins345zwei67%8;
    f(1%S2%s3@2%S4@%s5, eins,  zwei ) =1eins2 zwei 3zwei4eins5;
    f(1%-2C2%3C3@2%3.2C4, eins,  zwei ) =1ei2 zw3zwe4;
    f(1@F1%s2@f2%s3@F3%s4, eins,  zwei ) =1fEins2fZwei3fDrei4;
$/tstF/ */
    call tst t, 'tstF'
    call tstF1 '1\s23%s345%s67\%8'
    call tstF1 '1\S23%s345%S67\%8'
    call tstF1 '1\s23%s345%s67\%8'
    call tstF1 '1\s23%s345%S67\%8'
    call tstF1 '1%S2%s3@2%S4@%s5'
    call tstF1 '1%-2C2%3C3@2%3.2C4'
    call tstF1 '1@F1%s2@f2%s3@F3%s4'
    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

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 fmtFTab abc, b
    call fmtFAddFlds fmtFReset(abc), oFlds(m.st.1)
    m.abc.1.tit = 'c3L'
    m.abc.2.fmt = 'e'
    m.abc.3.tit = 'drei'
    m.abc.4.fmt = 'l7'
    call fmtFWriteSt abc, b'.BUF'
    call tstEnd t
    return
endProcedure tstFmt


tstfmtUnits: procedure
/*
$=/tstFmtUnits/
    ### start tst tstFmtUnits #########################################
    .            .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
$/tstFmtUnits/ */
    call jIni
    call tst t, "tstFmtUnits"
    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) ,
                 '==>' fmtTime(   word(lst, wx)   ) ,
                 '++>' fmtTime(   word(lst, wx), 1),
                 '-+>' fmtTime('-'word(lst, wx),  ),
                 '-->' fmtTime('-'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) ,
                 '==>' fmtDec(    word(lst, wx)   ) ,
                 '++>' fmtDec(    word(lst, wx), 1),
                 '-+>' fmtDec('-'word(lst, wx),   ),
                 '-->' fmtDec('-'word(lst, wx), 1)
        end
    call tstEnd t
    return
endProcedure tstfmtUnits

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 scanReadIni
    call tst t, 'tstScanRead'
    b = jBuf('erste Zeile  ',,'  dritte Zeile  schluss  ')
    s = jOpen(scanRead(b), m.j.cRead)
    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

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 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), m.j.cRead)
    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, m.j.cRead))
    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

tstScanSqlStmt: procedure expose m.
/*
$=/tstScanSqlStmt/
    ### start tst tstScanSqlStmt ######################################
    --info 0: last token  scanPosition select -- $ä c1                /+
    * c1 $ö\npos 1 in line 1: select -- $ä c1
    cmd1 select current time                stamp from s.1
    cmd2 .
    cmd3 .
    --info 3: last token ; scanPosition update ";--""'$ä";;       delet+
    e '$ä''"'\npos 2 in line 7: ;update ";--""'$ä";;       del
    cmd4 update ";--""'$ä"
    cmd5 .
    cmd6 delete '$ä''"' .
    --info end: last token  scanPosition \natEnd after line 9: $äc8 $ö
$/tstScanSqlStmt/ */
    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 ''/*''''"''  -- c7', '/*c8 */   ')
    s = jOpen(scanOpts(scanWin(b, , , 1, 30), , , '--'), m.j.cRead)
    call tstOut t, '--info 0:' scanInfo(s)
    do sx=1 while scanSqlStmt(s)
        call tstOut t, 'cmd'sx m.s.val
        if sx=3 then call tstOut t, '--info 3:' scanInfo(s)
        end
    call tstOut t, '--info end:' scanInfo(s)
    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 = jOpen(scanSql(b), m.j.cRead)
    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), m.j.cRead)
    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), m.j.cRead)
    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), m.j.cRead)
    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), m.j.cRead)
    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
        m.m.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 <> 1 then
                call tstErr m, 'm.pipe.0' m.pipe.0 '<> 1'
            call pipeBeLa m.j.cRead 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 = repAll(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
   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
        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
    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
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
    oldOut = outDst(jOpen(oNew('JStem', mCut(tstErrHandler, 0)), '>'))
    call errSay ggTxt
    call outDst oldOut
    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)",
             , "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 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
/* copy time end -----------------------------------------------------*/
/* copy fmt    begin **************************************************/
/*--- format the first arg by the format in the second ---------------*/
fmt: procedure
parse arg v, f 2 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
fmtTime: procedure expose m.
parse arg s, signed
    return fmtUnits(s, 't', signed==1)
endProcedure fmtTime

fmtDec: procedure expose m.
parse arg s, signed
    return fmtUnits(s, 'd', signed==1)
endProcedure fmtDec

fmtUnits: procedure expose m.
parse arg s, scale, signed
    if s >= 0 then
        res = fmtUnitsNN(s, scale, wi)
    else
        res = '-'fmtUnitsNN(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 fmtUnits

fmtUnitsNN: 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 'fmtUnitsNN 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 fmtUnitsNN

fmtIni: procedure expose m.
    if m.fmt.ini == 1 then
        return
    m.fmt.ini = 1
    call fmtIniUnits 't', '?smhd', 0 100 60 60 24 100, 0.01, 2
    call fmtIniUnits 'd', '?.kMGTPE', 0 copies('1000 ',8), 0.001, 3
    return
endProcedure fmtIni

fmtIniUnits: 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 'fmtIniUnits 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 fmtIniUnits

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

fmtFTab: procedure expose m.
parse arg m, rdr, wiTi
    if m == '' then
        m = 'FMTF.F'
    return fmtFWriteSt(fmtFReset('FMTF.F'), env2buf(rdr)'.BUF', wiTi)
endProcedure fmtFTab

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

fmtFWriteSt: procedure expose m.  ?????????
parse arg m, st, wiTi
    if m.st.0 < 1 then
        return 0
    if m.m.0 < 1 then
        call fmtFAddFlds m, oFlds(m.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, m.st.sx)
        end
    return st.0
fmtFWriteSt

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 = m.st.sx || suf
        v = m.f
        aMa = max(aMa, length(v))
        if \ dataType(v, 'n') then do
            aCnt = aCnt + 1
            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
        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
***********************************************************************/
/***** 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.stem.0 = 0
    m.comp.idChars = m.scan.alfNum'@_'
    call compIniKI '=', "skeleton", "expression or block"
    call compIniKI '.', "object", "expression or block"
    call compIniKI '-', "string", "expression or block"
    call compIniKI '@', "shell", "pipe or $;"
    call compIniKI ':', "assignAttributes", "assignment or statement"
    call compIniKI '|', "assignTable", "header, sfmt or expr"
    call compIniKI '#', "text", "literal data"
    return
endProcedure compIni

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.chKin2 = '.-=#;:|'
    m.m.chKinC = '.-=@'
    m.m.chOp = '.-<@|?'
    m.m.chOpNoFi = '.-@|?'
    return m
endProcedure compReset

compIniKI: procedure expose m.
parse arg ki, m.comp.kind.ki.name, m.comp.kind.ki.expec
return

/*--- constructor of Compiler ----------------------------------------*/
comp: procedure expose m.
parse arg src
    nn = oNew('Compiler')
    if src \== '' then
        m.nn.cmpRdr = o2File(src)
    else
        m.nn.cmpRdr = ''
    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 pipeBeLa '>' ouO
    call oRun r
    if ouO \== '' then
        call pipeEnd
    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
    kind = '@'
    spec = strip(spec)
    do while pos(left(spec, 1), m.m.chKind) > 0
       kind = left(spec, 1)
       spec = strip(substr(spec, 2))
       end
    call scanSrc m.m.scan, spec
    m.m.compSpec = 1
    res = compCUnit(m, kind, 1)
    do while abbrev(m.m.dir, '$#')
        call envPutO substr(m.m.dir, 3, length(m.m.dir)-4),
            , compCUnit(m, right(m.m.dir, 1))
        end
    if \ m.m.compSpec then
        call jClose m.m.scan
    return res
endProcedure compile

/*--- cUnit = compilation Unit = separate compilations
              no nesting| --------------------------------------------*/
compCUnit: procedure expose m.
parse arg m, ki, isFirst
    s = m.m.scan
    code = ''
    do forever
        m.m.dir = ''
        src = compUnit(m, ki, '$#')
        if \ compDirective(m) then
            return scanErr(s, m.comp.kind.ki.expec "expected: compile",
                 m.comp.kind.ki.name "stopped before end of input")
        if \ compIsEmpty(m, src) then do
                /*wkTst??? allow assTb in separatly compiled units */
            if isFirst == 1 & m.src.type == ':' ,
              & pos(' ', src) < 1 &  abbrev(src, 'COMP.AST.') then
                call mAdd src, '', ''
            code = code || ';'compAst2code(m, src, ';')
            end
        if m.m.dir == 'eof' then do
            if \ m.m.compSpec | m.m.cmpRdr == '' then
                return oRunner(code)
            call scanReadReset s, m.m.cmpRdr
            call jOpen s, m.j.cRead
            m.m.compSpec = 0
            end
        else if length(m.m.dir) == 3 then
            ki = substr(m.m.dir, 3, 1)
        else
            return oRunner(code)
        end
endProcedure compCUnit

/*--- directives divide cUnits ---------------------------------------*/
compDirective: procedure expose m.
parse arg m
    m.m.dir = ''
    s = m.m.scan
    lk = scanLook(s)
    cx = pos('#', lk, 3)
    if \ abbrev(lk, '$#') then do
        if \ scanAtEnd(m.m.scan) then
            return 0
        m.m.dir = 'eof'
        return 1
        end
    else if scanLit(s, '$#end' , '$#out') then do
        m.m.dir = 'eof'
        return 1
        end
    else if pos(substr(lk, 3, 1), m.m.chKinD) > 0 then do
        m.m.dirKind = substr(lk, 3, 1)
        m.m.dir = left(lk, 3)
        end
    else if cx > 3 & pos(substr(lk, cx+1, 1), m.m.chKinD) > 0 then do
        m.m.dirKind = substr(lk, 3, 1)
        m.m.dir = left(lk, cx+1)
        end
    else
        call scanErr s, 'bad directive:' word(lk, 1)
    if \ scanLit(s, m.m.dir) then
            call scanErr m.m.scan, 'directive mismatch' m.m.dir
    return 1
endProcedure compDirective

/**** parse the whole syntax *******************************************
          currently, with the old code generation,
              parsing and code generation is intermixec
              migrating to AST should will separate these tasks
***********************************************************************/
compUnit: procedure expose m.
parse arg m, kind, stopper
    s = m.m.scan
    if pos(kind, m.m.chKind';') < 1 then
        return scanErr(s, 'bad kind' kind 'in compUnit(...'stopper')')
    if stopper == '}' then do
        if kind \== '#' then do
            one = compExpr(m, 'b', translate(kind, ';', '@'))
            if compisEmpty(m, one) then
                return compAST(m, 'block')
            else
                return compAST(m, 'block', one)
            end
        tx = '= '
        cb = 1
        do forever /* scan nested { ... } pairs */
            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
        return compAst(m, 'block', tx)
        end
    else if pos(kind, '.-=') > 0 then do
        return compData(m, kind)
        end
    else if pos(kind, '@;') > 0 then do
        call compSpNlComment m
        return compShell(m)
        end
    else if kind == '|' | kind == ':' then do
        if kind == '|' then
            res = compAssTab(m)
        else
            res = compAssAtt(m)
        if abbrev(res, '#') then
            return compAst(m, ':', substr(res, 3))
        else
            return compAst(m, ';', substr(res, 3))
        end
    else if kind == '#' then do
        res = compAST(m, 'block')
        call compSpComment m
        if \ scanReadNl(s) then
            call scanErr s,
                , 'space nl expected in heredata until' stopper
        do while \ abbrev(m.s.src, stopper)
            call mAdd res, '=' strip(m.s.src, 't')
            if \ scanReadNl(s, 1) then do
                if stopper = '$#' then
                    leave
                call scanErr s, 'eof in heredata until' stopper
                end
            end
        return res
        end
endProcedure compUnit

/*--- compile data lines return stmts or expr ------------------------*/
compData: procedure expose m.
parse arg m, ki
    s = m.m.scan
    lines = compAST(m, 'block')
    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 lines
endProcedure compData

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

/*--- 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 length(type) \== 1 | pos(type, 'dsbw') < 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,
            , if(type=='w', m.m.chNotWord,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, 1)
            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

/*--- compile a primary and return code ------------------------------*/
compPrimary: procedure expose m.
parse arg m, ki, withChain
    s = m.m.scan
    if \ scanLit(s, '$') then
        return ''
    if scanString(s) then     /*wkTst??? brauchts beides? */
        return translate(ki, '.--', '@;=')'=' m.s.val
    if withChain then do
        if scanLit(s, '.', '-') then do
            op = m.s.tok
            return op'('compCheckNN(m, compObj(m, op),
                , 'objRef expected after $'op)
            end
        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

compObj: procedure expose m.
parse arg m, ki
    s = m.m.scan
    pk = compOpKi(m, '?')
    one = compBlock(m, ki pk)
    if one \== '' then
        return compAstAddOp(m, one, ki)
    pp = ''
    if pk \== '' then do
        ki = right(pk, 1)
        pp = left(pk, length(pk)-1)
        end
    one = compPrimary(m, translate(ki, '.', '@'), 0)
    if one \== '' then
        return pp || one
    if ki == '.' then do
        if scanLit(s, 'compile') then do
            if pos(scanLook(s, 1), m.m.chKinC) < 1 then
                call scanErr s, 'compile kind expected'
            call scanChar s, 1
            return pp'. compile(comp(env2Buf()), "'m.s.tok'")'
            end
        end
    call scanBack s, pk
    return ''
endProcedure compObj

compFile: procedure expose m.
parse arg m
    res = compCheckNE(m, compExprBlock(m, '='),
        , 'block or expr expected for file')
    if \ abbrev(res, '.') then do
        end
    else if substr(res, verify(res, '.', n), 3) == '0* ' then do
        st = word(res, 2)
        if m.st.0 = 1 & abbrev(m.st.1, '. envGetO(') then
                /* if undefined variable use new jbuf */
            if pos(')', m.st.1) == length(m.st.1) then
                m.st.1 = left(m.st.1, length(m.st.1)-1) ,
                         || ", '-b')"
        end
    return compASTAddOp(m, res, '<')
endProcedure compFile

/*--- scan an operator chain and a kind ------------------------------*/
compOpKi: procedure expose m.
parse arg m, opt
    s = m.m.scan
    op = ''
    if opt == '<' then do
        call scanVerify s, m.m.chOpNoFi
        op = m.s.tok
        if scanLit(s, '<') then
            return op'<'
        end
    call scanVerify s, m.m.chOp
    op = op || m.s.tok
    k1 = scanLook(s, 1)
    if k1 \== '' & pos(k1, m.m.chKind) > 0 then do
        call scanLit s, k1
        return op || k1
        end
    if opt == '?' | op == '' | pos(right(op, 1), m.m.chKind) > 0 then
        return op
    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
    pk = compOpKi(m, '<')
    if right(pk, 1) == '<' then
        return compAstAddOp(m, compFile(m), pk)
    res = compBlock(m, ki pk)
    if res \== '' then
        return res
    if pk \== '' then
        lk = right(pk, 1)
    else
        lk = translate(ki, '.', '@')
    res = compExpr(m, 's', lk)
    if res \== '' then
        return compASTAddOp(m, res, pk)
    call scanBack s, pk
    return res
endProcedure compExprBlock

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

/*--- compile a single statement -------------------------------------*/
compStmt: procedure expose m.
parse arg m
    s = m.m.scan
    if scanLit(s, "$=") then do
        res = compAss(m)
        if res == '' then
            call scanErr s, 'assignment expected after $='
        return res
        end
    if scanLit(s, '$@') then do
        if \ scanName(s) then
            return 'l;' comp2Code(m,
                , '@'compCheckNE(m, compExprBlock(m, '@'),
                , "block or expr expected after $@"))
        fu = m.s.tok
        if fu == 'for' | fu == 'with' | fu == 'forWith' then do
            v = comp2Code(m, '-'compCheckNE(m, compExpr(m, 'b', '='),
                   , "variable name after $@for"))
            call compSpComment m
            st = comp2Code(m, ';'compCheckNN(m, compStmt(m, 'with'),
                     , "statement after $@for" v))
            if fu == 'forWith' then
                st = 'call envSetWith envGetO('v');' st
            if abbrev(fu, 'for') then
                st = 'do while envReadO('v');' st'; end'
            if fu == 'forWith' then
                st = 'call envPushWith "";' st '; call envPopWith'
            else if fu == 'with' then
                st = 'call envPushName' v';' st '; call envPopWith'
            return ';' st
            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 = ''
            call compSpComment m
            suf = compExpr(m, 's', ';')
            if \ compIsEmpty(m, suf) then
                suf = comp2Code(m, ':'suf)
            else if var \== '' then
                call scanErr s, "$@do control construct expected"
            else
                suf = ''
            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 compInter('return' comp2Code(m, '-'nm)), st
            return '; '
            end
        if scanLit(s, '(') then do
            call compSpComment m
            if \ scanLit(s, ')') then
                call scanErr s, 'closing ) expected after $@'fu'('
            return '; call oRun envGetO("'fu'")'
            end
        if scanLit(s, '{', '.{', '-{', '={') then do
            br = m.s.tok
            a = compExpr(m, 'b', if(br == '{', '-', left(br, 1)))
            if \ scanLit(s, '}') then
                call scanErr s, 'closing } expected after $@'fu || br
            res = '; call oRun envGetO("'fu'")'
            if pos(left(a, 1), 'ec') < 1 then
                res = res',' comp2code(m, a)
            return res
            end
        call scanErr s, 'procCall, for, do, ct, proc' ,
                 'or objRef expected after $@'
        end
    if scanLit(s, '$$') then
        return  compCheckNN(m, compExprBlock(m, '='),
            , 'block or expression expected after $$')
    return ''
endProcedure compStmt

compAss: procedure expose m.
parse arg m, aExt
    s = m.m.scan
    sla = scanLook(s)
    slx = verify(sla, m.m.chKind'/'m.m.chOp, 'n')
    if slx > 0 then
       sla = left(sla, slx-1)
    sla = pos('/', sla) > 0
    nm = ''
    if \ sla then do
        nm = compExpr(m, 'b', '=')
        if compIsEmpty(m, nm) then
            return ''
        nm = comp2Code(m, '-'nm)
        if \ scanLit(s, "=") then
            return scanErr(s, '= expected after $=' nm)
        end
    m.m.bName = ''
    vl = compCheckNE(m, compExprBlock(m, '='),
        , 'block or expression after $=' nm '=')
    if sla then
        if m.m.bName == '' then
            call scanErr s, 'missing blockName'
        else
            nm = "'"m.m.bName"'"
    va = compAstAftOp(m, vl)
    if va \== '' & m.va.type == ':' then do
        pu = "call envPushName" nm
        if abbrev(m.m.astOps, '<') then
            call mAdd va, pu ", 'asM'", "call envPopWith"
        else if abbrev(m.m.astOps, '<<') then
            call mAdd va, pu ", 'asM'", "call envPopWith"
        else
            call mAdd va, pu ", 'as1'", "call envPopWith"
        return va
        end
    if compAstKind(m, vl) == '-' then
        return '; call envPut' nm',' comp2Code(m, vl)aExt
    else
        return '; call envPutO' nm',' comp2Code(m, '.'vl)aExt
endProcedure compAss

/*--- block deals with the correct kind and operators
      the content is parsed by compUnit ------------------------------*/
compBlock: procedure expose m.
parse arg m, dKi ops
    s = m.m.scan
    if \ scanLit(s, '{', '[', '/') then
        return ''
    start = m.s.tok
    if (ops \== '' & pos(right(ops, 1), m.m.chKind) < 1) ,
        | pos(dKi, m.m.chKind) < 1 then
        return scanErr(s, 'bad kind' ops 'for block (def' dKi')')
    if ops == '' then do
        ki = dKi
        end
    else do
       ki = right(ops, 1)
       ops = left(ops, length(ops)-1)
       end
    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
    res = compUnit(m, ki, stopper)
    if \ scanLit(s, stopper) then do
        if pos(ki, ':|') < 1 | \ abbrev(stopper, '$') then
            call scanErr s, 'ending' stopper 'expected after' starter
        else if \ scanLit(s, substr(stopper, 2)) then
            call scanErr s, 'ending' stopper 'or' substr(stopper, 2),
                    'expected after' starter
        end
    if abbrev(starter, '/') then
        m.m.bName = substr(starter, 2, length(starter)-2)
    else
        m.m.bName = ''
    if m.res.text == '' then
        m.res.text = ' '
    return compAstAddOp(m, res, ops)
endProcedure compBlock

compAssAtt: procedure expose m. aClass
parse arg m
    res = ''
    aClass = ''
    s = m.m.scan
    last = ''
    do forever
        if compSpNlComment(m, '*') then do
            end
        else if pos(scanLook(s, 1), '/]}') > 0 then do
            leave
            end
        else if scanLit(s, ';', '$;') then do
            if last = ';' then
                res = res'; call envWithNext'
            last = ';'
            end
        else do
            s1 = compAss(m, ", 1")
            if s1 == '' then do
                s1 = compStmt(m)
                if s1 == '' then
                    leave
                end
            else do
                if last == ';' then
                    res = res'; call envWithNext'
                last = 'a'
                end
            res = res';' comp2code(m, ';'s1)
            end
        if res ==  '' then
            res = ';'
        end
    if last == '' then
        return res
    else
        return '# call envWithNext "b";' res ,
               '; call envWithNext "e";'
endProcedure compAssAtt

compAssTab: procedure expose m. aClass
parse arg m
    s = m.m.scan
    call compSpNlComment m, '*'
    hy = 0
    tab = ''
    do forever
        bx = m.s.pos
        if \ scanName(s) then
            leave
        hx = hy + 1
        h.hx.beg = bx
        if hx > 1 & bx <= h.hy.end then
            call scanErr s, 'header overlap' m.s.tok 'pos' bx
        h.hx = m.s.tok
        tab = tab', f' m.s.tok 'v'
        h.hx.end = m.s.pos
        hy = hx
        call compSpComment m, '*'
        end
    if tab \== '' then
       aClass = classNew('n* Ass u' substr(tab, 3))
    res = ''
    isFirst = 1
    do while scanReadNl(s)
        do forever
            call compSpNlComment m, '*'
            s1 = compStmt(m)
            if s1 == '' then
                leave
            res = res';' comp2code(m, ';'s1)
            last = 's'
            end
        if pos(scanLook(s, 1), '/]}') > 0 then
            leave

        do qx=1
            bx = m.s.pos
            s1 = compExpr(m, 'w', '=')
            if compIsEmpty(m, s1) then
                leave
            ex = m.s.pos
            if ex <= bx then
                return scanErr(s, 'colExpr backward')
            do hy=1 to hx while bx >= h.hy.end
                end
            hz = hy+1
            if hz <= hx & ex > h.hz.beg then
                call scanErr s, 'value on hdr' h.hy 'overlaps' h.hz
            else if hy > hx | bx >= h.hy.end | ex <= h.hy.beg then
                call scanErr s, 'value from' bx 'to' ex ,
                    'no overlap with header' h.hy
            if qx > 1 then
                nop
            else if isFirst then do
                res = res"; call envWithNext 'b', '"aClass"'"
                isFirst = 0
                end
            else
                res = res"; call envWithNext"
            res = res"; call envPut '"h.hy"'," comp2Code(m, "-"s1)", 1"
            call compSpComment m, '*'
            end
        end
    if isFirst then
        return res
    else
        return '#' res"; call envWithNext 'e'"
endProcedure compassTab

/**** lexicals ********************************************************/
/*--- 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, xtra
    s = m.m.scan
    sp = 0
    co = 0
    do forever
        if scanVerify(s, m.m.chSpa) then
            sp = 1
        else if compComment(m) then
            co = 1
        else if xtra == '' then
            leave
        else if \ scanLit(s, xtra) then
            leave
        else do
            co = 1
            m.s.pos = 1+length(m.s.src)
            end
        end
    m.m.gotComment = co
    return co | sp
endProcedure compSpComment

/*--- skip spaces, NLs and comments ----------------------------------*/
compSpNlComment: procedure expose m.
parse arg m, xtra
    found = 0
    do forever
        if compSpComment(m, xtra) then
            found = 1
        else if scanReadNl(m.m.scan) then
            found = 1
        else
            return found
        end
endProcedure compSpComment
/**** 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, ex
    if pos(' ', ex) < 1 & pos('COMP.AST.', ex) > 0 then do
         a = substr(ex, pos('COMP.AST.', ex))
         a = compAstAftOp(m, a)
         if m.a.type = 'block' then
             return 0 /* m.a.0 == 0 */
         else
             return m.a.text == ''
         end
    e1 = word(ex, 1)
    return ex = '' | verify(e1, 'ec', 'm') > 0
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
***********************************************************************/
/*--- create a new AST -----------------------------------------------*/
compAST: procedure expose m.
parse arg m, tp
    n = mNew('COMP.AST')
    m.n.type = tp
    if wordPos(tp, 'block') > 0 then do
        do cx=1 to arg()-2
            m.n.cx = arg(cx+2)
            end
        m.n.0 = cx-1
        end
    else do
        m.n.text = arg(3)
        m.n.0 = 0
        end
    m.a.isAnnotated = 1
    return n
endProcedure compAST

/*--- add operandands to an AST -------------------------------------*/
compASTAddOp: procedure expose m.
parse arg m, a, ops
    if ops == '' then
        return a
    if pos('COMP.AST.', a) < 1 then
        return ops || a
    if m.a.type = 'ops' then do
        m.a.text = ops || m.a.text
        return a
        end
    n = compAst(m, 'ops', ops)
    call mAdd n, a
    return n
endProcedure compAstAddOp

/*--- return the first AST after the operand chain
          put the operands into m.m.astOps ---------------------------*/
compASTaftOp: procedure expose m.
parse arg m, a
    m.m.astOps = ''
    if \ abbrev(a, 'COMP.AST.') then
        return ''
    do while m.a.type == 'ops'
        m.m.astOps = m.a.text || m.m.astOps
        a = m.a.1
        end
    return a
endProcedure compASTAftOpType

/*--- 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.type == '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

/*--- return the code for an AST with operand chain trg --------------*/
compAst2Code: procedure expose m.
parse arg m, a, aTrg
    if pos(' ', a) > 0 | \ abbrev(a, 'COMP.AST.') then
        return comp2Code(m, aTrg || a)
    if \ abbrev(a, 'COMP.AST.') then
        call err 'bad ast' a
    do while m.a.type == 'ops'
        aTrg = aTrg || m.a.text
        a = m.a.1
        end
    trg = compAstOpsReduce(m, aTrg)
    if m.a.type == translate(right(trg, 1), ';', '@') then do
        if length(trg) == 1 then do
            if pos(trg, ';@') > 0  then
                return 'do;' m.a.text ';end'
            else
                return m.a.text
            end
        else
            return compAST2Code(m, a, left(trg, length(trg)-1))
        end
    if m.a.type == 'block' then do
        op = right(trg, 1)
        tLe = left(trg, length(trg)-1)
        call compASTAnnBlock m, a
        if pos(m.a.maxKind, '.-<') > 0 & pos(op, '.-|?') > 0 then do
            if m.a.0 = 1 then do
                o1 = if(op=='-', '-', '.')
                r = compAst2Code(m, m.a.1, o1)
                r = compC2C(m, o1, compAstOpsReduce(m, tLe||o1), r)
                if pos(op, '.-<') > 0 then
                    return '('r')'
                else
                    return r
                end
            if m.a.0 = 0 & op == '?' then
                return compC2C(m, '.', compAstOpsReduce(m, tLe'.'))
            if op == '-' then do
                cd = ''
                do cx = 1 to m.a.0
                    cd = cd '('compAst2Code(m, m.a.cx, '-')')'
                    end
                return compC2C(m, '-', trg, substr(cd, 2))
                end
            call scanErr m.m.scan, 'bad block cardinality' aTrg
            end
        cd = ''
        do cx = 1 to m.a.0
            cd = cd';' compAst2Code(m, m.a.cx, ';')
            end
        if right(trg, 1) == '@' then
            trg = overlay(';', trg, length(trg))
        return compC2C(m, ';', trg, 'do;' cd'; end')
        end
    else if m.a.type == ';' then do
        return compC2C(m, ';', trg, m.a.text)
        if right(trg, 1)  == '-' then
            return compAst2Code(m, "- o2String('"oRunner(m.a.text)"')",
                , trg)
        if right(trg, 1)  == '<' then
            return compAst2Code(m, "< o2File('"oRunner(m.a.text)"')",
                , trg)
        end
    else if m.a.type == ':' then do
        if m.a.0 = 0 then
            call mAdd a, 'call envPushWith', 'call envPopWith'
        return compC2C(m, ';', trg,
            , 'do;' m.a.1';' m.a.text';' m.a.2'; end')
        end
    call scanErr m.m.scan, 'implement type' m.a.type 'for' a 'trg' trg
endProcedure compAst2Code

/*--- do a chain of code transformations
          from code of kind fr by opList


    op  as from kind               operand
     =  constant                   -
     -  rexx string Expr           cast to string/ concat file/output
     .  rexx object Expr           cast to object
     <  rexx file   Expr           cast to file
     ;  rexx Statements            execute, write obj, Str
     @  -                          cast to ORun, run an obj, write file
     |  -                          extract exactlyOne
     ?  -                          extract OneOrNull
----------------------------------------------------------------------*/

compC2C: procedure expose m.
parse arg m, fr, opList, code
oldCode = fr':' code '==>' opList '==>'
    do tx=length(opList) by -1 to 1
        to = substr(opList, tx, 1)
        if fr == to then
            iterate
        nn = '||||'
        if to == '-' then do
            if fr == '=' then
                 nn = quote(code)
            else if abbrev(fr code, '. envGetO(') then
                nn =  'envGet(' || substr(code, 9)
            else if fr == ';' then
                nn = "o2String('"oRunner(code)"')"
            else if pos(fr, '.<') > 0 then
                nn = "o2String("code")"
            end
        else if to == '.' then do
            if fr == '=' then
                 nn = quote(s2o(code))
            else if abbrev(fr expr, '- envGet(') then
                nn = 'envGetO('substr(expr, 8)
            else if fr == '-' then
                nn = 's2o('code')'
            else if fr == '<' then
                 nn = code
            else if fr == ';' then
                nn = quote(oRunner(code))
            end
        else if to == '@' then do
            if fr == '.' then
                nn = 'call oRun' code
            else if fr == '<' then
                nn = 'call pipeWriteAll' code
            else if fr == ';' then
                nn = code
            to = ';'
            end
        else if to == ';' then do
            if fr == '=' then
                nn = 'call out' quote(code)
            else if fr == '-' then
                nn = 'call out' code
            else if fr == '.' | fr == '<' then
                nn = 'call outO' code
            end
        else if to == ':' then do
            if fr == '=' then
                 nn = quote(code)
            else
                nn = code
            to = ';'
            end
        else if to == '<' then do
            if fr == '-' then
                 nn = 'file('code')'
            else if fr == '=' then
                 nn = "file("quote(code)")"
            else if fr == '.' then
                nn = 'o2File('code')'
            else if fr == ';' then
                nn = 'o2File('oRunner(code)')'
            end
        else if to == '|' | to == '?' then do
            if fr == '<' | fr == '.' then
                nn = 'fileSingle('code if(to == '|','', ", ''")')'
            else if fr == '@' | fr == ';' then
                      /* ???wkTst optimize: do it directly */
                nn = compC2C(m, fr, to'<', code)
            to = '.'
            end
        if nn == '||||' then
            return scanErr(m.m.scan,
                ,'compC2C bad fr' fr 'to' to 'list' opList)
        fr = to
        code = nn
        end
    return code
endProcedure compC2C

/*--- reduce a chain of operands -------------------------------------*/
          eliminate duplicates and identity transformations ----------*/
compAstOpsReduce: procedure expose m.
parse arg m, ops
    ki = ops
    ki  = space(translate(ops, ' ', 'e('), 0)
    fr = ';<; <;< -.- <@<'
    to = ';   <   -   <  '
    fr = fr '== -- .. << ;; @@ @('
    to = to '=  -  .  <  ;  @  (@'
    wc = words(fr)
    do until ki = oldKi
        oldKi = ki
        do wx=1 to wc
            do forever
                wf = word(fr, wx)
                cx = pos(wf, ki)
                if cx < 1 then
                    leave
                ki = left(ki, cx-1) || word(to, wx) ,
                                    || substr(ki, cx+length(wf))
                end
            end
        end
    return ki
endProcedure compASTOpsReduce

/*--- annotate a block if necessary ----------------------------------*/
compASTAnnBlock: procedure expose m.
parse arg m, a
    if m.a.isAnnotated == 1 then
        return
    mk = ''
    do cx=1 to m.a.0
       c = m.a.cx
       if pos(' ', c) > 0 | \ abbrev(c, 'COMP.AST.') then
           ki = left(c, 1)
       else if \ abbrev(c, 'COMP.AST.') then
           return scanErr(m.m.scan, 'bad ast' c 'parent' a) /0
       else
           call scanErr m.m.scan, 'implement kind of' c 'type' m.c.type
       if pos(ki, '=-.<;@:|') < 1 then do
           if pos(ki, 'el0') < 1 then
               call err 'bad kind' ki
           end
       else if mk == '' | pos(ki, '=-.<;@:|') > pos(mk, '=-.<;@:|') then
           mk = ki
       end
    m.a.maxKind = mk
    m.a.isAnnotated = 1
    return
endProcedrue compASTAnnBlock
/**** old code generator ***********************************************
          plan is to replace it with AST ******************************/
/*--- 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
    if expr == '' & pos(' ', ki) < 1 & pos('COMP.AST.', ki) > 0 then do
         cx = pos('COMP.AST.', ki)
         return compAst2Code(m, substr(ki, cx), left(ki, cx-1))
         end
    /* 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 | (to == '*' & fr == '*') then do
            opt = to
            to = substr(ki, length(ki)-2, 1)
            end
        toBef = to
        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 == '.' | fr == '<' then
                nn = 'call outO' expr
            else if fr == '#' then
                nn = 'call envPushWith ;'expr'; call envPopWith'
            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
        else if to == '|' | to == '?' then do
            if fr == '<' | fr == '.' then do
                nn = 'fileSingle('expr if(to == '|','', ", ''")')'
                to = '.'
                end
            else if fr == '@' | fr == ';' then do
                to = to'<'fr
                nn = expr
                end
            end
        if nn == '||||' then
            return scanErr(m.m.scan,
                ,'comp2code bad fr' fr 'to' toBef '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; -.- -;- .-. .;. ;<; ;<( <(; @(- @(l |(l ?(l'
    to.3 = ' 0;  l;   -   -   .   .   ; ;<  <;  ;(- ;(l (|l (?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 then do
        if trgt == '|' | trgt == '?' then
            return left(m.st.1, 1)  comp2Code(m, m.st.1)
        else if trgt \== '<' then
            return trgt comp2Code(m, trgt || m.st.1)
        end
    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

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

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

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

/*--- 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 jIni
    call classNew 'n ScanWin u JRW', 'm',
        , 'jReset call scanWinReset m, arg, arg2, arg3',
        , 'jOpen call scanWinOpen m, arg(3) ',
        , 'jClose call scanWinClose m ',
        , 'scanReadNl return scanWinNl(m, unCond)',
        , 'scanSpaceNl scanWinSpaceNl(m)',
        , 'scanInfo scanWinInfo(m)',
        , 'scanPos  scanWinPos(m)'
    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, 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 scanWinOpts

/*--- 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.
parse arg 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
        call scanWinRead m
        if scanVerify(m, ' ') then do
            res = 1
            iterate
            end
        else 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
            res = 1
            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
                return res
            m.m.pos = np
            res = 1
            end
        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
    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 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)  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 noSp, use scanNum instead'
    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
    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

/*--- scan a sql number with a unit which may follow without space ---*/
scanSqlStmt: procedure expose m.
parse arg m, delim
    if delim == '' then
        delim = ';'
    res = ''
    vChrs = strip('''"/'delim || left(m.m.scanComment, 1))
    do forever
        if scanSpaceNl(m) then
            if right(res, 1) \== ' ' then
                res = res' '
        if scanVerify(m, vChrs, 'm') then
            res = res || m.m.tok
        else if scanString(m) then
            res = res || m.m.tok
        else if scanLit(m, delim) then do
            m.m.val = res
            return 1
            end
        else if scanChar(m, 1) then do
            res = res || m.m.tok
            end
        else do
            m.m.val = res
            return res \= ''
            end
        end
endProcedure scanSqlStmt
/* 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 = 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

/*--- 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 classNew "n PipeFrame u"
    call mapReset env.vars
    m.env.with.0 = 0
    call mapReset env.c2w
    call mNewArea 'ENV.WICO', '='
    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
    call jOpen m.e.in, m.j.cRead
    if m.e.out == '' then
        m.e.out = m.j.out
    call jOpen m.e.out, m.e.outOp
    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()
    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.j.cRead)
    m.f.out = jOpen(Cat(), '>')
    m.j.in = m.f.in
    m.j.out = m.f.out
    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
    if m.f.out == '' then do
        preX = px-1
        preF = m.pipe.preX
        m.f.out = m.preF.out
        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

pipeFrame: procedure expose m.
     m = oMutate(mBasicNew("PipeFrame"), "PipeFrame")
     m.m.in = ''
     m.m.inCat = 0
     m.m.out = ''
     m.m.outOp = '>'
     return m
endProcedure pipeFrame

pipeClose: procedure expose m.
parse arg m, finishLazy
    call jClose m.m.in
    call jClose m.m.out
    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

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

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

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

env2Buf: procedure expose m.
    parse arg rdr
    if envInp(rdr) then
        return jBuf(ggStr)
    if classInheritsOf(ggCla, class4Name('JBuf')) ,
            & m.ggObj.jUsers < 1 then
        return ggObj
    b = jOpen(jBuf(), m.j.cWri)
    call jWriteNow b, o2File(ggObj)
    return jClose(b)
endProcedure env2Buf
/*--- return true iff input is a kind of string  ---------------------*/
envInp: procedure expose m. expose ggStr ggObj ggCla
    parse arg inp
    if inp == '' then
        inp = m.j.in
    return oStrOrObj(inp)
endProcedure envInp

envIsDefined: procedure expose m.
parse arg na
    return   '' \== mapValAdr(env.vars, na)
endProcedure envIsDefined

envPushWith: procedure expose m.
parse arg obj, cl, fn, elCl
    tos = m.env.with.0 + 1
    m.env.with.0 = tos
    m.env.with.tos.fun = fn
    m.env.with.tos.muElCl = ''
    if fn == '' then do
        call envSetWith obj, cl
        return
        end
    if cl == '' then
        cl = objClass(obj)
    if fn == 'as1' then do
        call envSetWith obj, cl
        m.env.with.tos.muElRef = m.cl.valueCl \== '',
                               & m.cl.valueCl \== m.class.classV
        if m.env.with.tos.muElRef then
            m.env.with.tos.muElCl = m.cl.valueCl
        else
            m.env.with.tos.muElCl = cl
        return
        end
    else if fn \== 'asM' then
        call err 'bad fun' fn
    if m.cl.stemCl == '' then
        call err 'class' className(cl) 'not stem'
    cc = m.cl.stemCl
    isRef = m.cc == 'r'
    m.env.with.tos.muElRef = isRef
    if m.cc \== 'r' then
        m.env.with.tos.muElCl = cc
    else if elCl \== '' then
        m.env.with.tos.muElCl = elCl
    else if m.cc.class == '' then
        call err 'elCl null for envPushWith('obj ','cl ','multi', ...)'
    else
        m.env.with.tos.muElCl = m.cc.class
    m.env.with.tos.class = ''
    m.env.with.tos.muCla = cl
    m.env.with.tos.muObj = obj
    return
endProcedure envPushWith

envSetWith: procedure expose m.
parse arg obj, cl
    if cl == '' & obj \== '' then
        cl = objClass(obj)
    tos = m.env.with.0
    m.env.with.tos = obj
    m.env.with.tos.class = cl
    return
endProcedure envSetWith

envWithObj: procedure expose m.
    tos = m.env.with.0
    if tos < 1 then
        call err 'no with in envWithObj'
    return m.env.with.tos
endProcedure envWithObj

envAccPath: procedure expose m. m cl
parse arg pa, stop, nllNw
    nullNew = nllNw == 1
    dx = verify(pa, m.class.cPath, 'm')
    if dx = 0 then do
        n1 = pa
        p2 = ''
        end
    else do
        n1 = left(pa, dx-1)
        p2 = substr(pa, dx)
        end
    wCla = ''
    do wx = m.env.with.0 by -1 to if(stop==1, m.env.with.0, 1)
        wCla = m.env.with.wx.class
        if symbol('m.wCla.f2c.n1') == 'VAR' then
            return oAccPath(m.env.with.wx, pa, m.env.with.wx.class)
        end
    if stop == 1 then
        return 'no field' n1 'in class' className(wCla)
    vv =  mapValAdr(env.vars, n1)
    if vv \== '' then
        if p2 == '' then
            return oAccPath(vv, '', m.class.classR)
        else
            return oAccPath(vv, '|'p2, m.class.classR)
    else if nullNew & p2 == '' then
        return oAccPath(mapValAdr(env.vars, n1,'a'), p2,m.class.classR)
    else
        return 'undefined variable' pa
endProcedure envAccPath

envWithNext: procedure expose m.
parse arg beEn, defCl, obj
    tos = m.env.with.0
    if tos < 1 then
        call err 'envWithNext with.0' tos
    st = m.env.with.tos.muObj
    if beEn  == 'b' then do
        if m.env.with.tos.fun == 'asM' then
            m.st.0 = 0
        if m.env.with.tos.muElCl == '' then
            m.env.with.tos.muElCl = defCl
        end
    else if m.env.with.tos.fun == 'asM' then
        m.st.0 = m.st.0 + 1
    else if m.env.with.tos.fun == '' then
        call outO m.env.with.tos
    else if beEn = '' then
        call err 'no multi allowed'
    if beEn == 'e' then
        return
    if m.env.with.tos.fun == 'as1' then do
         if m.env.with.tos == '' then
             call err 'implement withNext null'
         return
         end
/*  if obj \== '' then do
        if \ m.env.with.tos.muElRef then
            call err 'obj but not ref'
        m.nn = obj
        call envSetWith obj
        end
*/
    if m.env.with.tos.fun == '' then do
        call envSetWith mNew(m.env.with.tos.muElCl)
        return
        end
    nn = st'.' || (m.st.0 + 1)
    if m.env.with.tos.muElRef then do
        m.nn = mNew(m.env.with.tos.muElCl)
        call envSetWith m.nn
        end
    else do
        call mReset nn, m.env.with.tos.muElCl
        call envSetWith nn
        end
    return
endProcedure envWithNext

envPushName: procedure expose m.
parse arg nm, multi, elCl
    res = envAccPath(nm, , 1)
    if res \== 1 then
        return err(res 'in envPushName('nm',' multi')')
    do while m.cl == 'r'
        if m.m == '' then do
            res = oRefSetNew(m, cl)
            if res \== 1 then
                call err res 'in envPushName('nm',' multi')'
            end
        m = m.m
        cl = objClass(m)
        end
    call envPushWith m, cl, multi, elCl
    return
endProcedure envPushName

envNewWiCo: procedure expose m.
parse arg co, cl
    k1 = strip(co cl)
    n = mapGet('ENV.C2W', k1, '')
    if n \== '' then
        return n
    k2 = k1
    if co \== '' then do
        k2 = strip(m.co.classes cl)
        n = mapGet('ENV.C2W', k2, '')
        end
    k3 = k2
    if n == '' then do
        cx = wordPos(cl, m.co.classes)
        if cx > 0 then do
            k3 = space(subWord(m.co.classes, 1, cx-1),
                     subWord(m.co.classes, cx+1) cl, 1)
            n = mapGet('ENV.C2W', k3, '')
            end
        end
    if n == '' then
        n = envNewWico2(co, k3)
    call mapAdd 'ENV.C2W', k1, n
    if k2 \== k1 then
        call mapPut 'ENV.C2W', k2, n
    if k3 \== k2 & k3 \== k1 then
        call mapPut 'ENV.C2W', k3, n
    return n
endProcedure envNewWiCo

envNewWiCo2: procedure expose m.
parse arg co, clLi
    n = mNew('ENV.WICO')
    if co == '' then
        m.n.level = 1
    else
        m.n.level = m.co.level + 1
    m.n.classes = clLi
    na = ''
    do cx = 1 to words(clLi)
        c1 = word(clLi, cx)
        na = na className(c1)
        do qx=1 to 2
            ff = c1 || word('.FLDS .STMS', qx)
            do fx = 1 to m.ff.0
                fn = m.ff.fx
                if fn == '' then
                    iterate
                fn = substr(fn, 2)
                m.n.f2c.fn = cx
                end
            end
        end
    m.n.classNames = space(na, 1)
    return n
endProcedure envNewWiCo2

envPopWith:procedure expose m.
    tos = m.env.with.0
    m.env.with.0 = tos - 1
    return
endProcedure envPopWith

envGet: procedure expose m.
parse arg na
    res = envAccPath(na)
    if res == 1 then
        res = oAccStr(m, cl)
    if res == 1 then
        return str
    return err(res 'in envGet('na')')
endProcedure envGet

envGetO: procedure expose m.
parse arg na, opt
    res = envAccPath(na, , opt == '-b')
    if res == 1 then
        res = oAccO(m, cl, opt)
    if res == 1 then
        return ref
    return err(res 'in envGetO('na')')
endProcedure envGetO

envPutO: procedure expose m.
parse arg na, ref, stop
    res = envAccPath(na, stop, 1)
    if res == 1 then
        res = ocPutO(m, cl, ref)
    if res = 1 then
        return ref
    return err(res 'in envPutO('na',' ref',' stop')')
endProcedure envPutO

envPut: procedure expose m.
parse arg na, va, stop
    res = envAccPath(na, stop , 1)
    if res == 1 then
        res = ocPut(m, cl, va)
    if res == 1 then
        return va
    return err(res 'in EnvPut('na',' va',' stop')')
endProcedure envPut

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

envReadO: procedure expose m.
parse arg na
    res = inO()
    if res == '' then
        return 0
    call envPutO na, res
    return 1
endProcedure envReadO

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

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.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

fileSingle: 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 fileSingle('m')'
        else
            res = arg(2)
    if two \== '' then
        call err '2 or more recs in fileSingle('m')'
    return res
endProcedure fileSingle

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  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 oAdd1Method m.class.classV, 'o2File return file(m.m)'
    call oAdd1Method m.class.classW, 'o2File return file(substr(m,2))'
    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 \== 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.fileTso.buf = m.fileTso.buf + 1
        m.m.defDD = 'CAT'm.fileTso.buf
        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)
        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)
    m.m.dsn = m.dsnAlloc.dsn
    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' 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

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  = mNew('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")", '*')
    interpret 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 sqlO   begin ***************************************************
    sql interface  mit  o und j Anbindung
***********************************************************************/
sqlOini: procedure expose m.
    if m.sqlO.ini == 1 then
        return
    call sqlIni
    m.sqlO.ini = 1
    m.sqlO.cursors  = left('', 200)
    call pipeIni
    call classNew 'n SqlSel u JRWO', 'm',
        , "jReset m.m.src = arg; m.m.type = arg2;",
        , "jOpen  call sqlSelOpen m, opt",
        , "jClose call sqlSelClose m",
        , "jReadO return sqlSelReadO(m)"
    call classNew 'n SqlDRS u SqlSel', 'm',
        , "jReset m.m.loc = arg; m.m.type = arg2;",
        , "jOpen  call sqlDRSOpen m, opt",
        , "jClose call sqlSelClose m",
        , "jReadO return sqlSelReadO(m)"
    return
endProcedure sqlOini
/*--- connect to the db2 subsystem sys -----------------------------*/
sqlOConnect: procedure expose m.
parse arg sys, retCon
    call sqlOIni
    return sqlConDis(sys, retCon)
endProcedure sqlOConnect

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

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

/*** execute sql's in a stream (separated by ;)
       opt: 'o' ==> write objects, otherwise fmtFTab
            's' ==> spufi formatting (window 72) otherwise linebreak */
sqlStmts: procedure expose m.
parse arg src, ggRet, opt
   dlm = ';'
   isStr = envInp(src)
   if isStr then
       s = scanSrc(scanSqlReset(scanReset(sqlStmts), '', 0), ggStr)
   else do
       fi = o2File(ggObj)
       if pos('c72', opt) > 0 then
           s = jOpen(scanSql(fi), '<')
       else
           s = jOpen(scanSqlReset(scanRead(fi), fi, 0), '<')
       end
   do while scanSqlStmt(s, dlm)
       if m.s.val = '' then
           iterate
       w1 = translate(word(m.s.val, 1))
       if w1 == 'TERMINATOR' then do
            dlm = strip(substr(m.s.val, 12))
            if length(dlm) \== 1 then
                call scanErr s, 'bad terminator' dlm 'in' strip(m.s.val)
            iterate
            end
       call out sqlStmt(m.s.val, ggRet, opt)
       end
   if \ isStr then
       call jClose s
   return 0
endProcedure sqlStmts

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

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

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

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

/*--- 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()
    call sqlPreOpen m.m.cursor, m.m.src, m.m.type == ''
    m.m.jReading = 1
    m.m.rowCount = 'open'
    return m
endProcedure sqlOpen

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

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

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

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

/*--- mark a cursor as closed ----------------------------------------*/
sqlFreeCursor: procedure expose m.
parse arg cx
    if substr(m.sqlo.cursors, cx, 1) \== 'u' then
         call err 'sqlFreeCursor('cx') not in use :'m.sqlo.cursors
    m.sqlO.cursors = overlay(' ', m.sqlO.cursors, cx)
    return
endProcedure sqlFreeCursor
/*--- create the type, fetch vars etc. from the sqlDA ---------------*/
sqlIntoClass: procedure expose m.
parse arg m
    da = 'SQL.'m.m.cursor
    if m.m.type = '' | m.m.type == '*' then do
        call sqlIntoVars m.m.cursor
        ff = mCat(da'.COL', '%+Q v, f ')
        m.m.type = classNew('n* SQL u f' ff 'v')
        end
    else do
        f = class4name(m.m.type)'.FLDS'
        if m.f.0 < sqlDescribeOutput(m.m.cursor) then
            call err 'not enough fields in' m.m.type 'for' m.m.src
        do ix=1 to m.da.d.sqlD
            if translate(m.f.ix) \== m.f.ix then
                call err 'fld' ix m.f.ix 'not uppercase for sql'
            m.da.d.col.ix = substr(m.f.ix, 2)
            end
        call sqlIntoVarsNull m.m.cursor
        end
    return
endProcedure sqlIntoClass

/*--- fetch cursor for this sqlSel -----------------------------------*/
sqlSelReadO: procedure expose m.
parse arg m
    if m.m.rowCount == 'open' then do
        call sqlIntoClass m
        m.m.rowCount = 0
        end
   trace ?r
    v = mNew(m.m.type)
    if \ sqlFetch(m.m.cursor, v) then
        return ''
    m.m.rowCount = m.m.rowCount + 1
    return v
endProcedure sqlSelReadO

/*--- close sql Cursor -----------------------------------------------*/
sqlSelClose: procedure expose m.
parse arg m, v
    call sqlClose m.m.cursor
    call sqlFreeCursor m.m.cursor
    return m
endProcedure sqlSelClose
/*--- generate the format ff for a sql cx as specified in sp
          use the information from the sqlDa -------------------------*/
deleteSqlGenFmt: procedure expose m.
parse arg ff, cx, sp
    if abbrev(sp, '=') then
        return substr(sp, 2)
    if sp = '' then
        sp = '*st'
    m.ff.0 = m.sql.cx.d.sqlD
    m.ff.flds = oFlds(sqlType(cx))
    if abbrev(sp, '*') then do
        do ix=1 to m.ff.0
            m.ff.ix = substr(sp, 2)
            end
        return ff
        end
    if abbrev(fmts, '=') then
        m.Sql.cx.FMT = substr(fmts, 2)
    defs = 'ir7 fr9 sl12 Tl26' sp
    do wx = 1 to words(defs)
        parse value word(defs, wx) with ty 2 fo
        select
            when ty = 'd' then      t.384 = fo
            when ty = 'f' then      t.480 = fo'/f'
            when ty = 'i' then      t.496 = fo'/i'
            when ty = 'n' then      t.484 = fo'/n'
            when ty = 's' then      t.448 = fo
            when ty = 't' then      t.388 = fo
            when ty = 'T' then      t.392 = fo
            otherwise          call err 'bad type' ty 'for format' fo
            end
        end
    if symbol('t.496') == 'VAR' then
        t.500 = t.496
    if symbol('t.448') == 'VAR' then do
        t.452 = t.448
        t.456 = t.448
        t.464 = t.448
        end
    do wx = 1 to m.ff.0
        ty = m.sql.cx.d.wx.sqlType
        le = m.sql.cx.d.wx.sqlLen
        withNulls = ty // 2
        ty = ty - withNulls
        if symbol('t.ty') <> 'VAR' then
            call err 'sqlType' ty 'not supported'
        parse var t.ty fo 2 fl '/' op
        if op = 'i' then
             if le = 2 then le = 6
             else           le = 12
        else if op <> '' then
            call err 'length for sqlType' ty 'op' op 'not implemented'
        if fl = '=' then
            fl = le
        else if abbrev(fl, '<') then
            fl = min(le, substr(fl, 2))
        m.ff.wx = fo || fl
        end
    return ff
endProcedure sqlGenFmt
/* copy sqlO   end   **************************************************/
/* copy sql    begin ***************************************************
    sql interface
***********************************************************************/
sqlIni: procedure expose m.
parse arg opt
    if m.sql.ini == 1 & opt \== 1 then
        return
    m.sqlNull = '---'
    m.sqlInd = 'sqlInd'
    m.sqlRetOK.0 = 0
    m.sqlMsgCa = 0
    m.sqlMsgDsntiar = 1
    m.sqlMsgCodeT   = 0
    call sqlPushRetOk
    m.sql.ini = 1
    m.sql.connected = ''
    isInProd = wordPos(sysvar(sysNode), 'RZ2 RZ4') > 0
    m.sql.handleRestrictOnDrop = \ isInProd
    return
endProcedure sqlIni

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

/*--- prepare and declare 'c'cx from sql src -------------------------*/
sqlPreDeclare: procedure expose m.
parse arg cx, src, descOut, descInp
     res = sqlPrepare(cx, src, descOut, descInp)
     if res >= 0 then
         return sqlExec('declare c'cx 'cursor for s'cx)
     return res
endProcedure sqlPreDeclare

/*--- prepare, declare and open 'c'cx from sql src -------------------*/
sqlPreOpen: procedure expose m.
parse arg cx, src, descOut, descInp
     res = sqlPreDeclare(cx, src, descOut, descInp)
     if res >= 0 then
         return sqlOpen(cx)
     return res
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
     return sqlExec('open c'cx 'using descriptor :M.SQL.'cx'.I')
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 */
    ggRes = sqlExec('fetch c'ggCx 'into' ggVars, 100 m.sqlRetOk)
    if ggRes == 0 then
        return 1
    if ggRes == 100 then
        return 0
    return ggRes
endProcedure sqlFetchInto

/*--- fetch cursor 'c'cx into destination dst and put sqlNull --------*/
sqlFetch: procedure expose m.
parse arg cx, dst, opts
    vars = sqlIntoVars(cx)
    if \ sqlFetchInto(cx, vars) then
        return 0
    call sqlSetNull cx, dst
    return 1
endProcedure sqlFetch

sqlSetNull: procedure expose m.
    parse arg cx, dst
    do nx=1 to m.sql.cx.sqlNull.0
        col = m.sql.cx.sqlNull.nx
        if m.dst.col.sqlInd < 0 then
            m.dst.col = m.sqlNull
        end
    return
endProcedure sqlSetNull

sqlIntoVars: procedure expose m.
parse arg cx
    if m.sql.cx.into \== '' then
        return m.sql.cx.into
    do ix=1 to sqlDescribeOutput(cx)
               /* fetch uppercases variable names */
        cn = translate(word(m.sql.cx.d.ix.sqlName, 1))
        if cn == '' | symbol(c.cn) == 'VAR' then
                cn = 'COL'ix
        c.cn = 1
        m.sql.cx.col.ix = cn
        end
    return sqlIntoVarsNull(cx)
endProcedure sqlIntoVars

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

sqlIntoVarsNull: procedure expose m.
parse arg cx
    nx = 0
    vars = ''
    do ix=1 to sqlDescribeOutput(cx)
        cn = m.sql.cx.col.ix
        vars = vars', :m.dst.'cn
        if m.sql.cx.d.ix.sqlType // 2 = 1 then do
            vars = vars' :m.dst.'cn'.sqlInd'
            nx = nx + 1
            m.sql.cx.sqlNull.nx = cn
            end
        end
    m.sql.cx.col.0 = m.sql.cx.d.sqlD
    m.sql.cx.sqlNull.0 = nx
    m.sql.cx.into = substr(vars, 3)
    return m.sql.cx.into
endProcedure sqlIntoVarsNull

/*--- 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.  /* ??????????????? ==> sqlJRopen */
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

/*--- 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
    ggRes = sqlOpen(ggCx)
    if ggRes < 0 then
        return ggRes
    do sx = 1 until ggRes \== 1
        ggRes = sqlFetchInto(ggCx, ggVars)
        end
    m.st.0 = sx - 1
    call sqlClose ggCx
    if ggRes == 0 then
        return m.st.0
    return ggRes
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)
    /* 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 sqlDataSet
/*--- prepare, declare open cursor 'c'cx, fetch all and close
      return number of rows fetched ----------------------------------*/
sqlPreAllCl:
parse arg ggCx, ggSrc, st, ggVars
    ggRes = sqlPreDeclare(ggCx, ggSrc)
    if ggRes >= 0 then
        return sqlOpAllCl(ggCx, st, ggVars)
    return ggRes
endProcedure sqlPreAllCl

/*--- execute statement 's'cx using arguments arg(2), arg(3)... ------*/
sqlExecute:
parse arg ggCx ggRetOk  /* no , for ggRetOk, arg(2) is used already| */
    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',
                   , ggRetOk)
endProcedure

/*--- execute immediate the sql src ----------------------------------*/
sqlExImm:
parse arg ggSrc, ggRetOk
     return sqlExec('execute immediate :ggSrc', ggRetOk)
endProcedure sqlExImm

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

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

sqlErrorHandler: procedure expose m.
parse arg a1 verb rest, cd, errMc
    if translate(a1) \== execSql then
        return 0
    upper verb
    if cd = -672 & verb == 'DROP' ,
           & m.sql.handleRestrictOnDrop == 1 then do
        say 'sqErrorHandler trying to drop restrict on drop on' errMc
        call sqlExec 'alter table' errMc ,
                'drop restrict on drop'
        say 'sqlErrorHandler retrying' verb rest
        call sqlExec verb rest
        return 1
        end
    return 0
endProcedure sqlErrHandler
/*--- connect to the db2 subsystem sys -----------------------------*/
sqlConnect: procedure expose m.
parse upper arg sys, retOk
    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
    res = sqlExec("connect" sys, retOk ,1)
    if res >= 0 then
        m.sql.connected = sys
    return res
endProcedure sqlConnect

/*--- diconnect from db2 ---------------------------------------------*/
sqlDisconnect: procedure expose m.
parse arg retOk
    m.sql.connected = ''
    return sqlExec("disconnect ", retOk, 1)
endProcedure sqlDisconnect

/*--- connect and/or disconnect to DB2 -------------------------------*/
sqlConDis: procedure expose m.
parse upper arg sys, retOk
    if sys \== '' then
        nop
    else if sysvar(sysnode) == 'RZ1' then
        sys = 'DBAF'
    else
        call err 'no default subsys for' sysvar(sysnode)
    call sqlIni
    if sys == m.sql.connected then
        return 0
    if m.sql.connected \== '' then
        call sqlDisconnect
    if sys = '-' then
        return 0
    return sqlConnect(sys, retOk)
endProcedure sqlConDis
/*--- push and pop currently accepted sqlCodes -----------------------*/
sqlPushRetOk: procedure expose m.
parse arg rr
    nx = m.sqlRetOk.0 + 1
    m.sqlRetOk.0 = nx
    m.sqlRetOk.nx = rr
    m.sqlRetOk    = rr
    return
endProcedure sqlPushRetOk

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

/*--- issue an sql error message -------------------------------------*/
sqlMsg: /* no procedure, to keep variables sql... */
    ggRes = ''
    if \ dataType(sqlCode, 'n') then do
        ggRes = 'sqlCode' sqlCode 'not numeric\n'sqlMsgCa()
        end
    else do
        signal on syntax name sqlMsgOnSyntax
        if m.sqlMsgCodeT == 1 then
            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 = sqlMsgCa(),
                    '\n<<rexx sqlCodeT not found or syntax>>'
            end
        signal off syntax
        if m.sqlMsgDsnTiar == 1 then do
            ggRes = ggRes || sqlDsntiar()
            ggWa = sqlMsgWarn(sqlWarn)
            if ggWa \= '' then
                ggRes = ggRes'\nwarnings' ggWa
            end
        if m.sqlMsgCa == 1 then
           ggRes = ggRes'\n'sqlMsgCa()
        end
    ggSqlSp = ' ,:+-*/&%?|()[]'
    ggXX = pos(':', ggSqlStmt)+1
    do ggSqlVx=1 to 12 while ggXX > 1
        ggYY = verify(ggSqlStmt, ggSqlSp, 'm', ggXX)
        if ggYY < 1 then
            ggYY = length(ggSqlStmt) + 1
        ggSqlVa.ggSqlVx = substr(ggSqlStmt, ggXX, ggYY - ggXX)
        do ggQQ = ggXX-2 by -1 to 1 ,
                while substr(ggSqlStmt, ggQQ, 1) == ' '
            end
        do ggRR = ggQQ by -1 to 1 ,
                while pos(substr(ggSqlStmt, ggRR, 1), ggSqlSp) < 1
            end
        if ggRR < ggQQ & ggRR > 0 then
            ggSqlVb.ggSqlVx = substr(ggSqlStmt, ggRR+1, ggQQ-ggRR)
        else
            ggSqlVb.ggSqlVx = ''
        ggXX = pos(':', ggSqlStmt, ggYY+1) + 1
        end
    ggSqlVa.0 = ggSqlVx-1
    if datatype(sqlErrd.5, 'n') & sqlErrd.5 > 0 then do
        ggW2 = translate(word(ggSqlStmt, 2))
        ggW3 = translate(word(ggSqlStmt, 3))
        if ggW2 == 'PREPARE' then
            ggRes = ggRes || sqlMsgSrF('FROM')
        else if ggW2 ggW3 == 'EXECUTE IMMEDIATE' then
            ggRes = ggRes || sqlMsgSrF(1)
        else
            ggRes = ggRes || sqlMsgSrF()
        end
    ggRes = ggRes'\nstmt = ' ggSqlStmt
    ggPref = '\nwith'
    do ggXX=1 to ggSqlVa.0
        ggRes = ggRes || ggPref ggSqlVb.ggXX ':'ggSqlVa.ggXX ,
                      '=' value(ggSqlVa.ggXX)
        ggPref = '\n    '
        end
    if abbrev(ggRes, '\n') then
        return substr(ggRes, 3)
    return  ggRes
endSubroutine sqlMsg

sqlMsgSrF:
parse arg ggF
    if ggF \== '' & \ datatype(ggF, 'n') then do
        do ggSqlVx=1 to ggSqlVa.0
            if translate(ggSqlVb.ggSqlVx) = ggF then
                return sqlMsgSrc(value(ggSqlVa.ggSqlVx), sqlErrd.5)
            end
        end
    if datatype(ggF, 'n') & ggF <= ggSqlVa.0 then
        return sqlMsgSrc(value(ggSqlVa.ggF), sqlErrd.5)
    return sqlMsgSrc(ggSqlStmt  , sqlErrd.5)
endSubroutine sqlMsgSrF

/*--- use dsnTiar to translate sql Info to error text ----------------*/
sqlDsnTiar:
    sqlWarn = sqlWarn.0 || sqlWarn.1 || sqlWarn.2 || sqlWarn.3,
             || sqlWarn.4 || sqlWarn.5 || sqlWarn.6 || sqlWarn.7,
             || sqlWarn.8 || sqlWarn.9 || sqlWarn.10
    if sqlCode = -438 then
        return '\nSQLCODE = -438:',
               'APPLICATION RAISED ERROR WITH sqlState' sqlState,
               'and DIAGNOSTIC TEXT:' sqlErrMc
    if digits() < 10 then
        numeric digits 10
    sqlCa = d2c(sqlCode, 4) ,
             || d2c(max(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) ,
             || sqlWarn || sqlState
    if length(sqlCa) <> 124 then
        call err 'sqlDa length' length(sqlCa) 'not 124' ,
                 '\nsqlCa=' sqlMsgCa()
    return sqlDsnTiarCall(sqlCa)

/*--- call dsnTiar o translate sql Info to error text ----------------*/
sqlDsnTiarCall: procedure expose m.
parse arg ca
    liLe = 78
    msLe = liLe * 10
    if length(ca) <> 124 then
        call err 'sqlDa length' length(ca) 'not 124:' ca', hex='c2x(ca)
    ca = 'SQLCA   ' || d2c(136, 4) || ca
    msg = d2c(msLe,2) || left('', msLe)
    len = d2c(liLe, 4)
    ADDRESS LINKPGM "DSNTIAR ca msg LEN"
    if rc <> 0 then
        call err 'linkPgm dsnTiar rc' rc 'sqlCa' ca
    res = ''
    do c=3 by liLe to msLe
        if c = 3 then do
            l1 = strip(substr(msg, c+10, 68))
            cx = pos(', ERROR: ', l1)
            if cx > 0 then
                l1 = left(l1, cx-1)':' strip(substr(l1, cx+9))
            res = res'\n'l1
            end
        else if substr(msg, c, 10) = '' then
            res = res'\n    'strip(substr(msg, c+10, 68))
        else
            leave
        end
    return res
endProcedure sqlDsnTiarCall

sqlMsgCa:
    ggWarn = ''
    do ggX=0 to 10
        if sqlWarn.ggX \== ' ' then
            ggWarn = ggWarn ggx'='sqlWarn.ggx
        end
    if ggWarn = '' then
        ggWarn = 'none'
    return 'sqlCode' sqlCode 'sqlState='sqlState,
           '\n    errMC='translate(sqlErrMc, ',', 'ff'x),
           '\n    warnings='ggWarn '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 sqlMsgCa

/*--- make the text for sqlWarnings
           input warn.0..warn.10 as a 11 character string ------------*/
sqlMsgWarn: procedure expose m.
parse arg w0 2 wAll
     if w0 = '' & wAll = '' then
         return ''
     if  length(wAll) \= 10 | ((w0 = '') <> (wAll = '')) then
         return 'bad warn' w0':'wAll
     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 = substr(wAll, wx, 1)
         if w = ' ' then
             iterate
         t = wx'='w
         cx = pos(' 'wx'='w' ', text)
         ex = pos(','         , text, cx + 1)
         if cx < 1 then
             r = r wx'='w '?,'
         else
             r = r substr(text, cx+1, ex-cx)
         end
     return strip(r, 't', ',')
endProcedure sqlMsgWarn

sqlMsgSrc: procedure expose m.
parse arg src, pos, opt
    if 0 then do /* old version, before and after txt */
        tLe = 150
        t1 = space(left(src, pos), 1)
        if length(t1) > tLe then
            t1 = '...'right(t1, tLe-3)
        t2 = space(substr(src, pos+1), 1)
        if length(t2) > tLe then
            t2 = left(t2, tLe-3)'...'
        res = '\nsource' t1 '<<<error>>>' t2
        end
    liLe = 68
    liCn = 3
    afLe = 25
    if translate(word(src, 1)) == 'EXECSQL' then
        src = substr(src, wordIndex(src, 2))
    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'<<<'
endProcdedur sqlMsgSrc

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

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

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

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

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

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

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

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

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

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

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

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

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

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

copyDSN: procedure expose m.
parse arg frSpec, toSpec, ggSay
    frDD = dsnAlloc(frSpec, 'SHR', 'FRDD')
    toDD = dsnAlloc(toSpec, 'OLD', 'TODD')
    call readDDBegin word(frDD, 1)
    call writeDDBegin word(toDD, 1)
    cnt = 0
    do while readDD(word(frDD, 1), r.)
        call writeDD word(toDD, 1), r.
        cnt = cnt + r.0
        end
    call readDDEnd word(frDD, 1)
    call writeDDEnd word(toDD, 1)
    interpret ';' subword(frDD, 2) '; ; ;' subword(toDD, 2)
    if ggSay == 1 | m.debug == 1 then
       say cnt 'records copied from' frSpec 'to' to toSpec
    return
endSubroutine writeDsn
/* copy adrTso end ****************************************************/
/* copy j begin *******************************************************
    the j framework
         jReset
         jOpen
         jClose
         jRead
         jWrite
***********************************************************************/
jRead: procedure expose m.
parse arg m, var
    call objMetClaM m, 'jRead'
    if m.m.jReading then
        interpret ggCode
    else
        return err('jRead('m',' var') but not opened r')
endProcedure jRead

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

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

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

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

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

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

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

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

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

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

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

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

jCat1: procedure expose m.
parse arg v, opt
    if opt == '' | abbrev(opt, '-b') then
        return v
    if opt == '-s' then
        return strip(v)
    if opt == '-c72' then
        return left(v, 72)
    call err "bad opt '"opt"' in jCat1("v", '"opt"')"
endProcedure jCat1

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

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

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

inO: procedure expose m.
    if arg() > 0 then call err '??? old interface'
    return jReadO(m.j.in)
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
/*--- jBuf: buffer read or write (supports datataypes) ---------------*/
jBufTxt: procedure expose m.
    m = oNew('JBufTxt') /* calls jBufReset */
    do ax=1 to arg()
        m.m.buf.ax = arg(ax)
        end
    m.m.buf.0 = ax-1
    m.m.allV = 1
    return m
endProcedure jBufTxt

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

/*--- return true if object is kind of String------------------------*/
oStrOrObj: procedure expose m. ggStr ggObj ggCla
parse arg ggObj
    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
    if oStrOrObj(m) then
        return 1
    ggObj = o2File(ggObj)
    return 0
endProcedure oStrOrFile

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

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

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

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

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

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

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

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

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


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

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

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


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

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

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

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

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

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

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

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

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

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

    if t == m.class.classV then
        return out(p1'=' m.a)
    if t == m.class.classW == 'w' then
        return out(p1'}' substr(a, 2))
    if m.t == 'f' then
        return classOutDone(m.t.class, a'.'m.t.name, pr, p1'.'m.t.name)
    if m.t == 'r' then do
        if m.a == '' then
            return out(p1'refTo :'className(m.t.class) '@null@')
        else
            return classOutDone(m.t.class, m.a, pr,
                    , p1'refTo @'m.a)
        end
    if m.t = 'u' then do
        t1 = m.t.1
        vv = m.t.0 > 0 & m.t.1 == m.class.classV
        call out p1 || if(m.t.name == '', 'union', ':'m.t.name) ,
             || copies(' =' m.a, vv)
        do ux=1+vv to m.t.0
            call classOutDone m.t.ux, a, pr' '
            end
        return 0
        end
    if m.t = 's' then do
        call out p1'stem' m.a.0
        do ux=1 to m.a.0
            call classOutDone m.t.class, a'.'ux, pr' ', pr' .'ux
            end
        return 0
        end
    if m.t = 'c' then do
        if m.t.name = m.a then
            call classOutDone m.t.class, a, pr, p1'choice' m.a
        return 0
        end
    if m.t = 'm' then
        return 0
    return err('bad class type' m.t)
endProcedure classOutDone
/* copy class end   ***************************************************/
/* copy 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
    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>.** to avoid conflicts: every rexx Module (copy) should
               only allocate addresses m.<mbr>.** with <mbr> the name of
               the rexx module
        we pass parameters around (e.g. a=address, m=memory, st=stem)
            and the called function may use m.a or m.a.subField etc.
    gg*: local variable in subroutines without procedure
    everything else: temporary data within procedure

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

f: procedure expose m.
parse arg ggFmt, ggA1, ggA2
    if symbol('M.F.FORMAT.ggFmt') == 'VAR' then
        interpret M.F.FORMAT.ggFmt
    else
        interpret fGen(ggFmt)
endProcedure f

fPlus: procedure expose m.
parse arg ggFmt, ggA1, ggA2
    interpret fFld(ggFmt)
endProcedure fPlus

fFld: procedure expose m.
parse arg ff
    px = lastPos(' ', ff)
    fld = substr(ff, px+1)
    fmt = left(ff, px-1)
    ff = 'F.FORMAT.'fmt'%-Q'fld
    if symbol('M.ff') == 'VAR' then
        return m.ff
    call fGen fmt
    if symbol('M.ff') == 'VAR' then
        return m.ff
    call err 'field' fld 'not in format' fmt
endProcedure fFld

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

 + \s   a single space
 + \n   a newLine
 + \%  \@ \\ the escaped char
   ('@' argN? '.'? field)?'%' flags? width? ('.' precision)? specifier
 specifier: is the most significant one and defines the type

 - c Character a
 - C Cut %-nC = left(v,n), %nC = right(v,n) %n.mC = substr(m, n)
 - d or i Signed decimal integer
 - e Scientific notation (mantissa/exponent) using e character 3.9265e+2
 - E Scientific notation (mantissa/exponent) using E character 3.9265E+2
 - f Decimal floating point
 - g Use the shorter of %e or %f
 - G Use the shorter of %E or %f
 - o Unsigned octal 610
 - S Strip(..., both)
 - u Unsigned decimal integer
 - x Unsigned hexadecimal integer
 - X Unsigned hexadecimal integer (capital letters)
 - p Pointer address
 - n Nothing printed. The argument must be a pointer to a signed int, wh
 + % A % followed by another % character will write % to stdout. %

 Flags:
 - - Left-justify within the given field width; Right justification is
 - + Forces to precede the result with a plus or minus sign (+ or -)
 - (space) If no sign is going to be written, a blank space is inserte
 - # Used with o, x or X specifiers the value is preceeded with 0, 0x
         force decimalpoint ...
 - 0 Left-pads the number with zeroes (0) instead of spaces, where pad
 + = reuse previous input argument

 length not implemented
----------------------------------------------------------------------*/
fGen: procedure expose m.
parse arg aS
    ft.src = aS
    ft.pos = 1
    ex = 0
    ax = 0
    qX = ''
    cd = ''
    do fx=1
        ftc.fx = fText()
        an = ''
        af = ''
        if fLit('@') \== '' then do
            an = fVerify('0123456789', 'n')
            if an == '' then
                an = 1
            call fLit '.'
            af = fText()
            end
        fta.fx = ''
        if fLit('%') == '' then do
            if ft.pos > length(ft.src) then
                leave
            call err 'missing % at' substr(aS, ft.pos) 'in format' aS
            end
        flags = fVerify('-+', 'n')
        len   = fVerify('0123456789', 'n')
        prec  = ''
        if fLit('.') \== '' then do
            if len == '' then
                call err 'empty len in' substr(aS,ft.pos) 'in format' aS
            prec = fVerify('0123456789', 'n')
            end
        sp = fChar(1)
        if sp \== 'Q' then do
            if an \== '' then
                ax = an
            else
                ax = ax + 1
            if ax < 3 then
                aa = 'ggA'ax
            else
                aa = 'arg(' || (ax+1) || ')'
            if af \== '' then do
                if \ abbrev(aa, 'ggA') then
                    call err 'implement ggA'ax
                if verify(af, m.mAlfUC'0123456789.') < 1,
                     & pos('.GG', '.'af) < 1 then do
                    aa = 'm.'aa'.'af
                    end
                else do
                    cd = fGenRexxAdd(cd, '; ggF'fx '=' quote(af))
                    aa = 'm.'aa'.ggF'fx
                    end
                end
            end
        if sp = 'C' then do
            if prec \== '' then
                fta.fx = 'substr('aa',' prec',' len')'
            else if pos('-', flags) > 0 then
                fta.fx = 'left('aa',' len')'
            else
                fta.fx = 'left('aa',' len')'
            end
        else if sp = 'Q' then do
            qX = qX fx
            fta.fx = 'Q?'flags
            end
        else if sp == 's' then
            fta.fx =  aa
        else if sp = 'S' then
            fta.fx = 'strip('aa')'
        else
            call err  'bad specifier' sp 'at' ft.pos 'in format' aS
        end
    if qX == '' then
        cd = fGenRexx(cd, fx)
    else
        cd = fGenQRexx(cd, fx, qX)
    m.f.format.aS = cd
    say '???' aS '==>' cd
    return cd
endProcedure fGen

fChar: procedure expose m. ft.
parse arg len
    ox = ft.pos
    if len > length(ft.src) + 1 - ox then
        len = length(ft.src) + 1 - ox
    ft.pos = ox+len
    return substr(ft.src, ox, len)
endProcedure fChar

fLit: procedure expose m. ft.
    do ax=1 to arg()
        if abbrev(substr(ft.src, ft.pos), arg(ax)) then do
            ft.pos = ft.pos + length(arg(ax))
            return arg(ax)
            end
        end
    return ''
endProcedure fLit

fVerify: procedure expose m. ft.
parse arg set, isMa
    ox = ft.pos
    nx = verify(ft.src, set, isMa, ox)
    if nx < ft.pos then
        ft.pos = length(ft.src) + 1
    else
        ft.pos = nx
    return substr(ft.src, ox, ft.pos-ox)
endProcedure fVerify

fText: procedure expose m. ft.
    res = ''
    do forever
        res = res || fVerify('\@%', 'm')
        if ft.pos > length(ft.src) then
            return res
        if substr(ft.src, ft.pos, 1) \== '\' then
            return res
        c1 = substr(ft.src, ft.pos+1, 1)
        if length(ft.src) = ft.pos | pos(c1, 's\@%') < 1 then do
            res = res'\'
            ft.pos = ft.pos + 1
            end
        else do
            res = res || translate(c1, ' ', 's')
            ft.pos = ft.pos + 2
            end
        end
endProcedure fText

fgenQRexx: procedure expose m. ft. fta. ftc.
parse arg c0, fx, qx qr
    if qx == '' | qr \== '' then
        call err 'multiple qx' qx' in format' ft.src
    if fta.qX \== 'Q?+' then
        call err 'bad q in format' ft.src
    if fx \= qX+1 then
        call err 'q not last in format' ft.src
    if qx = 1 then do
        ftc.3 = ftc.2
        ftc.2 = ''
        fta.2 = fta.1
        fta.1 = 'arg(2)'
        qx = 2
        fx = 3
        end
    fEnd = ft.src'%-Qend'
    m.f.format.fEnd = ftc.qx
    cd = fgenRexx(c0, qx-1)
    ftc.1 = ftc.qx || ftc.fx || ftc.1
    fNxt = ft.src'%-Qnxt'
    m.f.format.fNxt = fgenRexx(c0, qx-1)
    say '???'fNxt'='m.f.format.fNxt',' fEnd'='m.f.format.fEnd
    return cd
endProcedure fGenQRexx

fgenRexx: procedure expose m. ft. fta. ftc.
parse arg cd, fTo
    do fx=1 to fTo
        if ftc.fx \== '' then
            cd = fGenRexxAdd(cd, quote(ftc.fx))
        if fta.fx \== '' then
            cd = fGenRexxAdd(cd, fta.fx)
        end
    if cd = '' then
        return "return ''"
    else if abbrev(cd, ';') then
        return substr(cd, 2)'; return r'
    else
        return "return" cd
endProcedure fGenRexx

fGenRexxAdd: procedure expose m. cnst ax
parse arg one, two
    if one == '' then
        if abbrev(two, ';') then
            return ";r=''"two";"
        else
            return two
    if right(one, 1) == ';' then
        if abbrev(two, ';') then
            return one substr(two, 2)
        else
            return one 'r = r ||' two
    else
        if \ abbrev(two, ';') then
            return one '||' two
        else if abbrev(one, ';') then
            return one two';'
        else
            return ';r='one two';'
endProcedure fGenRexxAdd

mIni: procedure expose m.
    if m.m.ini = 1 then
        return
    m.m.ini = 1
    m.mAlfLC  = 'abcdefghijklmnopqrstuvwxyz'
    m.mAlfUC  = translate(m.mAlfLC)
    m.mAlfa   = m.mAlfLC || m.mAlfUC
    m.mAlfNum = m.mAlfa || '0123456789'
    m.mAlfDot = m.mAlfNum || '.'
    m.m.area.0 = 0
    call mNewArea
    return
endProcedure mIni
/* copy m end *********************************************************/
/* copy err begin ******************************************************
    messages, errorhandling,help
***********************************************************************/
errIni: procedure expose m.
    if m.err.ini == 1 then
        return
    call outIni
    m.err.ini     = 1
    m.err.handler = ''
    m.err.cleanup = ';'
    m.err.opt     = ''
    return
endProcedure errIni
/* configure err -----------------------------------------------------*/
errReset: procedure expose m.
    call errIni
    parse arg m.err.opt, m.err.handler
    if pos('I', translate(m.err.opt)) > 0 then
        if errOS() \== 'LINUX' then
            if sysVar('sysISPF') = 'ACTIVE' then
                    call adrIsp 'control errors return'
    return
endSubroutine errReset

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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