[0001]
[0002]
[0003]
[0004]
[0005]
[0006]
[0007]
[0008]
[0009]
[0010]
[0011]
[0012]
[0013]
[0014]
[0015]
[0016]
[0017]
[0018]
[0019]
[0020]
[0021]
[0022]
[0023]
[0024]
[0025]
[0026]
[0027]
[0028]
[0029]
[0030]
[0031]
[0032]
[0033]
[0034]
[0035]
[0036]
[0037]
[0038]
[0039]
[0040]
[0041]
[0042]
[0043]
[0044]
[0045]
[0046]
[0047]
[0048]
[0049]
[0050]
[0051]
[0052]
[0053]
[0054]
[0055]
[0056]
[0057]
[0058]
[0059]
[0060]
[0061]
[0062]
[0063]
[0064]
[0065]
[0066]
[0067]
[0068]
[0069]
[0070]
[0071]
[0072]
[0073]
[0074]
[0075]
[0076]
[0077]
[0078]
[0079]
[0080]
[0081]
[0082]
[0083]
[0084]
[0085]
[0086]
[0087]
[0088]
[0089]
[0090]
[0091]
[0092]
[0093]
[0094]
[0095]
[0096]
[0097]
[0098]
[0099]
[0100]
[0101]
[0102]
[0103]
[0104]
[0105]
[0106]
[0107]
[0108]
[0109]
[0110]
[0111]
[0112]
[0113]
[0114]
[0115]
[0116]
[0117]
[0118]
[0119]
[0120]
[0121]
[0122]
[0123]
[0124]
[0125]
[0126]
[0127]
[0128]
[0129]
[0130]
[0131]
[0132]
[0133]
[0134]
[0135]
[0136]
[0137]
[0138]
[0139]
[0140]
[0141]
[0142]
[0143]
[0144]
[0145]
[0146]
[0147]
[0148]
[0149]
[0150]
[0151]
[0152]
[0153]
[0154]
[0155]
[0156]
[0157]
[0158]
[0159]
[0160]
[0161]
[0162]
[0163]
[0164]
[0165]
[0166]
[0167]
[0168]
[0169]
[0170]
[0171]
[0172]
[0173]
[0174]
[0175]
[0176]
[0177]
[0178]
[0179]
[0180]
[0181]
[0182]
[0183]
[0184]
[0185]
[0186]
[0187]
[0188]
[0189]
[0190]
[0191]
[0192]
[0193]
[0194]
[0195]
[0196]
[0197]
[0198]
[0199]
[0200]
[0201]
[0202]
[0203]
[0204]
[0205]
[0206]
[0207]
[0208]
[0209]
[0210]
[0211]
[0212]
[0213]
[0214]
[0215]
[0216]
[0217]
[0218]
[0219]
[0220]
[0221]
[0222]
[0223]
[0224]
[0225]
[0226]
[0227]
[0228]
[0229]
[0230]
[0231]
[0232]
[0233]
[0234]
[0235]
[0236]
[0237]
[0238]
[0239]
[0240]
[0241]
[0242]
[0243]
[0244]
[0245]
[0246]
[0247]
[0248]
[0249]
[0250]
[0251]
[0252]
[0253]
[0254]
[0255]
[0256]
[0257]
[0258]
[0259]
[0260]
[0261]
[0262]
[0263]
[0264]
[0265]
[0266]
[0267]
[0268]
[0269]
[0270]
[0271]
[0272]
[0273]
[0274]
[0275]
[0276]
[0277]
[0278]
[0279]
[0280]
[0281]
[0282]
[0283]
[0284]
[0285]
[0286]
[0287]
[0288]
[0289]
[0290]
[0291]
[0292]
[0293]
[0294]
[0295]
[0296]
[0297]
[0298]
[0299]
[0300]
[0301]
[0302]
[0303]
[0304]
[0305]
[0306]
[0307]
[0308]
[0309]
[0310]
[0311]
[0312]
[0313]
[0314]
[0315]
[0316]
[0317]
[0318]
[0319]
[0320]
[0321]
[0322]
[0323]
[0324]
[0325]
[0326]
[0327]
[0328]
[0329]
[0330]
[0331]
[0332]
[0333]
[0334]
[0335]
[0336]
[0337]
[0338]
[0339]
[0340]
[0341]
[0342]
[0343]
[0344]
[0345]
[0346]
[0347]
[0348]
[0349]
[0350]
[0351]
[0352]
[0353]
[0354]
[0355]
[0356]
[0357]
[0358]
[0359]
[0360]
[0361]
[0362]
[0363]
[0364]
[0365]
[0366]
[0367]
[0368]
[0369]
[0370]
[0371]
[0372]
[0373]
[0374]
[0375]
[0376]
[0377]
[0378]
[0379]
[0380]
[0381]
[0382]
[0383]
[0384]
[0385]
[0386]
[0387]
[0388]
[0389]
[0390]
[0391]
[0392]
[0393]
[0394]
[0395]
[0396]
[0397]
[0398]
[0399]
[0400]
[0401]
[0402]
[0403]
[0404]
[0405]
[0406]
[0407]
[0408]
[0409]
[0410]
[0411]
[0412]
[0413]
[0414]
[0415]
[0416]
[0417]
[0418]
[0419]
[0420]
[0421]
[0422]
[0423]
[0424]
[0425]
[0426]
[0427]
[0428]
[0429]
[0430]
[0431]
[0432]
[0433]
[0434]
[0435]
[0436]
[0437]
[0438]
[0439]
[0440]
[0441]
[0442]
[0443]
[0444]
[0445]
[0446]
[0447]
[0448]
[0449]
[0450]
[0451]
[0452]
[0453]
[0454]
[0455]
[0456]
[0457]
[0458]
[0459]
[0460]
[0461]
[0462]
[0463]
[0464]
[0465]
[0466]
[0467]
[0468]
[0469]
[0470]
[0471]
[0472]
[0473]
[0474]
[0475]
[0476]
[0477]
[0478]
[0479]
[0480]
[0481]
[0482]
[0483]
[0484]
[0485]
[0486]
[0487]
[0488]
[0489]
[0490]
[0491]
[0492]
[0493]
[0494]
[0495]
[0496]
[0497]
[0498]
[0499]
[0500]
[0501]
[0502]
[0503]
[0504]
[0505]
[0506]
[0507]
[0508]
[0509]
[0510]
[0511]
[0512]
[0513]
[0514]
[0515]
[0516]
[0517]
[0518]
[0519]
[0520]
[0521]
[0522]
[0523]
[0524]
[0525]
[0526]
[0527]
[0528]
[0529]
[0530]
[0531]
[0532]
[0533]
[0534]
[0535]
[0536]
[0537]
[0538]
[0539]
[0540]
[0541]
[0542]
[0543]
[0544]
[0545]
[0546]
[0547]
[0548]
[0549]
[0550]
[0551]
[0552]
[0553]
[0554]
[0555]
[0556]
[0557]
[0558]
[0559]
[0560]
[0561]
[0562]
[0563]
[0564]
[0565]
[0566]
[0567]
[0568]
[0569]
[0570]
[0571]
[0572]
[0573]
[0574]
[0575]
[0576]
[0577]
[0578]
[0579]
[0580]
[0581]
[0582]
[0583]
[0584]
[0585]
[0586]
[0587]
[0588]
[0589]
[0590]
[0591]
[0592]
[0593]
[0594]
[0595]
[0596]
[0597]
[0598]
[0599]
[0600]
[0601]
[0602]
[0603]
[0604]
[0605]
[0606]
[0607]
[0608]
[0609]
[0610]
[0611]
[0612]
[0613]
[0614]
[0615]
[0616]
[0617]
[0618]
[0619]
[0620]
[0621]
[0622]
[0623]
[0624]
[0625]
[0626]
[0627]
[0628]
[0629]
[0630]
[0631]
[0632]
[0633]
[0634]
[0635]
[0636]
[0637]
[0638]
[0639]
[0640]
[0641]
[0642]
[0643]
[0644]
[0645]
[0646]
[0647]
[0648]
[0649]
[0650]
[0651]
[0652]
[0653]
[0654]
[0655]
[0656]
[0657]
[0658]
/*****************************************************************************/
/*
                                sadHOC.c

Pronounced: sad-dock (yep, kinda melancholy)

Really just a development/experimental tool for the screper module.

Allows ad hoc commands to be screped.

Also see SCREPER.C code.

sadHOC requires server authorisation.  If access for the great unwashed is
desired (not recommended), define the logical name SADHOC_REMOTE_USER to
anything at all.


HOW IT WORKS
------------
See the prologue to SCREPER.C.


COPYRIGHT
---------
Copyright (C) 2021 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.


VERSION LOG
-----------
01-AUG-2021  MGD  initial
*/
/*****************************************************************************/

