CGI installation


CGI installation


This site is distributed as a zipfile which contains all the programs to run the Rexx Parser and its child project programs, such as the Rexx Highlighter, as well as the related documentation: the file you are reading now, for example, is part of this documentation.

The documentation consists of a set of Markdown files. On the reference distribution site, https://rexx.epbcn.com/rexx-parser/, Markdown is translated on-the-fly and then served as HTML by a Rexx CGI program, CGI.markdown.rex, reproduced at the end of this page.

[CGI stands for Common Gateway Interface, and it is a language-neutral specification that allows the development of dynamic web pages using any computer language --- including Rexx! You can read more about CGI in this Apache tutorial.]

                                       How does CGI.markdown.rex work?

                                   ┌────────────────────────────────────────┐
                                   │  Markdown source file                  │
                                   │    May also contain:                   │
                              (1)  │      • Standard HTML/CSS/JavaScript    │
                                   │      • Bootstrap HTML and CSS          │
                                   │      • Rexx fenced code blocks         │
                                   └───────────────────┬────────────────────┘
                                                       ▼
                                   ╔═══════════════════╧════════════════════╗
                                   ║  bin/FencedCode.cls                    ║
                              (2)  ║    • Processes Rexx  code blocks       ║
                                   ║    • Produces standard Markdown        ║
                                   ╚═══════════════════╤════════════════════╝
                                                       ▼
                                   ╔═══════════════════╧════════════════════╗
                                   ║  Pandoc                                ║
                              (3)  ║    • Processes Markdown file           ║
                                   ║    • Produces standard HTML            ║
                                   ╚═══════════════════╤════════════════════╝
                                                       ▼
                                   ╔═══════════════════╧════════════════════╗
                                   ║  cgi/rexx.epbcn.com.optional.cls       ║
                              (4)  ║    Adds rexx.epbcn.com localisms:      ║
                                   ║    headers, footers, sidebars, etc.    ║
                                   ╚═══════════════════╤════════════════════╝
                                                       ▼
                                   ┌───────────────────┴────────────────────┐
                                   │  Final HTML file                       │
                              (5)  │    → cgi/CGI.markdown.rex sends the    │
                                   │      resulting HTML to the browser     │
                                   └────────────────────────────────────────┘

Such a workflow has a number of advantages:

  • Your web pages are written in Markdown. Markdown is much easier to write, learn, teach, remember and maintain than HTML/CSS + a framework like Bootstrap.
  • Nevertheless, Since we are using Bootstrap as the underlying framework plus Pandoc to translate the Markdown sources to HTML, we can always resort to use Bootstrap-enriched HTML+CSS in our Markdown files when we really need it, almost without limitations (GitHub Markdown, on the other hand, is very aggresive and limiting regarding which subsets of HTML and CSS you can use in your Markdown pages)
  • Our CGI makes automatic use of the Rexx Highlighter, as it highlights all Rexx fenced code blocks. We can use all the possible customizations that the Highlighter allows (like extra letters, selectable styles, style patches, etc.) on a fenced block-by-fenced block basis.
  • If we are using a minimally decent Unicode-supporting text editor (something relatively simple like Notepad++ will do), we can generate web pages containing highlighted Rexx mixed with non-latin alphabets, as well as emojis, with absolute ease.

CGI.markdown.rex and the accompanying files were specifically designed to support the https://rexx.epbcn.com/ site only, but they are provided as a reference, since you may want to install them locally, to play and to experiment with them, and then eventually adapt them for your own use.

Installing a local copy of the rexx-parser tree: a Windows tutorial

We will learn how to install a working copy of the whole rexx-parser subtree of https://rexx.epbcn.com/ on a Windows machine; installing under Linux or Mac should be similar. Everything should work as in the original site, except for the logo (you can always substitute it by one of your own instead).

Prerequisites

We will need:

