/*****************************************************************************/
/*
                                   Gift.c

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

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

/* VMS header files */
#include <descrip.h>
#include <ssdef.h>
#include <stdlib.h>
#include <stsdef.h>

/* application header files */
#include "gift.h"
#include "alpha_08pt.h"
#include "alpha_09pt.h"
#include "alpha_10pt.h"
#include "alpha_11pt.h"
#include "alpha_12pt.h"
#include "alpha_14pt.h"
#include "alpha_16pt.h"
#include "alpha_18pt.h"
#include "alpha_20pt.h"
#include "alpha_24pt.h"
#include "alpha_28pt.h"
#include "alpha_36pt.h"
#include "alpha_48pt.h"

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

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

/* macro provides NULL pointer if CGI variable does not exist */
#define GetCgiVarIfExists(CharPointer,CgiVariableName) \
   CharPointer = getenv(CgiVariableName)

/* macro provides pointer to empty string even if CGI variable does not exist */
#define GetCgiVar(CharPointer,CgiVariableName) \
   if ((CharPointer = getenv(CgiVariableName)) == NULL) \
       CharPointer = ""; \
   if (Debug) fprintf (stdout, "%s |%s|\n", CgiVariableName, CharPointer);

extern boolean  Debug,
                HttpHasBeenOutput;

extern char  *SoftwareID;

extern FILE  *HttpOut;

boolean  GraphicFontsInitialized,
         GraphicNoCacheGif = false;

int  GraphicBackground = 0,
     GraphicBitsPerPixel = 4,
     GraphicByteCount,
     GraphicColourBorder,
     GraphicColourBg,
     GraphicColourCount = 16,
     GraphicColourFg,
     GraphicHeight,
     GraphicHttpOutFd,
     GraphicFontPoints,
     GraphicPixelCount,
     GraphicTransparencyColour = -1,
     GraphicWidth,
     GraphicWidthBorder,
     GraphicWidthFg;

unsigned char  *GraphicPtr;

#define MaxFonts 48
struct GraphicFontStruct  Font [MaxFonts+1];

unsigned char  GraphicRgbRed [] =
{ 000, 255, 000, 000, 255, 255, 000, 255, 
  192, 128, 000, 000, 128, 128, 128, 250 };
unsigned char  GraphicRgbGreen [] =
{ 000, 000, 255, 000, 255, 000, 255, 255,
  192, 000, 128, 000, 128, 000, 128, 240 };
unsigned char  GraphicRgbBlue [] =
{ 000, 000, 000, 255, 000, 255, 255, 255,
  192, 000, 000, 128, 000, 128, 128, 230 };

int  GraphicColourBlack = 0,
     GraphicColourRed = 1,
     GraphicColourGreen = 2,
     GraphicColourBlue = 3,
     GraphicColourYellow = 4,
     GraphicColourMagenta = 5,
     GraphicColourCyan = 6,
     GraphicColourWhite = 7,
     GraphicColourGrey = 8,
     GraphicColourDRed = 9,
     GraphicColourDGreen = 10,
     GraphicColourDBlue = 11,
     GraphicColourDYellow = 12,
     GraphicColourDMagenta = 13,
     GraphicColourDCyan = 14,
     GraphicColourDWhite = 15;

char *GraphicRgbName [] =
{ "black", "red", "green", "blue", "yellow", "magenta", "cyan", "white",
  "grey", "dred", "dgreen", "dblue", "dyellow", "dmagenta", "dcyan", "dwhite",
  "" };

/*****************************************************************************/
/*
Create and send an image of example/test colours.
*/

GiftTestColours ()

{
   register int  x, y;

   int  Colour,
        ColourCount,
        Width;

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

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

   for (ColourCount = 0; GraphicRgbName[ColourCount][0]; ColourCount++);
   GraphicHeight = y = (ColourCount * 35) + 100 + 50;
   GraphicWidth = 800;

   GiftNewGraphic (GraphicWidth, GraphicHeight, GraphicColourBlack);

   y -= 100;

   Font[36].Colour = GraphicColourYellow;
   GiftDrawString (150, y+30, "GIFT Colour Test", &Font[36]);
   GiftDrawString (150, y+20, "________________", &Font[36]);
   GiftDrawBorder (GraphicColourYellow, 1);

   for (Colour = 0; GraphicRgbName[Colour][0]; Colour++)
   {
      y -= 35;
      GiftDrawBlock (50, y, 140, y+30, GraphicColourWhite);
      GiftDrawString (60, y+(30-Font[10].CharHeight)/2,
                      GraphicRgbName[Colour], &Font[10]);
      GiftDrawBlock (145, y, 750, y+30, Colour);
   }

   /* send the graphic to the client */
   GiftImage ();
}

/*****************************************************************************/
/*
Create and send an image of example/test graphics.
*/

GiftTestGraphics ()

{
   register int  x, y;

   int  Colour,
        Width;

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

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

   GiftNewGraphic (800, 800, GraphicColourBlack);

   Font[36].Colour = GraphicColourYellow;
   GiftDrawString (150, 730, "GIFT Graphics Test", &Font[36]);
   GiftDrawString (150, 720, "__________________", &Font[36]);

   Colour = GraphicColourRed;
   Font[8].Colour = GraphicColourGreen;
   Font[12].Colour = GraphicColourGreen;

   y = 610;
   GiftDrawString (10, y+50, "Lines done with GiftDrawLine()", &Font[12]);
   GiftDrawString (10, y+35, "widths 1, 2, 4 & 8", &Font[8]);
   GiftDrawLine (250, y+40, 550, y+40, Colour, 1);
   GiftDrawLine (250, y+30, 550, y+30, Colour, 2);
   GiftDrawLine (250, y+17, 550, y+17, Colour, 4);
   GiftDrawLine (250, y, 550, y, Colour, 8);

   y = 360;
   GiftDrawString (10, y+215, "Shape done with GiftDrawLine()", &Font[12]);
GiftDrawString (10, y+200,
"Colour = GiftColour (\"blue\");\n\
y = 360;\n\
GiftDrawLine (100, y, 700, y, Colour, 1);\n\
GiftDrawLine (100, y+100, 700, y+100, Colour, 1);\n\
GiftDrawLine (100, y, 100, y+100, Colour, 1);\n\
GiftDrawLine (700, y, 700, y+100, Colour, 1);\n\
GiftDrawLine (100, y, 700, y+100, Colour, 1);\n\
GiftDrawLine (100, y+100, 700, y, Colour, 1);", &Font[8]);

   Colour = GraphicColourBlue;
   GiftDrawLine (100, y, 700, y, Colour, 1);
   GiftDrawLine (100,y+100, 700, y+100, Colour, 1);
   GiftDrawLine (100, y, 100, y+100, Colour, 1);
   GiftDrawLine (700, y, 700, y+100, Colour, 1);
   GiftDrawLine (100, y, 700, y+100, Colour, 1);
   GiftDrawLine (100, y+100, 700, y, Colour, 1);

   y = 180;
   GiftDrawString (10, y+145, "Blocks done with GiftDrawBlock()", &Font[12]);

   Font[10].Colour = GraphicColourBlack;
   Font[10].Italic = true;
   Colour = GraphicColourCyan;
   GiftDrawBlock (100, y+40, 700, y+130, Colour);
   GiftDrawString (110, y+43, "cyan", &Font[10]);
   Colour = GraphicColourMagenta;
   GiftDrawBlock (200, y+60, 600, y+110, Colour);
   GiftDrawString (210, y+63, "magenta", &Font[10]);
   Colour = GraphicColourYellow;
   GiftDrawBlock (300, y+70, 500, y+100, Colour);
   GiftDrawString (310, y+73, "yellow", &Font[10]);
   Font[10].Italic = false;

   Font[8].Colour = Colour = GraphicColourRed;
   GiftDrawLine (240, y+65, 340, y+30, Colour, 1);
   GiftDrawString (340, y+20,
      "(black against magenta doesn't\n stand out very well at all!)",
      &Font[8]);

   GiftDrawBorder (GraphicColourYellow, 1);

   /* send the graphic to the client */
   GiftImage ();
}

/*****************************************************************************/
/*
Create and send an image of all available fonts in various colours.
*/

GiftTestFonts ()