#define SOFTWAREVN "v1.0.0"
#define SOFTWARENM "SADHOC"
#ifdef __ALPHA
#  define SOFTWAREID SOFTWARENM " " SOFTWAREVN " AXP"
#endif
#ifdef __ia64
#  define SOFTWAREID SOFTWARENM " " SOFTWAREVN " IA64"
#endif
#ifdef __x86_64
#  define SOFTWAREID SOFTWARENM " " SOFTWAREVN " X86"
#endif

#include <ctype.h>
#include <descrip.h>
#include <errno.h>
#include <fcntl.h>
#include <in.h>
#include <ints.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stat.h>
#include <unistd.h>
#include <unixio.h>

#include <jpidef.h>
#include <libdef.h>
#include <lib$routines.h>
#include <starlet.h>
#include <ssdef.h>
#include <syidef.h>

#include "screper.h"

#define FI_LI "SHTTPMON",__LINE__

#ifndef UINT64PTR
/* mainly to allow easy use of the __unaligned directive */
#define UINTPTR __unaligned unsigned int*
#define ULONGPTR __unaligned unsigned long*
#define USHORTPTR __unaligned unsigned short*
#define UINT64PTR __unaligned uint64*
#define INT64PTR __unaligned int64*
#endif

#define SAD_MAX 4