Step-by-step instructions

  1. Install Visual C++ Redistributable Visual Studio 2015-2022.

    This is needed to run Apache Lounge; other distributions of Apache httpd may not have this dependency. You may also find that the redistributables are already installed, as they are a prerequisite of many other programs.

  2. Install the Apache httpd web server.

    1. Open the zipped Apache httpd distribution, and copy the Apache24 directory to your C: disk: this will create a C:\Apache24 directory (the included readme contains instructions that you can follow if you prefer to install it in a different directory).
    2. Execute C:\Apache24\bin\httpd.exe. You will get a prompt from the Windows firewall. Allow net access.
    3. Open a web browser, and point to localhost. You should see a page that contains the words "It works!". You can now close the Apache window you opened in the previous step.
  3. Install Pandoc.

  4. Create your new local server directory.

    You will need to create or select a directory to store the copy of the Rexx Parser distribution that will be served by your newly installed Apache web server. In this tutorial, we will assume that this directory is C:\Parser.

  5. Install Bootstrap

    1. Open the Bootstrap zip file.
    2. Create C:\Parser\css and copy css\bootstrap.min.css and css\bootstrap.theme.min.css from the Bootstrap zip file.
    3. Create C:\Parser\js and copy js\bootstrap.min.js.
    4. Create C:\Parser\fonts and copy all the content of the fonts subdirectory.
  6. Install the Rexx Parser files.

    Create C:\Parser\rexx-parser, and copy all the contents of the zipped Rexx Parser distribution there.

  7. Customize the Apache httpd configuration file.

    1. Open C:\Apache24\conf\httpd.conf with a text editor.

    2. Locate the "DocumentRoot" line. You will find two lines that read

      DocumentRoot "${SRVROOT}/htdocs"
      <Directory "${SRVROOT}/htdocs">

      Substitute ${SRVROOT}/htdocs by the server directory you just created:

      DocumentRoot "C:/Parser"
      <Directory "C:/Parser">

      [Note the forward slashes, and beware of the quotes.]

      The "document root" is where our served files will reside.

    3. Just after the DocumentRoot "C:/Parser" line, add the following lines:

      Action RexxCGIMarkdown "/cgi-bin/cgi.rex"
      <Files *.md>
        SetHandler RexxCGIMarkdown
      </Files>

      This defines an "Action", and associates it with a "Handler". The handler is fired whenever a Markdown file ("*.md") is requested; the handler then invokes the corresponding action, which is our "cgi.rex" Rexx program.

    4. We must now create the cgi.rex program file.

      Locate the C:\Apache24\cgi-bin directory, and create a cgi.rex file containing exactly the following two lines:

      #!rexx
      Call "C:\Parser\rexx-parser\cgi\CGI.markdown.rex"

      The first line ensures that the ooRexx interpreter will be called. The second one delegates the work to cgi\CGI.markdown.rex. This indirection mechanism greatly simplifies setup of the httpd server, and it allows us to distribute CGI.markdown.rex as part of the Rexx Parser tree.

    5. Locate a line that reads DirectoryIndex index.html. Change that line so that it reads

      DirectoryIndex readme.md slides.md article.md index.html

      When a URL that refers to a directory (that is, a URL ending with "/", like the root directory) is requested, we will automatically serve a readme.md file, if one is found; if not, we will try to serve a slides.md file; and so on.

  8. Start the Apache httpd server we just configured.

  9. Point your browser to http://localhost/rexx-parser/. You should see a copy of the Rexx Parser web page (without the EPBCN logo, which is not included).

You can find a copy of an updated httpd.conf file here for your reference. Please note that this was generated on 20250510 against the Apache 2.4.63-250207 Win64 version of Apache Lounge, and may not work in your installation, depending on your choices and on the versions used.

CSS Paged Media

The workflow defined above can be extended with a further last step: by using the W3C CSS Paged Media Level 3 specification, we can prepare special Markdown pages such that, when displayed on the browser and then printed, we obtain, automatically, a set of slides (or an article). The procedure and the rules to follow are explained in detail in a separate page.

CGI.markdown.rex

Location: [installation directory]/cgi/CGI.markdown.rex.

