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:
Bootstrap 3, which you can download at https://getbootstrap.com/docs/3.4/.
Bootstrap defines a set of CSS classes and Javascript procedures that allow the easy development of mobile-first, responsive web pages.
Pandoc, which you can download at https://pandoc.org/.
We will use Pandoc to dynamically produce HTML files from Markdown sources.
A version of the Apache httpd web server. In this readme, we will be using Apache Lounge, which you can download at https://www.apachelounge.com/download/, but other distributions should also work.
The Visual C++ Redistributable installation files. You can find download links in the Apache Lounge download page.
The Rexx Parser distribution itself. You can download it here.
And of course ooRexx.
Step-by-step instructions
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.
Install the Apache httpd web server.
- Open the zipped Apache httpd distribution, and copy the
Apache24
directory to yourC:
disk: this will create aC:\Apache24
directory (the included readme contains instructions that you can follow if you prefer to install it in a different directory). - Execute
C:\Apache24\bin\httpd.exe
. You will get a prompt from the Windows firewall. Allow net access. - 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.
- Open the zipped Apache httpd distribution, and copy the
Install Pandoc.
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
.Install Bootstrap
- Open the Bootstrap zip file.
- Create
C:\Parser\css
and copycss\bootstrap.min.css
andcss\bootstrap.theme.min.css
from the Bootstrap zip file. - Create
C:\Parser\js
and copyjs\bootstrap.min.js
. - Create
C:\Parser\fonts
and copy all the content of thefonts
subdirectory.
Install the Rexx Parser files.
Create
C:\Parser\rexx-parser
, and copy all the contents of the zipped Rexx Parser distribution there.Customize the Apache httpd configuration file.
Open
C:\Apache24\conf\httpd.conf
with a text editor.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.
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.
We must now create the
cgi.rex
program file.Locate the
C:\Apache24\cgi-bin
directory, and create acgi.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 distributeCGI.markdown.rex
as part of the Rexx Parser tree.Locate a line that reads
DirectoryIndex index.html
. Change that line so that it readsDirectoryIndex 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 aslides.md
file; and so on.
Start the Apache httpd server we just configured.
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