/*****************************************************************************/
/*
                                IPCtickler.c

The CGI script Inter-Process Communication (IPC) tickler is designed to allow a
script programmer to gain an appreciation of the way WASD interacts with VMS'
record-oriented Input/Output (I/O) design, how the C Language Run-Time Library
interprets U**x I/O conventions into this, how WASD attempts to accomodate
both, how the mechanisms a script can use to explicitly convey exact
requirements to WASD ... and finally, how these affect output (in particular
the carriage-control) delivered to the client.  Use

  http://the.host.name/cgi-bin/ipctickler

to obtain an HTML form allowing control of several parameters into the script.


COPYRIGHT
---------
Copyright (C) 2005-2022 Mark G.Daniel.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


BUILD DETAILS
-------------
$ @BUILD_ONE BUILD IPCTICKLER


VERSION HISTORY
---------------
30-JAN-2007  MGD  v1.2.0, modify for non-WASD but CGI compliant server
                          (in this case to investigate Purveyor CGI)
30-AUG-2005  MGD  v1.1.0, ctx=xplct (thanks JPP)
27-AUG-2005  MGD  v1.0.0, initial development
*/
/*****************************************************************************/

#define SOFTWAREVN "1.2.0"
#define SOFTWARENM "IPCTICKLER"
#ifdef __ALPHA
   char SoftwareID [] = SOFTWARENM " AXP-" SOFTWAREVN;
#endif
#ifdef __ia64
   char SoftwareID [] = SOFTWARENM " IA64-" SOFTWAREVN;
#endif
#ifdef __VAX
   char SoftwareID [] = SOFTWARENM " VAX-" SOFTWAREVN;
#endif
#ifdef __x86_64
   char SoftwareID [] = SOFTWARENM " X86-" SOFTWAREVN;
#endif

#define SS$_FISH 2928

#ifndef SETENV_FORM
#define SETENV_FORM 0
#endif

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

int TicklerForm (int);

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

main ()