{
   static char  *Colours [] = { "blue", "green", "red", "" };

   register int  idx;

   int  Count,
        CountColour,
        CountFont,
        x, y;
   char  FontSize [32],
         FwdString [96],
         RevString [96];
   char  *StringPtr;

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

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

   if (!GraphicWidth) GraphicWidth = 800;
   if (!GraphicHeight) GraphicHeight = 1150;

   GiftNewGraphic (GraphicWidth, GraphicHeight, GraphicColourBlack);

   idx = 0;
   for (Count = 0x20; Count < 0x7f; Count++)
      FwdString[idx++] = Count;
   FwdString[idx] = '\0';

   idx = 0;
   for (Count = 0x7e; Count >= 0x20; Count--)
      RevString[idx++] = Count;
   RevString[idx] = '\0';

   y = 3;
   for (CountColour = 0; Colours[CountColour][0]; CountColour++)
   {
      for (CountFont = MaxFonts; CountFont; CountFont--)
      {
         if (!Font[CountFont].PointSize) continue;
         Font[CountFont].Colour = GiftColour (Colours[CountColour]);
         x = 3;
         sprintf (FontSize, "%dpts:", Font[CountFont].PointSize);
         x = GiftDrawString (x, y, FontSize, &Font[CountFont]);
         x = GiftDrawString (x, y, FwdString, &Font[CountFont]);
         y += Font[CountFont].CharHeight;
      }
   }
   Font[36].Colour = GraphicColourYellow;
   GiftDrawString (190, 1095, "GIFT Font Test", &Font[36]);
   GiftDrawString (190, 1085, "______________", &Font[36]);

   GiftDrawBorder (GraphicColourYellow, 1);

   /* send the graphic to the client */
   GiftImage ();
}

/*****************************************************************************/
/*
Allocate memory for a graphic of the specified size, then set all the pixels
to the specifiied colour.
*/

GiftNewGraphic
(
int Width,
int Height,
int Colour
)
{
   /*********/
   /* begin */
   /*********/

   if (Debug)
      fprintf (stdout, "GiftNewGraphic() %d,%d %d\n", Width, Height, Colour);

   if (!(GraphicWidth = Width)) GraphicWidth = DefaultGraphicWidth;
   if (!(GraphicHeight = Height)) GraphicHeight = DefaultGraphicHeight;
   GraphicPixelCount = GraphicWidth * GraphicHeight;

   if ((GraphicPtr = malloc (GraphicPixelCount)) == NULL)
      exit (ErrorGeneral ("Memory allocation failed.", __FILE__, __LINE__));

   /* set all the pixels to background colour */
   memset (GraphicPtr, Colour, GraphicPixelCount);
}

/*****************************************************************************/
/*
Set all the pixels in the graphic to the specified colour.
*/

GiftBackground (int Colour)

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

   if (Debug) fprintf (stdout, "GiftBackground() %d\n", Colour);

   memset (GraphicPtr, Colour, GraphicPixelCount);
}

/*****************************************************************************/
/*
Initialize any defaults then create a new graphic.
*/

GiftInitGraphic ()

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

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

   if (GraphicPtr != NULL)
      exit (GiftGraphicErrorReport ("Graphic already initialized."));

   if (!GraphicWidth) GraphicWidth = DefaultGraphicWidth;
   if (!GraphicHeight) GraphicHeight = DefaultGraphicHeight;
   if (!GraphicColourBorder)
      GraphicColourBorder = GiftColour (DefaultBorderColour);
   if (!GraphicWidthBorder) GraphicWidthBorder = DefaultBorderWidth;
   if (!GraphicColourBg)
      GraphicColourBg = GiftColour (DefaultGraphicColourBg);
   if (!GraphicColourFg)
      GraphicColourFg = GiftColour (DefaultGraphicColourFg);
   if (!GraphicWidthFg) GraphicWidthFg = DefaultGraphicWidthFg;

   GiftNewGraphic (GraphicWidth, GraphicHeight, GraphicColourBg);
}

/*****************************************************************************/
/*
Returns the index number for the colour represented by the supplied name.  If
no colour matching the supplied string a graphic error message is generated.
*/

int GiftColour (char *ColourString)

{
   register int  idx;

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

   if (Debug) fprintf (stdout, "GiftColour() |%s|\n", ColourString);

   if (isdigit(*ColourString))
   {
      idx = atol(ColourString);
      if (idx >= 0 || idx < (1 << GraphicBitsPerPixel)) return (idx);
   }
   else
   {
      for (idx = 0; GraphicRgbName[idx][0]; idx++)
      {
         if (Debug) fprintf (stdout, "GraphicRgbName[%d] |%s|\n", idx, GraphicRgbName[idx]);
         if (strsame (GraphicRgbName[idx], ColourString, -1)) break;
      }
      if (GraphicRgbName[idx][0]) return (idx);
   }

   {
      /* local storage */
      char  String [256];

      sprintf (String, "Colour \"%s\" unknown.", ColourString);
      exit (GiftGraphicErrorReport (String));
   }
}

/*****************************************************************************/
/*
Generate a graphic error message.  This is designed to allow errors to be
reported even though the browser is retrieving and displaying an image.  The
error message can contain multiple lines, and adjusts its height accordingly,
but lines should be limited to 80 characters.
*/

int GiftGraphicErrorReport (char *ErrorMessage)

{
   register char  *cptr;

   int  y,
        LineCount;
   char  String [256];

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

   if (Debug) fprintf (stdout, "GiftGraphicErrorReport() |%s|\n", ErrorMessage);

   LineCount = 1;
   for (cptr = ErrorMessage; *cptr; cptr++)
      if (*cptr == '\n') LineCount++;

   sprintf (String, "Report generated by server (%s)", SoftwareID);

   GraphicWidth = (Font[10].CharWidth * 80) + 25;
   GraphicHeight = 100 + LineCount * 15;
   y = GraphicHeight - 100;

   GiftNewGraphic (GraphicWidth, GraphicHeight, GraphicColourBlack);

   Font[28].Colour = GraphicColourRed;
   Font[12].Colour = Font[10].Colour = GraphicColourYellow;

   GiftDrawString (10, y+50, "ERROR!", &Font[28]);
   GiftDrawString (13, y+30, String, &Font[12]);
   GiftDrawString (13, y, ErrorMessage, &Font[10]);

   GiftDrawBorder (GraphicColourYellow, 2);

   /* send the graphic to the client */
   GiftImage ();

   return (SS$_NORMAL);
}

/*****************************************************************************/
/*
Only returns if the font size is supported, if not generates a graphic error
message.
*/

GiftCheckFont (int GraphicFontPoints)

{
   char  String [64];

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

   if (Debug) fprintf (stdout, "GiftCheckFont() %d\n", GraphicFontPoints);

   if (GraphicFontPoints >= 8 && GraphicFontPoints <= MaxFonts &&
       Font[GraphicFontPoints].PointSize)
      return;

   sprintf (String, "Font size %d not supported.", GraphicFontPoints);
   exit (GiftGraphicErrorReport (String));
}

/*****************************************************************************/
/*
Initialize the font structures with "define"d information from the included
X-bitmap files.  An array of font structures is used, where each element
corresponding to a supported font point size has font data in it.  This
function may be called at any time to re-initialize all fonts.  The fonts
sizes supported are indicated by indicies being initialized.
*/

GiftInitFonts ()

