zOs/REXX/J

/* copy j begin *******************************************************
    the j framework
         jReset
         jOpen
         jClose
         jRead
         jWrite
**********************************************************************/
jRead: procedure expose m.
parse arg m
    ix = m.m.readIx + 1
    if ix > m.m.buf.0 then do
        if m.m.jReading \== 1 then
            return err('jRead('m') but not opened r')
        if \ jReadBuf(m, m'.BUF') then
            return 0
        ix = 1
        end
    m.m.readIx = ix
    m.m = m.m.buf.ix
    return 1
endProcedure jRead

jReadBuf: procedure expose m.
parse arg m, rStem
    interpret objMet(m, 'jRead')
    m.m.bufI0  = m.m.bufI0 + m.rStem.0
    return m.rStem.0 > 0
endProcedure jReadBuf

jReadVar: procedure expose m.
parse arg m, var
if arg() > 2 then call err '???  old interface' / 0
    if \ jRead(m) then
        return 0
    m.var = m.m
    return 1
endProcedure jReadVar

/*--- read next NonEmpty line ---------------------------------------*/
jReadNE: procedure expose m.
parse arg m
    do while jRead(m)
        if m.m <> '' then
            return 1
        end
    return 0
endProcedure jReadNE

/*--- read next lines to stem ---------------------------------------*/
jReadSt: procedure expose m.
parse arg m, st
    sx = 0
    if m.m.readIx >= m.m.buf.0 then do
        if jReadBuf(m, st) then
            return 1
        m.st.0 = 0
        return 0
        end
    do rx = m.m.readIx+1 to m.m.buf.0
        sx = sx + 1
        m.st.sx = m.m.buf.rx
        end
    m.m.readIx = m.m.buf.0
    m.st.0 = sx
    return sx > 0
endProcedure jReadSt

jReadObRe: procedure expose m.
parse arg m
if arg() > 1 then call err '???  old interface' / 0
    if jRead(m) then
        return m.m
    else
        return ''
endProcedure jReadObRe

jReadO: procedure expose m.
parse arg m
if arg() > 1 then call err '???  old interface'  /0
    return jRead(m)
endProcedure jReadO

jWrite: procedure expose m.
parse arg m, line
    ix = m.m.buf.0 + 1
    m.m.buf.0 = ix
    m.m.buf.ix = line
    if ix > m.m.bufMax then
        call jWriteBuf m
    return
endProcedure jWrite

/*--- write the buf to destination ----------------------------------*/
jWriteBuf: procedure expose m.
parse arg m
    if \ m.m.jWriting then
        return err('jWrite('m') but not opened w')
    wStem = m'.BUF'
    interpret objMet(m, 'jWriteMax')
    return
endProcedure jWriteBuf

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

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

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

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

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

jWriteNowImpl: procedure expose m.
parse arg m, rdr
    call jOpen rdr, m.j.cRead
    do while jRead(rdr)
        if m.rdr.readIx == 1 then do
            call jWriteSt m, rdr'.BUF'
            m.rdr.readIx = m.rdr.buf.0
            end
        else
            call jWrite m, m.rdr
        end
    call jClose rdr
    return
endProcedure jWriteNow

/*--- reset JRW: fail if open, initialise ---------------------------*/
jReset0: procedure expose m.
parse arg m
    if m.m.jReading == 1 | m.m.jWriting == 1 then
        return err('still open jReset0('m')')
    m.m.jUsers = 0
    m.m.buf.0  = 0
    m.m.wriMax = 0
    call jCloseSet m
    return m
endProcedure jReset0

jCloseSet: procedure expose m.
parse arg m
    m.m.jReading = 0
    m.m.jWriting = 0
    m.m.readIx = 55e55
    m.m.bufMax = -55e55
    return m
endProcedure jCloseSet

jReset: procedure expose m.
parse arg m, arg, arg2
    interpret objMet(m, 'oResetNoMut')
    return m
endProcedure jReset

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

/*--- close JRW flush buffer if writing ... -------------------------*/
jClose: procedure expose m.
parse arg m
    oUsers = m.m.jUsers
    if oUsers = 1 then do
        if m.m.jWriting then do
            wStem = m'.BUF'
            interpret objMet(m, 'jWriteFlu')
            end
        interpret objMet(m, 'jClose')
        call jCloseSet m
        end
    else if oUsers < 1 then
        call err 'jClose' m 'but already closed'
    m.m.jUsers = oUsers - 1
    return m
endProcedure jClose

/*--- force physical close for errCleanup ---------------------------*/
jCloseClean: procedure expose m.
parse arg m
    if m.m.jUsers = 0 then
        return
    m.m.jUsers = 1
    return jClose(m)
