/*****************************************************************************/
/*
                              In[line]Config.c

Woohoo!  After several false starts, finally came up up with a workable, single
file, configuration mechanism.  Historically, WASD uses separate files to
configure global settings, authorisation, resource mapping, and virtual
services.  Before complexity became the norm this was workable.

With multiple virtual services and sites, each requiring some customisation, it
has become increasingly cumbersome maintaining service, mapping, and
authorisation configurations across multiple files.  So this inline
configuration allows those aspects to be combined, with emphemeral (temporary)
WASD configuration files generated and fed to the standard WASD configuration
mechanisms.

To use inline configuration create a logical name WASD_CONFIG_INLINE specifying
the file prior to server startup.  For example

  $ DEFINE /TABLE=WASD_TABLE -
           WASD_CONFIG_INLINE -
           WASD_LOCAL:CONFIG_INLINE.CONF

[IncludeFile]s are inspected for indication of content; does it look like
WASD_CONFIG_GLOBAL directives, WASD_CONFIG_SERVICE, WASD_CONFIG_MAP or
WASD_CONFIG_AUTH directives?  If not recognised an error is flagged and
configuration stops.  The following inline-specific directives can be used to
explicitly specify a file's content.

  [Auth]     to   WASD_CONFIG_AUTH 
  [Global]   to   WASD_CONFIG_GLOBAL
  [Map]      to   WASD_CONFIG_MAP
  [Service]  to   WASD_CONFIG_SERVICE

These may ALSO be used to specify the content at any stage during
configuration.  The destination remains current until the end-of-file or next
destination directive.

Best not include the standard WASD_CONFIG_.. filenames, via logical or
explicitly by specification.  While not forbidden, this often will result in
UNDEFINED BEHAVIOUR.  Use inline specific file names.  One exception is the
WASD_CONFIG_GLOBAL defined file, which will automagically be included if not
explicitly [IncludeFile]d.

A variant allows a single directive to be inserted into a specific
configuration file using a destination directive as a prefix, followed on the 
same line by the single directive.  For example

  [Map] pass /* /wasd_root/44443/*

[IncludeFile]ing a GLOBAL configuration file is optional.  If not explicitly
included WASD attempts to automatically include the latest non-ephemeral
WASD_CONFIG_GLOBAL file before the first [Global] prefix directive.

Note that the [Service] inline directive conflicts with a WASD_CONFIG_GLOBAL
directive of the same name.  The global directive is obsolete but also responds
to [ServiceGlobal] directive :-)


EXAMPLE FILE
------------

|# WASD_CONFIG_INLINE
|[IncludeFile] wasd_local:inline_config_global.conf
|
|[IncludeFile] wasd_local:inline_config_map.conf
|
|[IncludeFile] wasd_local:inline_config_service.conf
|
|[IncludeFile] wasd_local:inline_config_auth.conf
|
|[Service]
|[[https://*:44443]]
|[Map]
|pass /* /wasd_root/44443/*


VERSION HISTORY
---------------
07-JUN-2022  MGD  initial
*/
/*****************************************************************************/

#ifdef WASD_VMS_V7
#undef _VMS__V6__SOURCE
#define _VMS__V6__SOURCE
#undef __VMS_VER
#define __VMS_VER 70000000
#undef __CRTL_VER
#define __CRTL_VER 70000000
#endif

#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <stat.h>

#include <lnmdef.h>

#include "wasd.h"

#define WASD_MODULE "INCONFIG"

#define INCLUDE_MAX 4

#define INCONFIG_EPHEMERAL "\
\n\
##!## *************************************************\n\
##!## *****  INLINE EPHEMERAL FILE - DO NOT EDIT  *****\n\
##!## *************************************************\n\
\n"

#if WATCH_MOD
#define INCONFIG_EXCISE 1
#else
#define INCONFIG_EXCISE 1
#endif

/******************/
/* global storage */
/******************/

static BOOL  InConfigStart;

BOOL  InConfigGlobal,
      InConfigLoading;

int  InConfigStatus;

char  AuthFileName [256],
      GlobalFileName [256],
      InConfigFileName [256],
      MapFileName [256],
      ServiceFileName [256];

FILE  *AuthFile,
      *GlobalFile,
      *MapFile,
      *ServiceFile;

static FILE  *AuthFileBuf,
             *GlobalFileBuf;
             *MapFileBuf,
             *ServiceFileBuf,
             *TheFile;

/********************/
/* external storage */
/********************/

extern int64  HttpdTime64;
extern unsigned long  SysPrvMask[];
extern BOOL  HttpdServerStartup;
extern WATCH_STRUCT  Watch;

/*****************************************************************************/
/*
Return a distinctive error status if the WASD_CONFIG_INLINE logical name does
not exist.  Otherwise initialise the inline configuration.
*/

int InConfigInit (char *cptr)
{
   int  status;
   char  *sptr, *zptr;

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

   if (WATCH_MODULE(WATCH_MOD_CONFIG))
      WatchThis (WATCHALL, WATCH_MOD_CONFIG, "InConfigInit()");

   /* allows the check for inline ephemeral files to be disabled */
   cptr = SysTrnLnm (WASD_INLINE_CHECK);
   if (!cptr) cptr = "1";
   if (atoi (cptr))
   {
      /* delete any inline ephemeral files still hanging around */
      sys$setprv (1, &SysPrvMask, 0, 0);
      cptr = SysTrnLnm (CONFIG_AUTH_FILE_NAME);
      InConfigCheckFile (cptr);
      cptr = SysTrnLnm (CONFIG_GLOBAL_FILE_NAME);
      InConfigCheckFile (cptr);
      cptr = SysTrnLnm (CONFIG_MAP_FILE_NAME);
      InConfigCheckFile (cptr);
      cptr = SysTrnLnm (CONFIG_SERVICE_FILE_NAME);
      InConfigCheckFile (cptr);
      sys$setprv (0, &SysPrvMask, 0, 0);
   }

   if (!(cptr = SysTrnLnm (CONFIG_INLINE_FILE_NAME))) return (SS$_NOLOGNAM);

   InConfigStart = InConfigLoading = true;

   zptr = (sptr = InConfigFileName) + sizeof(InConfigFileName)-1;
   while (*cptr && sptr < zptr) *sptr++ = *cptr++;
   *sptr = '\0';

   FaoToStdout ("%HTTPD-I-INCONFIG, from !AZ\n", InConfigFileName);

   status = InConfigOpen ();
   if (VMSnok (status)) return (status);

   status = InConfigLoad ();
   if (VMSnok (status)) return (status);

   return (SS$_NORMAL);
}

/*****************************************************************************/
/*
OdsOpenReadOnly() needed fab$b_shr = FAB$M_SHRGET | FAB$M_SHRUPD to allow
InConfigInit() to fopen(..."w","shr=get") otherwise FLK.  Hmmm.
*/