int  begun,
     inspect;

char  *CgiQueryString,
      *CgiRemoteUser,
      *CgiRequestUri,
      *CgiScriptName,
      *CgiServerSoftware;

static char  ScriptName [64],
             SyiNodeName [16];

void  *scrptr;

char  *sadhocStyle =
"<style type=\"text/css\">\n\
body { font-family:arial,verdana,helvetica,sans; font-size:11pt; \
background-color:white; color:black; margin:1em; }\n\
h1 { font-size:1.3em; }\n\
iframe { border:dotted gray 1px; }\n\
.problem { color:red; }\n\
</style>\n";

char  *sadhocSmallerFont =
"<script>setTimeout(\"document.body.style.fontSize = \
\'smaller\'\",100);</script>\n";

/* prototypes */
int sadhocCount ();
void sadhocExit (int, int);
char* sadhocHtmlEncode (char*);
int sadhocNumber (int);
void sadhocPage ();
void sadhocPopulate ();
void sadhocSubmit ();
void sadhocTerminal (int);
int sadhocUrlDecode (char*);

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

int main (int argc, char *argv[])

{
   static unsigned long  JpiPidItem = JPI$_PID;
   static unsigned long  SyiNodeNameItem = SYI$_NODENAME;
   static $DESCRIPTOR (SyiNodeNameDsc, SyiNodeName);

   int  interval, page;
   ushort  slen;
   char  *cptr, *sptr, *zptr;;
   char  cmd [256];

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

   if (argc > 1)
   {
      if (!strncasecmp (argv[1], "/VERSION", 4))
         fprintf (stdout, "%s\n%s\n", SOFTWAREID, ScreperDo (NULL, "-version"));
      exit (SS$_NORMAL);
   }

   if (!(stdout = freopen ("SYS$OUTPUT", "w", stdout, "ctx=bin")))
      sadhocExit (vaxc$errno, __LINE__);

   lib$getsyi (&SyiNodeNameItem, 0, &SyiNodeNameDsc, &slen, 0, 0);
   SyiNodeName[slen] = '\0';

   if (!(CgiServerSoftware = getenv ("WWW_SERVER_SOFTWARE")))
      CgiServerSoftware = getenv ("SERVER_SOFTWARE");

   if (!CgiServerSoftware) sadhocExit (SS$_NORMAL, __LINE__);

   strcpy (ScriptName, "sadHOC"); 

   if (!(CgiRemoteUser = getenv ("SADHOC_REMOTE_USER")))
      if (!(CgiRemoteUser = getenv ("WWW_REMOTE_USER")))
         if (!(CgiRemoteUser = getenv ("REMOTE_USER")))
            CgiRemoteUser = "";

   if (!*CgiRemoteUser)
   {
      begun = 1;

      fprintf (stdout,
"Status: 403 Forbidden\r\n\
Content-Type: text/html\r\n\
\r\n\
<!DOCTYPE html>\n\
<html>\n\
<head>\n\
<meta name=\"generator\" content=\"%s\">\n\
<title>%s:: %s</title>\n\
%s\
</head>\n\
<body>\n\
<pre><span class=\"problem\">authorization failure</span></pre>\n\
</body>\n\
</html>\n",
               SOFTWAREID, SyiNodeName, ScriptName, sadhocStyle);

      sadhocExit (SS$_NORMAL, __LINE__);
   }

   scrptr = ScreperInit ();

   if (cptr = getenv ("SADHOC_INSPECT"))
      inspect = atol (cptr);

   if (!(CgiRequestUri = getenv ("WWW_REQUEST_URI")))
      if (!(CgiRequestUri = getenv ("REQUEST_URI")))
         CgiRequestUri = "";

   if (!(CgiScriptName = getenv ("WWW_SCRIPT_NAME")))
      if (!(CgiScriptName = getenv ("SCRIPT_NAME")))
         CgiScriptName = "";

   if (!(CgiQueryString = getenv ("WWW_QUERY_STRING")))
      if (!(CgiQueryString = getenv ("QUERY_STRING")))
         CgiQueryString = "";

   if (*CgiQueryString)
   {
      if (strstr (CgiQueryString, "populate="))
         sadhocPopulate ();
      else
         sadhocPage ();
   }
   else
      sadhocSubmit ();

   sadhocExit (SS$_NORMAL, __LINE__);
}