{
   static char  fillch [] =
"0123456789_abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ";

   int  hexch, total_chars, mrs, result, rec_size,
        use_fputs, use_fwrite_n1, use_fwrite_1n;
   char  *bptr, *bufptr, *cptr, *ctxptr, *ctypeptr, *xplctptr,
         *modeptr, *mrsptr, *qsptr, *sptr, *zptr;
   char  vname [256] = "WWW_FORM_",
         value [256];

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

   mrsptr = getenv("WWW_GATEWAY_MRS");
   if (mrsptr)
      mrs = atoi(mrsptr);
   else
      mrs = 0;

   qsptr = getenv("WWW_QUERY_STRING");
   if (qsptr && !*qsptr) qsptr = NULL;
   if (!qsptr)
   {
      TicklerForm (mrs);
      exit (1);
   }

   if (!getenv("WWW_FORM_CHARS"))
   {

#if SETENV_FORM
#if __CRTL_VER >= 70000000 /* yup - only V7.0 and later */

      /* no "FORM_" CGI environment variables, make some */
      cptr = qsptr;
      while (*cptr)
      {
         sptr = vname + 9;
         while (*cptr && *cptr != '=') *sptr++ = toupper(*cptr++);
         if (!*cptr) exit (SS$_FISH);
         *sptr = '\0';
         cptr++;
         sptr = value;
         while (*cptr && *cptr != '&')
         {
            if (*cptr == '%')
            {
               if (sscanf (cptr+1, "%2X", &hexch) != 1) exit (SS$_FISH);
               *sptr++ = (char)hexch;
               cptr += 3;
            }
            else
               *sptr++ = *cptr++;
         }
         *sptr = '\0';
         if (*cptr) cptr++;
         setenv (vname, value, 1);
      }

#endif /* __CRTL_VER >= 70000000 */
#endif /* SETENV_FORM */

      if (!getenv("WWW_FORM_CHARS")) exit (SS$_FISH);
   }

   cptr = getenv("WWW_FORM_CHARS");
   if (!cptr) cptr = "2048";
   total_chars = atoi(cptr);

   cptr = getenv("WWW_FORM_SIZE");
   if (!cptr) cptr = "0";
   rec_size = atoi(cptr);

   cptr = getenv("WWW_FORM_CTX");
   if (!cptr) cptr = "";
   if (!strcmp (cptr, "rec")) 
       ctxptr = "ctx=rec";
   else
   if (!strcmp (cptr, "bin")) 
       ctxptr = "ctx=bin";
   else
       ctxptr = "ctx=rec";

   cptr = getenv("WWW_FORM_XPLCT");
   if (!cptr) cptr = "";
   if (*cptr)
      xplctptr = "ctx=xplct";
   else
      xplctptr = NULL;

   cptr = getenv("WWW_FORM_FUNC");
   if (!cptr) cptr = "";
   use_fputs = use_fwrite_n1 = use_fwrite_1n = 0;
   if (!strcmp (cptr, "fputs")) 
      use_fputs = 1;
   else
   if (!strcmp (cptr, "fwrite_n1")) 
      use_fwrite_n1 = 1;
   else
   if (!strcmp (cptr, "fwrite_1n")) 
      use_fwrite_1n = 1;
   else
      use_fputs = 1;

   cptr = getenv("WWW_FORM_MODE");
   if (!cptr) cptr = "";
   if (!strcmp (cptr, "x-record-mode")) 
       modeptr = "Script-Control: X-record-mode\n";
   else
   if (!strcmp (cptr, "x-stream-mode")) 
       modeptr = "Script-Control: X-stream-mode\n";
   else
       modeptr = "";

   cptr = getenv("WWW_FORM_BUFFER");
   if (!cptr) cptr = "";
   if (!strcmp (cptr, "x-buffer-records")) 
       bufptr = "Script-Control: X-buffer-records\n";
   else
       bufptr = "";

   ctypeptr = getenv("WWW_FORM_CTYPE");
   if (!ctypeptr) ctypeptr = "text/plain";

   /* make it obvious for an observer by tuning off transfer optimisations */
   fprintf (stdout,
"Content-Type: %s\n\
Script-Control: X-content-encoding-gzip=0\n\
Script-Control: X-transfer-encoding-chunked=0\n\
%s%s\n",
            ctypeptr, modeptr, bufptr);

   if (xplctptr)
      if (!(stdout = freopen ("SYS$OUTPUT:", "w", stdout, ctxptr, xplctptr)))
         exit (vaxc$errno);
   else
      if (!(stdout = freopen ("SYS$OUTPUT:", "w", stdout, ctxptr)))
         exit (vaxc$errno);

   fprintf (stdout,
"mailbox mrs: %d\nrecord size: %d\ntotal chars: %d\n\
fopen(%s%s%s)\n%s%s%s\n%s%s",
            mrs, rec_size, total_chars,
            ctxptr, xplctptr ? "," : "", xplctptr ? xplctptr : "",
            use_fputs ? "fputs()" : "",
            use_fwrite_n1 ? "fwrite(n,1)" : "",
            use_fwrite_1n ? "fwrite(1,n)" : "",
            modeptr, bufptr);
   fflush (stdout);

   if (!rec_size) rec_size = sizeof(fillch);
   bptr = sptr = calloc (1, total_chars+2);
   if (!bptr) exit (vaxc$errno);
   zptr = sptr + total_chars;
   while (sptr < zptr)
   {
      if (!*cptr) cptr = fillch;
      if (!((sptr - bptr) % rec_size))
      {
         *sptr++ = '\n';
         *sptr++ = '*';
         cptr = fillch;
      }
      *sptr++ = *cptr++;
   }
   *sptr = '\n';
   *sptr = '\0';

   if (use_fputs)
   {
      result = fputs (bptr, stdout);
      if (result >= 0) result = total_chars;
   }
   else 
   if (use_fwrite_n1)
   {
      result = fwrite (bptr, total_chars, 1, stdout);
      result = result * total_chars;
   }
   else 
      result = fwrite (bptr, 1, total_chars, stdout);
   fflush (stdout);

   if (!strcmp (modeptr, "Script-Control: X-stream-mode\n"))
      fputs ("\n", stdout);
   if (result >= 0)
      fprintf (stdout, "\nOUTPUT: %d chars\n", result);
   else
      fprintf (stdout, "+++ OUTPUT FAILED +++\n");
}

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

TicklerForm (int mrs)