int InConfigOpen ()
{
   int  status;
   char  *cptr, *sptr, *zptr;
   char  fname [256],
         DateTime [32];

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

   if (WATCH_MODULE(WATCH_MOD_CONFIG))
      WatchThis (WATCHALL, WATCH_MOD_CONFIG, "InConfigOpen()");

   FaoToBuffer (DateTime, sizeof(DateTime), NULL, "!20%D", &HttpdTime64);

   status = SS$_NORMAL;
   sys$setprv (1, &SysPrvMask, 0, 0);

   cptr = SysTrnLnm (CONFIG_AUTH_FILE_NAME);
   strzcpy (AuthFileName, cptr, sizeof(AuthFileName));
   if (!(AuthFile = fopen (cptr, "w+", "shr=get", "fop=dlt")))
      status = vaxc$errno;
   else
   if (!fgetname (AuthFile, fname, 1))
      sprintf (fname, "%%X%08.08X", status = vaxc$errno);
   InConfigNoVersion (fname);
   /* add sentinal */
   fprintf (AuthFile, "##!## %s %s\n", fname, DateTime);
   fprintf (AuthFile, "%s", INCONFIG_EPHEMERAL);

   if (VMSok(status))
   {
      cptr = SysTrnLnm (CONFIG_GLOBAL_FILE_NAME);
      strzcpy (GlobalFileName, cptr, sizeof(GlobalFileName));
      if (!(GlobalFile = fopen (cptr, "w+", "shr=get", "fop=dlt")))
         status = vaxc$errno;
      else
      if (!fgetname (GlobalFile, fname, 1))
         sprintf (fname, "%%X%08.08X", status = vaxc$errno);
      InConfigNoVersion (fname);
      /* add sentinal */
      fprintf (GlobalFile, "##!## %s %s\n", fname, DateTime);
      fprintf (GlobalFile, "%s", INCONFIG_EPHEMERAL);
   }

   if (VMSok(status))
   {
      cptr = SysTrnLnm (CONFIG_MAP_FILE_NAME);
      strzcpy (MapFileName, cptr, sizeof(MapFileName));
      if (!(MapFile = fopen (cptr, "w+", "shr=get", "fop=dlt")))
         status = vaxc$errno;
      else
      if (!fgetname (MapFile, fname, 1))
         sprintf (fname, "%%X%08.08X", status = vaxc$errno);
      InConfigNoVersion (fname);
      /* add sentinal */
      fprintf (MapFile, "##!## %s %s\n", fname, DateTime);
      fprintf (MapFile, "%s", INCONFIG_EPHEMERAL);
   }

   if (VMSok(status))
   {
      cptr = SysTrnLnm (CONFIG_SERVICE_FILE_NAME);
      strzcpy (ServiceFileName, cptr, sizeof(ServiceFileName));
      if (!(ServiceFile = fopen (cptr, "w+", "shr=get", "fop=dlt")))
         status = vaxc$errno;
      else
      if (!fgetname (ServiceFile, fname, 1))
         sprintf (fname, "%%X%08.08X", status = vaxc$errno);
      InConfigNoVersion (fname);
      /* add sentinal */
      fprintf (ServiceFile, "##!## %s %s\n", fname, DateTime);
      fprintf (ServiceFile, "%s", INCONFIG_EPHEMERAL);
   }

   sys$setprv (0, &SysPrvMask, 0, 0);

   return (status);
}

/*****************************************************************************/
/*
It is possble, under exceptional circumstances, for an ephemeral configuration
file not to be closed properly (InConfigClose() not executed) and therefore to
to remain undeleted.  This function attempts to cleanup such files by examining
the distinctive sentinal present in the first line of such a file.  For each
file the header is parsed for the sentinal and if present the file is deleted.
This is repeated for all file name versions until it fails.  In-use versions,
from other instances, are locked and therefore not deleted. 
InConfigCheckFile() is called with SYSPRV already enabled.
*/

void InConfigCheckFile (char *fname)
{
   int  status;
   char  line [INCONFIG_LINE_MAX],
         vname [256];
   FILE  *filptr;

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

   if (WATCH_MODULE(WATCH_MOD_CONFIG))
      WatchThis (WATCHALL, WATCH_MOD_CONFIG, "InConfigCheckFile() !AZ", fname);

   for (;;)
   {
      /* if we can't open it then it's either not there or locked */
      filptr = fopen (fname, "r", "shr=get");
      if (!filptr) break;
      if (!InConfigSentinal (filptr)) break;
      fgetname (filptr, vname, 1);
      fclose (filptr);
      filptr = NULL;
      remove (fname);
      if (!(status = vaxc$errno)) status = SS$_NORMAL;
      FaoToStdout ("%HTTPD-W-INCONFIG, remove !AZ !&S\n", vname, status);
      if (VMSnok (status)) break;
   }
   if (filptr) fclose (filptr);
}

/*****************************************************************************/
/*
Check the first line of the file for something that looks like

   ##!## WASD_CONFIG:WASD_CONFIG_GLOBAL.CONF 08-JUN-2022 22:44:55

If the file is empty, or has a first line as above, return true, else false;
*/

BOOL InConfigSentinal (FILE *filptr)
{
   int  status;
   int64  time64;
   char  *cptr, *sptr;
   char  line [INCONFIG_LINE_MAX];
   $DESCRIPTOR (BinTimeDsc, "");

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

   if (WATCH_MODULE(WATCH_MOD_CONFIG))
      WatchThis (WATCHALL, WATCH_MOD_CONFIG, "InConfigSentinal()");

   rewind (filptr);
   sptr = fgets (line, sizeof(line), filptr);
   rewind (filptr);
   if (!sptr) return (true);
   if (!MATCH6 (line, "##!## ")) return (false);
   /* span over file name */
   for (cptr = line + 6; *cptr && !isspace(*cptr); cptr++);
   if (*cptr) cptr++;
   for (sptr = cptr; *sptr && *sptr != '\n'; sptr++);
   if (*sptr != '\n') return (false);
   /* attempt to convert what should be a VMS time string */
   BinTimeDsc.dsc$a_pointer = cptr;
   BinTimeDsc.dsc$w_length = sptr - cptr;
   status = sys$bintim (&BinTimeDsc, &time64);
   if (VMSnok (status)) return (false);
   return (true);
}

/*****************************************************************************/
/*
Read the WASD_CONFIG_INLINE configuration file, processing the appropriate
section directives to the respective ephemeral configuration files.
*/