/*****************************************************************************/
/*
Provide a form providing a single or multiple ad hoc commands.
*/

void sadhocSubmit ()

{

   int  idx;
   char  *cptr;
   const char *sptr = "-page=48 -width=80 -repeat=2 -timestamp\n"
                      "-dcl=SHOW SYSTEM";

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

   begun = 1;

   fprintf (stdout,
"Status: 200 OK\r\n\
Content-Type: text/html\r\n\
Script-Control: X-stream-mode=1\r\n\
\r\n\
<!DOCTYPE html>\n\
<html>\n\
<head>\n\
<meta name=\"generator\" content=\"%s\">\n\
<title>%s:: %s</title>\n\
%s\n\
</style>\n\
</head>\n\
<body>\n\
<h1>sadHOC</h1>\n\
<form method=\"GET\" action=\"%s\" \
content-type=\"application/x-www-form-urlencoded\">\n\
<textarea name=\"adhoc1\" rows=\"4\" cols=\"80\">%s</textarea>\n\
<p><input type=\"submit\" name=\"try\" value=\"Try One\">\n\
<br><br>\n",
            SOFTWAREID, SyiNodeName, ScriptName, sadhocStyle,
            CgiScriptName, sptr);

   for (idx = 2; idx <= SAD_MAX; idx++)
      fprintf (stdout,
"<p><textarea name=\"adhoc%d\" rows=\"4\" cols=\"80\"></textarea>\n",
               idx);

   fprintf (stdout,
"<p><input type=\"submit\" name=\"try\" value=\"Try All\">\n\
&nbsp;&nbsp;\
<input type=\"reset\" value=\"reset\">\n\
</form>\n\
</body>\n\
</html>\n");

   sadhocExit (SS$_NORMAL, __LINE__);
}

/*****************************************************************************/
/*
A page with multiple terminal screen outputs, each in an iframe.
If a single screen then sadhocTerminal() provides that directly.
*/

void sadhocPage ()