#!/usr/bin/env rexx
/******************************************************************************/
/*                                                                            */
/* CGI.markdown.rex -- Sample Apache CGI markdown processor                   */
/* ========================================================                   */
/*                                                                            */
/* This program is part of the Rexx Parser package                            */
/* [See https://rexx.epbcn.com/rexx-parser/]                                  */
/*                                                                            */
/* Copyright (c) 2024-2025 Josep Maria Blasco <josep.maria.blasco@epbcn.com>  */
/*                                                                            */
/* License: Apache License 2.0 (https://www.apache.org/licenses/LICENSE-2.0)  */
/*                                                                            */
/* Requirements:                                                              */
/*                                                                            */
/* + Apache                                                                   */
/* + Pandoc                                                                   */
/* + Use the "Action" directive to define an action pointing to               */
/*   this CGI routine, for example                                            */
/*      Action Markdown /cgi-bin/CGI.markdown.rex                             */
/* + Tell to Apache which files you want to process using the action handler  */
/*   you have defined. For example,                                           */
/*                                                                            */
/*        <Files *.md>                                                        */
/*          SetHandler Markdown                                               */
/*        </Files>                                                            */
/*                                                                            */
/* Version history:                                                           */
/*                                                                            */
/* Date     Version Details                                                   */
/* -------- ------- --------------------------------------------------------- */
/* 20241220    0.1c First public release                                      */
/* 20241230    0.1e Switch to a local copy of Bootstrap 3 to allow for colors */
/*                  in code sections to be printed correctly.                 */
/* 20250222    0.2  Add drive to path for requires                            */
/* 20250222         Change Address PATH to Address COMMAND for pandoc         */
/* 20250317         Allow style=(dark|light) query string                     */
/* 20250328         Main dir is now rexx-parser instead of rexx[.]parser      */
/* 20250523    0.2b Move HTTP.Request, HTTP.Response and Array.OutputStream   */
/*                  to separate files.                                        */
/* 20250524         Move generic CGI behaviour to Rexx.CGI.cls.               */
/* 20250621    0.2c Add support for .rex and .cls files.                      */
/*                                                                            */
/******************************************************************************/

  Signal On Syntax

--------------------------------------------------------------------------------
-- ::REQUIRES does not work well with "../" paths                             --
--------------------------------------------------------------------------------
  package   = .context~package
  local     =  package~local
  mypath    =  FileSpec( "Drive", package~name )FileSpec( "Path", package~name )
  local ~ . = .File~new( mypath"../" )~absolutePath      -- Creates ".."

  Call Requires .."/bin/FencedCode.cls"
  Call Requires mypath"rexx.epbcn.com.optional.cls"

 .MyCGI~new~execute

Exit

Requires:
  package~addPackage( .Package~new( Arg(1) ) )
  Return

--------------------------------------------------------------------------------
-- Error handling in CGIs is complicated, we'd better have a decent handler   --
--------------------------------------------------------------------------------

Syntax:
 .output~destination( .stdOut )
  ConditionObject = Condition("O")
  major     = ConditionObject~rc
  code      = ConditionObject~code
  message1  = ConditionObject~errorText
  message2  = ConditionObject~message
  program   = ConditionObject~program
  line      = ConditionObject~position
  traceBack = ConditionObject~traceBack
  Say "Content-type: text/plain; charset=utf8"
  Say ""
  Say "Syntax error" major "in" program "line" line": " message1
  Say "Error" code":" message2
  Say ""
  Say "Traceback follows:"
  Do line Over traceBack
    Say line
  End
Exit

/******************************************************************************/

::Requires "Rexx.CGI.cls"

/******************************************************************************/
/******************************************************************************/
/* MYCGI                                                                      */
/******************************************************************************/
/******************************************************************************/

-- We subclass Rexx.CGI and implement the abstract PROCESS method,
-- which encapsulates the specificities of our CGIs.

::Class MyCGI SubClass Rexx.CGI

/******************************************************************************/
/* PROCESS                                                                    */
/******************************************************************************/

