/*****************************************************************************/
/*
                              RTE_example.c

Simple example showing the essential elements of CGIplus-enabled, persistant
Run-Time Environments.  These ARE FUNDAMENTALLY DIFFERENT to CGIplus scripts,
although built using the same mechanisms.  A CGIplus script is a
single-purpose, persistant script, that once activated does essentially the
same task for each request it is passed.  By way of contrast, a persistant
run-time environment can be given a different script "source" each time and so
can perform a different task each time.  It is intended for environments such
as Perl and Java where the "interpreter" startup costs can be expensive on a
per-script basis.  If the intepreter can be started once, then "initialized"
afresh for each request passed to it both response latency and system impact
can be significantly improved.

This example demonstrates the essential elements, environment startup, waiting
for each request in a CGIplus loop, getting CGI environment variables,
processing a single request, indicating end-of-request.  The actual
"interpretation" performed by this example is just to list each record (line)
against a line number.  As can be seen from this example the basic
infrastructure for this type of environment is quite straight-forward. 
Needless-to-say, with persistant environments it is important the "interpreter"
cleans up thoroughly after each request (i.e. releases all dynamic memory
allocated, closes all file handles, etc.) and ensures a thorough initialization
of itself before commencing each request's processing.

Remember, the behaviours of these scripts are WATCHable.


WARNING!
--------
Don't forget these are persistant environments.  This means that especially
during development, once changes have been made to source code and the
environment rebuilt any currently executing instances of the previous build
must be purged from the server environment (wish I had a dollar for every time
I'd been caught like this myself!)

  $ HTTPD/DO=DCL=PURGE


MAPPING RULES
-------------
Each persistant run-time environment must have it's own mapping environment. 
This may, or may not, correspond to physically distinct directory areas.  It is
the mapping rules that identify run-time environments.  Hence the following
example shows two such environments each with it's own interpreter.

  exec+ /plbin/* (/cgi-bin/perlrt.exe)/wasd_root/perl_local/*
  exec+ /pybin/* (/cgi-bin/pythonrt.exe)/wasd_root/python_local/*

The "/plbin/*" identifies request paths beginning with this string as a
hypothetical, persistant Perl run-time interpreter, with the Perl source
scripts available to it from WASD_ROOT:[PERL_LOCAL].  Similarly with a
hypothetical Python interpreter.

If a request with the following URI is given to the server

  http://the.host.name/plbin/plsearch/web/doc/?search=whatever

the following components will be derived

  run-time interpreter ... CGI-BIN:[000000]PERLRT.EXE
  Perl source script ..... WASD_ROOT:[PERL_LOCAL]PLSEARCH.PL
  request path-info ...... WEB:[DOC]
  request query-string ... search=whatever

This particular example RTE script "interpreter" can be mapped using

  exec+ /rtbin/* (/cgi-bin/rte_example.exe)/wasd_root/src/httpd/*

The HTTPd server will normally search for the script file as with any other
scripting environment, and report "script not found" if it does not map or
exist as a "real" file.  This behaviour may be suppressed for RTE's that "know"
where their own scripts are (i.e. may not be files, but some "internal"
script), or will do their own error reporting for missing script files.  Use
the following HTTPD$MAP mapping rule to suppress the server's script finding
functionality.

  set /rtbin/* script=nofind
  exec+ /rtbin/* (/cgi-bin/rte_example.exe)/wasd_root/src/httpd/*


LOGICAL NAMES
-------------
RTE_EXAMPLE$DBUG   turns on all "if (Debug)" statements


BUILD DETAILS
-------------
$ @BUILD_RTE_EXAMPLE BUILD  !compile+link
$ @BUILD_RTE_EXAMPLE LINK   !link-only


COPYRIGHT
---------
Copyright (c) 2000-2020 Mark G.Daniel
This program, comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under the conditions of the GNU GENERAL PUBLIC LICENSE, version 2.


VERSION HISTORY
---------------
28-OCT-2000  MGD  v1.0.0, initial development
*/

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