endProcedure jCloseClean

/*--- cat the lines of the file together, with mid between lines,
                fail if not all lines are strings -------------------*/
jCatLines: procedure expose m.
parse arg m, fmt
    if abbrev(fmt, '-sql') then do
        call err '-sql in jCatLines'
        end
    f2 = '%##fCatFmt' fmt
    call jOpen m, m.j.cRead
    if \ jRead(m) then do
        call jClose m
        return f(f2'%#0')
        end
    res = f(f2'%#1', m.m)
    do while jRead(m)
        res = res || f(f2, m.m)
        end
    call jClose m
    return res || f(f2'%#r')
endProcedure jCatLines

jIni: procedure expose m.
    if m.j.ini == 1 then
        return
    m.j.ini = 1
    m.j.cRead = '<'
    m.j.cWri = '>'
    m.j.cApp = '>>'
    call classIni
    am = "call err 'call of abstract method"
    cLa= classNew('n JRWLazy u LazyRun', 'm',
        , "oReset" m.class_lazyRetMutate,
                   "'call jReset0 m;' classMet(cl, 'jReset')",
        , "jWriteMax return classMet(cl, 'jWrite') '; m.m.buf.0 = 0'" ,
        , "jWriteFlu return classMet(cl, 'jWriteMax')",
        , "jWriteSt  return 'if m.m.buf.0 <> 0" ,
                     "| m.qStem.0 < m.m.bufMax / 2  then do;" ,
                "call mAddSt m''.BUF'', qStem;" ,
                "if m.m.buf.0 > m.m.bufMax then do;" ,
                     "wStem = m''.BUF'';'" ,
                     "classMet(cl, 'jWriteMax')'; end; end;",
              "else do; wStem = qStem;' classMet(cl, 'jWrite') ';end'",
        )
    c1 = classNew('n JRW u ORun, f JREADING v, f JWRITING v', 'm',
        , "METHODLAZY" cLa,
        , "jReset" ,
        , "jRead" am "jRead('m')'" ,
        , "jWrite" am "jWrite('m',' wStem')'" ,
        , "jWriteAll call jWriteNowImpl m, rdr",
        , "jWriteNow call jWriteNowImpl m, rdr",
        , "jOpen" am" jOpen('m',' opt')'" ,
        , "jClose" ,
        , "oRun call pipeWriteAll m",
        , "o2String return jCatLines(m, fmt)",
        , "o2File return m")
    call classNew 'n JRWDelegOC u JRW', 'm',
        , "jReset m.m.deleg = arg;" ,
        , "jOpen     call jOpen m.m.deleg, opt" ,
        , "jClose    call jClose m.m.deleg"
    call classNew 'n JRWDeleg u JRWDelegOC', 'm',
        , "jRead if \ jReadSt(m.m.deleg, rStem) then return 0",
        , "jWrite  call jWriteSt m.m.deleg, wStem" ,
    am = "call err 'call errObject"
    call classNew 'n JRWErr u JRW', 'm',
        , "jWriteAll" er "jWriteAll 'm', rdr'",
        , "jWriteNow" er "jWriteNow 'm', 'rdr'",
        , "jClose" er "jClose 'm'"
    call classNew 'n JSay u JRW', 'm',
        , "jWrite do wx=1 to m.wStem.0;say o2Text(m.wStem.wx,157);end",
        , "jOpen if \ abbrev(opt, m.j.cWri) then",
            "call err 'can only write JSay#jOpen('m',' opt')';"
    call classNew 'n JRWEof u JRW', 'm',
        , "jRead  return 0",
        , "jOpen if opt \=='<' then call err 'JRWEof#open('m',' opt')'"
    m.j.in = jOpen(oNew('JRWEof'), m.j.cRead)
    m.j.out = jOpen(oNew('JSay'), '>')
    m.j.say = m.j.out
    m.j.errRead  = "return err('jRead('m') but not opened r')"
    m.j.errWrite = "return err('jWrite('m',' line') but not opened w')"
    call classNew "n JBuf u JRW, f BUF s r", "m",
        , "jReset call jBufReset m, arg, arg2" ,
        , "jOpen call jBufOpen m, opt",
        , "jRead return 0",
        , "jWriteMax call err 'buf overflow'",
        , "jWriteFlu ",
        , "jWriteSt  call mAddSt m'.BUF', qStem" ,
        , "jWrite call mAddSt m'.BUF', wStem;" ,
              "if m.m.buf.0 > m.m.bufMax then call err 'buf overflow'",
        , "jPosBefore m.m.readIx = m.m.readIx+0;m.m.readIx = lx-1"
    return
endProcedure jIni