{
   register int  idx;

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

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

   for (idx = 0; idx <= MaxFonts; idx++)
      memset (&Font[idx], 0, sizeof(Font[idx]));

   Font[8].XbmHeight = alpha_08pt_height;
   Font[8].XbmWidth = alpha_08pt_width;
   Font[8].Bits = alpha_08pt_bits;
   Font[8].NumberOfChar = alpha_08pt_font_number_of_char;
   Font[8].FirstChar = alpha_08pt_font_first_char;
   Font[8].CharHeight = alpha_08pt_font_height;
   Font[8].CharWidth = alpha_08pt_font_width;
   Font[8].PointSize = 08;
   Font[8].Colour = GraphicColourBlack;

   Font[9].XbmHeight = alpha_09pt_height;
   Font[9].XbmWidth = alpha_09pt_width;
   Font[9].Bits = alpha_09pt_bits;
   Font[9].NumberOfChar = alpha_09pt_font_number_of_char;
   Font[9].FirstChar = alpha_09pt_font_first_char;
   Font[9].CharHeight = alpha_09pt_font_height;
   Font[9].CharWidth = alpha_09pt_font_width;
   Font[9].PointSize = 09;
   Font[9].Colour = GraphicColourBlack;

   Font[10].XbmHeight = alpha_10pt_height;
   Font[10].XbmWidth = alpha_10pt_width;
   Font[10].Bits = alpha_10pt_bits;
   Font[10].NumberOfChar = alpha_10pt_font_number_of_char;
   Font[10].FirstChar = alpha_10pt_font_first_char;
   Font[10].CharHeight = alpha_10pt_font_height;
   Font[10].CharWidth = alpha_10pt_font_width;
   Font[10].PointSize = 10;
   Font[10].Colour = GraphicColourBlack;

   Font[11].XbmHeight = alpha_11pt_height;
   Font[11].XbmWidth = alpha_11pt_width;
   Font[11].Bits = alpha_11pt_bits;
   Font[11].NumberOfChar = alpha_11pt_font_number_of_char;
   Font[11].FirstChar = alpha_11pt_font_first_char;
   Font[11].CharHeight = alpha_11pt_font_height;
   Font[11].CharWidth = alpha_11pt_font_width;
   Font[11].PointSize = 11;
   Font[11].Colour = GraphicColourBlack;

   Font[12].XbmHeight = alpha_12pt_height;
   Font[12].XbmWidth = alpha_12pt_width;
   Font[12].Bits = alpha_12pt_bits;
   Font[12].NumberOfChar = alpha_12pt_font_number_of_char;
   Font[12].FirstChar = alpha_12pt_font_first_char;
   Font[12].CharHeight = alpha_12pt_font_height;
   Font[12].CharWidth = alpha_12pt_font_width;
   Font[12].PointSize = 12;
   Font[12].Colour = GraphicColourBlack;

   Font[14].XbmHeight = alpha_14pt_height;
   Font[14].XbmWidth = alpha_14pt_width;
   Font[14].Bits = alpha_14pt_bits;
   Font[14].NumberOfChar = alpha_14pt_font_number_of_char;
   Font[14].FirstChar = alpha_14pt_font_first_char;
   Font[14].CharHeight = alpha_14pt_font_height;
   Font[14].CharWidth = alpha_14pt_font_width;
   Font[14].PointSize = 14;
   Font[14].Colour = GraphicColourBlack;

   Font[16].XbmHeight = alpha_16pt_height;
   Font[16].XbmWidth = alpha_16pt_width;
   Font[16].Bits = alpha_16pt_bits;
   Font[16].NumberOfChar = alpha_16pt_font_number_of_char;
   Font[16].FirstChar = alpha_16pt_font_first_char;
   Font[16].CharHeight = alpha_16pt_font_height;
   Font[16].CharWidth = alpha_16pt_font_width;
   Font[16].PointSize = 16;
   Font[16].Colour = GraphicColourBlack;

   Font[18].XbmHeight = alpha_18pt_height;
   Font[18].XbmWidth = alpha_18pt_width;
   Font[18].Bits = alpha_18pt_bits;
   Font[18].NumberOfChar = alpha_18pt_font_number_of_char;
   Font[18].FirstChar = alpha_18pt_font_first_char;
   Font[18].CharHeight = alpha_18pt_font_height;
   Font[18].CharWidth = alpha_18pt_font_width;
   Font[18].PointSize = 18;
   Font[18].Colour = GraphicColourBlack;

   Font[20].XbmHeight = alpha_20pt_height;
   Font[20].XbmWidth = alpha_20pt_width;
   Font[20].Bits = alpha_20pt_bits;
   Font[20].NumberOfChar = alpha_20pt_font_number_of_char;
   Font[20].FirstChar = alpha_20pt_font_first_char;
   Font[20].CharHeight = alpha_20pt_font_height;
   Font[20].CharWidth = alpha_20pt_font_width;
   Font[20].PointSize = 20;
   Font[20].Colour = GraphicColourBlack;

   Font[24].XbmHeight = alpha_24pt_height;
   Font[24].XbmWidth = alpha_24pt_width;
   Font[24].Bits = alpha_24pt_bits;
   Font[24].NumberOfChar = alpha_24pt_font_number_of_char;
   Font[24].FirstChar = alpha_24pt_font_first_char;
   Font[24].CharHeight = alpha_24pt_font_height;
   Font[24].CharWidth = alpha_24pt_font_width;
   Font[24].PointSize = 24;
   Font[24].Colour = GraphicColourBlack;

   Font[28].XbmHeight = alpha_28pt_height;
   Font[28].XbmWidth = alpha_28pt_width;
   Font[28].Bits = alpha_28pt_bits;
   Font[28].NumberOfChar = alpha_28pt_font_number_of_char;
   Font[28].FirstChar = alpha_28pt_font_first_char;
   Font[28].CharHeight = alpha_28pt_font_height;
   Font[28].CharWidth = alpha_28pt_font_width;
   Font[28].PointSize = 28;
   Font[28].Colour = GraphicColourBlack;

   Font[36].XbmHeight = alpha_36pt_height;
   Font[36].XbmWidth = alpha_36pt_width;
   Font[36].Bits = alpha_36pt_bits;
   Font[36].NumberOfChar = alpha_36pt_font_number_of_char;
   Font[36].FirstChar = alpha_36pt_font_first_char;
   Font[36].CharHeight = alpha_36pt_font_height;
   Font[36].CharWidth = alpha_36pt_font_width;
   Font[36].PointSize = 36;
   Font[36].Colour = GraphicColourBlack;

   Font[48].XbmHeight = alpha_48pt_height;
   Font[48].XbmWidth = alpha_48pt_width;
   Font[48].Bits = alpha_48pt_bits;
   Font[48].NumberOfChar = alpha_48pt_font_number_of_char;
   Font[48].FirstChar = alpha_48pt_font_first_char;
   Font[48].CharHeight = alpha_48pt_font_height;
   Font[48].CharWidth = alpha_48pt_font_width;
   Font[48].PointSize = 48;
   Font[48].Colour = GraphicColourBlack;

   GraphicFontsInitialized = true;
}

/*****************************************************************************/
/*
Draws a string of characters using the supplied font.  A new-line character
moves the line down the graphic by the height of the font.
*/

int GiftDrawString
(
int atx,
int aty,
char *String,
struct GraphicFontStruct *FontPtr
)
{
   register char  *cptr;

   int  x, y;

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

   if (Debug)
      fprintf (stdout, "GiftDrawString() %d,%d |%s|\n", atx, aty, String);

   if (!GraphicFontsInitialized) GiftInitFonts ();

   x = atx;
   y = aty;

   for (cptr = String; *cptr; cptr++)
   {
      switch (*cptr)
      {
         case '\r' :
            x = atx;
            break;
         case '\n' :
            x = atx;
            y -= FontPtr->CharHeight;
            break;
         default :
            x = GiftDrawChar (x, y, *cptr, FontPtr);
      }
   }
   return (x);
}

/*****************************************************************************/
/*
Draws ASCII character in parameter 'c' at (x,y) from the font pointed to by
'FontPtr'.  The fonts used are X-bitmap representations of fixed-fonts of
various point sizes.  Each character has a fixed width and height, and so is
relatively easy to render.  Characters can be italicised, bolded, underlined,
doubled in height, reverse-videoed, by setting the appropriate boolean in the
font structure.
*/

GiftDrawChar
(
int atx,
int aty,
int c,
struct GraphicFontStruct *FontPtr
)
{
   register unsigned char  fb;
   register int  x, y, tox, toy, fbit, flip, pixcnt;

   int  PixelSize;
   int  BytesPerRow,
        FontByte,
        PixelWidth,
        OriginalAtX,
        RowCount,
        UnderLineRow,
        XWidth;

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

   if (Debug) fprintf (stdout, "GiftDrawChar() %d %d,%d\n", c, atx, aty);

   if (c < 0x20 || c > 0x7e) return;

   if (!FontPtr->PointSize) return;

   OriginalAtX = atx;

   if (FontPtr->Double)
      PixelWidth = PixelSize = 2;
   else
      PixelWidth = PixelSize = 1;

   if (FontPtr->Bold)
      PixelSize = 2;
   else
   if (!FontPtr->Double)
      PixelSize = 1;

   if (FontPtr->UnderLine)
      UnderLineRow = FontPtr->XbmHeight - 1;
   else
      UnderLineRow = -1;

   XWidth = (FontPtr->CharWidth * PixelWidth);

   /* zero equals one pixel, one equals 2 pixels, etc. */
   PixelSize--;

   /* X bitmap rows always end in a whole byte */
   if (FontPtr->XbmWidth % 8)
      BytesPerRow = (FontPtr->XbmWidth / 8) + 1;
   else
      BytesPerRow = FontPtr->XbmWidth / 8;

   for (RowCount = FontPtr->CharHeight - 1; RowCount >= 0; RowCount--)
   {
      if (Debug)
         fprintf (stdout, "RowCount: %d x,y: %d,%d\n", RowCount, atx, aty);

      if (RowCount == UnderLineRow)
      {
         if (FontPtr->Inverse)
            flip = 0x00;
         else
            flip = 0xff;
      }
      else
      {
         if (FontPtr->Inverse)
            flip = 0xff;
         else
            flip = 0x00;
      }

      FontByte = (RowCount * BytesPerRow) +
                 (((c - FontPtr->FirstChar) * FontPtr->CharWidth) / 8);
      fbit = ((c - FontPtr->FirstChar) * FontPtr->CharWidth) % 8;
      if (fbit)
         fbit = 0x01 << fbit;
      else
         fbit = 0x01;

      pixcnt = FontPtr->CharWidth;
      fb = (*(FontPtr->Bits + FontByte)) ^ flip;

      if (Debug)
         fprintf (stdout, "FontByte: %d fbit: %d pixcnt: %d\n",
                  FontByte, fbit, pixcnt);

      while (pixcnt--)
      {
         if (fb & fbit)
         {
            if (atx >= 0 && atx < GraphicWidth &&
                aty >= 0 && aty < GraphicHeight)
            {
               if ((tox = atx + PixelSize) >= GraphicWidth)
                  tox = GraphicWidth - 1;
               if ((toy = aty + PixelSize) >= GraphicHeight)
                  tox = GraphicHeight - 1;
               if (PixelSize)
               {
                  for (x = atx; x <= tox; x++)
                     for (y = aty; y <= toy; y++)
                        *(GraphicPtr + (y * GraphicWidth) + x) =
                            FontPtr->Colour;
               }
               else
                  *(GraphicPtr + (aty * GraphicWidth) + atx) = FontPtr->Colour;
            }
         }

         atx += PixelWidth;

         if (!pixcnt) break;

         if (fbit & 0x80)
         {
            fbit = 0x01;
            fb = (*(FontPtr->Bits + ++FontByte)) ^ flip;
         }
         else
            fbit = fbit << 1;
      }

      if (!FontPtr->Italic || RowCount % 3)
         atx -= XWidth;
      else
         atx -= XWidth - 1;

      aty += PixelWidth;
   }

   return (OriginalAtX + XWidth);
}

