/*****************************************************************************/
/*
                                 glist.c

CGI-compliant script to list graphics.  Call with the path to the directory 
and it returns a form allowing selection of what graphics are to be displayed.  
Each directory of graphics is displayed with the a list of subdirectories, 
the path to each graphic in the directory, and the image generated by the 
graphic.

Image sizes may be specified as using the following form field formats.  This
allows considerable flexibility in designing form inpur elements for
specification of the image dimensions.

  size='width/height'        e.g. size=100
  size='width'x'height'           size=100x200
  size='width'X'height'           size=200X100
  sizew='width'                   sizew=200&sizeh=100
  sizeh='height'

Image sizes may also be specified using the /SIZE[W|H]= qualifiers.


PAGE LAYOUT
-----------
Page colouration may be specified via the appropriate command-line qualifiers
(or corresponding logical name). Defaults for any not specified.  Specifiy as
/WHATEVER="" to NOT specify the corresponding colour (i.e. leave it to the
browser). See "Qualifiers" section below, and also about the logical name
"GLIST$PARAM".

An example of changing the page colour to white and the banner to red!

  /PBGCOLOR="#ffffff" /PHBGCOLOR="#ff0000"


CGI VARIABLE NAMES
------------------
WWW_FORM_GIF            non-null includes GIF images
WWW_FORM_JPG            non-null includes JPEG images
WWW_FORM_LAYOUT         integer, list (0) or table width (columns)
WWW_FORM_LIST           non-null list the directory now
WWW_FORM_SIZE           integer, image size (pixels, 0 is normal size)
WWW_FORM_SIZEX          integer, image size (X axis pixels)
WWW_FORM_SIZEY          integer, image size (Y axis pixels)
WWW_FORM_XBM            non-null includes X-bitmap images
WWW_PATH_INFO           the URL path component
WWW_PATH_TRANSLATED     the VMS equivalent of the URL path component
WWW_QUERY_STRING        any of the form components
WWW_SCRIPT_NAME         the name of the script being executed


LOGICAL NAMES
-------------
GLIST$DBUG              turns on all "if (Debug)" statements
GLIST$PARAM             equivalent to (overrides) the command line
                        parameters/qualifiers (define as a system-wide logical)


QUALIFIERS
----------
/BUTTONS=       string containing button labels/paths
/DBUG           turns on all "if (Debug)" statements
/LAYOUT         default for layout
/[NO]ODS5       control extended file specification (basically for testing)
/PBACKGROUND=   <body> background image path
/PBGCOLOR=      <body> background colour
/PBBGCOLOR=     button background color
/PHBGCOLOR=     page heading background color
/PHLOCAL=       local information to be included in header
/PHTEXT=        page heading text colour
/PLAYOUT=       1 is coloured header & buttons, 2 is text & horizontal rules
/PLINK=         <body> link colour
/PTEXT=         <body> text colour
/PVLINK=        <body> visited link colour
/SIZE=          default for image size
/SIZEW=         default for image X size (width)
/SIZEH=         default for image Y size (height)


BUILD
-----
See BUILD_ONE.COM


VERSION HISTORY
---------------
23-DEC-2003  MGD  v1.4.2, minor conditional mods to support IA64
12-APR-2003  MGD  v1.4.1, link colour changed to 0000cc
20-NOV-2002  MGD  v1.4.0, check access to directory contents before listing
15-JUL-2001  MGD  v1.3.3, allow image X and Y to be independently specified
28-OCT-2000  MGD  v1.3.2, *no changes* (leave this one as a check
                          against the #include variant of CGILIB)
12-APR-2000  MGD  v1.3.1, minor changes for CGILIB 1.4
22-JAN-2000  MGD  v1.3.0, use CGILIB functionality,
                          support extended file specifications (ODS-5)
08-DEC-1999  MGD  v1.2.0, layout and image size
24-JUL-1998  MGD  v1.1.1, suppress table background colours if empty
20-MAY-1998  MGD  v1.1.0, cosmetic changes
09-FEB-1996  MGD  v1.0.0, quick hack
*/
/*****************************************************************************/

#define SOFTWAREVN "1.4.2"
#define SOFTWARENM "GLIST"
#ifdef __ALPHA
   char SoftwareID [64] = SOFTWARENM " AXP-" SOFTWAREVN;
#endif
#ifdef __ia64
   char SoftwareID [64] = SOFTWARENM " IA64-" SOFTWAREVN;
#endif
#ifdef __VAX
   char SoftwareID [64] = SOFTWARENM " VAX-" SOFTWAREVN;
#endif

#ifndef __VAX
#   pragma nomember_alignment
#endif

#ifndef __VAX
#  ifndef NO_ODS_EXTENDED
#     define ODS_EXTENDED 1
      /* this is smaller than the technical maximum, but still quite large! */
#     define ODS_MAX_FILE_NAME_LENGTH 511
#     define ODS_MAX_FILESYS_NAME_LENGTH 264
#  endif
#endif
#define ODS2_MAX_FILE_NAME_LENGTH 255
#ifndef ODS_MAX_FILE_NAME_LENGTH
#  define ODS_MAX_FILE_NAME_LENGTH ODS2_MAX_FILE_NAME_LENGTH
#endif
#if ODS_MAX_FILE_NAME_LENGTH < ODS2_MAX_FILE_NAME_LENGTH
#  define ODS_MAX_FILE_NAME_LENGTH ODS2_MAX_FILE_NAME_LENGTH
#endif

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

#include <atrdef.h>
#include <descrip.h>
#include <dvidef.h>
#include <fibdef.h>
#include <iodef.h>
#include <rms.h>
#include <ssdef.h>
#include <stsdef.h>
#include <syidef.h>

#include "enamel.h"

#define FI_LI __FILE__, __LINE__

#define VMSok(x) ((x) & STS$M_SUCCESS)
#define VMSnok(x) !(((x) & STS$M_SUCCESS))

#define boolean int
#define true 1
#define false 0

#define DEFAULT_BUTTONS ""

/* this macro just plugs in some script-specific code into ButtonBar() */
#define SCRIPT_SPECIFIC_BUTTON_CODE /* no script-internal buttons */

#define DEFAULT_PS_BGCOLOR        "#ffffff"
#define DEFAULT_PS_TEXT           "#000000"
#define DEFAULT_PS_LINK           "#0000cc"
#define DEFAULT_PS_VLINK          "#0000cc"
#define DEFAULT_PS_HEADBGCOLOR    "#cccccc"
#define DEFAULT_PS_HEADBORDER     "0"
#define DEFAULT_PS_HEADTEXT       "#000000"
#define DEFAULT_PS_BUTTONBGCOLOR  "#ffffff"
#define DEFAULT_PS_BUTTONBORDER   "1"

#define PS_BACKGROUND     0
#define PS_BGCOLOR        1
#define PS_TEXT           2
#define PS_LINK           3
#define PS_VLINK          4
#define PS_HEADBGCOLOR    5
#define PS_HEADTEXT       6
#define PS_HEADBORDER     7
#define PS_BUTTONBGCOLOR  8
#define PS_BUTTONBORDER   9
#define PS_BODYTAG       10
#define PS_LAYOUT        11
#define PS_HEADLOCAL     12
#define PS_HEADPADDING   13

char  *PageScheme [16];

char  ErrorInternal [] = "Internal error.",
      Utility [] = "GLIST";

boolean  Debug,
         IncludeGif,
         IncludeJpg,
         IncludeXbm;

int  ImageSizeH,
     ImageSizeW,
     Layout,
     OdsExtended;

char  *ButtonPtr = DEFAULT_BUTTONS,
      *CgiEnvironmentPtr,
      *CgiFormGifPtr,
      *CgiFormJpgPtr,
      *CgiFormLayoutPtr,
      *CgiFormListPtr,
      *CgiFormXbmPtr,
      *CgiPathInfoPtr,
      *CgiPathTranslatedPtr,
      *CgiQueryStringPtr,
      *CgiScriptNamePtr,
      *CgiFormSizePtr,
      *CgiFormSizeHPtr,
      *CgiFormSizeWPtr;
      
/* required function prototypes */
char* CommaNumber (unsigned long);

/*****************************************************************************/
/*
Include the CGILIB.H/.C function code from the standard WASD location if
CGILIB_INCLUDE_H/_C has not been defined.  If it has been defined then the
value must be the #include location.
*/

/* indicate required functionality */
#define CGILIB_CGIVAR_SUPPORT   1
#define CGILIB_OSU_SUPPORT      1
#define CGILIB_GENERAL_SUPPORT  1
#define CGILIB_RESPONSE_SUPPORT 1

#define CGILIB_RESPONSE_BODY           PageScheme[PS_BODYTAG]
#define CGILIB_RESPONSE_SOFTWAREID     SoftwareID
#define CGILIB_RESPONSE_ERROR_MESSAGE  "Reported by Glist"