/*--- return a JRW from rdr or in -----------------------------------*/
in2File: procedure expose m.
    parse arg m
    interpret objMet(m, 'in2File')
    return err('in2File did not return')
endProcedure in2File
      /* jstr is part of out interface --> in2Str */
in2Str: procedure expose m.
    parse arg m, fmt
    interpret objMet(m, 'in2Str')
    return err('in2Str did not return')
endProcedure in2Str

in2Buf: procedure expose m.
parse arg m
    interpret objMet(m, 'in2Buf')
    return err('in2Buf did not return')
endProcedure in2Buf

in: procedure expose m.
    if arg() > 0 then call err '??? old interface'
    r = m.j.in
    m.in_ret = jRead(r)
    m.in = m.r
    return m.in_ret
endProcedure in

inVar: procedure expose m.
parse arg var
    return jReadVar(m.j.in, var)
endProcedure inVar

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

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

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

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

outX: procedure expose m.
parse arg line
    if symbol('m.tst_m') \== 'VAR' then
        call jWrite m.j.out, line
    else
        call tstOut m.tst_m, line
    return 0
endProcedure out

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

/*--- jBuf: buffer read or write (supports datataypes) --------------*/
jBuf: procedure expose m.
    m = oNew(m.class_jBuf) /* calls jBufReset */
    do ax=1 to arg()
        m.m.buf.ax = arg(ax)
        end
    m.m.buf.0 = ax-1
    return m
endProcedure jBuf
/*--- jText: write text to deleg ------------------------------------*/
jText: procedure expose m.
parse arg rdr, opt
    return oNew(jClassNew1sRdr('JText', 'm.m.maxL = arg2' , ,
            , 'call mAdd rStem, o2Text($i, m.m.maxL)'),rdr, opt)

jBufReset: procedure expose m.
parse arg m
    call oMutate m, m.class_jBuf
    do ax=1 to arg() - 1
        m.m.buf.ax = arg(ax+1)
        end
    m.m.buf.0 = ax-1
    m.m.wriMax = 1e30
    return m
endProcedure jBufReset

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

jBufCopy:
parse arg rdr
    b = jOpen(jBuf(), m.j.cWri)
    call jWriteNow b, rdr
    return jClose(b)
endProcedure jBufCopy

jSingle: procedure expose m.
parse arg m
    call jOpen m, '<'
    one = jRead(m)
    two = jRead(m)
    call jClose m
    if \ one then
        if arg() < 2 then
            call err 'empty file in jSingle('m')'
        else
            return arg(2)
    if two then
        call err '2 or more recs in jSingle('m')'
    return m.m
endProcedure jSingle

/*--- lazily create a reader class for 1s protocol ------------------*/
jClassNew1sRdr: procedure expose m.
parse arg cla, reset, op, rd, cls
    return classNew('n?' cla 'u JRWDelegOC', 'm',
        , 'jReset m.m.delegSp = in2file(arg);' reset ,
        , 'jOpen m.m.deleg = in2file(m.m.delegSp);' ,
              'call jOpen m.m.deleg, opt;' op ,
        , 'jRead if \ jRdr1sRead(m, rStem,' ,
                   quote(repAll(rd, '$i', 'm.dg.buf.ix'), '"'),
                   ') then return 0' ,
        , 'jWrite call jRdr1sWrite m, wStem,' ,
                   quote(repAll(rd, '$i', 'm.wStem.wx'), '"'),
        , 'jClose' cls||left(';', cls <> '') 'call jClose m.m.deleg')
endProcedure jNewClassRdr1s

jRdr1sRead: procedure expose m.
parse arg m, rStem, add1s
    m.rStem.0 = 0
    dg = m.m.deleg
    do while jRead(dg)
        do ix = m.dg.readIx to m.dg.buf.0
            interpret add1s
            end
        m.dg.readIx = ix - 1
        if m.rStem.0 >= 100 then
            return 1
        end
    return m.rStem.0 > 0
endProcedure jRdr1sRead

jRdr1sWrite: procedure expose m.
parse arg m, wStem, add1s
    dg = m.m.deleg
    rStem = dg'.BUF'
    do wx=1 to m.wStem.0
        interpret add1s
        end
    if m.rStem.0 > m.dg.bufMax then
        call jWriteBuf dg
    return
endProcedure jRdr1sWrite


/*--- jTalkRdr: say strings, out objects ----------------------------*/
jTalkRdr: procedure expose m.
parse arg rdr, opt
    return oNew(jClassNew1sRdr('JTalkRdr', , ,
          , "if oKindOfString($i) then say o2string($i);" ,
            "else call mAdd rStem, $i"), rdr, opt)
/* copy j end ********************************************************/