/* REXX exec/edit macro to convert ASM to 'HILITE'd HTML              */
/*** trace ?r ***************************************************** \| *
*               (C) Copyright Robert AH Prins, 2007-2016               *
************************************************************************
*  ------------------------------------------------------------------  *
* | Date       | By   | Remarks                                      | *
* |------------+------+----------------------------------------------| *
* |            |      |                                              | *
* |------------+------+----------------------------------------------| *
* | 2016-11-18 | RAHP | Multiple updates/simplifications             | *
* |------------+------+----------------------------------------------| *
* | 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-06-15 | RAHP | Translate entities in End-Of-Line comments   | *
* |------------+------+----------------------------------------------| *
* | 2009-04-22 | RAHP | Update comment                               | *
* |------------+------+----------------------------------------------| *
* | 2009-04-01 | RAHP | Add font selection for generated HTML        | *
* |------------+------+----------------------------------------------| *
* | 2009-02-09 | RAHP | - Correct comment                            | *
* |            |      | - 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-17 | RAHP | Complete operand parser                      | *
* |------------+------+----------------------------------------------| *
* | 2007-09-11 | RAHP | Initial version (copy of EHIREXX)            | *
* |------------+------+----------------------------------------------| *
************************************************************************
* EHIASM is a REXX exec/edit macro that analyses ASM 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 assembler source           */
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