int InConfigLoad ()
{
   BOOL  IncludeGlobal;
   int  fcount, status, which;
   char  *aptr, *cptr, *sptr, *zptr;
   char  line [INCONFIG_LINE_MAX];

   int  lcount [INCLUDE_MAX+1];
   char  fname [INCLUDE_MAX+1][256];
   char  service [INCLUDE_MAX+1][128];
   FILE  *filptr [INCLUDE_MAX+1],
         *thebuf [INCLUDE_MAX+1];

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

   if (WATCH_MODULE(WATCH_MOD_CONFIG))
      WatchThis (WATCHALL, WATCH_MOD_CONFIG, "InConfigLoad()");

   memset (lcount, 0, sizeof(lcount));
   memset (fname, 0, sizeof(fname));
   memset (service, 0, sizeof(service));
   memset (filptr, 0, sizeof(filptr));
   memset (thebuf, 0, sizeof(thebuf));

   strzcpy (fname[0], InConfigFileName, sizeof(fname[0]));
   TheFile = NULL;
   InConfigGlobal = false;
  
   fcount = 0;
   for (;;)
   {
      sys$setprv (1, &SysPrvMask, 0, 0);
      filptr[fcount] = InConfigOpenFile (fname[fcount]);
      sys$setprv (0, &SysPrvMask, 0, 0);
      status = InConfigStatus;
      if (!filptr[fcount])
      {
         FaoToStdout ("%HTTPD-E-INCONFIG, failed to open !AZ\n", fname[fcount]);
         return (status);
      }
      /* overload inline config name with one containing the version number */
      if (!fcount) fgetname (filptr[fcount], InConfigFileName, 1);

      if ((which = InConfigIdentify (filptr[fcount])) == INCONFIG_UNKNOWN)
      {
         if (fcount)
         {
            FaoToStdout ("%HTTPD-E-INCONFIG, cannot identify !AZ\n",
                         fname[fcount]);
            return (SS$_ABORT);
         }
      }
      switch (which)
      {
         case INCONFIG_AUTH : TheFile = AuthFile; break;
         case INCONFIG_GLOBAL : TheFile = GlobalFile; break;
         case INCONFIG_MAP : TheFile = MapFile; break;
         case INCONFIG_SERVICE : TheFile = ServiceFile; break;
         case INCONFIG_UNKNOWN : TheFile = NULL; break;
         default : return (SS$_BUGCHECK);
      }

      thebuf[fcount] = TheFile;
      if (TheFile == GlobalFile) InConfigGlobal = true;

      if (fcount)
         fprintf (TheFile, "!#+# %s line %d [IncludeFile] %s\n",
                  fname[fcount-1], lcount[fcount-1], fname[fcount]);

      lcount[fcount] = 0;
      for (;;)
      {
         cptr = fgets (line, sizeof(line), filptr[fcount]);
         if (!cptr)
         {
            /* end of file */
            fclose (filptr[fcount--]);
            if (fcount < 0) break;
            fprintf (TheFile, "!#-# %s line %d [IncludeFile] %s\n",
                     fname[fcount], lcount[fcount], fname[fcount+1]);
            TheFile = thebuf[fcount];
            IncludeGlobal = false;
            continue;
         }
         lcount[fcount]++;

         /* find the first non-white-space */
         while (*cptr && ISLWS(*cptr)) cptr++;
         /* remove any trailing newline */
         for (sptr = cptr; *sptr && *sptr != '\n'; sptr++);
         *sptr = '\0';

         if (strsame (cptr, "[IncludeFile]", 13))
         {
            if (fcount++ > INCLUDE_MAX)
            {
               FaoToStdout ("%HTTPD-E-INCONFIG, [IncludeFile] exceeded %d\n",
                            INCLUDE_MAX-1);
               return (SS$_ABORT);
            }
            for (cptr += 13; *cptr && ISLWS(*cptr); cptr++);
            zptr = (sptr = fname[fcount]) + sizeof(fname[0])-1;
            while (*cptr && !ISLWS(*cptr) && sptr < zptr) *sptr++ = *cptr++;
            *sptr = '\0';
            if (!fname[fcount][0])
            {
               FaoToStdout ("%HTTPD-E-INCONFIG, no [IncludeFile] "
                            "name !AZ at !UL\n",
                            fname[fcount-1], lcount[fcount-1]);
               return (SS$_ABORT);
            }
            break;
         }

         aptr = NULL;
         if (strsame (cptr, "[Auth]", 6))
         {
            for (aptr = cptr + 6; *aptr && ISLWS(*aptr); aptr++);
            if (*aptr)
            {
               fprintf (AuthFile, "%s\n", aptr);
               continue;
            }
            TheFile = AuthFile;
         }
         else
         if (strsame (cptr, "[Global]", 8))
         {
            InConfigAddGlobal (fname[fcount], lcount[fcount]);
            for (aptr = cptr + 8; *aptr && ISLWS(*aptr); aptr++);
            if (*aptr)
            {
               fprintf (GlobalFile, "%s\n", aptr);
               continue;
            }
            TheFile = GlobalFile;
         }
         else
         if (strsame (cptr, "[Map]", 5))
         {
            for (aptr = cptr + 5; *aptr && ISLWS(*aptr); aptr++);
            if (*aptr)
            {
               fprintf (MapFile, "%s\n", aptr);
               continue;
            }
            TheFile = MapFile;
         }
         else
         if (strsame (cptr, "[Service]", 9))
         {
            if (TheFile != GlobalFile)
            {
               for (aptr = cptr + 9; *aptr && ISLWS(*aptr); aptr++);
               if (*aptr)
               {
                  fprintf (ServiceFile, "%s\n", aptr);
                  continue;
               }
               TheFile = ServiceFile;
            }
         }
         else
         if (*(USHORTPTR)cptr == '[[')
         {
            if (TheFile == ServiceFile)
            {
               zptr = (sptr = service[fcount]) + sizeof(service[0])-1;
               while (*cptr && !ISLWS(*cptr) && sptr < zptr) *sptr++ = *cptr++;
               *sptr = '\0';
               fprintf (AuthFile, "%s\n", service[fcount]);
               fprintf (MapFile, "%s\n", service[fcount]);
            }
            else
            if (TheFile)
               fprintf (TheFile, "%s\n", line);
            else
               continue;
         }

         if (!TheFile) continue;

         if (aptr && !*aptr)
         {
            /* note the changed target configuration file */
            fprintf (TheFile, "!#=# %s\n", cptr);
         }
         if (!aptr) fprintf (TheFile, "%s\n", line);
      }

      /* if end of original inline file */
      if (fcount < 0) break;
   }

   if (!InConfigGlobal) InConfigAddGlobal ("^Z", 0);

   /* remove consecutive duplicate entries */
   if (!(AuthFile = InConfigExcise (AuthFile))) return (InConfigStatus);
   if (!(MapFile = InConfigExcise (MapFile))) return (InConfigStatus);

   fsync (fileno(AuthFile));
   fsync (fileno(GlobalFile));
   fsync (fileno(MapFile));
   fsync (fileno(ServiceFile));

   return (SS$_NORMAL);
}