/*****************************************************************************/
/*
The basic point drawing/plotting function.  Draws a point at (x,y) in the
specified colour.  Width specifies the size of the point, 1 being one pixel,
2 being 2x2 pixels, 3 being 3x3 pixels, etc.  Points with widths greater than
1 begin at the specified (x,y) and grow in the x and y directions.  Generates
a graphic error message if coordinates are out-of-range.
*/

GiftDrawPoint
(
int atx,
int aty,
int Colour,
int Width
)
{
   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "GiftDrawPoint() %d,%d\n", atx, aty);

   if (!Width) return;

   if (atx >= 0 && atx < GraphicWidth && aty >= 0 && aty <= GraphicHeight)
   {
      if (Width == 1)
      {
         *(GraphicPtr + (aty * GraphicWidth) + atx) = Colour;
         return;
      }
      else
      if (Width > 1)
      {
         /* local storage */
         register int  x, y, tox, toy;

         if (Width > MaxPointWidth) Width = MaxPointWidth;
         if ((tox = atx + Width) >= GraphicWidth) tox = GraphicWidth - 1;
         if ((toy = aty + Width) >= GraphicHeight) tox = GraphicHeight - 1;
         for (x = atx; x <= tox; x++)
            for (y = aty; y <= toy; y++)
               *(GraphicPtr + (y * GraphicWidth) + x) = Colour;
         return;
      }
   }

   {
      /* local storage */
      char  String [256];

      sprintf (String, "Point (%d,%d) out-of-range within %dx%d.",
               atx, aty, GraphicWidth, GraphicHeight);
      exit (GiftGraphicErrorReport (String));
   }
}

/*****************************************************************************/
/*
Draws a straight line from (x1,y1) to (x2,y2) of the specified colour and
width.  Generates a graphic error message if coordinates out-of-range.
For efficiency plots its own points without calling GiftDrawPoint().
*/

GiftDrawLine
(
int x1,
int y1,
int x2,
int y2,
int Colour,
int Width
)
{
   register int  i, x, xinc, y, ygrad, yinc;

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

   if (Debug)
      fprintf (stdout, "GiftDrawLine() %d,%d %d,%d %d %d\n",
               x1, y1, x2, y2, Colour, Width);

   if (!Width) return;

   if (x1 < 0 || x1 >= GraphicWidth ||
       x2 < 0 || x2 >= GraphicWidth ||
       y1 < 0 || y1 >= GraphicHeight ||
       y2 < 0 || y2 >= GraphicHeight)
   {
      /* local storage */
      char  String [256];

      sprintf (String, "Line (%d,%d)(%d,%d) out-of-range within %dx%d.",
               x1, y1, x2, y2, GraphicWidth, GraphicHeight);
      exit (GiftGraphicErrorReport (String));
   }

   if (x1 > x2)
   {
      x = x1;
      x1 = x2;
      x2 = x;
      y = y1;
      y1 = y2;
      y2 = y;
   }

   if (x1 == x2)
      xinc = 0;
   else
      xinc = 1;

   if (y2 - y1 == 0)
      i = ygrad = 0;
   else
   if ((i = ygrad = (x2 - x1) / (y2 - y1)) == 0)
      i = ygrad = 1;

   if (ygrad < 0)
      yinc = -1;
   else
      yinc = 1;

   if (Debug)
      fprintf (stdout, "%d,%d %d,%d yinc: %d ygrad: %d\n",
               x1, y1, x2, y2, yinc, ygrad);

   y = y1;
   x = x1;
   while (x <= x2 && ((yinc > 0 && y <= y2) || (yinc < 0 && y >= y2)))
   {
      /* plot the point */
      if (Width == 1)
         *(GraphicPtr + (y * GraphicWidth) + x) = Colour;
      else
      {
         /* local storage */
         register int  xx, yy, tox, toy;

         if (Width > MaxPointWidth) Width = MaxPointWidth;
         if ((tox = x + Width) >= GraphicWidth) tox = GraphicWidth - 1;
         if ((toy = y + Width) >= GraphicHeight) tox = GraphicHeight - 1;
         for (xx = x; xx <= tox; xx++)
            for (yy = y; yy <= toy; yy++)
               *(GraphicPtr + (yy * GraphicWidth) + xx) = Colour;
      }

      x += xinc;
      i -= yinc;
      if (!i && ygrad)
      {
         if (y2 - y == 0)
            i = ygrad = 0;
         else
         if ((i = ygrad = (x2 - x) / (y2 - y)) == 0)
            i = ygrad = 1;

         y += yinc;
      }
   }
}

/*****************************************************************************/
/*
Draw a rectangle with opposite corners defined by (x1,y1) and (x2,y2) of the
specified colour and width.
*/

GiftDrawRect
(
int x1,
int y1,
int x2,
int y2,
int Colour,
int Width
)
{
   register int  x, y;

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

   if (Debug)
      fprintf (stdout, "GiftDrawRect() %d,%d %d,%d %d\n",
               x1, y1, x2, y2, Colour);

   if (x1 < 0 || x1 >= GraphicWidth ||
       x2 < 0 || x2 >= GraphicWidth ||
       y1 < 0 || y1 >= GraphicHeight ||
       y2 < 0 || y2 >= GraphicHeight)
   {
      /* local storage */
      char  String [256];

      sprintf (String, "Rectangle (%d,%d %d,%d) out-of-range within %dx%d.",
               x1, y1, x2, y2, GraphicWidth, GraphicHeight);
      exit (GiftGraphicErrorReport (String));
   }

   GiftDrawLine (x1, y1, x1, y2, Colour, Width);
   GiftDrawLine (x1, y2, x2, y2, Colour, Width);
   GiftDrawLine (x2, y2, x2, y1, Colour, Width);
   GiftDrawLine (x2, y1, x1, y1, Colour, Width);
}

/*****************************************************************************/
/*
Draw a rectangle with opposite corners defined by (x1,y1) and (x2,y2) filled
with the specified colour.
*/

GiftDrawBlock
(
int x1,
int y1,
int x2,
int y2,
int Colour
)
{
   register int  x, y;

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

   if (Debug)
      fprintf (stdout, "GiftDrawBlock() %d,%d %d,%d %d\n",
               x1, y1, x2, y2, Colour);

   if (x1 < 0 || x1 >= GraphicWidth ||
       x2 < 0 || x2 >= GraphicWidth ||
       y1 < 0 || y1 >= GraphicHeight ||
       y2 < 0 || y2 >= GraphicHeight)
   {
      /* local storage */
      char  String [256];

      sprintf (String, "Block (%d,%d %d,%d) out-of-range within %dx%d.",
               x1, y1, x2, y2, GraphicWidth, GraphicHeight);
      exit (GiftGraphicErrorReport (String));
   }

   if (x1 > x2)
   {
      x = x1;
      x1 = x2;
      x2 = x;
   }
   if (y1 > y2)
   {
      y = y1;
      y1 = y2;
      y2 = y;
   }

   for (x = x1; x <= x2; x++)
      GiftDrawLine (x, y1, x, y2, Colour, 1);
}

/*****************************************************************************/
/*
Place a border of the specified colour and width around the entire graphic.
*/

GiftDrawBorder
(
int Colour,
int Width
)
{
   register int  x, y, cnt;
   register unsigned char  *gptr;

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

   if (Debug) fprintf (stdout, "GiftDrawBorder() %d %d\n", Colour, Width);

   if (Width <= 0)
      Width = 1;
   else
   if (Width >= GraphicWidth)
      Width = GraphicWidth - 1;
   else
   if (Width >= GraphicHeight)
      Width = GraphicHeight - 1;

   if (Width > MaxPointWidth) Width = MaxPointWidth;
   for (cnt = 0; cnt < Width; cnt++)
   {
      gptr = GraphicPtr + (cnt * GraphicWidth);
      for (x = 0; x < GraphicWidth; x++) *gptr++ = Colour;
   }
   for (cnt = Width; cnt; cnt--)
   {
      gptr = GraphicPtr + (GraphicHeight - cnt) * GraphicWidth;
      for (x = 0; x < GraphicWidth; x++) *gptr++ = Colour;
   }
   for (cnt = 0; cnt < Width; cnt++)
   {
      gptr = GraphicPtr + cnt;
      for (y = 0; y < GraphicHeight; y++)
        *(gptr + (y * GraphicWidth)) = Colour;
   }
   for (cnt = Width; cnt; cnt--)
   {
      gptr = GraphicPtr + GraphicWidth - cnt;
      for (y = 0; y < GraphicHeight; y++)
        *(gptr + (y * GraphicWidth)) = Colour;
   }
}