#define CGILIB_DEBUG (Debug)

#ifndef CGILIB_INCLUDE_H
#   include "/ht_root/src/misc/cgilib.h"
#   include "/ht_root/src/misc/cgilib.c"
#else
#   include CGILIB_INCLUDE_H
#   include CGILIB_INCLUDE_C
#endif

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

main
(
int argc,
char *argv[]
)
{
   int  acnt;

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

   strcat (SoftwareID+15, " (");
   strcat (SoftwareID+15, CGILIB_SOFTWAREID);
   strcat (SoftwareID+15, ")");

   if (getenv ("GLIST$DBUG") != NULL) Debug = true;

   CgiLibEnvironmentInit (argc, argv, false);
   CgiEnvironmentPtr = CgiLibEnvironmentName ();

#ifdef ODS_EXTENDED
   OdsExtended = (GetVmsVersion() >= 72);
   ENAMEL_NAML_SANITY_CHECK
   ENAMEL_FIBDEF_SANITY_CHECK
#endif /* ODS_EXTENDED */

   GetParameters ();

   SetPageScheme ();

   /*********************/
   /* get CGI variables */
   /*********************/

   CgiPathInfoPtr = CgiLibVar("WWW_PATH_INFO");
   CgiPathTranslatedPtr = CgiLibVar("WWW_PATH_TRANSLATED");
   CgiQueryStringPtr = CgiLibVar("WWW_QUERY_STRING");
   CgiScriptNamePtr = CgiLibVar("WWW_SCRIPT_NAME");
   CgiFormListPtr = CgiLibVar("WWW_FORM_LIST");
   CgiFormGifPtr = CgiLibVar("WWW_FORM_GIF");
   CgiFormJpgPtr = CgiLibVar("WWW_FORM_JPG");
   CgiFormLayoutPtr = CgiLibVar("WWW_FORM_LAYOUT");
   CgiFormSizePtr = CgiLibVar("WWW_FORM_SIZE");
   CgiFormSizeWPtr = CgiLibVar("WWW_FORM_SIZEW");
   if (!isdigit(CgiFormSizeWPtr[0]))
      CgiFormSizeWPtr = CgiLibVar("WWW_FORM_SIZEX");
   CgiFormSizeHPtr = CgiLibVar("WWW_FORM_SIZEH");
   if (!isdigit(CgiFormSizeHPtr[0]))
      CgiFormSizeHPtr = CgiLibVar("WWW_FORM_SIZEY");
   CgiFormXbmPtr = CgiLibVar("WWW_FORM_XBM");

   /***********************/
   /* execute the request */
   /***********************/

   IncludeGif = IncludeJpg = IncludeXbm = false;
   if (CgiFormGifPtr[0]) IncludeGif = true;
   if (CgiFormJpgPtr[0]) IncludeJpg = true;
   if (CgiFormXbmPtr[0]) IncludeXbm = true;
   if (!(IncludeGif || IncludeJpg || IncludeXbm))
      IncludeGif = IncludeXbm = true;

   if (isdigit(CgiFormSizePtr[0]))
      if (sscanf (CgiFormSizePtr, "%dx%d", &ImageSizeW, &ImageSizeH) != 2)
         if (sscanf (CgiFormSizePtr, "%dX%d", &ImageSizeW, &ImageSizeH) != 2)
            ImageSizeW = ImageSizeH = atoi(CgiFormSizePtr);

   if (isdigit(CgiFormSizeWPtr[0])) ImageSizeW = atoi(CgiFormSizeWPtr);
   if (isdigit(CgiFormSizeHPtr[0])) ImageSizeH = atoi(CgiFormSizeHPtr);
   if (isdigit(CgiFormLayoutPtr[0])) Layout = atoi(CgiFormLayoutPtr);

   if (CgiFormListPtr[0])
      ProcessFileSpec ();
   else
      ProvideForm ();

   exit (SS$_NORMAL);
}

/*****************************************************************************/
/*
Get "command-line" parameters, whether from the command-line or from a
configuration logical containing the equivalent.
*/

GetParameters ()

{
   static char  CommandLine [256];
   static unsigned long  Flags = 0;

   register char  *aptr, *cptr, *clptr, *sptr;

   int  status;
   unsigned short  Length;
   char  ch;
   $DESCRIPTOR (CommandLineDsc, CommandLine);

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

   if ((clptr = getenv ("GLIST$PARAM")) == NULL)
   {
      /* get the entire command line following the verb */
      if (VMSnok (status =
          lib$get_foreign (&CommandLineDsc, 0, &Length, &Flags)))
         exit (status);
      (clptr = CommandLine)[Length] = '\0';
   }

   aptr = NULL;
   ch = *clptr;
   for (;;)
   {
      if (aptr != NULL) *aptr = '\0';
      if (!ch) break;

      *clptr = ch;
      if (Debug) fprintf (stdout, "clptr |%s|\n", clptr);
      while (*clptr && isspace(*clptr)) *clptr++ = '\0';
      aptr = clptr;
      if (*clptr == '/') clptr++;
      while (*clptr && !isspace (*clptr) && *clptr != '/')
      {
         if (*clptr != '\"')
         {
            clptr++;
            continue;
         }
         cptr = clptr;
         clptr++;
         while (*clptr)
         {
            if (*clptr == '\"')
               if (*(clptr+1) == '\"')
                  clptr++;
               else
                  break;
            *cptr++ = *clptr++;
         }
         *cptr = '\0';
         if (*clptr) clptr++;
      }
      ch = *clptr;
      if (*clptr) *clptr = '\0';
      if (Debug) fprintf (stdout, "aptr |%s|\n", aptr);
      if (!*aptr) continue;

      if (strsame (aptr, "/BUTTONS=", 4))
      {
         for (cptr = aptr; *cptr && *cptr != '='; cptr++);
         if (*cptr) cptr++;
         ButtonPtr = cptr;
         continue;
      }
      if (strsame (aptr, "/DBUG", -1))
      {
         Debug = true;
         continue;
      }
      if (strsame (aptr, "/LAYOUT=", 4))
      {
         for (cptr = aptr; *cptr && *cptr != '='; cptr++);
         if (*cptr) cptr++;
         Layout = atoi(cptr);
         continue;
      }
      if (strsame (aptr, "/ODS5", 5))
      {
         OdsExtended = true;
         continue;
      }
      if (strsame (aptr, "/NOODS5", 7))
      {
         OdsExtended = false;
         continue;
      }
      if (strsame (aptr, "/SIZEW=", 6) ||
          strsame (aptr, "/SIZEX=", 6)) 
      {
         for (cptr = aptr; *cptr && *cptr != '='; cptr++);
         if (*cptr) cptr++;
         ImageSizeW = atoi(cptr);
         continue;
      }
      if (strsame (aptr, "/SIZEH=", 6) ||
          strsame (aptr, "/SIZEY=", 6))
      {
         for (cptr = aptr; *cptr && *cptr != '='; cptr++);
         if (*cptr) cptr++;
         ImageSizeH = atoi(cptr);
         continue;
      }
      if (strsame (aptr, "/SIZE=", 4))
      {
         for (cptr = aptr; *cptr && *cptr != '='; cptr++);
         if (*cptr) cptr++;
         ImageSizeW = ImageSizeH = atoi(cptr);
         continue;
      }
      if (GetPageParameter (aptr)) continue;

      if (*aptr != '/')
      {
         fprintf (stdout, "%%%s-E-MAXPARM, too many parameters\n \\%s\\\n",
                  Utility, aptr);
         exit (STS$K_ERROR | STS$M_INHIB_MSG);
      }
      else
      {
         fprintf (stdout, "%%%s-E-IVQUAL, unrecognized qualifier\n \\%s\\\n",
                  Utility, aptr+1);
         exit (STS$K_ERROR | STS$M_INHIB_MSG);
      }
   }
}

/*****************************************************************************/
/*
Get command-line parameters associated with page scheme.
*/

boolean GetPageParameter (char *aptr)

