/*****************************************************************************/
/*
                                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
---------
WASD VMS Hypertext Services, Copyright (C) 1996-2005 Mark G.Daniel.

This package 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; version 2 of the License, or any later version.

This package 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, write to the Free Software Foundation, Inc., 675 Mass
Ave, Cambridge, MA 02139, USA.


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
#  define 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>

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

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);
}

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