::Method Process

  file = self~file
  URI  = self~URI

  ------------------------------------------------------------------------------
  -- We only accept one optional query, of the form "style=(light|dark)"      --
  -- or "view=highlight" (only for .rex and .cls files)                       --
  ------------------------------------------------------------------------------

  style = "dark"
  view  = "text"
  If uri~contains("?")  Then Do
    Parse Var uri uri"?"query
    ok = 0
    If query~startsWith("view="), -
      (uri~endsWith(".cls") | uri~endsWith(".rex")) Then Do
      Parse Var query "view="view
      If view == "highlight" Then ok = 1
    End
    Else If query~startsWith("style=") Then Do
      Parse Var query "style="style
      If style == "dark" | style == "light" Then ok = 1
    End
    If \ok              Then Do
     .Response~404
     Return self~FAIL
    End
  End

  ------------------------------------------------------------------------------
  -- We are using "readme.md", "slides.md" and "article.md" as  index pages,  --
  -- using the Apache DirectoryIndex directive.                               --
  ------------------------------------------------------------------------------

  -- In case we need to form canonical URLs
  If      URI~endsWith(  "readme.md" ) Then URI = Left(URI, Length(URI) -  9)
  Else If URI~endsWith(  "slides.md" ) Then URI = Left(URI, Length(URI) -  9)
  Else If URI~endsWith( "article.md" ) Then URI = Left(URI, Length(URI) - 10)

  ------------------------------------------------------------------------------
  -- See if an accompanying extra style .css file exists                      --
  --   This is is a file with the same name as the cgi, with ".css" added at  --
  --   the end. It is useful for specifying variables that are file-dependent,--
  --   like the running header and footer (this should be done with the       --
  --   string-set property, but it is not properly implemented in the major   --
  --   browsers).                                                             --
  ------------------------------------------------------------------------------

  Select Case FileSpec("Name",file)
    When "slides.md"  Then ownStyle = "slides"
    When "article.md" Then ownStyle = "article"
    Otherwise              ownStyle = ""
  End
  extraStyle = Stream(file".css","c","Q exists")
  If extraStyle \== "" Then Do
    p = LastPos(.File~separator,extraStyle)
    extraStyle = SubStr(extraStyle,p+1)
  End

  ------------------------------------------------------------------------------
  -- Ok, now we have a file to process. Read it into an array                 --
  ------------------------------------------------------------------------------

  source = CharIn( file, 1, Chars(file) )~makeArray
  Call Stream file, "c", "close"

  ------------------------------------------------------------------------------
  -- Special case for .rex or .cls files                                      --
  ------------------------------------------------------------------------------

  If URI~endsWith( ".rex") Then Signal View
  If URI~endsWith( ".cls") Then Signal View

  ------------------------------------------------------------------------------
  -- We process Rexx fenced code blocks first                                 --
  ------------------------------------------------------------------------------

  source = FencedCode( file, source, style )

  ------------------------------------------------------------------------------
  -- We now call pandoc. It will transform markdown into html by default      --
  ------------------------------------------------------------------------------

  contents = .Array~new
  Address COMMAND 'pandoc --from markdown-smart+footnotes' -
    '--reference-location=section' -
    With Input Using (source) Output Using (contents)

  ------------------------------------------------------------------------------
  -- As the document title, pick the contents of the first h1 header          --
  ------------------------------------------------------------------------------

  title = "Missing title"
  chunk = contents~makeString("L"," ")
  If chunk~contains("<h1") Then
    Parse Var chunk "<h1" ">"title"</h1>"

  ------------------------------------------------------------------------------
  -- Copy the HTML resource, with some substitutions                          --
  ------------------------------------------------------------------------------

  template = .Resources~HTML

  Do line Over template
    Select
      When line = "%title%"         Then Say  title
      When line = "%header%"        Then Call OptionalCall PageHeader, title
      When line = "%contents%"      Then Do line Over contents; Say line; End
      When line = "%footer%"        Then Call OptionalCall PageFooter
      When line = "%sidebar%"       Then Call OptionalCall Sidebar, uri
      When line = "%contentheader%" Then Call OptionalCall ContentHeader, uri
      When line = "%extrastyle%"    Then
        If extraStyle \== "" Then
          Say "    <link rel='stylesheet' media='print' href='"extraStyle"'>"
      When line = "%ownstyle%"      Then
        If ownStyle \== ""   Then
          Say "    <link rel='stylesheet'" -
              "href='/rexx-parser/css/print/"ownstyle".css'>"
      Otherwise Say line
    End
  End

  ------------------------------------------------------------------------------
  -- Hack: Review entire .Array.Output array, detect CSS styles used          --
  -- in fenced code blocks, and dynamically update the array to refer         --
  -- to these CSS files.                                                      --
  ------------------------------------------------------------------------------
