/* REXX exec/edit macro to convert JCL to 'HILITE'd HTML              */
/*** trace ?r ***************************************************** \| *
*               (C) Copyright Robert AH Prins, 2007-2016               *
************************************************************************
*  ------------------------------------------------------------------  *
* | Date       | By   | Remarks                                      | *
* |------------+------+----------------------------------------------| *
* |            |      |                                              | *
* |------------+------+----------------------------------------------| *
* | 2016-11-18 | RAHP | Multiple updates/simplifications             | *
* |------------+------+----------------------------------------------| *
* | 2012-11-26 | RAHP | Remove '$', '#' & '@' in names (ooREXX)      | *
* |------------+------+----------------------------------------------| *
* | 2012-06-25 | RAHP | Add IP address                               | *
* |------------+------+----------------------------------------------| *
* | 2009-07-27 | RAHP | Use pop-up on ISPF to display progress       | *
* |------------+------+----------------------------------------------| *
* | 2009-07-02 | RAHP | Add selection for short CSS 'em' colors      | *
* |------------+------+----------------------------------------------| *
* | 2009-04-22 | RAHP | Update comment                               | *
* |------------+------+----------------------------------------------| *
* | 2009-04-01 | RAHP | Add font selection for generated HTML        | *
* |------------+------+----------------------------------------------| *
* | 2009-02-09 | RAHP | RACF problem with 'html' extension @ NVSM    | *
* |------------+------+----------------------------------------------| *
* | 2007-09-24 | RAHP | Further tweaks                               | *
* |------------+------+----------------------------------------------| *
* | 2007-09-18 | RAHP | - add GPL V3 License                         | *
* |            |      | - use EHISUPP 'get_options' to retrieve some | *
* |            |      |   processing options                         | *
* |------------+------+----------------------------------------------| *
* | 2007-09-06 | RAHP | Cater for PC environment                     | *
* |            |      | Handle split quoted parameters               | *
* |------------+------+----------------------------------------------| *
* | 2007-08-30 | RAHP | Further fine-tuning (HTLM 4.01 Strict)       | *
* |------------+------+----------------------------------------------| *
* | 2007-08-23 | RAHP | Initial version (copy of EHIREXX)            | *
* |------------+------+----------------------------------------------| *
************************************************************************
* EHIJCL is a REXX exec/edit macro that analyses JCL and builds        *
* a HTML file with the color attributes as used by ISPF Edit.          *
*                                                                      *
* This file can be transferred to the PC by using ISPF Workstation     *
* Agent. In addition the exec might invoke the Windows application     *
* associated with file extension ".html"                               *
*                                                                      *
* The exec runs as ISPF edit macro or might be used as line command    *
* on the extended member list of ISPF List Utility (usually menu       *
* option 3.4).                                                         *
*                                                                      *
* In addition the exec can be invoked on the command line. In this     *
* case the dataset name has to be supplied as invocation parameter.    *
*                                                                      *
* t_rex will contain the environment. It can be:                       *
*                                                                      *
* - TSO     - TSO/ISPF                                                 *
* - MVS     - z/OS (PGM=IRXJCL)                                        *
* - SYSTEM  - Regina                                                   *
* - COMMAND - PC DOS 7/2000                                            *
* - CMD     - Object REXX (OS/2, Windoze)                              *
************************************************************************
* Send questions, suggestions and/or bug reports to:                   *
*                                                                      *
* robert@prino.org / robert.ah.prins@gmail.com                         *
*                                                                      *
* Robert AH Prins                                                      *
* Taboralaan 46                                                        *
* 8400 Oostende                                                        *
* Belgium                                                              *
************************************************************************
* This program is free software: you can redistribute it and/or        *
* modify it under the terms of the GNU General Public License as       *
* published by the Free Software Foundation, either version 3 of       *
* the License, or (at your option) any later version.                  *
*                                                                      *
* This program is distributed in the hope that it will be useful,      *
* but WITHOUT ANY WARRANTY; without even the implied warranty of       *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the         *
* GNU General Public License for more details.                         *
*                                                                      *
* You should have received a copy of the GNU General Public License    *
* along with this program. If not, see <http://www.gnu.org/licenses/>  *
***********************************************************************/
parse source source
parse value source with . . moi . . . cmdenv aspace .