{
   int  number;
   char  *cptr;

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

   if (strstr (CgiQueryString, "=Try+One"))
   {
      sadhocTerminal (1);
      return;
   }

   if ((number = sadhocCount()) <= 1)
   {
      sadhocTerminal (1);
      return;
   }

   number = 0;
   if (cptr = strstr (CgiQueryString, "&number="))
      number = atoi (cptr+8);
   if (number >= 1 && number <= SAD_MAX)
   {
      sadhocTerminal (number);
      return;
   }

   begun = 1;

   fprintf (stdout,
"Status: 200 OK\r\n\
Content-Type: text/html\r\n\
Script-Control: X-stream-mode=1\r\n\
\r\n\
<!DOCTYPE html>\n\
<html>\n\
<head>\n\
<meta name=\"generator\" content=\"%s\">\n\
<title>%s:: %s</title>\n\
%s\
%s\
</head>\n\
<body>\n",
            SOFTWAREID, SyiNodeName, ScriptName,
            ScreperDo(NULL,"-resize"), sadhocStyle);

   fprintf (stdout,
"<div style=\"display:inline-flex; flex-wrap:wrap;\">\n");

   for (number = 1; number <= SAD_MAX; number++)
      if (sadhocNumber (number))
         fprintf (stdout,
"<iframe id=\"sadhoc%d\" src=\"%s&number=%d\" scrolling=\"no\">\n\
</iframe>\n",
                  number, CgiRequestUri, number);

   fprintf (stdout,
"</div>\n\
<p>%s\
</body>\n\
</html>\n", ScreperDo(NULL,"-pause"));

   sadhocExit (SS$_NORMAL, __LINE__);
}

/*****************************************************************************/
/*
A page with the embedded terminal screen output.
If multiple concurrent terminals then this will be in an iframe.
*/

void sadhocTerminal (int number)

{
   int  idx, count, status;
   char  *cptr, *sptr;
   char  class [64],
         scrdo [128];

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

   begun = 1;

   sprintf (scrdo, "-screen=%s", ScriptName);

   fprintf (stdout,
"Status: 200 OK\r\n\
Content-Type: text/html\r\n\
Script-Control: X-stream-mode=1\r\n\
\r\n\
<!DOCTYPE html>\n\
<html>\n\
<head>\n\
<meta name=\"generator\" content=\"%s\">\n\
<title>%s:: %s</title>\n\
%s\
%s\
%s\
</head>\n\
<body onload=\"doScreper()\">\n\
%s\
</body>\n\
</html>\n",
            SOFTWAREID, SyiNodeName, ScriptName,
            ScreperDo (NULL, "-css"),
            ScreperDo (NULL, "-javascript"),
            sadhocCount() > 1 ? sadhocSmallerFont : "",
            ScreperDo (NULL, scrdo));

   sadhocExit (SS$_NORMAL, __LINE__);
}

/*****************************************************************************/
/*
Spawn the command subprocess and provide the HTML-ified, scraped terminal
screen output back to the embedded virtual screen.
*/

void sadhocPopulate ()

{
   int  number, status;
   char  *cptr, *rptr, *sptr, *zptr;
   char  scratch [256],
         scrdo [512];

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

   number = 0;
   if (cptr = strstr (CgiQueryString, "number="))
      number = atoi (cptr+7);
   if (number < 1 || number > SAD_MAX) number = 1;

   sprintf (scratch, "adhoc%d=", number);
   if (cptr = strstr (CgiQueryString, scratch))
   {
      while (*cptr && *cptr != '=') cptr++;
      if (*cptr) cptr++;
      zptr = (sptr = scrdo) + sizeof(scrdo)-32;
      sptr += sprintf (sptr, "-inspect=%d -utility=\"%s\"",
                       inspect, "sadHOC");
      while (*cptr && *cptr != '&' && sptr < zptr) *sptr++ = *cptr++;
      *sptr = '\0';
      sadhocUrlDecode (scrdo);
   }
   else
      sadhocExit (SS$_BUGCHECK, __LINE__);

   /* facilitate development - no referer then is populate=1 directly */
   if (!(rptr = getenv ("WWW_HTTP_REFERER")))
      rptr = getenv ("HTTP_REFERER");

   begun = 1;

   if (inspect && rptr)
      fputs ("Status: 200 OK\r\n\
Content-Type: text/plain\r\n\
Script-Control: X-record-mode=1\r\n\
\r\n", stdout);
   else
      fputs ("Status: 200 OK\r\n\
Content-Type: text/html\r\n\
Script-Control: X-stream-mode=1\r\n\
Script-Control: X-timeout-output=none\r\n\
Script-Control: X-content-encoding-gzip=0\r\n\
\r\n", stdout);
   fflush (stdout);

   if (!rptr)
      /* no referer then is populate=1 directly */
      fprintf (stdout, "<!DOCTYPE html>\n<html>\n%s<body>\n<pre>\n",
               ScreperDo (NULL, "-css"));
 
   if (inspect)
   {
      fprintf (stdout, "|%s|\n", scrdo);
      fflush (stdout);
   }

   sptr = ScreperDo (scrptr, scrdo);
   if (*(USHORTPTR)sptr == '%X')
   {
      fprintf (stdout, "%s\n", sptr);
      status = strtol (sptr+2, NULL, 16);
   }
   else
      status = SS$_BUGCHECK;

   if (status & 1) sys$hiber();

   sadhocExit (status, __LINE__);
}