{
   register char  *cptr;

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

   if (strsame (aptr, "/PBACKGROUND=", 4))
   {
      for (cptr = aptr; *cptr && *cptr != '='; cptr++);
      if (*cptr) cptr++;
      PageScheme[PS_BACKGROUND] = cptr;
      return (true);
   }
   if (strsame (aptr, "/PBGCOLOR=", 4))
   {
      for (cptr = aptr; *cptr && *cptr != '='; cptr++);
      if (*cptr) cptr++;
      PageScheme[PS_BGCOLOR] = cptr;
      return (true);
   }
   if (strsame (aptr, "/PBBGCOLOR=", 5))
   {
      for (cptr = aptr; *cptr && *cptr != '='; cptr++);
      if (*cptr) cptr++;
      PageScheme[PS_BUTTONBGCOLOR] = cptr;
      return (true);
   }
   if (strsame (aptr, "/PBBORDER=", 5))
   {
      for (cptr = aptr; *cptr && *cptr != '='; cptr++);
      if (*cptr) cptr++;
      PageScheme[PS_BUTTONBORDER] = cptr;
      return (true);
   }
   if (strsame (aptr, "/PHBGCOLOR=", 5))
   {
      for (cptr = aptr; *cptr && *cptr != '='; cptr++);
      if (*cptr) cptr++;
      PageScheme[PS_HEADBGCOLOR] = cptr;
      return (true);
   }
   if (strsame (aptr, "/PHBORDER=", 5))
   {
      for (cptr = aptr; *cptr && *cptr != '='; cptr++);
      if (*cptr) cptr++;
      PageScheme[PS_HEADBORDER] = cptr;
      return (true);
   }
   if (strsame (aptr, "/PHTEXT=", 4))
   {
      for (cptr = aptr; *cptr && *cptr != '='; cptr++);
      if (*cptr) cptr++;
      PageScheme[PS_HEADTEXT] = cptr;
      return (true);
   }
   if (strsame (aptr, "/PLAYOUT=", 4))
   {
      for (cptr = aptr; *cptr && *cptr != '='; cptr++);
      if (*cptr) cptr++;
      PageScheme[PS_LAYOUT] = cptr;
      return (true);
   }
   if (strsame (aptr, "/PLINK=", 4))
   {
      for (cptr = aptr; *cptr && *cptr != '='; cptr++);
      if (*cptr) cptr++;
      PageScheme[PS_LINK] = cptr;
      return (true);
   }
   if (strsame (aptr, "/PHLOCAL=", 4))
   {
      for (cptr = aptr; *cptr && *cptr != '='; cptr++);
      if (*cptr) cptr++;
      PageScheme[PS_HEADLOCAL] = cptr;
      return (true);
   }
   if (strsame (aptr, "/PTEXT=", 4))
   {
      for (cptr = aptr; *cptr && *cptr != '='; cptr++);
      if (*cptr) cptr++;
      PageScheme[PS_TEXT] = cptr;
      return (true);
   }
   if (strsame (aptr, "/PVLINK=", 4))
   {
      for (cptr = aptr; *cptr && *cptr != '='; cptr++);
      if (*cptr) cptr++;
      PageScheme[PS_VLINK] = cptr;
      return (true);
   }
   return (false);
}

/*****************************************************************************/
/*
Set the page layout and colouration.
*/

SetPageScheme ()

{
   int  size;
   char  *sptr;

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

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

   if (PageScheme[PS_LAYOUT] == NULL)
      PageScheme[PS_LAYOUT] = "1";

   if (PageScheme[PS_BACKGROUND] == NULL)
      PageScheme[PS_BACKGROUND] = "";

   if (PageScheme[PS_HEADLOCAL] == NULL)
      PageScheme[PS_HEADLOCAL] = "";

   if (PageScheme[PS_LAYOUT][0] == '2')
   {
      if (PageScheme[PS_BGCOLOR] == NULL) PageScheme[PS_BGCOLOR] = "";
      if (PageScheme[PS_TEXT] == NULL) PageScheme[PS_TEXT] = "";
      if (PageScheme[PS_LINK] == NULL) PageScheme[PS_LINK] = "";
      if (PageScheme[PS_VLINK] == NULL) PageScheme[PS_VLINK] = "";
      if (PageScheme[PS_HEADBGCOLOR] == NULL) PageScheme[PS_HEADBGCOLOR] = "";
      if (PageScheme[PS_HEADBORDER] == NULL) PageScheme[PS_HEADBORDER] = "";
      if (PageScheme[PS_HEADTEXT] == NULL) PageScheme[PS_HEADTEXT] = "";
      if (PageScheme[PS_BUTTONBGCOLOR] == NULL) PageScheme[PS_BUTTONBGCOLOR] = "";
      if (PageScheme[PS_BUTTONBORDER] == NULL) PageScheme[PS_BUTTONBORDER] = "";
   }
   else
   {
      if (PageScheme[PS_BGCOLOR] == NULL)
         PageScheme[PS_BGCOLOR] = DEFAULT_PS_BGCOLOR;
      if (PageScheme[PS_TEXT] == NULL)
         PageScheme[PS_TEXT] = DEFAULT_PS_TEXT;
      if (PageScheme[PS_LINK] == NULL)
         PageScheme[PS_LINK] = DEFAULT_PS_LINK;
      if (PageScheme[PS_VLINK] == NULL)
         PageScheme[PS_VLINK] = DEFAULT_PS_VLINK;
      if (PageScheme[PS_HEADBGCOLOR] == NULL)
         PageScheme[PS_HEADBGCOLOR] = DEFAULT_PS_HEADBGCOLOR;
      if (PageScheme[PS_HEADBORDER] == NULL)
         PageScheme[PS_HEADBORDER] = DEFAULT_PS_HEADBORDER;
      if (PageScheme[PS_HEADTEXT] == NULL)
         PageScheme[PS_HEADTEXT] = DEFAULT_PS_HEADTEXT;
      if (PageScheme[PS_BUTTONBGCOLOR] == NULL)
         PageScheme[PS_BUTTONBGCOLOR] = DEFAULT_PS_BUTTONBGCOLOR;
      if (PageScheme[PS_BUTTONBORDER] == NULL)
         PageScheme[PS_BUTTONBORDER] = DEFAULT_PS_BUTTONBORDER;
   }

   /* <BODY> tag attributes */
   size = strlen(PageScheme[PS_BACKGROUND]) +
          strlen(PageScheme[PS_BGCOLOR]) +
          strlen(PageScheme[PS_TEXT]) +
          strlen(PageScheme[PS_LINK]) +
          strlen(PageScheme[PS_VLINK]);
   if (size)
   {
      if ((sptr = calloc (1, size+64)) == NULL) exit (vaxc$errno);
      PageScheme[PS_BODYTAG] = sptr;
      if (PageScheme[PS_BACKGROUND][0])
         sptr += sprintf (sptr, " BACKGROUND=\"%s\"", PageScheme[PS_BACKGROUND]);
      if (PageScheme[PS_BGCOLOR][0])
         sptr += sprintf (sptr, " BGCOLOR=\"%s\"", PageScheme[PS_BGCOLOR]);
      if (PageScheme[PS_TEXT][0])
         sptr += sprintf (sptr, " TEXT=\"%s\"", PageScheme[PS_TEXT]);
      if (PageScheme[PS_LINK][0])
         sptr += sprintf (sptr, " LINK=\"%s\"", PageScheme[PS_LINK]);
      if (PageScheme[PS_VLINK][0])
         sptr += sprintf (sptr, " VLINK=\"%s\"", PageScheme[PS_VLINK]);
   }
   else
      PageScheme[PS_BODYTAG] = "";

   if (PageScheme[PS_HEADBGCOLOR][0])
   {
      if ((sptr = calloc (1, strlen(PageScheme[PS_HEADBGCOLOR])+16)) == NULL)
         exit (vaxc$errno);
      sprintf (sptr, " BGCOLOR=\"%s\"", PageScheme[PS_HEADBGCOLOR]);
      PageScheme[PS_HEADBGCOLOR] = sptr;
      PageScheme[PS_HEADPADDING] = "10";
   }
   else
      PageScheme[PS_HEADPADDING] = "0";

   if (PageScheme[PS_BUTTONBGCOLOR][0])
   {
      if ((sptr = calloc (1, strlen(PageScheme[PS_BUTTONBGCOLOR])+16)) == NULL)
         exit (vaxc$errno);
      sprintf (sptr, " BGCOLOR=\"%s\"", PageScheme[PS_BUTTONBGCOLOR]);
      PageScheme[PS_BUTTONBGCOLOR] = sptr;
   }
}

/*****************************************************************************/
/*
Provides a divider for top and bottom of the content of the page. This can be
a coloured bar (using <TABLE>) or a horizontal rule depending on the page
layout. "Buttons" providing script-internal and/or additional user-specified
links ('ButtonPtr' string) can be placed with(in) this bar. All button labels
are derived from 'ButtonPtr', with script-internal buttons using link-paths
set up via 'ButtonInternal[]' array, or any user-specified path depending on
requirement. An empty path (i.e. PathPtr[0] == '\0') obviously does not have a
link created, it just displays the button label. For a button-bar at the top
of the document use 1, bottom of the document use 2, and for just a bar with
no buttons at all use 0.
*/

ButtonBar (int Top1Bottom2)