/*****************************************************************************/
/*
Open the named file.  If it opens check that it is not an ephemeral config
file.  This is performed by examining the first line looking for the ephemeral
file sentinal.  If it contains that sentinal then work back by one version each
time a file contains the ephemeral file sentinal.  Return a pointer to the open
file, or a NULL if not/none found.  Global storage InConfigStatus contains any
related VMS status value.
*/

FILE* InConfigOpenFile (char *FileName)

{
   int  status, version;
   char  *cptr, *sptr, *vptr, *zptr;
   char  fname [256];
   FILE  *filptr;

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

   if (WATCH_MODULE(WATCH_MOD_CONFIG))
      WatchThis (WATCHALL, WATCH_MOD_CONFIG, "InConfigOpenFile()");

   InConfigStatus = 0;

   sys$setprv (1, &SysPrvMask, 0, 0);
   filptr = fopen (FileName, "r", "shr=get");
   sys$setprv (0, &SysPrvMask, 0, 0);
   if (!filptr)
   {
      if (vaxc$errno != RMS$_FLK)
      {
         InConfigStatus = vaxc$errno;
         return (NULL);
      }
   }
   else
   if (!InConfigSentinal (filptr))
   {
      /* does not contain the ephemeral sentinal */
      return (filptr);
   }

   /* copy the file name allowing space for the appended version */
   zptr = (sptr = fname) + sizeof(fname)-8;
   for (cptr = FileName; *cptr && sptr < zptr; *sptr++ = *cptr++);
   *sptr = '\0';
   for (vptr = sptr; sptr > fname && *vptr != ';'; vptr--);
   if (sptr > fname) vptr = sptr;

   version = -1;
   filptr = NULL;

   for (;;)
   {
      sprintf (vptr, ";%d", version--);
      sys$setprv (1, &SysPrvMask, 0, 0);
      if (filptr) fclose(filptr);
      filptr = fopen (fname, "r", "shr=get");
      sys$setprv (0, &SysPrvMask, 0, 0);
      status = vaxc$errno;
      if (!filptr)
      {
         if (status == RMS$_FLK) continue;
         if (status == RMS$_FNF) return (NULL);
         InConfigStatus = status;
         return (NULL);
      }
      if (!InConfigSentinal (filptr)) return (filptr);
   }

   InConfigStatus = SS$_BUGCHECK;
   return (NULL);
}

/*****************************************************************************/
/*
Buffering the current emphemeral files, load temporary files.
InConfigUnload() closes (deleting) the temporary files, restoring from buffers.
*/

int InConfigReload ()
{
   int  status;
   char  fname [256],
         DateTime [32];

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

   if (WATCH_MODULE(WATCH_MOD_CONFIG))
      WatchThis (WATCHALL, WATCH_MOD_CONFIG, "InConfigReload()");

   if (!InConfigStart) return (SS$_NORMAL);

   InConfigGlobal = false;

   FaoToBuffer (DateTime, sizeof(DateTime), NULL, "!20%D", &HttpdTime64);

   /* buffer all the current file pointers */
   AuthFileBuf = AuthFile;
   GlobalFileBuf = GlobalFile;
   MapFileBuf = MapFile;
   ServiceFileBuf = ServiceFile;
   AuthFile = GlobalFile = MapFile = ServiceFile = NULL;

   status = SS$_NORMAL;
   sys$setprv (1, &SysPrvMask, 0, 0);

   if (!(AuthFile = fopen (AuthFileName, "w+", "shr=get", "fop=dlt")))
      status = vaxc$errno;
   if (VMSok(status))
      if (!(GlobalFile = fopen (GlobalFileName, "w+", "shr=get", "fop=dlt")))
         status = vaxc$errno;
   if (VMSok(status))
      if (!(MapFile = fopen (MapFileName, "w+", "shr=get", "fop=dlt")))
         status = vaxc$errno;
   if (VMSok(status))
      if (!(ServiceFile = fopen (ServiceFileName, "w+", "shr=get", "fop=dlt")))
         status = vaxc$errno;

   if (VMSnok (status))
   {
      if (AuthFile) fclose (AuthFile);
      if (GlobalFile) fclose (GlobalFile);
      if (MapFile) fclose (MapFile);
      if (ServiceFile) fclose (ServiceFile);
   }

   sys$setprv (0, &SysPrvMask, 0, 0);

   if (VMSok (status))
   {
      fprintf (AuthFile, "##!## %s %s\n", AuthFileName, DateTime);
      fprintf (AuthFile, "%s", INCONFIG_EPHEMERAL);
      fprintf (GlobalFile, "##!## %s %s\n", GlobalFileName, DateTime);
      fprintf (GlobalFile, "%s", INCONFIG_EPHEMERAL);
      fprintf (MapFile, "##!## %s %s\n", MapFileName, DateTime);
      fprintf (MapFile, "%s", INCONFIG_EPHEMERAL);
      fprintf (ServiceFile, "##!## %s %s\n", ServiceFileName, DateTime);
      fprintf (ServiceFile, "%s", INCONFIG_EPHEMERAL);
      status = InConfigLoad ();
   }
   
   if (WATCH_MODULE(WATCH_MOD_CONFIG))
      WatchThis (WATCHALL, WATCH_MOD_CONFIG, "!&S", status);

   return (status);
}

/*****************************************************************************/
/*
Close (deleting) the temporary emphemeral files and restore buffered files.
*/

void InConfigUnload ()
{
   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_CONFIG))
      WatchThis (WATCHALL, WATCH_MOD_CONFIG, "InConfigUnload()");

   sys$setprv (1, &SysPrvMask, 0, 0);

   if (AuthFile) fclose (AuthFile);
   if (GlobalFile) fclose (GlobalFile);
   if (MapFile) fclose (MapFile);
   if (ServiceFile) fclose (ServiceFile);

   sys$setprv (0, &SysPrvMask, 0, 0);

   AuthFile = AuthFileBuf;
   GlobalFile = GlobalFileBuf;
   MapFile = MapFileBuf;
   ServiceFile = ServiceFileBuf;

   AuthFileBuf = GlobalFileBuf = MapFileBuf = ServiceFileBuf = NULL;
}

/*****************************************************************************/
/*
Close (deleting) current emphemeral authorisation file then swap with new file.
*/