/*****************************************************************************/
/*
Create and send a graphic percentage-used bar.
*/

GiftUsageBar
(
int Percentage,
int AlertPercentage,
char *Text
)
{
   int  UsageColour, y;

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

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

   if (!GraphicWidth) GraphicWidth = 500;
   if (!GraphicHeight) GraphicHeight = 30;

   GiftNewGraphic (GraphicWidth, GraphicHeight, GraphicColourWhite);

   if (AlertPercentage && Percentage >= AlertPercentage)
      UsageColour = GraphicColourRed;
   else
      UsageColour = GraphicColourGreen;

   /* draw a "dirty-white" progress bar to overlay the white background */
   GiftDrawBar (GraphicColourDWhite, 100);
   /* draw the appropriate colour progress bar to indicate usage */
   GiftDrawBar (UsageColour, Percentage);
   /* put a black border around the graphic */
   GiftDrawBorder (GraphicColourBlack, 1);

   if (Text[0])
   {
      y = GraphicHeight / 4;
      if (AlertPercentage && Percentage >= AlertPercentage)
      {
         Font[10].Bold = true;
         Font[10].Colour = GraphicColourWhite;
         GiftDrawString (11, y-1, Text, &Font[10]);
      }
      Font[10].Colour = GraphicColourBlack;
      GiftDrawString (10, y, Text, &Font[10]);
      Font[10].Bold = false;
   }

   /* send the graphic to the client */
   GiftImage ();
}

/*****************************************************************************/
/*
Draws a percentage bar from left to right.  The bar is solid colour, but
graduated by leaving 2 pixels blank at 10% points, one pixel at 5% points.
*/

GiftDrawBar
(
int Colour,
int Percentage
)
{
   register int  x, y, cnt;
   register unsigned char  *pptr;

   int  SolidPixels,
        Width;

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

   if (Debug) fprintf (stdout, "GiftDrawBar() %d %d\n", Colour, Percentage);

   /* convert from a percentage to an actual number of pixels */
   if ((Width = (Percentage * GraphicWidth) / 100) >= GraphicWidth)
      Width = GraphicWidth - 1;

   SolidPixels = GraphicWidth / 20;
   for (y = 0; y < GraphicHeight; y++)
   {
      pptr = GraphicPtr + (y * GraphicWidth);
      x = 0;
      while (x < Width)
      {
         if (x % 2)
            cnt = SolidPixels - 1;
         else
         {
            if (x+1 < Width) { x++; pptr++; }
            cnt = SolidPixels - 2;
         }
         while (cnt-- && x++ < Width) *pptr++ = Colour;
         if (x+1 < Width) { x++; pptr++; }
      }
   }
}

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

GiftButton
(
char *ColourName,
char *Text,
int FontPoints
)
{
   int  ButtonColour;

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

   if (Debug)
       fprintf (stdout, "GiftButton() |%s|%s| %d\n",
                ColourName, Text, FontPoints);

   GraphicWidth = GraphicHeight = 0;

   if (ColourName == NULL) ColourName = "";
   if (Text == NULL) Text = "";
   if (ColourName[0])
      ButtonColour = GiftColour (ColourName);
   else
      ButtonColour = GiftColour (DefaultButtonColour);
   if (!FontPoints) FontPoints = DefaultButtonFontPoints;

   GiftDrawButton (ButtonColour, Text, &Font[FontPoints]);
}

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

GiftDrawButton
(
int ButtonColour,
char *Text,
struct GraphicFontStruct *FontPtr
)
{
   register char  *cptr, *sptr;

   int  x, y,
        TextHeight,
        LongestLine,
        LineCount,
        TextWidth;

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

   if (Debug)
       fprintf (stdout, "GiftDrawButton() %d |%s|\n", ButtonColour, Text);

   if (!GraphicFontsInitialized) GiftInitFonts ();

   LongestLine = 0;
   LineCount = 1;
   for (cptr = sptr = Text; *cptr; cptr++)
   {
      if (*cptr == '\n')
      {
         LineCount++;
         if (cptr - sptr > LongestLine) LongestLine = cptr - sptr;
         sptr = ++cptr;
      }
   }
   if (cptr - sptr > LongestLine) LongestLine = cptr - sptr;

   TextWidth = (FontPtr->CharWidth * LongestLine);
   TextHeight = (FontPtr->CharHeight * LineCount);

   if (!GraphicWidth) GraphicWidth = TextWidth + (FontPtr->CharWidth * 2);
   if (!GraphicHeight) GraphicHeight = TextHeight + (FontPtr->CharHeight / 2);

   GiftNewGraphic (GraphicWidth, GraphicHeight, GraphicColourGrey);

   x = (GraphicWidth - TextWidth) / 2;
   y = GraphicHeight - ((GraphicHeight - TextHeight) / 2) -
       FontPtr->CharHeight - 1;

   /** if (FontPtr->PointSize > 10) FontPtr->Bold = true; **/
   FontPtr->Colour = GraphicColourWhite;
   GiftDrawString (x+1, y-1, Text, FontPtr);
   FontPtr->Colour = GraphicColourBlack;
   GiftDrawString (x, y, Text, FontPtr);
   /** if (FontPtr->PointSize > 10) FontPtr->Bold = false; **/

   GiftDrawBorder (GraphicColourBlack, 2);
   GiftDrawLine (0, 1, 0, GraphicHeight-2, ButtonColour, 1);
   GiftDrawLine (1, GraphicHeight-1, GraphicWidth-2, GraphicHeight-1,
                 ButtonColour, 1);

   /* send the graphic to the client */
   GiftImage ();
}

/*****************************************************************************/
/*
Parses the supplied string for directives.
*/