t_rex = address()

if t_rex  = 'TSO' &,
   aspace = 'ISPF' then
  do
    "ispexec vget (zenvir)"
    envir = strip(substr(zenvir, 17, 8))
  end
else
  envir = 'OTHER'

parse arg idsn

call get_source                /* Read the JCL                        */
call init_vars                 /* Initialize the global variables     */
call build_html                /* Now go on and build the HTML output */

call ehisupp 'generate_output,'sep','htmlout  || sep ||,
                                     odsn     || sep ||,
                                     title    || sep ||,
                                     header   || sep ||,
                                     footer   || sep ||,
                                     htmlfont

if t_rex  = 'TSO' &,
   aspace = 'ISPF' then
  if envir \= 'BATCH' then
    do
      /*****************************************************************
      * Show the resulting dataset, if desired                         *
      *****************************************************************/
      if view_html = 'YES' then
        "ispexec view dataset("odsn")"

      /*****************************************************************
      * Transfer the html file to the PC                               *
      *****************************************************************/
      if xfer_wsa = 'YES' then
        call ehisupp 'xfer_and_show_html,'sep','dir_pc   || sep ||,
                                                htmlfile || sep ||,
                                                odsn     || sep ||,
                                                ipaddr   || sep ||,
                                                start_browser
    end
  else
    do
      if macmode then
        "isredit end"
    end
exit

/***********************************************************************
* TRANSLATE_ENTITIES                                                   *
*                                                                      *
* This procedure translates special characters to HTML entities        *
***********************************************************************/
translate_entities: procedure expose special_chars special_html
  parse arg in

  out = ''

  if translate(in, ' ', special_chars) = in then
    out = in
  else
    do while in \== ''
      c = left(in, 1)
      k = wordpos(c, special_chars)

      if k \= 0 then
        out = out || word(special_html, k)
      else
        out = out || c

      in = substr(in, 2)
    end
return out

/***********************************************************************
* QUOTER:                                                              *
*                                                                      *
* This procedure adds HTML for quoted strings                          *
***********************************************************************/
quoter: procedure expose col_str in_apostq
  parse arg in

  out = ''

  if in_apostq then
    do
      in_apost = 1

      p   = pos(' ', in)
      p   = verify(in, ' ',, p)
      out = left(in, p - 1)col_str
      in  = substr(in, p)
    end
  else
    in_apost = 0

  do while in \== ''
    c = left(in, 1)

    select
      when c = '''' &,
           in_apost then
        do
          out       = out || '''</em>'
          in_apost  = 0
          in_apostq = 0
        end

      when c = '''' &,
          \in_apost then
        do
          out       = out || col_str''''
          in_apost  = 1
          in_apostq = 1
        end

      otherwise
        out = out || c
    end

    in = substr(in, 2)
  end

if in_apost then
  out = out'</em>'
return out

/***********************************************************************
* HILITE:                                                              *
*                                                                      *
* This procedure adds HTML for special characters                      *
***********************************************************************/
hilite: procedure expose special_hilite special_chars special_html,
                         col_spc paren col_par. in_apostq
  parse arg in

  out = ''

  if in_apostq then
    do
      in_apost = 1

      p   = pos(' ', in)
      p   = verify(in, ' ',, p)
      out = left(in, p - 1)
      in  = substr(in, p)
    end
  else
    in_apost = 0

  do while in \== ''
    c = left(in, 1)

    select
      when c = '''' &,
           in_apost then
        do
          out      = out || c
          in_apost = 0
        end

      when c = '''' &,
          \in_apost then
        do
          out      = out || c
          in_apost = 1
        end

      when c = '(' &,
          \in_apost then
        do
          paren = (paren + 1) // 5
          out   = out || col_par.paren'(</em>'
        end

      when c = ')' &,
          \in_apost then
        do
          out   = out || col_par.paren')</em>'
          paren = (paren + 4) // 5
        end

      when pos(c, special_hilite) \= 0 &,
          \in_apost then
        do
          p = wordpos(c, special_chars)

          if p \= 0 then
            c = word(special_html, p)

          out = out || col_spc || c'</em>'
        end

      otherwise
        out = out || c
    end

    in = substr(in, 2)
  end