/* standard C header files */
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

/* VMS-specific header files */
#include <ssdef.h>

/* CGILIB header file */
#include "cgilib.h"

/* macros */

#define FI_LI __FILE__, __LINE__

/* global storage */

int  Debug;

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

main ()
       
{
   /*********/
   /* begin */
   /*********/

   Debug = (getenv ("RTE_EXAMPLE$DBUG") != NULL);
   CgiLibEnvironmentSetDebug (Debug);

   CgiLibEnvironmentInit (0, NULL, 0);

   if (!CgiLibEnvironmentIsCgiPlus())
   {
      /* no point in a persistant environment if it never persists! */
      CgiLibResponseSetErrorStatus (500);
      CgiLibResponseError (FI_LI, 0, "Not CGIplus environment!");
      exit (SS$_NORMAL);
   }

   for (;;)
   {
      /* block waiting for the first/next request */
      CgiLibVar ("");
      ProcessRequest ();
      CgiLibCgiPlusEOF ();
   }
}

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

ProcessRequest ()
       
{
   static int  UsageCount;

   char  *cptr,
         *ScriptName,
         *ScriptFileName;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ProcessRequest()\n");

   UsageCount++;

   if (strcmp (CgiLibVar ("WWW_REQUEST_METHOD"), "GET"))
   {
      CgiLibResponseSetErrorStatus (501);
      CgiLibResponseError (FI_LI, 0, "Only supports GET method!");
      return;
   }
   ScriptName = CgiLibVar ("WWW_SCRIPT_NAME");
   if (!ScriptName[0])
   {
      CgiLibResponseSetErrorStatus (500);
      CgiLibResponseError (FI_LI, 0, "Internal error: no CGI \"SCRIPT_NAME\"");
      return;
   }
   ScriptFileName = CgiLibVar ("WWW_SCRIPT_FILENAME");
   if (!ScriptFileName[0])
   {
      CgiLibResponseSetErrorStatus (500);
      CgiLibResponseError (FI_LI, 0, "Internal error: no CGI \"SCRIPT_FILENAME\"");
      return;
   }

   CgiLibResponseHeader (200, "text/plain");
   
   fprintf (stdout,
"CGIplus-enabled Run-time Environment Example\n\
--------------------------------------------\n\
\n\
***** FIRST, EVIDENCE OF PERSISTANCE *****\n\
\n\
Usage Count: %d\n\
\n\
***** SECOND, THE CGI ENVIRONMENT AVAILABLE *****\n\
\n",
   UsageCount);

   /* retrieve a string displaying each successive CGI variable, NULL ends */
   while ((cptr = CgiLibVar ("*")) != NULL) fprintf (stdout, "%s\n", cptr);

   fprintf (stdout,
"\n\
***** THIRD, AN \"INTERPRETED\" FILE (WWW_SCRIPT_NAME/WWW_SCRIPT_FILENAME) *****\n\
\n");

   ProcessSourceFile (ScriptFileName);
}

/*****************************************************************************/
/*
Simple file "processing".  Read each line of the specified "source" file,
outputing it preceded by the line number.  Of course, for a "real" interpreter 
this would be the initiation point of the main "thread" of execution for the
script.
*/

ProcessSourceFile (char *ScriptFileName)
       
{
   int  LineCount = 0;
   char  Line [256];
   FILE  *SourceFile;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ProcessSourceFile()\n");

   if ((SourceFile = fopen (ScriptFileName, "r", "shr=get")) == NULL)
   {
      fprintf (stdout, "Error: could not open \"%s\", %%X%08.08X\n",
               ScriptFileName, vaxc$errno);
      return;
   }

   while (fgets (Line, sizeof(Line), SourceFile) != NULL)
      fprintf (stdout, "[%04.04d] %s", ++LineCount, Line);

   fclose (SourceFile);
}

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