GiftParseParameters
(
char *String,
int LineNumber
)
{
   register char  *cptr, *sptr;

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

   if (Debug) fprintf (stdout, "GiftParseParameters() |%s|\n", String);

   cptr = String;
   while (*cptr)
   {
      if (Debug) fprintf (stdout, "cptr |%s|\n", cptr);

      if (toupper(cptr[0]) == 'P' &&
          toupper(cptr[1]) == 'N' &&
          toupper(cptr[2]) == 'T' &&
          (cptr[3] == '=' || cptr[3] == ':'))
      {
         /**************/
         /* draw point */
         /**************/

         int  x, y;

         if (Debug) fprintf (stdout, "draw point\n");
         sptr = (cptr += 4);
         while (isdigit(*cptr)) cptr++;
         if (*cptr != ',')
            exit (GiftParseError ("Syntax", sptr-String, LineNumber));
         *cptr++ = '\0';
         if (!isdigit(*sptr))
            exit (GiftParseError ("Syntax", sptr-String, LineNumber));
         x = atol(sptr);

         sptr = cptr;
         while (isdigit(*cptr)) cptr++;
         if (*cptr && (*cptr != '&' || *cptr != ';'))
            exit (GiftParseError ("Syntax", sptr-String, LineNumber));
         if (*cptr) *cptr++ = '\0';
         if (!isdigit(*sptr))
            exit (GiftParseError ("Syntax", sptr-String, LineNumber));
         y = atol(sptr);

         if (GraphicPtr == NULL) GiftInitGraphic ();
         GiftDrawPoint (x, y, GraphicColourFg, GraphicWidthFg);

         continue;
      }

      if (toupper(cptr[0]) == 'P' &&
          toupper(cptr[1]) == 'R' &&
          toupper(cptr[2]) == 'O' &&
          (cptr[3] == '=' || cptr[3] == ':'))
      {
         /***********************/
         /* draw a progress bar */
         /***********************/

         int  Percentage,
              ProgressBarAlertPercentage;

         if (Debug) fprintf (stdout, "draw progress-bar\n");
         sptr = (cptr += 4);
         while (*cptr && *cptr != '&' && *cptr != ';' && *cptr != ',') cptr++;
         if (*cptr == ',')
            ProgressBarAlertPercentage = 0;
         else
            ProgressBarAlertPercentage = 101;
         if (*cptr) *cptr++ = '\0';
         Percentage = atol(sptr);
         if (!isdigit(*sptr) || Percentage < 0 || Percentage > 100)
            exit (GiftParseError ("Syntax", sptr-String, LineNumber));

         if (!ProgressBarAlertPercentage)
         {
            sptr = cptr;
            while (*cptr && *cptr != '&' && *cptr != ';') cptr++;
            if (*cptr) *cptr++ = '\0';
            ProgressBarAlertPercentage = atol(sptr);
            if (!isdigit(*sptr) ||
                ProgressBarAlertPercentage < 0 ||
                ProgressBarAlertPercentage > 100)
               exit (GiftParseError ("Syntax", sptr-String, LineNumber));
         }

         if (!GraphicWidth) GraphicWidth = DefaultProgressBarWidth;
         if (!GraphicHeight) GraphicHeight = DefaultProgressBarHeight;
         if (!GraphicColourFg) GraphicColourFg = GiftColour (DefaultProgressBarColour);
         if (Percentage >= ProgressBarAlertPercentage)
            GraphicColourFg = GiftColour (DefaultProgressBarAlertColour);

         if (GraphicPtr == NULL) GiftInitGraphic ();
         GiftBackground (GraphicColourWhite);
         GiftDrawBar (GraphicColourDWhite, 100);
         GiftDrawBar (GraphicColourFg, Percentage);
         GiftDrawBorder (GraphicColourBorder, GraphicWidthBorder);

         continue;
      }

      if (toupper(cptr[0]) == 'B' &&
          toupper(cptr[1]) == 'U' &&
          toupper(cptr[2]) == 'T' &&
          (cptr[3] == '=' || cptr[3] == ':'))
      {
         /*****************/
         /* draw a button */
         /*****************/

         char  Text [256];

         if (Debug) fprintf (stdout, "draw button\n");
         sptr = (cptr += 4);
         while (*cptr && *cptr != '&' && *cptr != ';' && *cptr != ',') cptr++;
         if (*cptr) *cptr++ = '\0';
         GiftCopyTextFromUrl (sptr, Text, sizeof(Text));

         if (!GraphicColourFg) GraphicColourFg = GiftColour (DefaultButtonColour);
         if (!GraphicFontPoints) GraphicFontPoints = DefaultButtonFontPoints;

         GiftDrawButton (GraphicColourFg, Text, &Font[GraphicFontPoints]);

         continue;
      }

      if (toupper(cptr[0]) == 'F' &&
          toupper(cptr[1]) == 'G' &&
          toupper(cptr[2]) == 'C' &&
          (cptr[3] == '=' || cptr[3] == ':'))
      {
         /*********************/
         /* foreground colour */
         /*********************/

         if (Debug) fprintf (stdout, "foreground colour\n");
         sptr = (cptr += 4);
         while (*cptr && *cptr != '&' && *cptr != ';') cptr++;
         if (*cptr) *cptr++ = '\0';
         GraphicColourFg = GiftColour (sptr);
         continue;
      }

      if (toupper(cptr[0]) == 'F' &&
          toupper(cptr[1]) == 'G' &&
          toupper(cptr[2]) == 'W' &&
          (cptr[3] == '=' || cptr[3] == ':'))
      {
         /********************/
         /* foreground width */
         /********************/

         if (Debug) fprintf (stdout, "foreground width\n");
         sptr = (cptr += 4);
         while (*cptr && *cptr != '&' && *cptr != ';') cptr++;
         if (*cptr) *cptr++ = '\0';
         GraphicWidthFg = atol(sptr);
         if (GraphicWidthFg < 0 || GraphicWidthFg >= GraphicWidth ||
             GraphicWidthFg >= GraphicHeight)
            exit (GiftParseError ("Foreground width", sptr-String, LineNumber));
         continue;
      }

      if (toupper(cptr[0]) == 'B' &&
          toupper(cptr[1]) == 'G' &&
          toupper(cptr[2]) == 'C' &&
          (cptr[3] == '=' || cptr[3] == ':'))
      {
         /*********************/
         /* background colour */
         /*********************/

         if (Debug) fprintf (stdout, "background colour\n");
         sptr = (cptr += 4);
         while (*cptr && *cptr != '&' && *cptr != ';') cptr++;
         if (*cptr) *cptr++ = '\0';
         GraphicColourBg = GiftColour (sptr);
         continue;
      }

      if (toupper(cptr[0]) == 'T' &&
          toupper(cptr[1]) == 'R' &&
          toupper(cptr[2]) == 'C' &&
          (cptr[3] == '=' || cptr[3] == ':'))
      {
         /***********************/
         /* transparency colour */
         /***********************/

         if (Debug) fprintf (stdout, "transparency colour\n");
         sptr = (cptr += 4);
         while (*cptr && *cptr != '&' && *cptr != ';') cptr++;
         if (*cptr) *cptr++ = '\0';
         GraphicTransparencyColour = GiftColour (sptr);
         continue;
      }

      if (toupper(cptr[0]) == 'B' &&
          toupper(cptr[1]) == 'D' &&
          toupper(cptr[2]) == 'C' &&
          (cptr[3] == '=' || cptr[3] == ':'))
      {
         /*****************/
         /* border colour */
         /*****************/

         if (Debug) fprintf (stdout, "border colour\n");
         sptr = (cptr += 4);
         while (*cptr && *cptr != '&' && *cptr != ';') cptr++;
         if (*cptr) *cptr++ = '\0';
         GraphicColourBorder = GiftColour (sptr);
         continue;
      }

      if (toupper(cptr[0]) == 'B' &&
          toupper(cptr[1]) == 'D' &&
          toupper(cptr[2]) == 'W' &&
          (cptr[3] == '=' || cptr[3] == ':'))
      {
         /****************/
         /* border width */
         /****************/

         if (Debug) fprintf (stdout, "border width\n");
         sptr = (cptr += 4);
         while (*cptr && *cptr != '&' && *cptr != ';') cptr++;
         if (*cptr) *cptr++ = '\0';
         GraphicWidthBorder = atol(sptr);
         /* possibly need to initialize the GIF to set height and width */
         if (GraphicPtr == NULL) GiftInitGraphic ();
         if (GraphicWidthBorder < 0 || GraphicWidthBorder >= GraphicWidth ||
             GraphicWidthBorder >= GraphicHeight)
            exit (GiftParseError ("Border width", sptr-String, LineNumber));
         GiftDrawBorder (GraphicColourBorder, GraphicWidthBorder);
         continue;
      }

      if (toupper(cptr[0]) == 'G' &&
          toupper(cptr[1]) == 'R' &&
          toupper(cptr[2]) == 'W' &&
          (cptr[3] == '=' || cptr[3] == ':'))
      {
         /*****************/
         /* graphic width */
         /*****************/

         if (Debug) fprintf (stdout, "graphic width\n");
         if (GraphicPtr != NULL)
            exit (GiftParseError ("Multiple graphic width",
                                   sptr-String, LineNumber));
         sptr = (cptr += 4);
         while (*cptr && *cptr != '&' && *cptr != ';') cptr++;
         if (*cptr) *cptr++ = '\0';
         GraphicWidth = atol(sptr);
         if (GraphicWidth <= 0 || GraphicWidth >= MaxGraphicWidth)
            exit (GiftParseError ("Graphic width", sptr-String, LineNumber));
         continue;
      }

      if (toupper(cptr[0]) == 'G' &&
          toupper(cptr[1]) == 'R' &&
          toupper(cptr[2]) == 'H' &&
          (cptr[3] == '=' || cptr[3] == ':'))
      {
         /******************/
         /* graphic height */
         /******************/

         if (Debug) fprintf (stdout, "graphic height\n");
         if (GraphicPtr != NULL)
            exit (GiftParseError ("Multiple graphic height",
                                  sptr-String, LineNumber));
         sptr = (cptr += 4);
         while (*cptr && *cptr != '&' && *cptr != ';') cptr++;
         if (*cptr) *cptr++ = '\0';
         GraphicHeight = atol(sptr);
         if (GraphicHeight <= 0 || GraphicHeight >= MaxGraphicHeight)
            exit (GiftParseError ("Graphic height", sptr-String, LineNumber));
         continue;
      }

      if (toupper(cptr[0]) == 'C' &&
          toupper(cptr[1]) == 'O' &&
          toupper(cptr[2]) == 'L' &&
          (cptr[3] == '=' || cptr[3] == ':'))
      {
         /*******************/
         /* redefine colour */
         /*******************/

         static char  ErrorRedefine [] = "Redefine colour error.";
         int  Index, Red, Green, Blue;

         if (Debug) fprintf (stdout, "redefine colour\n");
         sptr = (cptr += 4);
         while (*cptr && *cptr != '&' && *cptr != ';' && *cptr != ',') cptr++;
         if (*cptr != ',')
            exit (GiftParseError ("Syntax", sptr-String, LineNumber));
         *cptr++ = '\0';
         Index = GiftColour (sptr);
         Red = Green = Blue = -1;

         sptr = cptr;
         while (*cptr && *cptr != '&' && *cptr != ';' && *cptr != ',') cptr++;
         if (*cptr != ',')
            exit (GiftParseError ("Syntax", sptr-String, LineNumber));
         *cptr++ = '\0';
         if (!isdigit(*sptr))
            exit (GiftParseError ("Syntax", sptr-String, LineNumber));
         Red = atol(sptr);

         sptr = cptr;
         while (*cptr && *cptr != '&' && *cptr != ';' && *cptr != ',') cptr++;
         if (*cptr != ',')
            exit (GiftParseError ("Syntax", sptr-String, LineNumber));
         *cptr++ = '\0';
         if (!isdigit(*sptr))
            exit (GiftParseError ("Syntax", sptr-String, LineNumber));
         Green = atol(sptr);

         sptr = cptr;
         while (*cptr && *cptr != '&' && *cptr != ';' && *cptr != ',') cptr++;
         if (*cptr && *cptr == ',')
            exit (GiftParseError ("Syntax", sptr-String, LineNumber));
         if (*cptr) *cptr++ = '\0';
         if (!isdigit(*sptr))
            exit (GiftParseError ("Syntax", sptr-String, LineNumber));
         Blue = atol(sptr);

         if (Red < 0 || Red > 255 ||
             Green < 0 || Green > 255 ||
             Blue < 0 || Blue > 255)
            exit (GiftParseError ("Colour specification",
                                  sptr-String, LineNumber));
         GraphicRgbRed[Index] = Red;
         GraphicRgbGreen[Index] = Green;
         GraphicRgbBlue[Index] = Blue;

         continue;
      }

      if (toupper(cptr[0]) == 'F' &&
          toupper(cptr[1]) == 'N' &&
          toupper(cptr[2]) == 'T' &&
          (cptr[3] == '=' || cptr[3] == ':'))
      {
         /***************/
         /* font points */
         /***************/

         if (Debug) fprintf (stdout, "font points\n");
         sptr = (cptr += 4);
         while (*cptr && *cptr != '&' && *cptr != ';') cptr++;
         if (*cptr) *cptr++ = '\0';
         if (!isdigit(*sptr))
            exit (GiftParseError ("Syntax", sptr-String, LineNumber));
         GraphicFontPoints = atol(sptr);
         GiftCheckFont (GraphicFontPoints);
         continue;
      }

      /*********************/
      /* unknown parameter */
      /*********************/

      sptr = cptr;
      while (*cptr && *cptr != '&' && *cptr != ';') cptr++;
      *cptr = '\0';
      exit (GiftParseError ("Unknown parameter", sptr-String, LineNumber));
   }
}