void InConfigKeepAuth ()
{
   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_CONFIG))
      WatchThis (WATCHALL, WATCH_MOD_CONFIG, "InConfigKeepAuth()");

   if (!InConfigStart) return;

   /* close (and delete) the original ephemeral file */
   sys$setprv (1, &SysPrvMask, 0, 0);
   fclose (AuthFileBuf);
   sys$setprv (0, &SysPrvMask, 0, 0);
   /* replace buffered with the new ephemeral file */
   AuthFileBuf = AuthFile;
   AuthFile = NULL;
}

/*****************************************************************************/
/*
Close (deleting) the current emphemeral mapping file then swap with new file.
*/

void InConfigKeepMap ()
{
   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_CONFIG))
      WatchThis (WATCHALL, WATCH_MOD_CONFIG, "InConfigKeepMap()");

   if (!InConfigStart) return;

   /* close (and delete) the original ephemeral file */
   sys$setprv (1, &SysPrvMask, 0, 0);
   fclose (MapFileBuf);
   sys$setprv (0, &SysPrvMask, 0, 0);
   /* replace buffered with the new ephemeral file */
   MapFileBuf = MapFile;
   MapFile = NULL;
}

/*****************************************************************************/
/*
Explicitly close the ephemeral configuration files so they are deleted with the
necessary privilege enabled.
*/

void InConfigClose ()
{
   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_CONFIG))
      WatchThis (WATCHALL, WATCH_MOD_CONFIG, "InConfigClose()");

   if (!InConfigStart) return;

   sys$setprv (1, &SysPrvMask, 0, 0);

   fclose (AuthFile);
   fclose (GlobalFile);
   fclose (MapFile);
   fclose (ServiceFile);

   sys$setprv (0, &SysPrvMask, 0, 0);
}

/*****************************************************************************/
/*
If |set| true then if an inline-config startup set the |InConfigLoading| flag,
otherwise reset the flag.  Always return the current state of the flag.
*/

BOOL InConfigStartup (int set)
{
   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_CONFIG))
      WatchThis (WATCHALL, WATCH_MOD_CONFIG, "InConfigStartup() !&B !&B",
                 set, InConfigStart);

   if (InConfigStart && set)
      InConfigLoading = true;
   else
      InConfigLoading = false;
   return (InConfigLoading);
}

/*****************************************************************************/
/*
Attempt to identify the (leading) configuration directives and return a pointer
to the configuration file.  Return a NULL pointer to indicate the configuration
could not be identified.
*/

int InConfigIdentify (FILE *filptr)
{
   char  ch;
   char  *aptr, *cptr;
   char  line [INCONFIG_LINE_MAX];

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

   if (WATCH_MODULE(WATCH_MOD_CONFIG))
      WatchThis (WATCHALL, WATCH_MOD_CONFIG, "InConfigIdentify()");

   rewind (filptr);

   /*****************/
   /* authorisation */
   /*****************/

   while (cptr = fgets (line, sizeof(line), filptr))
   {
      while (*cptr && *cptr != '[') cptr++;
      if (!*cptr) continue;
      /* inline directives */
      if (strsame (cptr, "[Auth]", 6)) { cptr = NULL; break; }
      if (strsame (cptr, "[Global]", 8)) { cptr = NULL; break; }
      if (strsame (cptr, "[Map]", 5)) { cptr = NULL; break; }
      if (strsame (cptr, "[Service]", 9))  { cptr = NULL; break; };
      if (*cptr == '[')
      {
         /* attempt to identify realm directives */
         if (strsame (cptr, "[ACME", 5)) break;
         if (strsame (cptr, "[EXTERNAL", 9)) break;
         if (strsame (cptr, "[NONE", 5)) break;
         if (strsame (cptr, "[OPAQUE", 7)) break;
         if (strsame (cptr, "[PROMISCUOUS", 13)) break;
         if (strsame (cptr, "[RFC1413", 8)) break;
         if (strsame (cptr, "[SKELKEY", 8)) break;
         if (strsame (cptr, "[TOKEN", 6)) break;
         if (strsame (cptr, "[VMS", 4)) break;
         if (strsame (cptr, "[WORLD", 6)) break;
         if (strsame (cptr, "[X509", 5)) break;
         /* complex multipart realm directive */
         for (aptr = cptr;
              *aptr != ']' && *aptr != '\"' && *aptr != ';';
              aptr++);
         if (*aptr != ']') break;
      }
   }
   rewind (filptr);
   if (cptr && *cptr == '[') return (INCONFIG_AUTH);

   /**********/
   /* global */
   /**********/

   while (cptr = fgets (line, sizeof(line), filptr))
   {
      while (*cptr && *cptr != '[') cptr++;
      if (!*cptr) continue;
      /* inline directives */
      if (strsame (cptr, "[Auth]", 6)) { cptr = NULL; break; }
      if (strsame (cptr, "[Global]", 8)) { cptr = NULL; break; }
      if (strsame (cptr, "[Map]", 5)) { cptr = NULL; break; }
      /* [Service] is a deprecated but current directive */
      /* global directives */
      if (strsame (cptr, "[InstanceMax]", 13)) break;
      if (strsame (cptr, "[ConnectMax]", 12)) break;
      if (strsame (cptr, "[Logging]", 9)) break;
      if (strsame (cptr, "[OpcomTarget]", 13)) break;
      if (strsame (cptr, "[TimeoutInput]", 14)) break;
      if (strsame (cptr, "[HttpProtocol]", 14)) break;
      if (strsame (cptr, "[SecureSocket]", 14)) break;
      if (strsame (cptr, "[AuthBasic]", 11)) break;
      if (strsame (cptr, "[Scripting]", 11)) break;
      if (strsame (cptr, "[DirectoryAccess]", 17)) break;
      if (strsame (cptr, "[AddIcon]", 9)) break;
   }
   rewind (filptr);
   if (cptr && *cptr == '[') return (INCONFIG_GLOBAL);

   /***********/
   /* mapping */
   /***********/

   while (cptr = fgets (line, sizeof(line), filptr))
   {
      while (*cptr && isspace(*cptr)) cptr++;
      if (!*cptr || *cptr == '#' || *cptr == '!') continue;
      /* inline directives */
      if (strsame (cptr, "[Auth]", 6)) { cptr = NULL; break; }
      if (strsame (cptr, "[Global]", 8)) { cptr = NULL; break; }
      if (strsame (cptr, "[Map]", 5)) { cptr = NULL; break; }
      if (strsame (cptr, "[Service]", 9)) { cptr = NULL; break; }
      /* mapping rules */
      for (aptr = cptr; *aptr && *aptr != '('; aptr++);
      if (*aptr)
      {
         for (aptr++; *aptr && *aptr != ')'; aptr++);
         if (*aptr) for (aptr++; *aptr && isspace(*aptr); aptr++);
         if (*aptr) cptr = aptr;
      }
      if (strsame (cptr, "exec", 4)) break;
      if (strsame (cptr, "fail", 4)) break;
      if (strsame (cptr, "map", 3)) break;
      if (strsame (cptr, "pass", 4)) break;
      if (strsame (cptr, "redirect", 8)) break;
      if (strsame (cptr, "script", 6)) break;
      if (strsame (cptr, "set", 3)) break;
      if (strsame (cptr, "user", 4)) break;
   }
   rewind (filptr);
   if (cptr && *cptr) return (INCONFIG_MAP);

   /***********/
   /* service */
   /***********/

   while (cptr = fgets (line, sizeof(line), filptr))
   {
      while (*cptr && *cptr != '[') cptr++;
      if (!*cptr) continue;
      /* inline directives */
      if (strsame (cptr, "[Auth]", 6)) { cptr = NULL; break; }
      if (strsame (cptr, "[Global]", 8)) { cptr = NULL; break; }
      if (strsame (cptr, "[Map]", 5)) { cptr = NULL; break; }
      if (strsame (cptr, "[Service]", 9)) { cptr = NULL; break; }
      /* all service directives begin with ... */
      if (strsame (cptr, "[Service", 8)) break;
   }
   rewind (filptr);
   if (cptr && *cptr == '[') return (INCONFIG_SERVICE);

   /********/
   /* nope */
   /********/

   return (INCONFIG_UNKNOWN);
}