/*****************************************************************************/
/*
By parsing the query string, return true or false if the specified terminal
number is present and being a textarea has non-space content.
*/

int sadhocNumber (int number)

{
   int  idx;
   char  *cptr;
   char  scratch [64];

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

   sprintf (scratch, "adhoc%d=", number);
   if (cptr = strstr (CgiQueryString, scratch))
   {
      while (*cptr && *cptr != '=' && *cptr != '&') cptr++;
      if (*cptr == '=')
      {
         for (cptr++; *cptr && *cptr != '&' && isspace(*cptr); cptr++);
         if (*cptr && *cptr != '&') return (1);
      }
   }
   return (0);
}

/*****************************************************************************/
/*
By parsing the query string, return the number of individual monitor terminals.
*/

int sadhocCount ()

{
   int  count, number;

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

   count = 0;
   for (number = 1; number <= SAD_MAX; number++)
      if (sadhocNumber (number)) count++;
   return (count);
}

/*****************************************************************************/
/*
Perform in-place URL decode.  Perform strdup() before calling to retain
original then free() when finished using, as required.
*/

int sadhocUrlDecode (char *string)

{
   uint  ch;
   char  *cptr, *sptr;

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

   for (cptr = sptr = string; *cptr; cptr++)
   {
      if (*cptr == '+')
         *sptr++ = ' ';
      else
      if (*cptr == '%')
      {
         *sptr = '\0';
         cptr++;
         if (*(cptr+1))
         {
            if (sscanf (cptr, "%02x", &ch) < 1) return (0);
            *sptr++ = ch;
         }
         else
            return (0);
         cptr++;
      }
      else
         *sptr++ = *cptr;
   }
   *sptr = '\0';
   return (sptr - string);
}

/*****************************************************************************/
/*
Return a pointer to an allocated string containing encoded HTML-forbidden
characters.  The string can be free()ed after use, as required.
*/

char* sadhocHtmlEncode (char *string)

{
   int  cnt = 0;
   char  *aptr, *cptr, *eptr, *sptr;

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

   for (cptr = string; *cptr; cptr++)
      if (*cptr && (*cptr == '<' || *cptr == '>' || *cptr != '&' ||
                    *cptr == '\"' || *cptr != '\'')) cnt++;
   aptr = sptr = calloc (1, cptr - string + (cnt * 6));
   for (cptr = string; *cptr; cptr++)
   {
      if (*cptr != '<' && *cptr != '>' && *cptr != '&' &&
          *cptr != '\"' && *cptr != '\'')
         *sptr++ = *cptr;
      else
      {
         if (*cptr == '<')
            eptr = "&lt;";
         else
         if (*cptr == '>')
            eptr = "&gt;";
         else
         if (*cptr == '&')
            eptr = "&amp;";
         else
         if (*cptr == '\"')
            eptr = "&quot;";
         else
            eptr = "&apos;";
         while (*eptr) *sptr++ = *eptr++;
      }
   }
   return (aptr);
}

/*****************************************************************************/
/*
Provide some WATCHable exit information.
*/

void sadhocExit (int status, int line)

{
   /*********/
   /* begin */
   /*********/

   if (!begun)
      fputs ("Status: 200 OK\r\n\
Content-Type: text/plain\r\n\
Script-Control: X-record-mode=1\r\n\
\r\n", stdout);

    fprintf (stdout, "\n<!-- sadHOC:%d %%X%08.08X -->", line, status);

    exit (SS$_NORMAL);
}

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