/*****************************************************************************/
/*
Copy text from one string to another, converting characters forbidden to 
appear as plain-text in an HTTP URL back into plain-text.  For example the 
'?', '+', '&', etc., are converted from "%nn" hexadecimal escaped characters. 
*/ 

int GiftCopyTextFromUrl
(
register char *sptr,
register char *DestPtr,
register int SizeOfDest
)
{
   register char  c;
   register char  *dptr, *zptr;

   if (!SizeOfDest) return (0);
   zptr = (dptr = DestPtr) + SizeOfDest;

   while (*sptr && dptr < zptr)
   {
      if (*sptr == '%')
      {
         /* an escaped character ("%xx" where xx is a hexadecimal number) */
         sptr++;
         if (*sptr) sptr++;
         if (*sptr) sptr++;
         c = *sptr;
         *sptr = '\0';
         *dptr++ = (unsigned char) strtol (sptr-2, NULL, 16);
         *sptr = c;
      }
      else
         *dptr++ = *sptr++;
   }
   if (sptr >= zptr) dptr = DestPtr;
   *dptr = '\0';

   return (dptr - DestPtr);
}

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

GiftParseError
(
char *Text,
int CharacterNumber,
int LineNumber
)
{
   char  String [256];

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

   if (Debug)
      fprintf (stdout, "GiftParseError() |%s| %d %d\n",
               Text, CharacterNumber, LineNumber);

   if (LineNumber)
      sprintf (String, "%s problem at character %d of line %d.",
               Text, CharacterNumber, LineNumber);
   else
      sprintf (String, "%s problem at character %d.",
               Text, CharacterNumber);

   exit (GiftGraphicErrorReport (String));
}

/*****************************************************************************/
/*
Return a bitmap image to the client as a GIF image.

The code in these functions is implemented in accordance with Compuserve's 
Graphic Interchange Format Programming Reference specification, version 89a, 
31st July 1990. 

The LZW compression employed by the GIF algorithm is implemented using code 
derived from the PBM suite.  Two functions, virtually unmodified, are 
employed, GiftCompress() ... formally called 'compgif()', and GiftPackBits()
...  formally called 'pack_bits()'.  The original commentary and copyright
notice  remains as per the code author's request.
*/ 

GiftImage ()

{
   static char  Http200NoCacheGifHeader [] = 
"HTTP/1.0 200 Success\r\n\
Content-Type: image/gif\r\n\
Expires: Fri, 13 Jan 1978 15:00:00 GMT\r\n\
Pragma: no-cache\r\n\
\r\n";

   static char  Http200GifHeader [] = 
"HTTP/1.0 200 Success\r\n\
Content-Type: image/gif\r\n\
Expires: Fri, 13 Jan 1978 15:00:00 GMT\r\n\
\r\n";

   register unsigned char  *bptr;
   register int  idx,
                 Byte;

   int LeftOffset,
       TopOffset,
       Resolution,
       ColorMapSize,
       InitCodeSize;

   unsigned char Buffer [256];

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

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

   /* get the descriptor associated with this file (used for GIF output) */
   GraphicHttpOutFd = fileno (HttpOut);

   ColorMapSize = 1 << GraphicBitsPerPixel;
   LeftOffset = TopOffset = 0;
   Resolution = GraphicBitsPerPixel;

   /* the initial code size */
   if (GraphicBitsPerPixel <= 1)
      InitCodeSize = 2;
   else
      InitCodeSize = GraphicBitsPerPixel;

   /* for convenience accumulate bytes into this buffer before output */
   bptr = Buffer;

   /***************/
   /* HTTP header */
   /***************/

   if (GraphicNoCacheGif)
   {
      memcpy (bptr, Http200NoCacheGifHeader,
              sizeof(Http200NoCacheGifHeader)-1);
      bptr += sizeof(Http200NoCacheGifHeader)-1;
   }
   else
   {
      memcpy (bptr, Http200GifHeader, sizeof(Http200GifHeader)-1);
      bptr += sizeof(Http200GifHeader)-1;
   }

   /**************************/
   /* GIF Data Stream header */
   /**************************/

   strcpy (bptr, "GIF89a");
   bptr += 6;

   /*****************************/
   /* Logical Screen Descriptor */
   /*****************************/

   /* width and height of logical screen */
   *bptr++ = GraphicWidth & 0xff;
   *bptr++ = (GraphicWidth >> 8) & 0xff;
   *bptr++ = GraphicHeight & 0xff;
   *bptr++ = (GraphicHeight >> 8) & 0xff;

   /* indicate that there is a global colour map */
   Byte = 0x80;
   /* OR in the resolution */
   Byte |= (Resolution - 1) << 5;
   /* OR in the Bits per Pixel */
   Byte |= (GraphicBitsPerPixel - 1);
   /* write it out */
   *bptr++ = Byte;

   /* background colour */
   *bptr++ = GraphicBackground;

   /* pixel aspect ratio */
   *bptr++ = 0;

   /***********************/
   /* Global Colour Table */
   /***********************/

   for (idx = 0; idx < ColorMapSize; idx++)
   {
      *bptr++ = GraphicRgbRed[idx];
      *bptr++ = GraphicRgbGreen[idx];
      *bptr++ = GraphicRgbBlue[idx];
   }

   /****************************************/
   /* Graphic Control Extension descriptor */
   /****************************************/

   if (GraphicTransparencyColour >= 0)
   {
      /* extension introducer and graphic control label */
      *bptr++ = 0x21;
      *bptr++ = 0xf9;
      /* fixed size of the following data block */
      *bptr++ = 0x04;
      /* Transparency Index is provided */
      *bptr++ = 0x01;
      /* no data in these */
      *bptr++ = 0x00;
      *bptr++ = 0x00;
      /* Transparent Color Index value, background SHOULD BE TRANSPARENT */
      *bptr++ = GraphicTransparencyColour;
      /* block terminator */
      *bptr++ = 0x00;
   }

   /********************/
   /* Image descriptor */
   /********************/

   /* write an Image separator */
   *bptr++ = 0x2c;

   /* location of image within logical screen */
   *bptr++ = LeftOffset & 0xff;
   *bptr++ = (LeftOffset >> 8) & 0xff;
   *bptr++ = TopOffset & 0xff;
   *bptr++ = (TopOffset >> 8) & 0xff;

   /* width and height of image within logical screen */
   *bptr++ = GraphicWidth & 0xff;
   *bptr++ = (GraphicWidth >> 8) & 0xff;
   *bptr++ = GraphicHeight & 0xff;
   *bptr++ = (GraphicHeight >> 8) & 0xff;

   /* no local color table, image is not interlaced, not ordered, etc. */
   *bptr++ = 0x00;

   /**************************/
   /* table-based image data */
   /**************************/

   /* write out the initial code size */
   *bptr++ = InitCodeSize;

   /* transfer what we've accumlated in the local buffer */
   write (GraphicHttpOutFd, Buffer, bptr-Buffer);

   /* LZW compress the data using PBM-derived algorithm and code */
   GiftCompress (InitCodeSize + 1);

   /****************************************/
   /* end of image data and GIF terminator */
   /****************************************/

   /* write out a zero-length packet (to end the series), and terminator */
   write (GraphicHttpOutFd, "\0;", 2);
}

