zOs/REXX/FTAB

/* copy fTab begin ****************************************************
    output Modes: t = tableMode 1 line per object with fixed colums th
                  c = colMode   1 line per column/field of object

    we build a format for each column
             and a set of title lines, one sequence printed before
                                     , one sequence printed after
    lifeCycle fTab           sql

        fTabReset            sqlFTabReset
        fTabAdd *               fTabAdd *       add col info
                             sqlFTabOthers ?
        fTabGenTab or fTabGenCol
        fTabBegin                                      header lines
        fTab1 * / tTabCol *
        fTabEnd                                        trailer lines
    primary data for each col
        .col     : column (rexx) name plus aDone
        .done    : == 0 sqlFtabOthers should add it again
        .fmt     : format
        .labelLo : long  label for multi line cycle titles
        .labelSh : short label for singel title line (colwidth)
        .tit.*   : title line piece for this col
**********************************************************************/
fTabIni: procedure expose m.
    if m.fTab_ini == 1 then
        return
    m.fTab_ini = 1
    call classIni
    m.fTab_class = classNew("n FTab u ORun, m oRun call fTab m")
    return
endProcedure fTabIni

fTabReset: procedure expose m.
parse arg m, m.m.titBef, m.m.titAft, m.m.opt
    call fTabIni
    if m.m.titBef == '' & m.m.titaft == '' then do
        m.m.titBef = 'c 1'
        m.m.titAft = '1 c'
        end
    if m.m.titBef == '-' then
        m.m.titBef = ''
    if m.m.titAft == '-' then
        m.m.titAft = ''
    m.m.generated = ''
    m.m.verbose = pos('s', m.m.opt) < 1 /* not silent */
    m.m.0 = 0
    m.m.set.0 = 0
    return fTabResetCols(oMutate(m, m.fTab_class))
endProcedure fTabReset

/*--- clean out all cols of ftab, but keep settings -----------------*/
fTabResetCols: procedure expose m.
parse arg m
    m.m.0 = 0
    return m

/*--- for column cx set title tx ------------------------------------*/
fTabSetTit: procedure expose m.
parse arg m, cx, tx, t1
    m.m.generated = ''
    if tx > m.m.cx.tit.0 then do
        do xx=m.m.cx.tit.0+1 to tx-1
            m.m.cx.tit.xx = ''
            end
        m.m.cx.tit.0 = tx
        end
    m.m.cx.tit.tx = t1
    return m
endProcedure fTabSetTit

/*--- set default atts for a col name ------------------------------*/
fTabSet: procedure expose m.
parse arg m, c1 aDone, f1, sh, lo
    sx = m.m.set.0 + 1
    m.m.set.0 = sx
    m.m.set.sx = c1 aDone
    m.m.set.sx.fmt = f1
    m.m.set.sx.labelSh = sh
    m.m.set.sx.labelLo = lo
    m.m.set.c1 = sx
    return
endProcedure fTabSet

/*--- add a column --------------------------------------------------
       m, rexxName done, fmt, labelShort, labelLong, titles... ------*/
fTabAdd: procedure expose m.
parse arg m, rxNm aDone
    m.m.generated = ''
    cx = m.m.0 + 1
    m.m.0 = cx
    cc = m'.'cx
    m.cc.col = rxNm
    m.cc.done = aDone \== 0
parse arg  , , m.cc.fmt, m.cc.labelSh, m.cc.labelLo
    if rxNm == '=' | rxNm == 0 | rxNm == 1 then
        call err 'bad rxNm' rxNm
    if \ (aDone == '' | aDone == 0 | aDone == 1) then
        call err 'bad aDone' aDone
    m.cc.tit.0 = max(arg()-4, 1)
    m.cc.tit.1 = ''
    do tx=2 to m.cc.tit.0
        m.cc.tit.tx = arg(tx+4)
        end
    return cc
endProcedure fTabAdd

/*--- complete column info-------------------------------------------*/
fTabColComplete: procedure expose m.
parse arg m
    do cx=1 to m.m.0
        nm = m.m.cx.col
        f1 = m.m.cx.fmt
        if f1 = '' then
            m.m.cx.fmt = '@.'nm'%-8C'
        else do
            px = pos('%', f1)
            ax = pos('@', f1)
            if px > 0 & (ax <= 0 | ax >= px) then
                m.m.cx.fmt = left(f1, px-1)'@.'nm || substr(f1, px)
            end
        if m.m.cx.labelLo = '' then
            if nm = '' then
                m.m.cx.labelLo = '='
            else
                m.m.cx.labelLo = nm
        if m.m.cx.labelSh = '' then
            m.m.cx.labelSh = m.m.cx.labelLo
        end
    return
endProcedure fTabColComplete