return out

/***********************************************************************
* QUOTE_CHECK:                                                         *
*                                                                      *
* This procedure checks post-keyword data (PARM=) for quotes           *
***********************************************************************/
quote_check: procedure expose l2 l3
  t2 = l2
  t3 = l3

  q2 = space(t2, 0)
  z2 = space(translate(t2, ' ', ''''), 0)

  /*********************************************************************
  * If d = 1 there are unmatched quotes                                *
  *********************************************************************/
  d  = (length(q2) - length(z2)) // 2

  do while d = 1 & t3 \= ' '
    p  = pos('''', t3)
    t2 = t2 || left(t3, p)
    t3 = substr(t3, p + 1)

    do while left(t3, 1) \= ' '
      t2 = t2 || left(t3, 1)
      t3 = substr(t3, 2)
    end

    q2 = space(t2, 0)
    z2 = space(translate(t2, ' ', ''''), 0)

    d  = (length(q2) - length(z2)) // 2
  end

  l2 = t2
  l3 = t3
return

/***********************************************************************
* BUILD_LIST_OF_KEYWORDS:                                              *
*                                                                      *
* This procedure loads the list of JCL keywords                        *
***********************************************************************/
build_list_of_keywords:
  keyword.        = 0

  keyword.CNTL    = 1
  keyword.COMMAND = 1
  keyword.DATA    = 1
  keyword.DD      = 1
  keyword.DLM     = 1
  keyword.ELSE    = 1
  keyword.ENDCNTL = 1
  keyword.ENDIF   = 1
  keyword.EXEC    = 1
  keyword.EXPORT  = 1
  keyword.IF      = 1
  keyword.INCLUDE = 1
  keyword.JCLLIB  = 1
  keyword.JOB     = 1
  keyword.OUTPUT  = 1
  keyword.PEND    = 1
  keyword.PROC    = 1
  keyword.SET     = 1
  keyword.THEN    = 1
  keyword.XMIT    = 1
return

/***********************************************************************
* GET_SOURCE:                                                          *
*                                                                      *
* Read the JCL                                                         *
***********************************************************************/
get_source:
  macmode = 0
  rxdata. = ''
  pgm     = ''

  select
    when t_rex = 'TSO' then call get_source_tso
    when t_rex = 'MVS' then call get_source_mvs
    otherwise               call get_source_pc
  end
return

/***********************************************************************
* GET_SOURCE_TSO:                                                      *
*                                                                      *
* Read the text when running under TSO                                 *
***********************************************************************/
get_source_tso:
  if aspace = 'ISPF' then
    "isredit macro (parm) NOPROCESS"
  else
    rc = 4

  /*********************************************************************
  * Running as edit macro                                              *
  *********************************************************************/
  if rc = 0 then
    do
      macmode = 1

      if parm = '?' then
        do
          "isredit ehihelp" moi
          exit
        end

      "isredit process range HI"
      if rc <= 4 then
        do
          "isredit (ZF) = linenum .zfrange"
          "isredit (ZL) = linenum .zlrange"
        end
      else
        do
          "isredit ehihelp" moi
          exit
        end

      "isredit (DSN) = dataset"
      "isredit (MEM) = member"

      if mem = '' then
        idsn = "'" || dsn || "'"
      else
        do
          pgm  = mem
          idsn = "'" || dsn || '(' || mem || ")'"
        end

      /*****************************************************************
      * Read the (selected) source into a stem                         *
      *****************************************************************/
      i = 0
      do j = +zf to +zl
        "isredit (DATALINE) = line" j

        i        = i + 1
        rxdata.i = strip(dataline, 'T')
      end

      rxdata.0 = i
    end
  /*********************************************************************
  * Running as TSO command                                             *
  *********************************************************************/
  else
    do
      if idsn = '' then
        do
          msg =     left('Error - No dataset name passed', 75)
          msg = msg left(moi 'can be used as an edit macro or',
                         'as a line command on the ISPF', 75)
          msg = msg left('dataset list utility. In both cases the',
                         'dataset name will be automatically', 75)
          msg = msg left('determined.', 75)
          msg = msg left('If you call' moi 'on the command line you',
                         'have to pass the name of the', 75)
          msg = msg left('dataset to be processed, e.g.', 75)
          msg = msg left('Command ===>' moi,
                         '''my.jcl.dataset(test)''', 75)

          zedsmsg = ''
          zedlmsg = msg

          if t_rex  = 'TSO'  &,
             aspace = 'ISPF' &,
             envir \= 'BATCH' then
            "ispexec setmsg msg(ISRZ001)"
          else
            do while msg \= ''
              say left(msg, 75)
              msg = substr(msg, 76)
            end

          exit 8
        end

      /*****************************************************************
      * Force single quotes around dataset name and check if it's OK   *
      *****************************************************************/
      idsn = "'" || strip(idsn,, '''') || "'"

      if sysdsn(idsn) \= 'OK' then
        do
          say 'Error - Dataset' idsn 'could not be found'
          exit 8
        end

      /*****************************************************************
      * Extract member name, if present                                *
      *****************************************************************/
      parse var idsn . '(' mem ')'

      if mem \= '' then
        pgm = mem

      /*****************************************************************
      * Read the JCL                                                   *
      *****************************************************************/
      dynlib = 'dyn'random(99999)

      "alloc f("dynlib") da("idsn") shr reu"
      if rc > 0 then
        do
          say 'Error - Dataset' idsn 'could not be allocated - rc' rc
          exit 8
        end

      "execio * diskr" dynlib "( stem rxdata. finis"
      if rc > 0 then
        do
          say 'Error - Dataset' idsn 'could not be read - rc' rc
          exit 8
        end

      "free f("dynlib")"
    end

  olines = rxdata.0
return

/***********************************************************************
* GET_SOURCE_PC:                                                       *
*                                                                      *
* Read the text when running on the PC                                 *
***********************************************************************/
get_source_pc:
  if idsn = '' then
    do
      say 'Syntax:' moi 'file.jcl'
      exit
    end

  do i = 1 by 1 while lines(idsn)
    rxdata.i = linein(idsn)
  end

  rxdata.0 = i - 1
  olines   = rxdata.0
return

/***********************************************************************
* INIT_VARS:                                                           *
*                                                                      *
* This procedure initialises the global variables                      *
***********************************************************************/
init_vars:
  /*********************************************************************
  * Parameter separator for EHISUPP exec                               *
  *********************************************************************/
  sep = x2c(00)d2c(random(2**16))x2c(ff)d2c(random(2**16))x2c(00)
  sep = translate(sep, x2c(bababababa), ' <>&"')

  /*********************************************************************
  * Get processing options                                             *
  *********************************************************************/
  opt = ehisupp('get_options,'sep','moi)
  parse value opt with view_html     (sep),
                       xfer_wsa      (sep),
                       start_browser (sep),
                       ispf_edit     (sep),
                       show_progress (sep),
                       dir_pc        (sep),
                       htmlfont      (sep),
                       ipaddr        (sep) .

  /*********************************************************************
  * Temporary output dataset                                           *
  *********************************************************************/
  if mem \= '' then
    odsn = "'" || userid() || '.' || mem || ".jcl.html'"
  else
    odsn = "'" || userid() || '.' || moi || ".jcl.html'"

  /*********************************************************************
  * Text strings for title, header and footer                          *
  *********************************************************************/
  title  = 'JCL:' strip(idsn,, '''')
  header = 'JCL:' strip(idsn,, '''')
  now    = date('S')
  now    = left(now, 4)'-'substr(now, 5, 2)'-'right(now, 2)'T'time()
  footer = 'Generated on' now 'by' userid() 'with' moi

  /*********************************************************************
  * Name of generated html file on PC                                  *
  *********************************************************************/
  if pgm \= '' then
    htmlfile = pgm || '.html'
  else
    htmlfile = 'jcl.html'

  /*********************************************************************
  * HTML colors                                                        *
  *                                                                    *
  * - lime(green) - default                                            *
  * - red         - keywords                                           *
  * - white       - quoted strings                                     *
  * - aqua(turq)  - comments                                           *
  * - yellow      - special characters                                 *
  * - blue        - in-stream data                                     *
  *********************************************************************/
  col_dft        = '<em class="l">'                        /* lime    */
  col_key        = '<em class="r">'                        /* red     */
  col_str        = '<em class="w">'                        /* white   */
  col_com        = '<em class="t">'                        /* turq    */
  col_spc        = '<em class="y">'                        /* yellow  */
  col_ins        = '<em class="b">'                        /* blue    */

  /*********************************************************************
  * Colors for nested parentheses                                      *
  *********************************************************************/
  col_par.0      = '<em class="f">'                        /* fuchsia */
  col_par.1      = '<em class="y">'                        /* yellow  */
  col_par.2      = '<em class="w">'                        /* white   */
  col_par.3      = '<em class="r">'                        /* red     */
  col_par.4      = '<em class="t">'                        /* turq    */

  /*********************************************************************
  * HTML special characters and their defined entities                 *
  *********************************************************************/
  special_chars  = '< > & "'
  special_html   = '&lt; &gt; &amp; &quot;'

  /*********************************************************************
  * Characters to be highlighted                                       *
  *********************************************************************/
  special_hilite = '(),|<>^&='

  /*********************************************************************
  * Characters separating words                                        *
  *********************************************************************/
  separator      = ' '

  /*********************************************************************
  * In-stream data formats                                             *
  *********************************************************************/
  drop !
  isd.1   = 'DD *'
  isd.!.1 = 'in_stream = 1'
  isd.2   = 'DD DATA'
  isd.!.2 = 'in_data   = 1'
  isd.0   = 2
return

/***********************************************************************
* BUILD_HTML:                                                          *
*                                                                      *
* This procedure builds the HTML output                                *
***********************************************************************/
build_html:
  /*********************************************************************
  * Load the list of JCL keywords                                      *
  *********************************************************************/
  call build_list_of_keywords

  /*********************************************************************
  * Switches                                                           *
  *********************************************************************/
  kwbegin   = 1                 /* Do we expect a new keyword ?       */

  in_if     = 0                 /* Inside a multi-line 'IF' statement */
  in_apost  = 0                 /* Inside a '(apost) delimited string */
  in_apostq = 0                 /* Inside a '(apost) delimited string */
  in_quote  = 0                 /* Inside a "(quote) delimited string */
  in_stream = 0                 /* Inside in-stream (DD *) data       */
  in_data   = 0                 /* Inside in-stream (DD DATA) data    */
  paren     = 1                 /* Nested parentheses level           */
  is        = -1                /* Not in in-stream data              */

  /*********************************************************************
  * Initialize the html output string                                  *
  *********************************************************************/
  if ispf_edit = 'ISPF' then
    htmlout = x2c(ff)ispf_edit || x2c(ff)right(olines, 6, '0')x2c(ff)
  else
    htmlout = ''

  tempout = ''

  /*********************************************************************
  * Loop over the JCL                                                  *
  *********************************************************************/
  i = time('E')

  do i = 1 to rxdata.0
    /*******************************************************************
    * Display (optional) progress messages                             *
    *******************************************************************/
    if show_progress > 0 then
      if i // show_progress = 0 then
        do
          progress = 'Elapsed time' right(time('E'), 12),
                     '- lines processed' right(i, 6)

          if t_rex  = 'TSO'  &,
             aspace = 'ISPF' then
            rc = ehisupp('monitor,'moi 'Progress,'progress)
          else
            say progress
        end

    /*******************************************************************
    * Process in-stream data                                           *
    *******************************************************************/
    if i = is then
      do
        drop !
        interpret isd.!.sj

        do i = is by 1 while (in_data | in_stream) & i <= rxdata.0
          line = rxdata.i

          /*************************************************************
          * 'DD *' in-stream data is always ended with // in pos 1-2|  *
          *************************************************************/
          if in_stream &,
             left(line, 2) = '//' then
            do
              in_stream = 0
              leave i
            end

          data = translate_entities(line)

          select
            when (in_data    |,
                  in_stream) &,
                 left(line, 2) = dlm then
              do
                in_data   = 0
                in_stream = 0

                if length(data) > 2 then
                  com = col_com || substr(data, 3)'</em>'
                else
                  com = ''

                tempout = tempout || col_dft || dlm'</em>'com'<br>'
              end

            when in_data |,
                 in_stream then
              tempout = tempout || col_ins || data'</em><br>'

            otherwise nop
          end
        end
      end

    /*******************************************************************
    * Refetch line and create a squeezed copy                          *
    *******************************************************************/
    line = strip(left(rxdata.i, 72), 'T')
    sqz  = space(line)
    noq  = space(translate(line, ' ', ''''))

    /*******************************************************************
    * Only w2 can be a JCL keyword                                     *
    *******************************************************************/
    w2   = word(line, 2)

    /*******************************************************************
    * Keywords needing special attention                               *
    *******************************************************************/
    if keyword.w2 then
      do
        /***************************************************************
        * Might be followed by in-stream data (and DLM=)               *
        ***************************************************************/
        if w2 = 'DD' then
          do
            dlm = '/*'
            is  = 0

            /***********************************************************
            * Two options, 'DD *' and 'DD DATA'                        *
            ***********************************************************/
            do j = 1 to isd.0 while is = 0
              p = pos(isd.j, sqz)

              if p \= 0 then
                do
                  sj = j
                  c  = substr(sqz, p + length(isd.j), 1)

                  select
                    /***************************************************
                    * If 'DD *' or 'DD DATA' is followed by a blank,   *
                    * there is no 'DLM=' keyword and the in-stream     *
                    * data starts on the next line of the JCL          *
                    ***************************************************/
                    when c = ' ' then
                      is  = i + 1

                    /***************************************************
                    * If 'DD *' or 'DD DATA' is followed by a comma,   *
                    * we need to parse the remainder of the line (and  *
                    * possibly following ones) to check if a 'DLM'     *
                    * keyword is present                               *
                    ***************************************************/
                    when c = ',' then
                      do
                        /***********************************************
                        * Break on the next space                      *
                        ***********************************************/
                        q   = pos(' ', sqz' ', p + length(isd.j))
                        sqz = left(sqz, q - 1)

                        /***********************************************
                        * Append subsequent lines                      *
                        ***********************************************/
                        do k = i + 1 by 1 while right(sqz, 1) = ','
                          tmp = space(substr(rxdata.k, 4))
                          q   = pos(' ', tmp' ')
                          sqz = sqz || left(tmp, q - 1)
                        end

                        is = k
                        q  = pos('DLM=', sqz)

                        /***********************************************
                        * Parse out the delimiter                      *
                        ***********************************************/
                        if q \= 0 then
                          do
                            /*******************************************
                            * Unlikely, but can be ''''''              *
                            *******************************************/
                            c6 = substr(sqz, q + 4, 6)

                            /*******************************************
                            * Remove initial quote, cannot use STRIP|  *
                            *******************************************/
                            if left(c6, 1) = '''' then
                              c6 = substr(c6, 2)

                            if left(c6, 1) = '''' then
                              dlm = substr(c6, 2, 2)
                            else
                              dlm = substr(c6, 1, 2)
                          end
                      end

                    otherwise nop
                  end
                end
            end
          end

        /***************************************************************
        * Can be multi-line - no comments allowed until after 'THEN'|  *
        ***************************************************************/
        if w2 = 'IF' then
          do
            in_if = 1

            if pos(' THEN', line) \= 0 then
              in_if = 2
          end
      end

    if in_if = 1 then
      if pos(' THEN ', line) \= 0 then
        in_if = 3

    select
      /*****************************************************************
      * End-of-JCL line                                                *
      *****************************************************************/
      when line = '//' then
        tempout = tempout || col_dft || line'</em><br>'

      /*****************************************************************
      * Comment line                                                   *
      *****************************************************************/
      when left(line, 3) = '//*' then
        tempout = tempout || col_com || line'</em><br>'

      /*****************************************************************
      * IF+THEN processing                                             *
      *****************************************************************/
      when in_if = 2 then
        do
          in_if = 0

          pi = pos(' IF ', line) + 1
          pt = pos(' THEN ', line' ') + 1

          l1 = substr(line, 1, pi - 1)
          l2 = substr(line, pi + 2, pt - (pi + 2))
          l3 = substr(line, pt + 4)

          if l2 \= translate(l2, ' ', special_hilite) then
            l2 = hilite(l2)

          if l3 \= '' then
            l3 = col_com || l3'</em>'

          tempout = tempout ||,
                    col_dft || l1'</em>' ||,
                    col_key'IF</em>' ||,
                    col_dft || l2'</em>' ||,
                    col_key'THEN</em>'l3'<br>'
        end

      /*****************************************************************
      * IF only processing                                             *
      *****************************************************************/
      when in_if = 1 then
        do
          pi = pos(' IF ', line) + 1

          if pi \= 1 then
            do
              l1 = substr(line, 1, pi - 1)
              l2 = substr(line, pi + 2)

              if l2 \= translate(l2, ' ', special_hilite) then
                l2 = hilite(l2)

              tempout = tempout ||,
                        col_dft || l1'</em>' ||,
                        col_key'IF</em>' ||,
                        col_dft || l2'</em><br>'
            end
          else
            do
              if line \= translate(line, ' ', special_hilite) then
                line = hilite(line)

              tempout = tempout || col_dft || line'</em><br>'
            end
        end

      /*****************************************************************
      * THEN only processing                                           *
      *****************************************************************/
      when in_if = 3 then
        do
          in_if = 0

          pt = pos(' THEN ', line' ') + 1

          l1 = substr(line, 1, pt - 1)
          l2 = substr(line, pt + 4)

          if l1 \= translate(l1, ' ', special_hilite) then
            l1 = hilite(l1)

          if l2 \= '' then
            l2 = col_com || l2'</em>'

          tempout = tempout ||,
                    col_dft || l1'</em>' ||,
                    col_key'THEN</em>'l2'<br>'
        end

      /*****************************************************************
      * Normal JCL                                                     *
      *****************************************************************/
      when left(line, 2) = '//' then
        if keyword.w2 then
          do
            lk = length(w2)

            /***********************************************************
            * Position of keyword and first following space            *
            ***********************************************************/
            p  = pos(' 'w2' ', line' ') + 1
            q  = pos(' ', line, p)

            /***********************************************************
            * Pre-keyword data                                         *
            ***********************************************************/
            l1 = substr(line, 1, p - 1)

            tempout = tempout ||,
                      col_dft || l1'</em>' ||,
                      col_key || w2'</em>'

            /***********************************************************
            * q can zero, ie no spaces after the keyword, on a 'DD'    *
            * override or 'ENDIF' statement|                           *
            ***********************************************************/
            if q = 0 then
              tempout = tempout || '<br>'
            else
              do
                /*******************************************************
                * Post-keyword text                                    *
                *******************************************************/
                l2 = substr(line, p + lk)

                if w2 = 'ENDIF' then
                  tempout = tempout || col_com || l2'</em><br>'
                else
                  do
                    p  = verify(l2, ' ')
                    q  = pos(' ', l2' ', p)

                    l3 = substr(l2, q)
                    l2 = substr(l2, 1, q - 1)

                    if pos('''', l2) \= 0 then
                      call quote_check

                    if l2 \= translate(l2, ' ', special_hilite) then
                      l2 = hilite(l2)

                    if pos('''', l2) \= 0 then
                      l2 = quoter(l2)

                    if l3 \= '' then
                      l3 = col_com || l3'</em>'

                    tempout = tempout || col_dft || l2'</em>'l3'<br>'
                  end
              end
          end
        else
          do
            p  = verify(line, ' ',, 4)
            p  = pos(' ', line' ', p)
            l2 = substr(line, 1, p - 1)
            l3 = substr(line, p)

            if pos('''', l2) \= 0 then
              call quote_check

            if l2 \= translate(l2, ' ', special_hilite) then
              l2 = hilite(l2)

            if pos('''', l2) \= 0 then
              l2 = quoter(l2)

            if l3 \= '' then
              l3 = col_com || l3'</em>'

            tempout = tempout || col_dft || l2'</em>'l3'<br>'
          end

      /*****************************************************************
      * We shouldn't get her at all - if the JCL ends in in-stream     *
      * data i > rxdata.0                                              *
      *****************************************************************/
      otherwise
        if i <= rxdata.0 then
          do
            text    = translate_entities(line)
            text    = line
            tempout = tempout || text || '<br>'
          end
    end

    if length(tempout) > 250 then
      do
        htmlout = htmlout || tempout
        tempout = ''
      end
  end

  htmlout = htmlout || tempout
return