{
   char  *CgiScriptNamePtr;

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

   CgiScriptNamePtr = getenv ("WWW_SCRIPT_NAME");
   if (!CgiScriptNamePtr) exit (SS$_FISH);

   fprintf (stdout,
"Content-Type: text/html\n\
\n\
<HTML>\n\
<HEAD>\n\
<TITLE>%s</TITLE>\n\
</HEAD>\n\
<BODY>\n\
<FORM ACTION=\"%s\" METHOD=\"GET\">\n\
<TABLE CELLPADDING=\"5\" CELLSPACING=\"2\" BORDER=\"0\">\n\
<TR><TH COLSPAN=\"4\"><U>CGI Script IPC Tickler</U></TH></TR>\n\
<TR><TD></TD></TR>\n\
<TR><TH ALIGN=\"right\">mailbox MRS:&nbsp;</TH>\
<TD ALIGN=\"left\">%d</TD></TR>\n\
<TR><TH ALIGN=\"right\">record size:&nbsp;</TH><TD ALIGN=\"left\">\
<INPUT TYPE=\"text\" NAME=\"size\" VALUE=\"0\" SIZE=\"5\">\
</TD></TR>\n\
<TR><TH ALIGN=\"right\">total chars:&nbsp;</TH><TD ALIGN=\"left\">\
<INPUT TYPE=\"text\" NAME=\"chars\" VALUE=\"%d\" SIZE=\"7\">\
</TD></TR>\n\
<TR><TH ALIGN=\"right\">fopen() ctx=&nbsp;</TH><TD ALIGN=\"left\">\
<INPUT TYPE=\"radio\" NAME=\"ctx\" VALUE=\"rec\" CHECKED>rec\
</TD><TD ALIGN=\"left\">\
<INPUT TYPE=\"radio\" NAME=\"ctx\" VALUE=\"bin\">bin\
</TD></TR>\n\
<TR><TD COLSPAN=\"2\"></TD><TD>\
<INPUT TYPE=\"checkbox\" NAME=\"xplct\" VALUE=\"yep\">xplct\
</TD></TR>\n\
<TR><TH ALIGN=\"right\">function:&nbsp;</TH><TD ALIGN=\"left\">\
<INPUT TYPE=\"radio\" NAME=\"func\" VALUE=\"fputs\" CHECKED>fputs()\
</TD><TD ALIGN=\"left\">\
<INPUT TYPE=\"radio\" NAME=\"func\" VALUE=\"fwrite_n1\">fwrite(n,1)\
</TD><TD ALIGN=\"left\">\
<INPUT TYPE=\"radio\" NAME=\"func\" VALUE=\"fwrite_1n\">fwrite(1,n)\
</TD></TR>\n\
<TR><TH ALIGN=\"right\" VALIGN=\"top\" ROWSPAN=\"2\">script-control:&nbsp;</TH>\
<TD ALIGN=\"left\">\
<INPUT TYPE=\"radio\" NAME=\"mode\" VALUE=\"x-record-mode\">X-record-mode\
</TD><TD ALIGN=\"left\">\
<INPUT TYPE=\"radio\" NAME=\"mode\" VALUE=\"x-stream-mode\">X-stream-mode\
</TD><TD ALIGN=\"left\">\
<INPUT TYPE=\"radio\" NAME=\"mode\" VALUE=\"\" CHECKED>(none)\
</TD></TR>\n\
<TR><TD></TD><TD>\
<INPUT TYPE=\"checkbox\" NAME=\"buffer\" VALUE=\"x-buffer-records\">X-buffer-records\
</TD></TR>\n\
<TR><TH ALIGN=\"right\">content-type:</TH>\
<TD ALIGN=\"left\">\
<INPUT TYPE=\"radio\" NAME=\"ctype\" VALUE=\"text/plain\" CHECKED>\
text/plain\
</TD><TD ALIGN=\"left\" COLSPAN=\"2\">\
<INPUT TYPE=\"radio\" NAME=\"ctype\" VALUE=\"application/octet-stream\">\
application/octet-stream\
</TD></TR>\n\
<TR><TD></TD></TR>\n\
<TR><TD ALIGN=\"center\" COLSPAN=\"4\">\
<INPUT TYPE=\"submit\" VALUE=\"tickle\">&nbsp;&nbsp;&nbsp;\
<INPUT TYPE=\"reset\" VALUE=\"reset\">\
</TD></TR>\n\
</TABLE>\n\
<TABLE CELLPADDING=\"20\" CELLSPACING=\"0\" BORDER=\"0\">\n\
<TR><TD>\n\
The CGI script Inter-Process Communication (IPC) tickler is designed to \
allow a script programmer to gain an appreciation of the way WASD interacts \
with VMS\' record-oriented Input/Output (I/O) design, how the C Language \
Run-Time Library interprets U**x I/O conventions into this, how WASD \
attempts to accomodate both, how the mechanisms a script can use to \
explicitly convey exact requirements to WASD ... and finally, how these \
affect output (in particular the carriage-control) delivered to the client.\n\
<P>Check the WASD <A HREF=\"/wasd_root/doc/scripting/\">Scripting</A> document \
for information on the handling of \
<A HREF=\"/wasd_root/doc/scripting/scripting_0200.html\">Script Output</A> in \
relation to &quot;text/..&quot; and non-&quot;text/..&quot; content-type.\n\
Use this utility to manipulate the record size (number of characters between \
newlines) and total characters output against stream mode and C-RTL output \
function, to observe how (implied) carriage-control is provided to the \
client.\n\
WASD CGI response fields can be added to manipulate the desired programming \
requirement and outcome.\n\
<P>Use the \
<A HREF=\"/wasd_root/doc/features/features_0900.html\">WATCH facility</A> \
with <I>CGI</I>, <I>DCL</I> and <I>Network Activity/Data</I> items to \
directly observe the octets provided by the script and subsequently provided \
to the client.\n\
</TD></TR>\n\
</TABLE>\n\
</BODY>\n\
</HTML>\n",
      SoftwareID, CgiScriptNamePtr, mrs, mrs/2);
}

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