/*****************************************************************************/
/*
If the first [IncludeFile] is not global directives or [Global] in-line
directive then look for and include the latest global configuration file.
*/

void InConfigAddGlobal
(
char *from,
int lnum
)
{
   char  *sptr;
   char  buf [256],
         fname [256],
         line [INCONFIG_LINE_MAX];
   FILE  *filptr;

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

   if (WATCH_MODULE(WATCH_MOD_CONFIG))
      WatchThis (WATCHALL, WATCH_MOD_CONFIG, "InConfigAddGlobal()");

   if (InConfigGlobal) return;

   InConfigGlobal = true;

   filptr = InConfigOpenFile (GlobalFileName);
   if (!filptr)
   {
      FaoToBuffer (buf, sizeof(buf), NULL, "!&S", InConfigStatus);
      fprintf (GlobalFile, "##### %s %s\n", GlobalFileName, buf);
      return;
   }

   fgetname (filptr, fname, 1);
   fprintf (GlobalFile, "!#+# %s line %d %s\n", fname, lnum, from);
   for (;;)
   {
      sptr = fgets (line, sizeof(line), filptr);
      if (!sptr) break;
      fputs (sptr, GlobalFile);
   }
   fclose (filptr);
}

/*****************************************************************************/
/*
Revisit the (authorisation and mapping) file removing (or commenting-out)
redundant virtual hosts and if-endif constructs.
*/

FILE* InConfigExcise (FILE *iFile)

{
   int  idx, status;
   char  *cptr, *sptr;
   char  fname [256],
         line1 [INCONFIG_LINE_MAX],
         line2 [INCONFIG_LINE_MAX];
   FILE  *oFile;
   fpos_t  fpos2;

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

   if (WATCH_MODULE(WATCH_MOD_CONFIG))
      WatchThis (WATCHALL, WATCH_MOD_CONFIG, "InConfigExcise()");

   InConfigStatus = 0;

   fsync (fileno(iFile));
   rewind (iFile);
   if (!fgetname (iFile, fname, 1))
   {
      InConfigStatus = vaxc$errno;
      return (NULL);
   }

   InConfigNoVersion (fname);
   sys$setprv (1, &SysPrvMask, 0, 0);
   oFile = fopen (fname, "w+", "shr=get", "fop=dlt");
   sys$setprv (0, &SysPrvMask, 0, 0);
   if (!oFile)
   {
      InConfigStatus = vaxc$errno;
      return (NULL);
   }

   /* read consecutive lines from the file */
   for (;;)
   {
      cptr = fgets (line1, sizeof(line1), iFile);
      if (!cptr) break;
      fgetpos (iFile, &fpos2);
      sptr = fgets (line2, sizeof(line2), iFile);
      if (!sptr)
      {
         fputs (line1, oFile);
         break;
      }
      while (*cptr && ISLWS(*cptr)) cptr++;
      while (*sptr && ISLWS(*sptr)) sptr++;
#if INCONFIG_EXCISE
      /* if not developing then excise */
      if (*(USHORTPTR)cptr == '[[' && *(USHORTPTR)sptr == '[[')
      {
         fsetpos (iFile, &fpos2);
         continue;
      }
      if (MATCH3(cptr,"if ") && MATCH5(sptr,"endif")) continue;
#else
      /* just comment out if developing */
      if (*(USHORTPTR)cptr == '[[' && *(USHORTPTR)sptr == '[[')
      {
         line1[0] = '#';
         fputs (line1, oFile);
         fsetpos (iFile, &fpos2);
         continue;
      }
      if (MATCH3(cptr,"if ") && MATCH5(sptr,"endif"))
      {
         line1[0] = line2[0] = '#';
         fputs (line1, oFile);
         fputs (line2, oFile);
         continue;
      }
#endif
      fsetpos (iFile, &fpos2);
      fputs (line1, oFile);
   }

   fsync (fileno(oFile));
   rewind (oFile);

   /* close (and thereby delete) the (previous) file */
   sys$setprv (1, &SysPrvMask, 0, 0);
   fclose (iFile);
   sys$setprv (0, &SysPrvMask, 0, 0);

   return (oFile);
}

/*****************************************************************************/
/*
Remove the version number from the supplied file name.
*/

void InConfigNoVersion (char *fname)

{
   char  *sptr;

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

   for (sptr = fname; *sptr; sptr++);
   while (sptr > fname && *sptr != ';') sptr--;
   if (sptr > fname) *sptr = '\0';
}

/*****************************************************************************/
/*
Provide an annotated report of the WASD_CONFIG_INLINE configuration file.
*/