/*--- generate line formats and title lines -------------------------*/
fTabGenTab: procedure expose m.
parse arg m, sep
    if m.m.generated == '' then
        call fTabColComplete m
    m.m.tit.0 = words(m.m.titBef m.m.titAft) + 5
    do tx=1 to m.m.tit.0
        m.m.tit.tx = ''
        end
    f = ''
    tLen = 0
    do kx=1 to m.m.0
       rxNm = m.m.kx.col
       call mPut 'F_TEMP'left('.', rxNm\=='')rxNm, m.m.kx.labelSh
       t1 = f(m.m.kx.fmt, 'F_TEMP')
       m.m.kx.len = length(t1)
       if pos(strip(t1), m.m.kx.labelSh) < 1 then /* corrupted| */
           t1 = left(left('', max(0, verify(t1, ' ') -1)) ,
                 || m.m.kx.labelSh, length(t1))
       m.m.kx.tit.1 = t1
       if kx = 1 then do
           f = m.m.kx.fmt
           end
       else do
           tLen = tLen + length(sep)
           f = f || sep || m.m.kx.fmt
           end
       m.m.kx.start = tLen+1
       do tx=1 to m.m.kx.tit.0
           if m.m.kx.tit.tx \== '' then
               if tx > 1 | pos('-', m.m.opt) < 1 then
                   m.m.tit.tx = left(m.m.tit.tx, tLen) ,
                       || strip(m.m.kx.tit.tx, 't')
               else if \ abbrev(m.m.kx.tit.tx, ' ') then
                   m.m.tit.tx = left(m.m.tit.tx, tLen, '-') ,
                       || strip(m.m.kx.tit.tx, 't')
               else
                   m.m.tit.tx = left(m.m.tit.tx, tLen, '-') ,
                          || right(strip(m.m.kx.tit.tx),
                                , length(m.m.kx.tit.tx), '-')
           end
       tLen = tLen + m.m.kx.len
       end
    m.m.len = tLen
    if pos('-', m.m.opt) > 0 then
        m.m.tit.1 = left(m.m.tit.1, tLen +3, '-')
    m.m.fmt = fGen('%>', f)

    cSta = m.m.tit.0+3          /* compute cycle titles */
    cycs = ''
    cyEq = 1
    do cEnd=cSta until kx > m.m.0
            /*try with cycle lines for cSta to cEnd */
        cycs = cycs cEnd
        cx = cSta
        firstRound = 1
        do kx=1 to m.m.0
            if firstRound then
                m.m.tit.cx =  left('', m.m.kx.start-1)m.m.kx.labelLo
            else if length(m.m.tit.cx) <= m.m.kx.start - 2 then
                m.m.tit.cx = left(m.m.tit.cx, m.m.kx.start - 1) ,
                             || m.m.kx.labelLo
            else
                leave
            if cyEq then
               cyEq = translate(m.m.kx.labelLo) ,
                    = translate(m.m.kx.labelSh)
            cx = cx + 1
            if cx > cEnd then do
                cx = cSta
                firstRound = 0
                end
            end
        end
    m.m.cycles = strip(cycs)
    if cyEq & words(cycs) <=  1 then
        m.m.cycles = ''
    m.m.generated = m.m.generated't'
    return
endProcedure fTabGenTab

/*--- generate column format ----------------------------------------*/
fTabGenCol: procedure expose m.
parse arg m
    if m.m.generated == '' then
        call fTabColComplete m
    do kx=1 to m.m.0
        t = m.m.kx.labelLo
        l = if(m.m.kx.labelSh == t, , m.m.kx.labelSh)
        f = lefPad(lefPad(strip(l), 10) t, 29)
        if length(f) > 29 then
           if length(l || t) < 29 then
               f = l || left('', 29 - length(l || t))t
           else
               f = lefPad(strip(l t), 29)
        g = strip(m.m.kx.fmt)
        o = right(g, 1)
        if pos(o, 'dief') > 0 then
            f = f '@'m.m.kx.col'%12e @'m.m.kx.col'%18c'
        else if o = 'C' then
            f = f left(g, length(g)-1)'c'
        else
            f = f g
        m.m.kx.colFmt = f
        end
    m.m.generated = m.m.generated'c'
    return
endProcedure fTabGenCol