Hack:

  out   = .Array.Output
  lines = out~items

  -- We choose "</head>" because it has no attributes.

  subs = 0
  Do i = 1 To lines Until out[i] = "</head>"
    If out[i] == "[*STYLES*]" Then subs = i
  End

  If i > items Then Raise Halt Array("No '</head>' line found.")
  If subs == 0 Then Raise Halt Array("No '[*STYLES*]' line found.")

  allowed = XRange(AlNum)".-_"
  styles = .Array~new
  Do i = i + 1 To out~items
    Parse Value out[i] With ' class="highlight-rexx-'style'">'
    If style == "" Then Iterate
    If Verify(style, allowed) > 0 Then Iterate
    If \styles~hasItem(style) Then styles~append(style)
  End

  new = "    "
  Do style Over styles
    new ||= "<link rel='stylesheet' href='/rexx-parser/css/rexx-"style".css'>"
  End

  out[subs] = new

  Return self~OK

  ------------------------------------------------------------------------------

OptionalCall: Procedure
  Signal On Syntax Name OptionalRoutineMissing
  routineName = Markdown"."Arg(1)
  Call (routineName) Arg(2)
  Return
OptionalRoutineMissing:
  code = Condition("O")~code
  If code == 43.1, Condition("A")[1] = routineName Then Return
Raise Propagate

--------------------------------------------------------------------------------

View:

  If view \== "highlight" Then Do
    self~Content_Type = "text/plain"
    Do line Over source
      Say line
    End
    Exit self~OK
  End

  fenced = .Array~of("~~~~~~~~~~~~~~rexx")
  fenced ~ appendAll(source)
  fenced ~ append("~~~~~~~~~~~~~~")

  contents = FencedCode( file, fenced )

  ------------------------------------------------------------------------------
  -- Copy the HTML resource, with some substitutions                          --
  ------------------------------------------------------------------------------

  template = .Resources~DisplayRexx

  Do line Over template
    Select
      When line = "%title%"         Then Say  URI
      When line = "%header%"        Then Call OptionalCall PageHeader, URI
      When line = "%contents%"      Then Do line Over contents; Say line; End
      When line = "%footer%"        Then Call OptionalCall PageFooter
      When line = "%sidebar%"       Then Call OptionalCall Sidebar, uri
      When line = "%contentheader%" Then Call OptionalCall ContentHeader, uri
      When line = "%extrastyle%"    Then Nop
      When line = "%ownstyle%"      Then Nop
      Otherwise Say line
    End
  End

  Signal Hack


-- We are loading a local copy of Bootstrap 3, customized to eliminate
-- print media styles, and then we add our own media styles css.

::Resource DisplayRexx
<!doctype html>
<html lang='en'>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>
      %title%
    </title>
    <link rel="stylesheet" href="/css/bootstrap.min.css">
[*STYLES*]
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Questrial&display=swap" rel="stylesheet">
    %extrastyle%
    <!--[if lt IE 9]>
      <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
      <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    %contents%
    <script src="/js/bootstrap.min.js"></script>
  </body>
</html>
::END


/******************************************************************************/
/******************************************************************************/
--                         Structure of a page:
--
--      +-------------------------------------------------------------+
--      |              page header, including the title               |
--      +-------------------------------------------------------------+
--      |                content header               |   side bar    |
--      +---------------------------------------------+               |
--      |                                             |               |
--      |                  contents                   |               |
--      |                                             |               |
--      |                 [9 columns]                 |  [3 columns]  |
--      +-------------------------------------------------------------+
--      |                          page footer                        |
--      +-------------------------------------------------------------+
--
/******************************************************************************/
/******************************************************************************/

::Resource HTML
<!doctype html>
<html lang='en'>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>
      %title%
    </title>
    <link rel="stylesheet" href="/css/bootstrap.min.css">
[*STYLES*]
    <link rel='stylesheet' href='/rexx-parser/css/markdown.css'>
    %ownstyle%
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Questrial&display=swap" rel="stylesheet">
    %extrastyle%
    <!--[if lt IE 9]>
      <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
      <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <div class='container bg-white' lang='en'>
      %header%
      <div class='row'>
        <div class='col-md-9'>
          %contentheader%
          <div class='content'>
            %contents%
          </div>
        </div>
        <div class="col-md-3 text-center text-larger">
          %sidebar%
        </div>
      </div>
      %footer%
    </div>
    <script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
    <script src="/js/bootstrap.min.js"></script>
  </body>
</html>
::END