{
#define MAX_BUTTON_COUNT 8

   static int  ButtonCount = -1;
   static char  *ButtonInternal [MAX_BUTTON_COUNT],
                *ButtonLabel [MAX_BUTTON_COUNT],
                *ButtonPath [MAX_BUTTON_COUNT];

   int  idx;
   char  *PathPtr;

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

   if (Debug) fprintf (stdout, "ButtonBar() %d\n", Top1Bottom2);

   if (ButtonCount == -1)
   {
      register char  *cptr, *sptr;

      if (Debug) fprintf (stdout, "|%s|\n", ButtonPtr);
      cptr = ButtonPtr;
      for (ButtonCount = 0;
           ButtonCount < MAX_BUTTON_COUNT && *cptr;
           ButtonCount++)
      {
         for (sptr = cptr; *sptr && *sptr != '=' && *sptr != ';'; sptr++)
            if (*sptr == '\\') memcpy (sptr, sptr+1, strlen(sptr));
         if (*sptr == '=') *sptr++ = '\0';
         ButtonLabel[ButtonCount] = cptr;
         cptr = sptr;
         for (sptr = cptr; *sptr && *sptr != ';'; sptr++)
            if (*sptr == '\\') memcpy (sptr, sptr+1, strlen(sptr));
         if (*sptr) *sptr++ = '\0';
         ButtonPath[ButtonCount] = cptr;
         cptr = sptr;
      }
   }

   if (Top1Bottom2)
   {
      /***********************************/
      /* set up script-specified buttons */
      /***********************************/

      SCRIPT_SPECIFIC_BUTTON_CODE
   }

   if (PageScheme[PS_LAYOUT][0] == '2')
   {
      /************/
      /* format 2 */
      /************/

      if (Top1Bottom2 == 2 || !Top1Bottom2)
      {
         fprintf (stdout, "<HR ALIGN=left SIZE=2 WIDTH=95%%>\n");
         if (!Top1Bottom2) return;
      }

      fprintf (stdout, "<FONT SIZE=-1><NOBR>\n");
      for (idx = 0; idx < ButtonCount; idx++)
      {
         if (ButtonInternal[idx] == NULL)
            PathPtr = ButtonPath[idx];
         else
            PathPtr = ButtonInternal[idx];
         if (idx) fprintf (stdout, "&nbsp;");
         if (PathPtr[0])
            fprintf (stdout, "[<A HREF=\"%s\">%s</A>]\n",
                     PathPtr, ButtonLabel[idx]);
         else
            fprintf (stdout, "[%s]\n", ButtonLabel[idx]);
      }
      fprintf (stdout, "</NOBR></FONT>\n");

      if (Top1Bottom2 == 1)
         fprintf (stdout, "<HR ALIGN=left SIZE=2 WIDTH=95%%>\n");
   }
   else
   {
      /************/
      /* format 1 */
      /************/

      fprintf (stdout,
"<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0>\
<TR><TD HEIGHT=2></TD></TR>\
</TABLE>\n\
<TABLE BORDER=%s CELLPADDING=%s CELLSPACING=0 WIDTH=100%%>\n\
<TR><TD%s>\n",
         PageScheme[PS_HEADBORDER],
         PageScheme[PS_HEADBORDER],
         PageScheme[PS_HEADBGCOLOR]);

      if (ButtonCount == 0 || !Top1Bottom2)
         fprintf (stdout, "&nbsp;\n");
      else
      {
         fprintf (stdout,
"<TABLE BORDER=%s CELLPADDING=1 CELLSPACING=0>\n",
            PageScheme[PS_BUTTONBORDER]);

         for (idx = 0; idx < ButtonCount; idx++)
         {
            if (ButtonInternal[idx] == NULL)
               PathPtr = ButtonPath[idx];
            else
               PathPtr = ButtonInternal[idx];
            if (PathPtr[0])
               fprintf (stdout,
"<TD ALIGN=center%s><FONT SIZE=-1>\
<NOBR>&nbsp;&nbsp;<A HREF=\"%s\">%s</A>&nbsp;&nbsp;</NOBR></FONT></TD>\n",
                  PageScheme[PS_BUTTONBGCOLOR], PathPtr, ButtonLabel[idx]);
            else
               fprintf (stdout,
"<TD ALIGN=center%s><FONT SIZE=-1>\
<NOBR>&nbsp;&nbsp;%s&nbsp;&nbsp;</NOBR></FONT></TD>\n",
                  PageScheme[PS_BUTTONBGCOLOR], ButtonLabel[idx]);
         }

         fprintf (stdout, "</TR></TABLE>\n");
      }

      fprintf (stdout,
"</TD></TR>\n\
</TABLE>\n\
<P>\n\
<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0>\
<TR><TD HEIGHT=2></TD></TR>\
</TABLE>\n");
   }
}

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

ProvideForm ()

{
   register char  *cptr;

   boolean  FormatLikeVms;
   char  EscapedTitle [ODS_MAX_FILE_NAME_LENGTH+1];
         
   /*********/
   /* begin */
   /*********/

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

   FormatLikeVms = false;
   for (cptr = CgiPathInfoPtr; *cptr; cptr++);
   while (cptr > CgiPathInfoPtr && *cptr != '/')
   {
      if (*cptr == ';') FormatLikeVms = true;
      cptr--;
   }

   if (FormatLikeVms)
   {
      char  c = '\0';
      for (cptr = CgiPathTranslatedPtr; *cptr; cptr++);
      while (cptr > CgiPathTranslatedPtr && *cptr != ']') cptr--;
      if (*cptr == ']')
      {
         c = *++cptr;
         *cptr = '\0';
      }
      CgiLibHtmlEscape (CgiPathTranslatedPtr, -1,
                        EscapedTitle, sizeof(EscapedTitle));
      if (c) *cptr = c;
   }
   else
      CgiLibHtmlEscape (CgiPathInfoPtr, -1,
                        EscapedTitle, sizeof(EscapedTitle));

   CgiLibResponseHeader (200, "text/html");

   fprintf (stdout,
"<HTML>\n\
<HEAD>\n\
<META NAME=\"generator\" CONTENT=\"%s\">\n\
<META NAME=\"environment\" CONTENT=\"%s\">\n\
<TITLE>Glist ... Graphics Browser</TITLE>\n\
</HEAD>\n\
<BODY%s>\n",
      SoftwareID,
      CgiEnvironmentPtr,
      PageScheme[PS_BODYTAG]);

   if (PageScheme[PS_LAYOUT][0] == '2')
   {
      fprintf (stdout,
"%s<FONT SIZE=+2><B>\n\
Graphics Browser\n\
</B></FONT>\n\
<BR>\n\
<FONT SIZE=-1>\n\
&nbsp;<SUP>*</SUP><U>Glist</U>\n\
</FONT>\n\
<HR ALIGN=left SIZE=2 WIDTH=95%%>\n\
<P>\n",
         PageScheme[PS_HEADLOCAL]);
   }
   else
   {
      fprintf (stdout,
"<P>\n\
<TABLE BORDER=%s CELLPADDING=%s CELLSPACING=0 WIDTH=100%%%s>\n\
<TR><TD>\n\
<FONT COLOR=\"%s\" SIZE=+2><B>\n\
Graphics Browser\n\
</B></FONT>\n\
<BR>\n\
<FONT COLOR=\"%s\" SIZE=-1>\n\
&nbsp;<SUP>*</SUP><U>Glist</U>\n\
</FONT>\n\
</TD>%s</TR>\n\
</TABLE>\n",
         PageScheme[PS_HEADBORDER],
         PageScheme[PS_HEADPADDING],
         PageScheme[PS_HEADBGCOLOR],
         PageScheme[PS_HEADTEXT],
         PageScheme[PS_HEADTEXT],
         PageScheme[PS_HEADLOCAL]);
   }

   fprintf (stdout,
"<BLOCKQUOTE>\n\
<FORM ACTION=\"%s%s\">\n\
<B>%s</B>\n\
<P><INPUT TYPE=checkbox NAME=gif VALUE=yes CHECKED> GIF\n\
<BR><INPUT TYPE=checkbox NAME=jpg VALUE=yes CHECKED> JPEG &nbsp;&nbsp;\
<BR><INPUT TYPE=checkbox NAME=xbm VALUE=yes CHECKED> X-bitmap\n\
<BR><SELECT NAME=size>\n",
      CgiScriptNamePtr, CgiPathInfoPtr, EscapedTitle);

   if (ImageSizeW || ImageSizeH)
      fprintf (stdout, "<OPTION VALUE=%dx%d SELECTED>%dx%d\n",
               ImageSizeW, ImageSizeH, ImageSizeW, ImageSizeH);

   fprintf (stdout,
"<OPTION VALUE=0>normal\n\
<OPTION VALUE=50x50>50x50\n\
<OPTION VALUE=75x75>75x75\n\
<OPTION VALUE=100x100>100x100\n\
<OPTION VALUE=125x125>125x125\n\
<OPTION VALUE=150x150>150x150\n\
<OPTION VALUE=200x200>200x200\n\
</SELECT>&nbsp;size\n\
<BR><SELECT NAME=layout>\n");

   if (Layout)
      fprintf (stdout, "<OPTION VALUE=%d SELECTED>table, %d across\n",
              Layout, Layout);

   fprintf (stdout,
"<OPTION VALUE=0>list\n\
<OPTION VALUE=2>table, 2 across\n\
<OPTION VALUE=3>table, 3 across\n\
<OPTION VALUE=4>table, 4 across\n\
<OPTION VALUE=5>table, 5 across\n\
<OPTION VALUE=6>table, 6 across\n\
<OPTION VALUE=7>table, 7 across\n\
<OPTION VALUE=8>table, 8 across\n\
<OPTION VALUE=9>table, 9 across\n\
<OPTION VALUE=10>table, 10 across\n\
<OPTION VALUE=15>table, 15 across\n\
<OPTION VALUE=20>table, 20 across\n\
</SELECT>&nbsp;layout\n\
<INPUT TYPE=hidden NAME=list VALUE=now>\n\
<P><INPUT TYPE=submit VALUE=\" list \"> <INPUT TYPE=reset VALUE=\" reset \">\n\
</FORM>\n\
</BLOCKQUOTE>\n\
<P>\n");

   ButtonBar (2);

   fprintf (stdout, "</BODY>\n</HTML>\n");
}

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