/***********************************************************************
* GET_SOURCE:                                                          *
*                                                                      *
* Read the assembler source                                            *
***********************************************************************/
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.asm.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 assembler source                                      *
      *****************************************************************/
      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.asm'
      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 || ".asm.html'"
  else
    odsn = "'" || userid() || '.' || moi || ".asm.html'"

  /*********************************************************************
  * Text strings for title, header and footer                          *
  *********************************************************************/
  title  = 'Assembler source:' strip(idsn,, '''')
  header = 'Assembler source:' 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 = 'asm.html'

  /*********************************************************************
  * HTML colors                                                        *
  *                                                                    *
  * - lime(green) - name                                               *
  * - red         - opcode                                             *
  * - lime        - operand                                            *
  * - white       - quoted strings                                     *
  * - aqua(turq)  - remarks                                            *
  * - yellow      - special characters                                 *
  * - blue        - *process                                           *
  *********************************************************************/
  col_nam        = '<em class="l">'                        /* lime    */
  col_opc        = '<em class="r">'                        /* red     */
  col_opr        = '<em class="l">'                        /* lime    */
  col_str        = '<em class="w">'                        /* white   */
  col_rem        = '<em class="t">'                        /* turq    */
  col_spc        = '<em class="y">'                        /* yellow  */
  col_pro        = '<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 = '+-*=/<>&^|:,'

  /*********************************************************************
  * No operand (pseudo-) instructions                                  *
  *********************************************************************/
  no_ops         = 'CSCH '   ||,
                   'HSCH '   ||,
                   'IPK '    ||,
                   'PTFF '   ||,
                   'PTLB '   ||,
                   'RCHP '   ||,
                   'RSCH '   ||,
                   'SAL '    ||,
                   'SAM '    ||,
                   'SAM24 '  ||,
                   'SAM31 '  ||,
                   'SAM64 '  ||,
                   'SCHM '   ||,
                   'SCKPF '  ||,
                   'TAM '    ||,
                   'TRAP2 '  ||,
                   'UPT '    ||,
                   'XSCH '   ||,
                   'AEJECT ' ||,
                   'ANOP '   ||,
                   'COM '    ||,
                   'CSECT '  ||,
                   'CXD '    ||,
                   'DSECT '  ||,
                   'EJECT '  ||,
                   'LTORG '  ||,
                   'MACRO '  ||,
                   'MEND '   ||,
                   'MEXIT '  ||,
                   'REPRO '  ||,
                   'RSECT '


return

/***********************************************************************
* BUILD_HTML:                                                          *
*                                                                      *
* This procedure builds the HTML output                                *
***********************************************************************/
build_html:
  /*********************************************************************
  * Switches                                                           *
  *********************************************************************/
  in_apost  = 0                 /* Inside a '(apost) delimited string */
  in_apostq = 0                 /* Inside a '(apost) delimited string */
  in_com    = 0                 /* Inside a comment                   */
  paren     = 1                 /* Nested parentheses level           */

  /*********************************************************************
  * 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 assembler source                                     *
  *********************************************************************/
  i = time('E')

  /*********************************************************************
  * Process (if present) any '*PROCESS' statements                     *
  *********************************************************************/
  do i = 1 by 1 while translate(left(rxdata.i, 8)) = '*PROCESS'
    tempout = tempout || col_pro ||,
              translate_entities(rxdata.i)'</em><br>'
  end

  do i = i 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

    /*******************************************************************
    * Get the line, a spaced copy and the continuation character       *
    *******************************************************************/
    line = left(rxdata.i, 71)
    sqz  = space(line)
    cont = substr(rxdata.i, 72, 1)
    if cont == ' ' then
      line = strip(left(rxdata.i, 71), 'T')

    /*******************************************************************
    * Process completely blank line                                    *
    *******************************************************************/
    if sqz = '' then
      do
        tempout = tempout || '<br>'
        iterate i
      end

    /*******************************************************************
    * Process comment line                                             *
    *******************************************************************/
    if in_com              |,
       left(line, 1) = '*' |,
       left(line, 2) = '.*' then
      do
        if cont \= '' then
          in_com = 1
        else
          in_com = 0

        tempout = tempout || col_rem ||,
                  translate_entities(line) || '</em><br>'
        iterate i
      end

    /*******************************************************************
    * Parse out the name (and following spaces)                        *
    *                                                                  *
    * As there may not always be a name present, a '~' is added to the *
    * line, so that there is in effect always a name. The '~' is later *
    * stripped off again.                                              *
    *******************************************************************/
    line = '~'line
    p    = pos(' ', line)
    name = substr(left(line, p - 1), 2)
    line = substr(line, p)
    p    = verify(line, ' ')

    if p \= 0 then
      do
        name = name || left(line, p - 1)
        line = substr(line, p)
      end

    if name \= '' then
      tempout = tempout || col_nam || name || '</em>'
    else
      tempout = tempout || name

    /*******************************************************************
    * Parse out the opcode (and following spaces)                      *
    *******************************************************************/
    p      = pos(' ', line || ' ')
    opcode = left(line, p - 1)
    upcode = translate(opcode)
    line   = substr(line, p)
    p      = verify(line, ' ')

    if p \= 0 then
      do
        opcode = opcode || left(line, p - 1)
        line   = substr(line, p)
      end
    else
      if line  = ' ' &,
         cont \= ' ' then
        do
          opcode = opcode || line
          line   = ''
        end

    tempout = tempout || col_opc || opcode || '</em>'

    /*******************************************************************
    * If followed by '(', these 'opcodes' allow non-breaking spaces in *
    * their operand field                                              *
    *******************************************************************/
    spc = (upcode = 'AIF'  |,
           upcode = 'SETA' |,
           upcode = 'SETB' |,
           upcode = 'SETC') & left(line, 1) = '('

    /*******************************************************************
    * 'no_ops' contains the list of non-operand opcodes and assembler  *
    * instructions                                                     *
    *******************************************************************/
    no_op = (wordpos(upcode, no_ops) \= 0)

    /*******************************************************************
    * Remainder of line is blank and no continuation? Exit             *
    *******************************************************************/
    if line \= '' |,
       cont \= ' ' then
      call parser

    tempout = tempout || '<br>'

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

  htmlout = htmlout || tempout
return

/***********************************************************************
* PARSER:                                                              *
*                                                                      *
* Parse the operand and remainder fields                               *
***********************************************************************/
parser:
  in_apost = 0
  par_cnt  = -1
  in_sym   = 0

  do while line \= '' | cont \= ' '
    if line == '' &,
       cont \= ' ' then
      do
        tempout = tempout ||,
                  col_rem || translate_entities(cont)'</em><br>'
        i       = i + 1
        line    = left(rxdata.i, 71)
        cont    = substr(rxdata.i, 72, 1)

        if cont = ' ' then
          line = strip(line, 'T')

        /***************************************************************
        * Assume valid code, so continuations start in column 16       *
        ***************************************************************/
        tempout = tempout || left(line, 15)
        line    = substr(line, 16)
      end

    do while line \== ''
      c1 = left(line, 1)
      u1 = translate(c1)
      c2 = left(line, 2)
      u2 = translate(c2)

      /*****************************************************************
      * Is it a special character?                                     *
      *****************************************************************/
      sc = wordpos(c1, special_chars)

      /*****************************************************************
      * Is it a special hilite character?                              *
      *****************************************************************/
      sh = pos(c1, special_hilite)

      if in_sym &,
         pos(u1, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$_#@') = 0 then
        in_sym = 0

      if c1 = '&' then
        in_sym = 1

      select
        /***************************************************************
        * Everything following a no-operand opcode or assembler        *
        * instruction is a comment                                     *
        ***************************************************************/
        when no_op then
          do
            tempout = tempout ||,
                      col_rem || translate_entities(line) || '</em>'
            line    = ''
          end

        /***************************************************************
        * Spaces are kept unchanged - process multiple spaces in one   *
        * go|                                                          *
        ***************************************************************/
        when c1 == ' ' then
          do
            if line = ' ' then
              do
                tempout = tempout || line
                line    = ''
              end
            else
              do
                n       = verify(line, ' ')
                tempout = tempout || left(line, n - 1)

                if in_apost then
                  line = substr(line, n)
                else
                  if spc then
                    if par_cnt = 0 then
                      do
                        tempout = tempout ||,
                                  col_rem ||,
                                 translate_entities(substr(line, n)) ||,
                                  '</em>'
                        line    = ''
                      end
                    else
                      line = substr(line, n)
                  else
                    do
                      tempout = tempout ||,
                                col_rem ||,
                                translate_entities(substr(line, n)) ||,
                                '</em>'
                      line    = ''
                    end
              end
          end

        /***************************************************************
        * Start of quoted operator                                     *
        ***************************************************************/
        when \in_sym   &,
             \in_apost &,
            (u2 = 'L''' |,
             u2 = 'L''' |,
             u2 = 'K''' |,
             u2 = 'D''' |,
             u2 = 'I''' |,
             u2 = 'N''' |,
             u2 = 'O''' |,
             u2 = 'S''' |,
             u2 = 'T''') then
          do
            tempout = tempout || c2
            line    = substr(line, 3)
          end

        /***************************************************************
        * End of quoted string                                         *
        ***************************************************************/
        when in_apost &,
             c1 = "'" then
          do
            in_apost = 0

            tempout = tempout || "'</em>"
            line    = substr(line, 2)
          end

        /***************************************************************
        * Start of quoted string                                       *
        ***************************************************************/
        when c1 = "'" then
          do
            in_apost = 1

            tempout = tempout || col_str"'"
            line    = substr(line, 2)
          end

        /***************************************************************
        * A special character has to be translated and highlighted     *
        ***************************************************************/
        when sc > 0 &,
             sh > 0 &,
             \in_apost then
          do
            tempout = tempout || col_spc ||,
                      word(special_html, sc)'</em>'
            line    = substr(line, 2)
          end

        /***************************************************************
        * A special character has to be translated                     *
        ***************************************************************/
        when sc > 0 then
          do
            tempout = tempout || word(special_html, sc)
            line    = substr(line, 2)
          end

        /***************************************************************
        * A special character has to be highlighted                    *
        ***************************************************************/
        when sh > 0 &,
             \in_apost then
          do
            tempout = tempout || col_spc || c1'</em>'
            line    = substr(line, 2)
          end

        /***************************************************************
        * It's a left parenthesis                                      *
        ***************************************************************/
        when c1 = '(' &,
             \in_apost then
          do
            if par_cnt = -1 then
              par_cnt = 1
            else
              par_cnt = par_cnt + 1

            paren   = (paren + 1) // 5
            tempout = tempout || col_par.paren'(</em>'
            line    = substr(line, 2)
          end

        /***************************************************************
        * It's a right parenthesis                                     *
        ***************************************************************/
        when c1 = ')' &,
             \in_apost then
          do
            par_cnt = par_cnt - 1

            tempout = tempout || col_par.paren')</em>'
            paren   = (paren + 4) // 5
            line    = substr(line, 2)
          end

        /***************************************************************
        * Anything else                                                *
        ***************************************************************/
        otherwise
          do
            tempout = tempout || c1
            line    = substr(line, 2)
          end
      end

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