void InConfigReport
(
REQUEST_STRUCT *rqptr,
BOOL UseServerConfig
)
{
   static char  SourceFao [] =
"<p><table class=\"ctgry\">\n\
<tr><th class=\"ctttl\">Source: &quot;!AZ&quot;</th></tr>\n\
<tr><td>\n\
<table class=\"rghtlft\">\n\
<tr><th>File:</th><td>!AZ</td>!&@</tr>\n\
<tr><th>!AZ</th><td>!20&W</td></tr>\n\
</table>\n\
</td></tr>\n\
</table>\n";

   static char  reduce [] = " <<<<<<<<< ";
   static char  expand [] = " >>>>>>>>> ";
   static char  spaces [] = "           ";

   BOOL  ConcealAfter, IncludeGlobal;
   int  excount, fcount, status, tlcount, which;
   char  *aptr, *cptr, *sptr, *zptr;
   char  line [INCONFIG_LINE_MAX];
   struct stat  stbuf;

   unsigned long  *vecptr;
   unsigned long  FaoVector [16];

   int  lcount [INCLUDE_MAX+1];
   char  fname [INCLUDE_MAX+1][256];
   char  service [INCLUDE_MAX+1][128];
   FILE  *filptr [INCLUDE_MAX+1],
         *thebuf [INCLUDE_MAX+1];
   uint64  mtime64 [INCLUDE_MAX+1];

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

   if (WATCH_MODULE(WATCH_MOD_CONFIG))
      WatchThis (WATCHALL, WATCH_MOD_CONFIG, "InConfigReport()");

   memset (lcount, 0, sizeof(lcount));
   memset (fname, 0, sizeof(fname));
   memset (service, 0, sizeof(service));
   memset (filptr, 0, sizeof(filptr));
   memset (thebuf, 0, sizeof(thebuf));
   memset (mtime64, 0, sizeof(mtime64));

   /* conceal remaining included files after line number */
   if (cptr = SysTrnLnm (WASD_INLINE_CONCEAL_AFTER))
   {
      ConcealAfter = atoi(cptr);
      if (ConcealAfter <= 0) ConcealAfter = 999999;
   }
   else
      ConcealAfter = 20;

   if (UseServerConfig)
   {
      if (InConfigFileName[0])
         cptr = InConfigFileName;
      else
         cptr = SysTrnLnm (CONFIG_INLINE_FILE_NAME);
   }
   else
   {
      cptr = SysTrnLnm (CONFIG_INLINE_FILE_NAME);
      /* development purposes only */
      if (!cptr) cptr = SysTrnLnm (CONFIG_INLINE_REPORT_FILE_NAME);
   }
   if (!cptr)
   {
      rqptr->rqResponse.HttpStatus = 500;
      ErrorGeneral (rqptr, MsgFor(rqptr,MSG_GENERAL_DISABLED), FI_LI);
      AdminEnd (rqptr);
      return;
   }
   strzcpy (fname[0], cptr, sizeof(fname[0]));

   sys$setprv (1, &SysPrvMask, 0, 0);
   filptr[0] = InConfigOpenFile (fname[0]);
   if (filptr[0])
   {
      fstat (fileno (filptr[0]), &stbuf);
      TimeUnix32ToVms64 (stbuf.st_mtime, &mtime64[0]);
      fclose (filptr[0]);
   }
   sys$setprv (0, &SysPrvMask, 0, 0);

   TheFile = NULL;
   InConfigGlobal = false;
   tlcount = 0;
  
   rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN;
   ResponseHeader200 (rqptr, "text/html", NULL);

   FaoToNet (rqptr,
"<script nonce=\"!AZ\">\n\
function inglobal(rest) {\n\
  var a = document.getElementById(\'a\'+rest);\n\
  var d = document.getElementById(\'d\'+rest);\n\
  if (d.style.display === \'none\') {\n\
     a.innerHTML = \'!AZ\';\n\
     d.style.display = \'block\';\n\
  } else {\n\
     a.innerHTML = \'!AZ\';\n\
     d.style.display = \'none\';\n\
  }\n\
}\n\
</script>\n\
<style type=\"text/css\">\n\
.inerror { font-size:110%; background-color:tomato; margin:0.5em; }\n\
.infile { font-size:110%; font-weight:bold; margin:0.2em; }\n\
.inhost { font-size:110%; font-weight:bold; margin:0.2em; }\n\
.inline { font-size:110%; background-color:powderBlue; }\n\
.inplus { color:black; text-decoration:none; }\n\
.inplus2 { display:inline-block; margin-bottom:0.15em; \
width:60em; height:0.2em; background-color:gainsboro; }\n\
.inservice { font-size:110%; font-weight:bold; margin:0.2em; }\n\
.inwasd { font-size:110%; font-weight:bold; margin:0.2em; }\n\
</style>\n",
             GenerateCspNonce(rqptr), reduce, expand);

   /*********/
   /* title */
   /*********/

   AdminPageTitle (rqptr, "Inline Config Report");

   vecptr = FaoVector;
   *vecptr++ = UseServerConfig ? "Server" : "File";
   *vecptr++ = fname[0];
   *vecptr++ =
"<th style=\"padding-left:1em;\">\
<a class=\"abttn bttn400\" href=\"!AZ\">View</a></th>\
<th><a class=\"abttn bttn400\" href=\"!AZ?do=edit\">Edit</a></th></tr>";
   *vecptr++ = cptr = rqptr->rqHeader.PathInfoPtr;
   *vecptr++ = cptr;

   *vecptr++ = UseServerConfig ? "Loaded" : "Revised";
   *vecptr++ = &mtime64[0];

   status = FaolToNet (rqptr, SourceFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   FaoToNet (rqptr,
"<p><table class=\"lftlft\">\n\
<tr><td style=\"padding-top:1em;\"><pre>");

   /**********/
   /* report */
   /**********/

   excount = fcount = 0;
   for (;;)
   {
      sys$setprv (1, &SysPrvMask, 0, 0);
      filptr[fcount] = InConfigOpenFile (fname[fcount]);
      sys$setprv (0, &SysPrvMask, 0, 0);
      status = InConfigStatus;
      if (!filptr[fcount])
      {
         FaoToNet (rqptr,
"<span class=\"inerror\">Failed to open !AZ !&S</span>\n",
                   fname[fcount], status);
         break;
      }

      if (fstat (fileno (filptr[fcount]), &stbuf) < 0)
      {
         status = InConfigStatus = vaxc$errno;
         FaoToNet (rqptr,
"<span class=\"inerror\">Failed to fstat() !AZ !&S</span>\n",
                   fname[fcount], status);
         break;
      }
      TimeUnix32ToVms64 (stbuf.st_mtime, &mtime64[fcount]);

      if ((which = InConfigIdentify (filptr[fcount])) == INCONFIG_UNKNOWN)
      {
         if (fcount)
         {
            FaoToNet (rqptr,
"<span class=\"inerror\">Cannot identify !AZ !&S</span>\n",
                      fname[fcount], SS$_ABORT);
            break;
         }
      }

      switch (which)
      {
         case INCONFIG_AUTH : cptr = "AUTH"; break;
         case INCONFIG_GLOBAL : cptr = "GLOBAL"; break;
         case INCONFIG_MAP : cptr = "MAP"; break;
         case INCONFIG_SERVICE : cptr = "SERVICE"; break;
         case INCONFIG_UNKNOWN : cptr = ""; break;
         default : cptr = "BUGCHECK";
      }
      if (*cptr)
         FaoToNet (rqptr,
"<span class=\"incounter\">!4ZL !1ZL !4ZL!#AZ</span> <b>[!AZ]</b>\n",
                   tlcount, fcount+1, lcount[fcount], fcount*2, spaces, cptr);

      if (which == INCONFIG_GLOBAL) InConfigGlobal = true;

      status = SS$_NORMAL;
      lcount[fcount] = 0;
      for (;;)
      {
         if (VMSnok (status)) break;
         cptr = fgets (line, sizeof(line), filptr[fcount]);
         if (!cptr)
         {
            if (fcount > 0 && lcount[fcount] >= ConcealAfter)
            {
               FaoToNet (rqptr,
"<span class=\"incounter\">\
<a id=\"a!4ZL\" class=\"inplus\" href=\"javascript:inglobal(\'!4ZL\');\">\
!&;AZ</a>!#AZ</span><div class=\"inplus2\"></div>\
</div>",
                      excount, excount, reduce, fcount*2, spaces, excount);
            }

            fclose (filptr[fcount--]);
            if (fcount < 0) break;

            if (WATCH_MODULE(WATCH_MOD_CONFIG))
               WatchThis (WATCHALL, WATCH_MOD_CONFIG, "!UL !AZ",
                          fcount, fname[fcount]);

            TheFile = thebuf[fcount];
            IncludeGlobal = false;
            continue;
         }
         tlcount++;
         lcount[fcount]++;

         if (fcount && lcount[fcount] == ConcealAfter + 1)
         {
            excount++;
            FaoToNet (rqptr,
"<span class=\"incounter\">\
<a id=\"a!4ZL\" class=\"inplus\" href=\"javascript:inglobal(\'!4ZL\');\">\
!&;AZ</a>!#AZ</span><div class=\"inplus2\"></div>\n\
<div id=\"d!4ZL\" style=\"display:none;\">",
                      excount, excount, expand, fcount*2, spaces, excount);
         }

         FaoToNet (rqptr,
"<span class=\"incounter\">!4ZL !1ZL !4ZL!#AZ</span> ",
                   tlcount, fcount+1, lcount[fcount], fcount*2, spaces);

         /* find the first non-white-space */
         while (*cptr && ISLWS(*cptr)) cptr++;
         /* remove any trailing newline */
         for (sptr = cptr; *sptr && *sptr != '\n'; sptr++);
         *sptr = '\0';

         if (strsame (cptr, "[IncludeFile]", 13))
         {
            if (fcount++ > INCLUDE_MAX)
            {
               FaoToNet (rqptr,
"<span class=\"inerror\">[IncludeFile] exceeded !UL</span>\n",
                         fname[fcount]);
               status = SS$_ABORT;
               break;
            }
            for (cptr += 13; *cptr && ISLWS(*cptr); cptr++);
            zptr = (sptr = fname[fcount]) + sizeof(fname[0])-1;
            while (*cptr && !ISLWS(*cptr) && sptr < zptr) *sptr++ = *cptr++;
            *sptr = '\0';
            if (!fname[fcount][0])
            {
               FaoToNet (rqptr,
"<span class=\"inerror\">[IncludeFile] not specified !AZ at !UL</span>\n",
                         fname[fcount-1], lcount[fcount-1]);
               status = SS$_ABORT;
               break;
            }
            FaoToNet (rqptr,
"<span class=\"include\">!AZ line !UL [IncludeFile] !AZ</span>\n",
                     fname[fcount-1], lcount[fcount-1], fname[fcount]);
            break;
         }

         aptr = NULL;
         if (strsame (cptr, "[Auth]", 6))
         {
            for (aptr = cptr + 6; *aptr && ISLWS(*aptr); aptr++);
            if (*aptr)
            {
               FaoToNet (rqptr, "<span class=\"inline\">!&;AZ</span>\n", cptr);
               continue;
            }
            which = INCONFIG_AUTH;
            FaoToNet (rqptr, "<span class=\"infile\">!&;AZ</span>\n", cptr);
            continue;
         }

         if (strsame (cptr, "[Global]", 8))
         {
            InConfigAddGlobal (fname[fcount], lcount[fcount]);
            for (aptr = cptr + 8; *aptr && ISLWS(*aptr); aptr++);
            if (*aptr)
            {
               FaoToNet (rqptr, "<span class=\"inline\">!&;AZ</span>\n", cptr);
               continue;
            }
            which = INCONFIG_GLOBAL;
            FaoToNet (rqptr, "<span class=\"infile\">!&;AZ</span>\n", cptr);
            continue;
         }

         if (strsame (cptr, "[Map]", 5))
         {
            for (aptr = cptr + 5; *aptr && ISLWS(*aptr); aptr++);
            if (*aptr)
            {
               FaoToNet (rqptr, "<span class=\"inline\">!&;AZ</span>\n", cptr);
               continue;
            }
            which = INCONFIG_MAP;
            FaoToNet (rqptr, "<span class=\"infile\">!&;AZ</span>\n", cptr);
            continue;
         }

         if (strsame (cptr, "[Service]", 9))
         {
            if (which != INCONFIG_GLOBAL)
            {
               for (aptr = cptr + 9; *aptr && ISLWS(*aptr); aptr++);
               if (*aptr)
               {
                  FaoToNet (rqptr, "<span class=\"inline\">!&;AZ</span>\n",
                            cptr);
                  continue;
               }
               which = INCONFIG_SERVICE;
               FaoToNet (rqptr, "<span class=\"infile\">!&;AZ</span>\n", cptr);
               continue;
            }
         }

         if (*(USHORTPTR)cptr == '[[')
         {
            if (strsame (cptr, "[[WASD", 6))
            {
               FaoToNet (rqptr, "<span class=\"inwasd\">!&;AZ</span>\n", cptr);
               continue;
            }
            if (which == INCONFIG_SERVICE)
            {
               FaoToNet (rqptr, "<span class=\"inservice\">!&;AZ</span>\n",
                         cptr);
               zptr = (sptr = service[fcount]) + sizeof(service[0])-1;
               while (*cptr && !ISLWS(*cptr) && sptr < zptr) *sptr++ = *cptr++;
               *sptr = '\0';
               continue;
            }
            FaoToNet (rqptr, "<span class=\"inhost\">!&;AZ</span>\n", cptr);
            continue;
         }

         if (*cptr == '#' || *(USHORTPTR)cptr == '!#')
         {
            FaoToNet (rqptr, "<span class=\"incomment\">!&;AZ</span>\n", cptr);
            continue;
         }

         FaoToNet (rqptr, "!&;AZ\n", cptr);
      }
      if (VMSnok (status)) break;
      if (fcount < 0) break;
   }

   FaoToNet (rqptr,
"<b>----</b>\n\
<pre>\n\
</td></tr>\n\
</table>\n");

   AdminEnd (rqptr);
}

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