ProcessFileSpec ()

{
   register char  *cptr, *sptr, *zptr;

   boolean  FormatLikeVms,
            FileOk,
            JpgGraphic;
   int  status,
        Bytes,
        CgiPathTranslatedLength,
        FileCount = 0,
        FileGroup = 0,
        SubdirectoryCount = 0,
        TotalGif = 0,
        TotalJpg = 0,
        TotalXbm = 0;
   char  *GraphicTypePtr,
         *QuestionMarkPtr,
         *ResNamePtr,
         *ResTypePtr,
         *ResVersionPtr;
   char  HtmlEscPathInfo [ODS_MAX_FILE_NAME_LENGTH+1],
         DirectoryPath [ODS_MAX_FILE_NAME_LENGTH+1], 
         EscapedTitlePath [ODS_MAX_FILE_NAME_LENGTH+1],
         ExpFileName [ODS_MAX_FILE_NAME_LENGTH+1],
         ResFileName [ODS_MAX_FILE_NAME_LENGTH+1],
         UrlEncDirectoryPath [ODS_MAX_FILE_NAME_LENGTH+1],
         UrlEncName [ODS_MAX_FILE_NAME_LENGTH+1],
         UrlEncPathInfo [ODS_MAX_FILE_NAME_LENGTH+1],
         UrlEncResFileName [ODS_MAX_FILE_NAME_LENGTH+1];
   struct FAB  SearchFab;
   struct NAM  SearchNam;
   struct RAB  FileRab;

#ifdef ODS_EXTENDED
   char  FileSysName [ODS_MAX_FILESYS_NAME_LENGTH+1];
   struct NAML  SearchNaml;
#endif /* ODS_EXTENDED */

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

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

   if (!Layout)
      FileGroup = 10;
   else
   {
      if (Layout > 5)
         FileGroup = Layout * 2;
      else
         FileGroup = Layout * Layout;
   }

   if (CgiQueryStringPtr[0])
      QuestionMarkPtr = "?";
   else
      QuestionMarkPtr = "";

   CgiPathTranslatedLength = strlen(CgiPathTranslatedPtr);

   CgiLibHtmlEscape (CgiPathInfoPtr, -1,
                     HtmlEscPathInfo, sizeof(HtmlEscPathInfo));

   /* find the directory (i.e. the "/dir1/dir2/" from "/dir1/dir2/*.*") */
   zptr = (sptr = DirectoryPath) + sizeof(DirectoryPath)-1;
   for (cptr = CgiPathInfoPtr; *cptr && sptr < zptr; *sptr++ = *cptr++);
   if (sptr >= zptr)
   {
      CgiLibResponseError (FI_LI, 0, ErrorInternal);
      exit (SS$_NORMAL);
   }
   *sptr = '\0';
   FormatLikeVms = false;
   while (sptr > DirectoryPath && *sptr != '/')
   {
      if (*sptr == ';') FormatLikeVms = true;
      sptr--;
   }
   if (sptr > DirectoryPath && *sptr == '/') *++sptr = '\0';

   CgiLibUrlEncodeFileName (DirectoryPath,
                            UrlEncDirectoryPath,
                            sizeof(UrlEncDirectoryPath),
                            FormatLikeVms, !FormatLikeVms);

   CgiLibUrlEncodeFileName (CgiPathInfoPtr,
                            UrlEncPathInfo,
                            sizeof(UrlEncPathInfo),
                            FormatLikeVms, !FormatLikeVms);

   /***************************/
   /* list any subdirectories */
   /***************************/

   /* initialize the file access block */
   SearchFab = cc$rms_fab;

#ifdef ODS_EXTENDED
   if (OdsExtended)
   {
      SearchFab.fab$l_dna = SearchFab.fab$l_fna = -1;
      SearchFab.fab$b_dns = SearchFab.fab$b_fns = 0;
      SearchFab.fab$l_nam = &SearchNaml;

      ENAMEL_RMS_NAML(SearchNaml)
      SearchNaml.naml$l_long_defname = CgiPathTranslatedPtr;
      SearchNaml.naml$l_long_defname_size = strlen(CgiPathTranslatedPtr);
      SearchNaml.naml$l_long_filename = "*.DIR";
      SearchNaml.naml$l_long_filename_size = 5;
      SearchNaml.naml$l_long_expand = ExpFileName;
      SearchNaml.naml$l_long_expand_alloc = sizeof(ExpFileName)-1;
      SearchNaml.naml$l_long_result = ResFileName;
      SearchNaml.naml$l_long_result_alloc = sizeof(ResFileName)-1;
   }
   else
#endif /* ODS_EXTENDED */
   {
      SearchFab.fab$l_dna = CgiPathTranslatedPtr;
      SearchFab.fab$b_dns = CgiPathTranslatedLength;
      SearchFab.fab$l_fna = "*.DIR";
      SearchFab.fab$b_fns = 5;
      SearchFab.fab$l_nam = &SearchNam;

      SearchNam = cc$rms_nam;
      SearchNam.nam$l_esa = ExpFileName;
      SearchNam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH;
      SearchNam.nam$l_rsa = ResFileName;
      SearchNam.nam$b_rss = ODS2_MAX_FILE_NAME_LENGTH;
   }

   if (VMSnok (status = sys$parse (&SearchFab, 0, 0)))
   {
      CgiLibResponseError (FI_LI, status, HtmlEscPathInfo);
      exit (SS$_NORMAL);
   }

#ifdef ODS_EXTENDED
   if (OdsExtended)
      ResNamePtr = SearchNaml.naml$l_long_name;
   else
#endif /* ODS_EXTENDED */
      ResNamePtr = SearchNam.nam$l_name;

   if (FormatLikeVms)
   {
      char  c;
      c = ResNamePtr[0];
      ResNamePtr[0] = '\0';
      CgiLibHtmlEscape (ExpFileName, -1,
                        EscapedTitlePath, sizeof(EscapedTitlePath));
      ResNamePtr[0] = c;
   }
   else
      CgiLibHtmlEscape (DirectoryPath, -1,
                        EscapedTitlePath, sizeof(EscapedTitlePath));

   CgiLibResponseHeader (200, "text/html");

   fprintf (stdout,
"<HTML>\n\
<HEAD>\n\
<META NAME=\"generator\" CONTENT=\"%s\">\n\
<META NAME=\"environment\" CONTENT=\"%s\">\n\
<TITLE>Glist ... Graphics Browser</TITLE>\n\
</HEAD>\n\
<BODY%s>\n",
      SoftwareID,
      CgiEnvironmentPtr,
      PageScheme[PS_BODYTAG]);

   if (PageScheme[PS_LAYOUT][0] == '2')
   {
      fprintf (stdout,
"%s<FONT SIZE=+2><B>\n\
Graphics Browser\n\
</B></FONT>\n\
<BR>\n\
<FONT COLOR=\"%s\" SIZE=-1>\n\
&nbsp;<SUP>*</SUP><U>Glist</U>\n\
</FONT>\n\
<HR ALIGN=left SIZE=2 WIDTH=95%%>\n\
<BLOCKQUOTE>\n\
<P><B>%s</B>\n\
<P>\n",
         PageScheme[PS_HEADLOCAL],
         EscapedTitlePath);
   }
   else
   {
      fprintf (stdout,
"<P>\n\
<TABLE BORDER=%s CELLPADDING=%s CELLSPACING=0 WIDTH=100%%%s>\n\
<TR><TD>\n\
<FONT COLOR=\"%s\" SIZE=+2><B>\n\
Graphics Browser\n\
</B></FONT>\n\
<BR>\n\
<FONT COLOR=\"%s\" SIZE=-1>\n\
&nbsp;<SUP>*</SUP><U>Glist</U>\n\
</FONT>\n\
</TD>%s</TR>\n\
</TABLE>\n\
<BLOCKQUOTE>\n\
<P><B>%s</B>\n\
<P>\n",
         PageScheme[PS_HEADBORDER],
         PageScheme[PS_HEADPADDING],
         PageScheme[PS_HEADBGCOLOR],
         PageScheme[PS_HEADTEXT],
         PageScheme[PS_HEADTEXT],
         PageScheme[PS_HEADLOCAL],
         EscapedTitlePath);
   }

   while (VMSok (status = sys$search (&SearchFab, 0, 0)))
   {
#ifdef ODS_EXTENDED
      if (OdsExtended)
      {
         ResNamePtr = SearchNaml.naml$l_long_name;
         ResTypePtr = SearchNaml.naml$l_long_type;
         ResVersionPtr = SearchNaml.naml$l_long_ver;
         ResVersionPtr[SearchNaml.naml$l_long_ver_size] = '\0';
      }
      else
#endif /* ODS_EXTENDED */
      {
         ResNamePtr = SearchNam.nam$l_name;
         ResTypePtr = SearchNam.nam$l_type;
         ResVersionPtr = SearchNam.nam$l_ver;
         ResVersionPtr[SearchNam.nam$b_ver] = '\0';
      }

      if (Debug)
      {
         ResVersionPtr[0] = '\0';
         fprintf (stdout, "ResFileName |%s|\n", ResFileName);
         ResVersionPtr[0] = ';';
      }

      /* can the directory contents be accessed? */
      if (VMSnok (status = DirectoryAccess (ResFileName))) continue;

      if (!SubdirectoryCount++)
         fprintf (stdout,
"<TABLE CELLPADDING=0 CELLSPACING=0 BORDER=0>\n\
<TR><TD><BLOCKQUOTE>\n");

      ResTypePtr[0] = '\0';
      if (FormatLikeVms)
      {
         CgiLibHtmlEscape (ResNamePtr, -1, UrlEncName, sizeof(UrlEncName));
         fprintf (stdout, "<B>[.<A HREF=\"%s%s%s/*.*%s%s%s\">%s</A>]</B><BR>\n",
                  CgiScriptNamePtr, UrlEncDirectoryPath, UrlEncName,
                  FormatLikeVms ? ";" : "",
                  QuestionMarkPtr, CgiQueryStringPtr, UrlEncName);
      }
      else
      {
         CgiLibUrlEncodeFileName (ResNamePtr, UrlEncName, sizeof(UrlEncName),
                                  FormatLikeVms, !FormatLikeVms);
         fprintf (stdout, "<B>./<A HREF=\"%s%s%s/*.*%s%s%s\">%s</A>/</B><BR>\n",
                  CgiScriptNamePtr, UrlEncDirectoryPath, UrlEncName,
                  FormatLikeVms ? ";" : "",
                  QuestionMarkPtr, CgiQueryStringPtr, UrlEncName);
      }
      ResTypePtr[0] = '.';
   }

   if (SubdirectoryCount)
      fprintf (stdout, "</BLOCKQUOTE></TD></TR>\n</TABLE>\n");

   if (status != RMS$_FNF && status != RMS$_NMF)
   {
      CgiLibResponseError (FI_LI, status, HtmlEscPathInfo);
      exit (SS$_NORMAL);
   }

   /*********************/
   /* list any graphics */
   /*********************/

   /* initialize the file access block */
   SearchFab = cc$rms_fab;

#ifdef ODS_EXTENDED
   if (OdsExtended)
   {
      SearchFab.fab$l_dna = SearchFab.fab$l_fna = -1;
      SearchFab.fab$b_dns = SearchFab.fab$b_fns = 0;
      SearchFab.fab$l_nam = &SearchNaml;

      ENAMEL_RMS_NAML(SearchNaml)
      SearchNaml.naml$l_long_defname = CgiPathTranslatedPtr;
      SearchNaml.naml$l_long_defname_size = strlen(CgiPathTranslatedPtr);
      SearchNaml.naml$l_long_filename = "*.*";
      SearchNaml.naml$l_long_filename_size = 3;
      SearchNaml.naml$l_long_expand = ExpFileName;
      SearchNaml.naml$l_long_expand_alloc = sizeof(ExpFileName)-1;
      SearchNaml.naml$l_long_result = ResFileName;
      SearchNaml.naml$l_long_result_alloc = sizeof(ResFileName)-1;
      SearchNaml.naml$l_filesys_name = FileSysName;
      SearchNaml.naml$l_filesys_name_alloc = sizeof(FileSysName)-1;
   }
   else
#endif /* ODS_EXTENDED */
   {
      SearchFab.fab$l_dna = CgiPathTranslatedPtr;
      SearchFab.fab$b_dns = CgiPathTranslatedLength;
      SearchFab.fab$l_fna = "*.*";
      SearchFab.fab$b_fns = 3;
      SearchFab.fab$l_nam = &SearchNam;

      SearchNam = cc$rms_nam;
      SearchNam.nam$l_esa = ExpFileName;
      SearchNam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH;
      SearchNam.nam$l_rsa = ResFileName;
      SearchNam.nam$b_rss = ODS2_MAX_FILE_NAME_LENGTH;
   }

   if (VMSnok (status = sys$parse (&SearchFab, 0, 0)))
   {
      CgiLibResponseError (FI_LI, status, HtmlEscPathInfo);
      exit (SS$_NORMAL);
   }

   while (VMSok (status = sys$search (&SearchFab, 0, 0)))
   {
#ifdef ODS_EXTENDED
      if (OdsExtended)
      {
         ResNamePtr = SearchNaml.naml$l_long_name;
         ResTypePtr = SearchNaml.naml$l_long_type;
         ResVersionPtr = SearchNaml.naml$l_long_ver;
         ResVersionPtr[SearchNaml.naml$l_long_ver_size] = '\0';
      }
      else
#endif /* ODS_EXTENDED */
      {
         ResNamePtr = SearchNam.nam$l_name;
         ResTypePtr = SearchNam.nam$l_type;
         ResVersionPtr = SearchNam.nam$l_ver;
         ResVersionPtr[SearchNam.nam$b_ver] = '\0';
      }

      if (Debug)
      {
         ResVersionPtr[0] = '\0';
         fprintf (stdout, "ResFileName |%s| ResTypePtr |%s|\n",
                  ResFileName, ResTypePtr);
         ResVersionPtr[0] = ';';
      }

      /* already listed the directories! */
      if (strsame (ResTypePtr, ".DIR;", 5)) continue;

      FileOk = JpgGraphic = false;
      if (IncludeGif && strsame (ResTypePtr, ".GIF;", 5))
         { FileOk = true; TotalGif++; GraphicTypePtr = "GIF"; }
      if (IncludeJpg)
      {
         if (strsame (ResTypePtr, ".JPG;", 5))
            { FileOk = JpgGraphic = true; TotalJpg++; GraphicTypePtr = "JPEG"; }
         if (strsame (ResTypePtr, ".JPEG;", 6))
            { FileOk = JpgGraphic = true; TotalJpg++; GraphicTypePtr = "JPEG"; }
      }
      if (IncludeXbm && strsame (ResTypePtr, ".XBM;", 5))
         { FileOk = true; TotalXbm++; GraphicTypePtr = "X-bitmap"; }

      if (!FileOk) continue;

      ResVersionPtr[0] = '\0';
      CgiLibUrlEncodeFileName (ResNamePtr, UrlEncName, sizeof(UrlEncName),
                               FormatLikeVms, !FormatLikeVms);
      ResVersionPtr[0] = ';';

#ifdef ODS_EXTENDED
      if (OdsExtended)
         status = FileSize (NULL, &SearchNaml, &Bytes);
      else
         status = FileSize (&SearchNam, NULL, &Bytes);
#else /* ODS_EXTENDED */
      status = FileSize (&SearchNam, &Bytes);
#endif /* ODS_EXTENDED */

      if (VMSnok (status))
      {
         ResVersionPtr[0] = '\0';
         CgiLibUrlEncodeFileName (ResFileName,
                                  UrlEncResFileName,
                                  sizeof(UrlEncResFileName),
                                  FormatLikeVms, !FormatLikeVms);

         CgiLibResponseError (FI_LI, status, UrlEncResFileName);
         exit (SS$_NORMAL);
      }

      if (!(FileCount % FileGroup))
      {
         if (FileCount) fprintf (stdout, "</TABLE>\n<P>\n");
         fprintf (stdout, "<P>\n\<TABLE BORDER=0 CELLPADDING=2 CELLSPACING=2>\n");
      }
      FileCount++;

      if (Layout)
      {
         if (FileCount > 1 && (FileCount % Layout) == 1)
            fprintf (stdout, "</TR>\n");
         if ((FileCount % Layout) == 1)
            fprintf (stdout, "<TR>\n");

         fprintf (stdout,
"<TD>\
<A HREF=\"%s%s\"><IMG SRC=\"%s%s\" ALIGN=TOP %s=%d %s=%d></A>\
<BR>%s<BR>%s&nbsp;bytes</TD>\n",
                  UrlEncDirectoryPath, UrlEncName,
                  UrlEncDirectoryPath, UrlEncName,
                  ImageSizeH ? "HEIGHT" : "H", ImageSizeH,
                  ImageSizeW ? "WIDTH" : "W", ImageSizeW,
                  UrlEncName, CommaNumber(Bytes));
      }
      else
      {
         if (ImageSizeW || ImageSizeH)
            fprintf (stdout,
"<TR><TD>\
<A HREF=\"%s%s\"><IMG SRC=\"%s%s\" ALIGN=TOP %s=%d %s=%d></A>",
               UrlEncDirectoryPath, UrlEncName,
               UrlEncDirectoryPath, UrlEncName,
               ImageSizeH ? "HEIGHT" : "H", ImageSizeH,
               ImageSizeW ? "WIDTH" : "W", ImageSizeW);
         else
            fprintf (stdout,
"<TR><TD><A HREF=\"%s%s\"><IMG SRC=\"%s%s\" ALIGN=TOP></A>",
               UrlEncDirectoryPath, UrlEncName,
               UrlEncDirectoryPath, UrlEncName);

         fprintf (stdout,
"<BR>%s&nbsp;&nbsp;%s&nbsp;&nbsp;<TT>%s%s</TT></TD></TR>\n",
              UrlEncName, CommaNumber(Bytes),
              UrlEncDirectoryPath, UrlEncName);
      }
   }

   if (FileCount && Layout) fprintf (stdout, "</TR>\n");
   if (FileCount) fprintf (stdout, "</TABLE>\n");

   if (status != RMS$_FNF && status != RMS$_NMF)
   {
      ResVersionPtr[0] = '\0';
      CgiLibUrlEncodeFileName (ResFileName,
                               UrlEncResFileName,
                               sizeof(UrlEncResFileName),
                               FormatLikeVms, !FormatLikeVms);

      CgiLibResponseError (FI_LI, status, UrlEncResFileName);
      exit (SS$_NORMAL);
   }

   if (FileCount)
   {
      if (PageScheme[PS_LAYOUT][0] == '2')
      {
         fprintf (stdout,
"<P>\n\
<FONT SIZE=-1><U>\
<B>GIF:</B> %d&nbsp;&nbsp;\
<B>JPEG:</B> %d&nbsp;&nbsp;\
<B>X-bitmap:</B> %d\
</U></FONT>\n",
         TotalGif, TotalJpg, TotalXbm);
      }
      else
      {
         fprintf (stdout,
"<P>\n\
<TABLE BORDER=%s CELLPADDING=2 CELLSPACING=0%s>\n\
<TR><TD>\n\
&nbsp;<FONT SIZE=-1 COLOR=\"%s\">\n\
<B>GIF:</B> %d&nbsp;&nbsp;\
<B>JPEG:</B> %d&nbsp;&nbsp;\
<B>X-bitmap:</B> %d\
&nbsp;</FONT>\n\
</TD></TR>\n\
</TABLE>\n",
         PageScheme[PS_HEADBORDER],
         PageScheme[PS_HEADBGCOLOR],
         PageScheme[PS_HEADTEXT],
         TotalGif, TotalJpg, TotalXbm);
      }
   }
   else
      fprintf (stdout, "<P>No graphics found!\n");

   fprintf (stdout, "</BLOCKQUOTE>\n");

   ButtonBar (2);

   fprintf (stdout, "</BODY>\n</HTML>\n");
}

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