/*****************************************************************************/
/*
Get a series of pixels from the bitmap.  It is done in such a way that the
origin (0,0) is in the bottom, left hand corner, although the memory origin is
at byte zero.
*/ 

int GiftNextPixel ()

{
   static int  CountX = 0,
               CountY = -1;
   static unsigned char  *PixelPtr;

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

   if (!CountX--)
   {
      if (CountY < 0) CountY = GraphicHeight;
      if (!CountY--) return (EOF);
      CountX = GraphicWidth - 1;
      PixelPtr = GraphicPtr + (CountY * GraphicWidth);
   }

   return (*PixelPtr++);
}

/****************************************************************************/
/*
 * This software is copyrighted as noted below.  It may be freely copied,
 * modified, and redistributed, provided that the copyright notice is 
 * preserved on all copies.
 * 
 * There is no warranty or other guarantee of fitness for this software,
 * it is provided solely "as is".  Bug reports or fixes may be sent
 * to the author, who may or may not act on them as he desires.
 *
 * You may not include this software in a program or other software product
 * without supplying the source, or without informing the end-user that the 
 * source is available for no extra charge.
 *
 * If you modify this software, you should include a notice giving the
 * name of the person performing the modification, the date of modification,
 * and the reason for such modification.
 */

/*  compgif.c */
/*
 *
 * GIF Image compression - LZW algorithm implemented with Trie type
 *                         structure.
 *                         Written by Bailey Brown, Jr.
 *                         last change May 24, 1990
 *                         file: compgif.c
 *
 *  You may use or modify this code as you wish, as long as you mention
 *  my name in your documentation.
 *
 *                  - Bailey Brown, Jr.
 *
 */

#define MAXIMUMCODE 4095   /* 2**maximum_code_size */
#define BLOCKSIZE 256   /* max block byte count + 1 */
#define NULLPREFIX -1

typedef struct str_table_entry {
        int code;
        int prefix;
        int suffix;
}  strTableEntry;

typedef struct str_table_node {
        strTableEntry entry;
    struct str_table_node *left;
    struct str_table_node *right;
struct str_table_node *children;
} strTableNode, *strTableNodePtr, **strTable;

/*
 ********************************************************************
 * compgif() recieves pointers to an input function and an output    *
 * stream, and the code size as parameters and outputs successive   *
 * blocks of LZW compressed gif data.  The calling routine should   *
 * have aready written the GIF file header out to the output file.  *
 * It assumes that there will be no more than 8 bits/pixel and that *
 * each data item comes from successive bytes returned by infun.    *
 ********************************************************************
 */

int GiftCompress (int code_size)

{
    strTable heap; /* our very own memory manager */
    int heap_index;
    int clear_code, end_code, cur_code;
    int i, found, num_colors, prefix, compress_size;
    int cur_char, end_of_data, bits_per_pix;
    strTableNodePtr cur_node;
    strTable root;  /* root of string table for LZW compression is */
                    /* an array of 2**bits_per_pix pointers to atomic nodes */
    heap_index = 0;
    heap = (strTable)malloc(sizeof(strTableNodePtr)*MAXIMUMCODE);
    if (heap == NULL) printf("can't allocate heap");
    for (i=0; i < MAXIMUMCODE; i++) {
        heap[i] = (strTableNodePtr)malloc(sizeof(strTableNode));
        if (heap[i] == NULL)
        {
           printf("can't allocate heap");
           exit (1);
        }
    }
    bits_per_pix = code_size - 1;
    compress_size = code_size;
    num_colors = 1<<(bits_per_pix);
    clear_code = num_colors;
    end_code = clear_code + 1;
    cur_code = end_code + 1;
    prefix = NULLPREFIX;
    root = (strTable)malloc(sizeof(strTableNodePtr)*num_colors);
    if (!root)
    {
       printf("memory allocation failure (root)");
       exit (1);
    }
    for(i=0; i<num_colors; i++) {
        root[i] = heap[heap_index++];
        root[i]->entry.code = i;
        root[i]->entry.prefix = NULLPREFIX;
        root[i]->entry.suffix = i;
        root[i]->left = NULL;
        root[i]->right = NULL;
        root[i]->children = NULL;
    }
    /* initialize  output block */
    GiftPackBits(compress_size, -1);
    GiftPackBits(compress_size, clear_code);
    end_of_data = 0;
    if ((cur_char = GiftNextPixel()) == EOF)
       printf("premature end of data");
    while (!end_of_data) {
        prefix = cur_char;
        cur_node = root[prefix];
        found = 1;
        if((cur_char = GiftNextPixel()) == EOF) {
            end_of_data = 1; break;
        }
        while(cur_node->children && found) {
            cur_node = cur_node->children;
            while(cur_node->entry.suffix != cur_char) {
                if (cur_char < cur_node->entry.suffix) {
                    if (cur_node->left) cur_node = cur_node->left;
                    else {
                        cur_node->left = heap[heap_index++];
                        cur_node = cur_node->left;
                        found = 0; break;
                    }
                }
                else {
                    if (cur_node->right) cur_node = cur_node->right;
                    else {
                        cur_node->right = heap[heap_index++];
                        cur_node = cur_node->right;
                        found = 0; break;
                    }
                }
            }
            if (found) {
                prefix = cur_node->entry.code;
                if((cur_char = GiftNextPixel()) == EOF) {
                    end_of_data = 1; break;
                }
            }
        }
        if (end_of_data) break;
        if (found) {
            cur_node->children = heap[heap_index++];
            cur_node = cur_node->children;
        }
        cur_node->children = NULL;
        cur_node->left = NULL;
        cur_node->right = NULL;
        cur_node->entry.code = cur_code;
        cur_node->entry.prefix = prefix;
        cur_node->entry.suffix = cur_char;
        GiftPackBits(compress_size, prefix);
        if (cur_code > ((1<<(compress_size))-1))
            compress_size++;
        if (cur_code < MAXIMUMCODE) {
            cur_code++;
        }
        else {
            heap_index = num_colors;  /* reinitialize string table */
            for (i=0; i < num_colors; i++ ) root[i]->children = NULL;
            GiftPackBits(compress_size, clear_code);
            compress_size = bits_per_pix + 1;
            cur_code = end_code + 1;
        }
    }
    GiftPackBits(compress_size, prefix);
    GiftPackBits(compress_size, end_code);
    GiftPackBits(compress_size, -1);
    for (i=0; i < MAXIMUMCODE; i++) free(heap[i]);
    free(heap);
    free(root);
    return (1);
}

/*
 ************************************************************************
 * GiftPackBits() packs the bits of the codes generated by gifenc() into   *
 * a 1..256 byte output block.  The first byte of the block is the      *
 * number 0..255 of data bytes in the block.  To flush or initialize    *
 * the block, pass a negative argument.                                 *
 ************************************************************************
 */

int GiftPackBits (int compress_size, int prefix)

{
    static int cur_bit = 8;
    static unsigned char block[BLOCKSIZE] = { 0 };
    int i, left_over_bits;

    /* if we are about to excede the bounds of block or if the flush
       code (code_bis < 0) we output the block */
    if((cur_bit + compress_size > (BLOCKSIZE-1)*8) || (prefix < 0)) {
        /* handle case of data overlapping blocks */
        if ((left_over_bits = (((cur_bit>>3) +
                ((cur_bit & 7) != 0))<<3) - cur_bit) != 0) {
            for (i=0; i < left_over_bits; i++) {
                if (prefix & (1<<i))
                   block[cur_bit>>3] |= (char)(1<<(cur_bit & 7));
                /* note n>>3 == n/8 and n & 7 == n % 8 */
                cur_bit++;
            }
        }
        compress_size -= left_over_bits;
        prefix = prefix>>left_over_bits;
        block[0] =  (unsigned char)((cur_bit>>3) - 1);

        if (block[0]) write (GraphicHttpOutFd, block, block[0]+1);

        for(i=0; i < BLOCKSIZE; i++) block[i] = 0;
        cur_bit = 8;
    }
    if (prefix >= 0) {
        for (i=0; i < compress_size; i++) {
           if (prefix & (1<<i))
           block[cur_bit>>3] |= (unsigned char)(1<<(cur_bit & 7));
           /* note n>>3 == n/8 and n & 7 == n % 8 */
           cur_bit++;
        }
    }
    return (1);
}

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