/*--- output all of rdr in tab format -------------------------------*/
fTab: procedure expose m.
parse arg m, rdr
    if pos('a', m.m.opt) < 1 then
        i = rdr
    else do
        i = in2Buf(rdr)
        if m.i.buf.0 > 0 then
            call fTabDetect m, i'.BUF'
        end
    if pos('o', m.m.opt) > 0 then do
        call pipeWriteAll i
        end
    else if pos('c', m.m.opt) > 0 then do
        if pos('c', m.m.generated) < 1 then
            call fTabGenCol m
        i = jOpen(in2file(i), '<')
        do rx=1 while jRead(i)
            call out left('--- row' rx '',  80, '-')
            call fTabCol m, m.i
            end
        call out left('--- end of' (rx-1) 'rows ', 80, '-')
        call jClose i
        end
    else do
        call fTabBegin m
        call fAll m.m.fmt, i
        return fTabEnd(m)
        end
    return m
endProcedure fTab

/*--- output object i in col format ---------------------------------*/
fTabCol: procedure expose m.
parse arg m, i
    do cx=1 to m.m.0
        call out f(m.m.cx.colFmt, i)
        end
    return 0
endProcedure fTabCol

fTabBegin: procedure expose m.
parse arg m
    if pos('t', m.m.generated) < 1 then
        call fTabGenTab m, ' '
    return fTabTitles(m, m.m.titBef)
endProcedure fTabBegin

fTabEnd: procedure expose m.
parse arg m
    return fTabTitles(m, m.m.titAft)

fTabTitles: procedure expose m.
parse arg m, list
    list = repAll(list, 'c', m.m.cycles)
    do tx=1 to words(list)
        t1 = word(list, tx)
        call out m.m.tit.t1
        end
    return m
endProcedure fTabTitles

/*--- auto tables detect format from data ---------------------------*/
/*--- detect fmt from a rdr and write it formatted to stdOut --------*/
fTabAuto: procedure expose m.
parse arg m, rdr
    if m == '' then
        m = fTabReset(f_auto, 1, , 'a')
    else if pos('a', m.m.opt) < 1 then
        m.m.opt = 'a'm.m.opt
    return fTab(m, rdr)
endProcedure fTabAuto

/*--- generate format for all fields of a stem of objects -----------*/
fTabDetect: procedure expose m.
parse arg m, b
    do cx=1 to m.m.0
        rxNm = m.m.cx.col
        done.rxNm = m.m.cx.done
        if m.m.cx.fmt == '' then
            m.m.cx.fmt = fTabDetectFmt(b, left('.', rxNm \== '')rxNm)
        end
    ff = oFldD(m.b.1)
    do fx=1 to m.ff.0
        rxNm = substr(m.ff.fx, 2)
        if done.rxNm \== 1 then do
             cc = fTabAdd(m, rxNm)
             m.cc.fmt = fTabDetectFmt(b, left('.', rxNm \== '')rxNm)
             end
        end
    return
endProcedure fTabDetect

/*--- detect format for one field in stem st ------------------------*/
fTabDetectFmt: procedure expose m.
parse arg st, suf
    lMa = -1
    rMa = -1
    bMa = -1
    aDiv = 0
    nMi =  9e999
    nMa = -9e999
    eMi =  9e999
    eMa = -9e999
    eDa = 2
    dMa = -9e999
    do sx=1 to m.st.0
        v = mGet(m.st.sx || suf)
        lMa = max(lMa, length(strip(v, 't')))
        rMa = max(rMa, length(strip(v, 'l')))
        bMa = max(bMa, length(strip(v, 'b')))
        if \ dataType(v, 'n') then do
            if length(v) > 100 then
                aDiv = 99
            else if aDiv <=3 then
                if aDiv.v \== 1 then do
                    aDiv.v = 1
                    aDiv = aDiv + 1
                    end
            iterate
            end
        v = strip(v)
        nMi = min(nMi, v)
        nMa = max(nMa, v)
        ex = verify(v, 'eEfF', 'm')
        if ex > 0 then do
            eMa = max(eMa, substr(v, ex+1))
            eMi = min(eMi, substr(v, ex+1))
            v = left(v, ex-1)
            do while pos(left(v,1), '+-0') > 0
                v = substr(v, 2)
                end
            eDa = max(eDa, length(v) - (pos('.', v) > 0))
            end
        dx = pos('.', v)
        if dx > 0 then do
            do while right(v, 1) == 0
                v = left(v, length(v)-1)
                end
            dMa = max(dMa, length(v)-dx)
            end
        end
    if nMi > nMa | aDiv > 3 then
        newFo = '-'max(1, (lMa+0))'C'
    else if eMi <= eMa then do
        newFo = ' ' || (eDa+max(length(eMa), length(eMi))+3) ,
             || '.'||(eDa-1)'e'
        end
    else do
        be = max(length(trunc(nMi)), length(trunc(nMa)))
        if dMa <= 0 then
            newFo = max(be, bMa)'I'
        else
            newFo = max(be+1+dMa, bMa)'.'dMa'I'
        end
    return '%'newFo
endProcedure fTabDetectFmt

/* copy fTab end   ***************************************************/