int DirectoryAccess (char *DirFileName)

{
   int  status;
   char  *cptr, *sptr, *zptr;
   char  Directory [ODS_MAX_FILE_NAME_LENGTH+1],
         ExpFileName [ODS_MAX_FILE_NAME_LENGTH+1],
         ResFileName [ODS_MAX_FILE_NAME_LENGTH+1];
   struct FAB  SearchFab;
   struct NAM  SearchNam;
#ifdef ODS_EXTENDED
   struct NAML  SearchNaml;
#endif /* ODS_EXTENDED */

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

   if (Debug) fprintf (stdout, "DirectoryAccess() |%s|\n", DirFileName);

   zptr = (sptr = Directory) + sizeof(Directory)-1;
   for (cptr = DirFileName; *cptr && sptr < zptr; *sptr++ = *cptr++);
   *sptr = '\0';
   while (sptr > Directory && *sptr != '.')
   {
      while (sptr > Directory && *sptr != '.') sptr--;
      if (strsame (sptr, ".DIR;", 5)) break;
   }
   if (*sptr != '.')
   {
      CgiLibResponseError (FI_LI, SS$_BUGCHECK, DirFileName);
      exit (SS$_NORMAL);
   }
   *(unsigned short*)sptr = ']\0';
   sptr--;
   while (sptr > Directory && *sptr != ']' && *sptr != '[') sptr--;
   if (*sptr == ']') *sptr = '.';
   if (Debug) fprintf (stdout, "|%s|\n", Directory);

   /* initialize the file access block */
   SearchFab = cc$rms_fab;

#ifdef ODS_EXTENDED
   if (OdsExtended)
   {
      SearchFab.fab$l_dna = SearchFab.fab$l_fna = -1;
      SearchFab.fab$b_dns = SearchFab.fab$b_fns = 0;
      SearchFab.fab$l_nam = &SearchNaml;

      ENAMEL_RMS_NAML(SearchNaml)
      SearchNaml.naml$l_long_defname = Directory;
      SearchNaml.naml$l_long_defname_size = strlen(Directory);
      SearchNaml.naml$l_long_filename = "*.*;";
      SearchNaml.naml$l_long_filename_size = 4;
      SearchNaml.naml$l_long_expand = ExpFileName;
      SearchNaml.naml$l_long_expand_alloc = sizeof(ExpFileName)-1;
      SearchNaml.naml$l_long_result = ResFileName;
      SearchNaml.naml$l_long_result_alloc = sizeof(ResFileName)-1;
   }
   else
#endif /* ODS_EXTENDED */
   {
      SearchFab.fab$l_dna = Directory;
      SearchFab.fab$b_dns = strlen(Directory);
      SearchFab.fab$l_fna = "*.*;";
      SearchFab.fab$b_fns = 4;
      SearchFab.fab$l_nam = &SearchNam;

      SearchNam = cc$rms_nam;
      SearchNam.nam$l_esa = ExpFileName;
      SearchNam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH;
      SearchNam.nam$l_rsa = ResFileName;
      SearchNam.nam$b_rss = ODS2_MAX_FILE_NAME_LENGTH;
   }

   if (VMSnok (status = sys$parse (&SearchFab, 0, 0)))
   {
      if (Debug) fprintf (stdout, "sys$parse() %%%08.08X\n", status);
      return (status);
   }

   status = sys$search (&SearchFab, 0, 0);
   if (Debug) fprintf (stdout, "sys$search() %%%08.08X\n", status);

   /* release context with a syntax-check only parse */
   SearchFab.fab$l_fna = "a:[b]c.d;";
   SearchFab.fab$b_fns = 9;
   SearchFab.fab$b_dns = 0;

   SearchNam.nam$l_esa = ExpFileName;
   SearchNam.nam$b_ess = sizeof(ExpFileName)-1;
   SearchNam.nam$l_rlf = 0;
   SearchNam.nam$b_nop = NAM$M_SYNCHK;

   sys$parse (&SearchFab, 0, 0);

   return (status);
}

/*****************************************************************************/
/*
Convert the integer into an ASCII number string containing commas. Returns a
pointer to the string.
*/

char* CommaNumber (unsigned long Value)

{
   static char  Scratch [32],
                String [32];
   static $DESCRIPTOR (ScratchDsc, Scratch);
   static $DESCRIPTOR (ValueFaoDsc, "!UL");

   register int  cnt;
   register char  *cptr, *sptr;

   int  status;
   unsigned short  Length;

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

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

   if (VMSnok (status = sys$fao (&ValueFaoDsc, &Length, &ScratchDsc, Value)))
      return ("*ERROR*");
   Scratch[Length] = '\0';
   if (((Length-1) / 3) < 1)
   {
      cptr = Scratch;
      sptr = String;
      while (*cptr) *sptr++ = *cptr++;
      *sptr = '\0';
      return (String);
   }
   else
   if (!(cnt = Length % 3))
      cnt = 3;
   
   cptr = Scratch;
   sptr = String;
   while (*cptr)
   {
      if (!cnt--)
      {
         *sptr++ = ',';
         cnt = 2;
      }
      *sptr++ = *cptr++;
   }
   *sptr = '\0';
   return (String);
}

/*****************************************************************************/
/*
This function uses the ACP-QIO interface detailed in the "OpenVMS I/O User's 
Reference Manual".
*/ 

int FileSize
(
struct NAM *SearchNamPtr,
#ifdef ODS_EXTENDED
   struct NAML *SearchNamlPtr,
#endif /* ODS_EXTENDED */
int *BytesPtr
)
{
   static $DESCRIPTOR (DeviceDsc, "");

   static struct {
      unsigned long  OfNoInterest1;
      unsigned short  AllocatedVbnHi;
      unsigned short  AllocatedVbnLo;
      unsigned short  EndOfFileVbnHi;
      unsigned short  EndOfFileVbnLo;
      unsigned short  FirstFreeByte;
      unsigned short  OfNoInterest2;
      unsigned long  OfNoInterestLots [4];
   } AtrRecord;

#ifdef ODS_EXTENDED
   /* only use the local ENAMEL.C FIB definition when necessary */
#  ifdef ENAMEL_FIBDEF
      static struct enamel_fibdef  FileFib;
#  else
      static struct fibdef  FileFib;
#  endif
#else /* ODS_EXTENDED */
   static struct fibdef  FileFib;
#endif /* ODS_EXTENDED */

   static struct atrdef  FileAtr [] =
   {
      { sizeof(AtrRecord), ATR$C_RECATTR, &AtrRecord },
      { 0, 0, 0 }
   };

   static struct {
      unsigned short  Length;
      unsigned short  Unused;
      unsigned long  Address;
   } FileNameAcpDsc,
     FileFibAcpDsc,
     FileAtrAcpDsc;

   static struct {
      unsigned short  Status;
      unsigned short  Unused1;
      unsigned long  Unused2;
   } AcpIOsb;

   int  status;
   unsigned short  AcpChannel;
   unsigned long  AllocatedVbn,
                  EndOfFileVbn;

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

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

   /* assign a channel to the disk device containing the file */
#ifdef ODS_EXTENDED
   if (SearchNamPtr != NULL)
   {
      DeviceDsc.dsc$a_pointer = SearchNamPtr->nam$l_dev;
      DeviceDsc.dsc$w_length = SearchNamPtr->nam$b_dev;
   }
   else
   if (SearchNamlPtr != NULL)
   {
      DeviceDsc.dsc$a_pointer = SearchNamlPtr->naml$l_long_dev;
      DeviceDsc.dsc$w_length = SearchNamlPtr->naml$l_long_dev_size;
   }
#else /* ODS_EXTENDED */
   DeviceDsc.dsc$w_length = SearchNamPtr->nam$b_dev;
   DeviceDsc.dsc$a_pointer = SearchNamPtr->nam$l_dev;
#endif /* ODS_EXTENDED */

   if (Debug)
      fprintf (stdout, "|%*.*s|\n",
               DeviceDsc.dsc$w_length, DeviceDsc.dsc$w_length,
               DeviceDsc.dsc$a_pointer);

   status = sys$assign (&DeviceDsc, &AcpChannel, 0, 0, 0);
   if (Debug) fprintf (stdout, "sys$assign() %%X%08.08X\n", status);
   if (VMSnok (status)) return (status);

   /* set up the File Information Block for the ACP interface */
   memset (&FileFib, 0, sizeof(struct fibdef));
   FileFibAcpDsc.Length = sizeof(FileFib);
   FileFibAcpDsc.Address = &FileFib;

#ifdef ODS_EXTENDED
   if (SearchNamPtr != NULL)
   {
      if (Debug) fprintf (stdout, "EXTENDED NAM\n");
      FileFib.fib$w_did[0] = SearchNamPtr->nam$w_did[0];
      FileFib.fib$w_did[1] = SearchNamPtr->nam$w_did[1];
      FileFib.fib$w_did[2] = SearchNamPtr->nam$w_did[2];

      FileNameAcpDsc.Address = SearchNamPtr->nam$l_name;
      FileNameAcpDsc.Length = SearchNamPtr->nam$b_name +
                              SearchNamPtr->nam$b_type +
                              SearchNamPtr->nam$b_ver;
   }
   else
   if (SearchNamlPtr != NULL)
   {
      if (Debug) fprintf (stdout, "EXTENDED NAML\n");
      FileFib.fib$w_did[0] = SearchNamlPtr->naml$w_did[0];
      FileFib.fib$w_did[1] = SearchNamlPtr->naml$w_did[1];
      FileFib.fib$w_did[2] = SearchNamlPtr->naml$w_did[2];

      FileNameAcpDsc.Address = SearchNamlPtr->naml$l_filesys_name;
      FileNameAcpDsc.Length = SearchNamlPtr->naml$l_filesys_name_size;

      FileFib.fib$b_name_format_in = FIB$C_ISO_LATIN;
      FileFib.fib$b_name_format_out = FIB$C_ISO_LATIN;
      FileFib.fib$w_nmctl = FIB$M_NAMES_8BIT;
   }
#else /* ODS_EXTENDED */
   if (Debug) fprintf (stdout, "NAM\n");
   FileFib.fib$w_did[0] = SearchNamPtr->nam$w_did[0];
   FileFib.fib$w_did[1] = SearchNamPtr->nam$w_did[1];
   FileFib.fib$w_did[2] = SearchNamPtr->nam$w_did[2];

   FileNameAcpDsc.Address = SearchNamPtr->nam$l_name;
   FileNameAcpDsc.Length = SearchNamPtr->nam$b_name +
                           SearchNamPtr->nam$b_type +
                           SearchNamPtr->nam$b_ver;
#endif /* ODS_EXTENDED */

   if (Debug)
      fprintf (stdout, "|%*.*s|\n",
               FileNameAcpDsc.Length, FileNameAcpDsc.Length,
               FileNameAcpDsc.Address);

   status = sys$qiow (0, AcpChannel, IO$_ACCESS, &AcpIOsb, 0, 0, 
                      &FileFibAcpDsc, &FileNameAcpDsc, 0, 0,
                      &FileAtr, 0);

   /* immediately deassign the channel in case we return on an error */
   sys$dassgn (AcpChannel);

   if (Debug)
      fprintf (stdout, "sys$qio() %%X%08.08X IOsb: %%X%08.08X\n",
              status, AcpIOsb.Status);

   if (VMSok (status)) status = AcpIOsb.Status;
   if (VMSnok (status)) return (status);

   AllocatedVbn = AtrRecord.AllocatedVbnLo + (AtrRecord.AllocatedVbnHi << 16);
   EndOfFileVbn = AtrRecord.EndOfFileVbnLo + (AtrRecord.EndOfFileVbnHi << 16);

   if (Debug)
      fprintf (stdout, "AllocatedVbn: %d EndOfFileVbn: %d FirstFreeByte %d\n",
               AllocatedVbn, EndOfFileVbn, AtrRecord.FirstFreeByte);

   if (EndOfFileVbn <= 1)
      *BytesPtr = AtrRecord.FirstFreeByte;
   else
      *BytesPtr = ((EndOfFileVbn - 1) << 9) + AtrRecord.FirstFreeByte;
   if (Debug) fprintf (stdout, "Bytes %d\n", *BytesPtr);

   return (SS$_NORMAL);
}

/****************************************************************************/
/*
Return an integer reflecting the major and minor version of VMS (e.g. 60, 61,
62, 70, 71, 72, etc.)
*/ 

#ifdef ODS_EXTENDED

int GetVmsVersion ()

{
   static char  SyiVersion [16];

   static struct {
      short int  buf_len;
      short int  item;
      unsigned char   *buf_addr;
      unsigned short  *ret_len;
   }
   SyiItems [] =
   {
      { 8, SYI$_VERSION, &SyiVersion, 0 },
      { 0,0,0,0 }
   };

   int  status,
        version;

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

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

   if (VMSnok (status = sys$getsyiw (0, 0, 0, &SyiItems, 0, 0, 0)))
      exit (status);
   SyiVersion[8] = '\0';
   version = ((SyiVersion[1]-48) * 10) + (SyiVersion[3]-48);
   if (Debug) fprintf (stdout, "|%s| %d\n", SyiVersion, version);
   return (version);
}

#endif /* ODS_EXTENDED */

/****************************************************************************/
/*
Does a case-insensitive, character-by-character string compare and returns 
true if two strings are the same, or false if not.  If a maximum number of 
characters are specified only those will be compared, if the entire strings 
should be compared then specify the number of characters as 0.
*/ 

boolean strsame
(
register char *sptr1,
register char *sptr2,
register int  count
)
{
   while (*sptr1 && *sptr2)
   {
      if (toupper (*sptr1++) != toupper (*sptr2++)) return (false);
      if (count)
         if (!--count) return (true);
   }
   if (*sptr1 || *sptr2)
      return (false);
   else
      return (true);
}

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

