s) etc. It looks like a
single line drawing functionality. These are ignored for HTML.
*/
ProcessGraphicLine ()
{
long DataLength;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "ProcessGraphicLine()\n");
DataLength = *(unsigned char*)(ChunkPtr+1);
if (DebugGraphics)
{
fprintf (stdout,"DataLength: %08.08X (%d)\n", DataLength, DataLength);
DumpLongWords ("GRAPHIC LINE:", ChunkPtr+2, (DataLength-2)/4);
}
ChunkPtr += DataLength;
}
/*****************************************************************************/
/*
These seem to do things like place a block of shading (stipple) over a section
of text. It must be some sort of graphical "block" functionality. The data
structure appears similar to figures (images). These are ignored for HTML.
*/
ProcessGraphicOverlay ()
{
long DataLength;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "ProcessGraphicOverlay()\n");
DataLength = *(long*)(ChunkPtr+2);
if (DebugGraphics)
{
fprintf (stdout,"DataLength: %08.08X (%d)\n", DataLength, DataLength);
DumpLongWords ("GRAPHIC OVERLAY:", ChunkPtr+2, (DataLength-2)/4);
}
ChunkPtr += DataLength;
}
/*****************************************************************************/
/*
Create a hotspot by placing HTML link(s) around text in the specified
location(s). Handles single and two-line-spanning hotspots.
*/
ProcessHotSpot
(
unsigned long H_Origin,
unsigned long V_Origin
)
{
unsigned char *chptr;
unsigned long DataLength,
Horizontal,
Vertical,
Length,
Width,
HotChunkNumber,
GraphicCount,
SpanningDataLength;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "ProcessHotSpot()\n");
DataLength = *(unsigned long*)(ChunkPtr+2);
HotChunkNumber = *(unsigned long*)(ChunkPtr+34);
SpanningDataLength = *(unsigned long*)(ChunkPtr+38);
if (DebugHotspots)
{
DumpLongWords ("HOTSPOT:", ChunkPtr+2, 10);
fprintf (stdout,"DataLength: %d\n", DataLength);
fprintf (stdout,"HotChunkNumber: %d\n", HotChunkNumber);
fprintf (stdout,"SpanningDataLength: %d\n", SpanningDataLength);
if (SpanningDataLength)
DumpLongWords ("SPANNING HOTSPOT:", ChunkPtr+42,
SpanningDataLength / sizeof(long));
}
if (SpanningDataLength)
{
/*
"Spanning hotspots" are generated when a hotspot spans more
than one line. Bookreader seems to draw a series of graphic
lines starting with the top text line, bottom left, up and
around and then down to the next line, and around that text.
Emulate this by drawing two hotspots.
If the hotspot spans three lines? Tough!
*/
chptr = ChunkPtr + 42;
GraphicCount = *(unsigned long*)chptr;
if (DebugHotspots) fprintf (stdout,"GraphicCount: %d\n", GraphicCount);
chptr += 4;
Horizontal = *(unsigned long*)(chptr+8);
Vertical = *(unsigned long*)(chptr+12);
Length = *(unsigned long*)(chptr+16) - Horizontal;
Width = *(unsigned long*)(chptr+4) - Vertical;
PlaceHotSpot (H_Origin, V_Origin, Horizontal, Vertical,
Length, Width, HotChunkNumber);
chptr += 32;
Horizontal = *(unsigned long*)(chptr+16);
Vertical = *(unsigned long*)(chptr+28);
Length = *(unsigned long*)chptr - Horizontal;
Width = *(unsigned long*)(chptr+20) - Vertical;
PlaceHotSpot (H_Origin, V_Origin, Horizontal, Vertical,
Length, Width, HotChunkNumber);
}
else
{
/* single line hotspot */
Horizontal = *(unsigned long*)(ChunkPtr+18);
Vertical = *(unsigned long*)(ChunkPtr+22);
Length = *(unsigned long*)(ChunkPtr+26);
Width = *(unsigned long*)(ChunkPtr+30);
PlaceHotSpot (H_Origin, V_Origin, Horizontal, Vertical,
Length, Width, HotChunkNumber);
}
ChunkPtr += DataLength;
}
/*****************************************************************************/
/*
Create a hotspot by placing the start of an HTML link at the line and column
represented by horizontal/vertical, and then placing the end of the link at
horizontal+length/vertical+width.
*/
PlaceHotSpot
(
unsigned long H_Origin,
unsigned long V_Origin,
unsigned long Horizontal,
unsigned long Vertical,
unsigned long Length,
unsigned long Width,
unsigned long HotChunkNumber
)
{
int ccnt,
StringLength,
Line,
Column;
char String [1024];
struct OutCharStruct *ocptr;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "PlaceHotSpot()\n");
if (DebugHotspots)
{
fprintf (stdout,"H_Origin: %d\n", H_Origin);
fprintf (stdout,"V_Origin: %d\n", V_Origin);
fprintf (stdout,"Horizontal: %d\n", Horizontal);
fprintf (stdout,"Vertical: %d\n", Vertical);
fprintf (stdout,"Length: %d\n", Length);
fprintf (stdout,"Width: %d\n", Width);
fprintf (stdout,"HotChunkNumber: %d\n", HotChunkNumber);
}
/*
Attempt to place this on the line containing the text to be
"hotspot"ed. 'Vert' is the vertical location, 'Width' must
be the the width of the hotspot area. Three-quarters down
the 'Width' value is used to calculate the line position.
BTW; seems to work well ... most of the time!
Use bit-wise shifting to enhance the speed of the divisions:
excerpt equals: "(((Vert + (Width / 2) + (Width / 4))"
*/
Line = (V_Origin + Vertical + (Width >> 1) + (Width >> 2))
/ LINE_POSITIONING_UNITS;
if (Line > LinesOnPage) PageSize (Line);
if (Line > LastLineOnPage) LastLineOnPage = Line;
/*
If this is a totally blank line then its probably an artifact of the
mapping process. Attempt a correction by stepping to the next line.
*/
for (;;)
{
ocptr = OUT_CHAR_AT(Line,0);
for (ccnt = 0; ccnt < MAX_OUTLINE_LENGTH; ccnt++)
{
if (ocptr->c != ' ') break;
ocptr++;
}
if (ccnt < MAX_OUTLINE_LENGTH || Line >= LinesOnPage) break;
Line++;
}
/*
Calculate the hotspot start column. If it lands on a space then
scan forward to the first non-space character encountered.
*/
Column = (H_Origin + Horizontal) / COLUMN_POSITIONING_UNITS;
if (Column >= MAX_OUTLINE_LENGTH) Column = MAX_OUTLINE_LENGTH-1;
ocptr = OUT_CHAR_AT(Line,Column);
for (ccnt = Column; ccnt < MAX_OUTLINE_LENGTH; ccnt++)
{
if (ocptr->c != ' ') break;
ocptr++;
}
if (ccnt < MAX_OUTLINE_LENGTH) Column = ccnt;
/* place the start of hotspot into the text */
StringLength = sprintf (String,
"",
CgiScriptNamePtr, UrlEncodedCgiPathInfoPtr, BookSpecifiedByFile,
(int)SectionChunkNumbersArrayPtr[HotChunkNumber],
UrlEncReferer, UrlEncTitle, UrlDoMassage);
ocptr = OUT_CHAR_AT(Line,Column);
ocptr->htptr = HtmlString (ocptr->htptr, StringLength);
strcat (ocptr->htptr, String);
/*
Calculate the hotspot end column. If it lands on a space then
scan backward to the first non-space character encountered.
*/
if (Column + Length / COLUMN_POSITIONING_UNITS <= Column)
{
Column++;
if (Column >= MAX_OUTLINE_LENGTH) Column = MAX_OUTLINE_LENGTH-1;
}
else
{
Column += (Length / COLUMN_POSITIONING_UNITS) - 1;
if (Column >= MAX_OUTLINE_LENGTH) Column = MAX_OUTLINE_LENGTH-1;
ocptr = OUT_CHAR_AT(Line,Column);
for (ccnt = Column; ccnt > 0; ccnt--)
{
if (ocptr->c != ' ') break;
ocptr--;
}
if (ccnt > 0) Column = ccnt + 1;
if (Column >= MAX_OUTLINE_LENGTH) Column = MAX_OUTLINE_LENGTH-1;
}
/* place the end of hotspot into the text */
ocptr = OUT_CHAR_AT(Line,Column);
ocptr->htptr = HtmlString (ocptr->htptr, 4);
strcat (ocptr->htptr, "");
}
/*****************************************************************************/
/*
Place a "string" of text characters into the "page". With Bookreader
documents a single "string" of characters NEVER spans multiple lines. The
"string" is pointed to by 'ChunkPtr'.
*/
int PlaceText
(
int Line,
int Column
)
{
static int CallCount = 0;
int idx, count,
FontNumber;
unsigned char TextType;
unsigned char Length;
unsigned short Horizontal;
unsigned short Vertical;
unsigned short Unknown;
unsigned char *Text;
struct OutCharStruct *ocptr;
/*********/
/* begin */
/*********/
if (Line > LinesOnPage) PageSize (Line);
if (Line > LastLineOnPage) LastLineOnPage = Line;
if (Column >= MAX_OUTLINE_LENGTH) Column = MAX_OUTLINE_LENGTH-1;
TextType = *ChunkPtr;
Length = *(ChunkPtr+1);
FontNumber = *(ChunkPtr+6);
Unknown = *(unsigned short*)(ChunkPtr+7);
if (DebugText)
{
fprintf (stdout,
"\nPlaceText()\n\
Len: %d ?: %d Line: %d Column: %d Font: %d Bld: %d It: %d Sym: %d\n",
Length, Unknown, Line, Column,
FontNumber,
FontArrayPtr[FontNumber].FontBold,
FontArrayPtr[FontNumber].FontItalic,
FontArrayPtr[FontNumber].FontUnsupported);
}
/********************/
/* text enhancement */
/********************/
if (EnhanceText)
{
if (FontArrayPtr[FontNumber].FontBold)
{
ocptr = OUT_CHAR_AT(Line,Column);
ocptr->htptr = HtmlString (ocptr->htptr, 3);
strcat (ocptr->htptr, "");
}
else
if (FontArrayPtr[FontNumber].FontItalic)
{
ocptr = OUT_CHAR_AT(Line,Column);
ocptr->htptr = HtmlString (ocptr->htptr, 3);
strcat (ocptr->htptr, "");
}
}
/*********************************************/
/* put the characters into the output buffer */
/*********************************************/
Text = ChunkPtr+9;
idx = 0;
while (idx < Length-8)
{
count = (int)Text[idx++];
while (count-- > 0)
{
if (Column < MAX_OUTLINE_LENGTH)
{
if (FontArrayPtr[FontNumber].FontUnsupported ||
Text[idx] < 0x20 ||
(Text[idx] > 0x7f && Text[idx] < 0xa0))
OUT_CHAR_AT(Line,Column)->c = NON_SUPPORTED_CHAR;
else
OUT_CHAR_AT(Line,Column)->c = Text[idx];
if (DebugText)
{
/*
fprintf (stdout, "%c", OUT_CHAR_AT(Line,Column)->c);
*/
fprintf (stdout, "%c(%02.02x)[%d] ",
OUT_CHAR_AT(Line,Column)->c,
OUT_CHAR_AT(Line,Column)->c,
Column);
}
}
Column++;
idx++;
}
if (idx < Length-9)
Column += (Text[idx] / COLUMN_POSITIONING_UNITS) + 1;
idx++;
}
if (DebugText) fputc ('\n', stdout);
if (Column >= MAX_OUTLINE_LENGTH) Column = MAX_OUTLINE_LENGTH-1;
/************************/
/* end text enhancement */
/************************/
if (EnhanceText)
{
if (FontArrayPtr[FontNumber].FontBold)
{
ocptr = OUT_CHAR_AT(Line,Column);
ocptr->htptr = HtmlString (ocptr->htptr, 4);
strcat (ocptr->htptr, "");
}
else
if (FontArrayPtr[FontNumber].FontItalic)
{
ocptr = OUT_CHAR_AT(Line,Column);
ocptr->htptr = HtmlString (ocptr->htptr, 4);
strcat (ocptr->htptr, "");
}
}
ChunkPtr += Length;
return (Column);
}
/*****************************************************************************/
/*
Put a link into the text to retrieve the image.
*/
PlaceImage
(
unsigned long H_Origin,
unsigned long V_Origin
)
{
int ccnt,
Length,
Line,
Column;
long DataLength,
Horizontal,
Offset,
Vertical;
char String [2048];
struct OutCharStruct *ocptr;
/*********/
/* begin */
/*********/
if (Debug)
fprintf (stdout, "PlaceImage() H_Origin: %d V_Origin: %d\n",
H_Origin, V_Origin);
DataLength = *(long*)(ChunkPtr+2);
Horizontal = *(long*)(ChunkPtr+18);
Vertical = *(long*)(ChunkPtr+22);
if (Debug)
{
DumpLongWords ("FIGURE:", ChunkPtr+2, 12);
fprintf (stdout,"DataLength : %d\n", DataLength);
fprintf (stdout,"Horizontal : %d\n", Horizontal);
fprintf (stdout,"Vertical : %d\n", Vertical);
}
Line = (V_Origin + Vertical) / LINE_POSITIONING_UNITS;
if (!Line) Line = 1;
if (Line > LinesOnPage) PageSize (Line);
if (Line > LastLineOnPage) LastLineOnPage = Line;
/*
Without the ability to flow text around an image (which current
browsers lack) and we're on a line containing text attempt to step
back to a blank line.
*/
for (;;)
{
ocptr = OUT_CHAR_AT(Line,0);
for (ccnt = 0; ccnt < MAX_OUTLINE_LENGTH; ccnt++)
{
if (ocptr->c != ' ') break;
ocptr++;
}
if (ccnt >= MAX_OUTLINE_LENGTH || Line <= 1) break;
Line--;
}
Column = (H_Origin + Horizontal) / COLUMN_POSITIONING_UNITS;
if (Column >= MAX_OUTLINE_LENGTH) Column = MAX_OUTLINE_LENGTH-1;
if (DebugFigures) fprintf (stdout,"Line: %d Column: %d\n", Line, Column);
/*
The four numbers in the URI are:
(note that parts one and two constitute the RMS RFA of the record)
1. Virtual Block Number of the start of the part
2. starting byte position in the VBN
3. total length of the chunk
4. offset from start of chunk the image data begins
This will allow the image to be retrieved very quickly by merely
opening the file, reading the part (one or more records) via the RFA
and jumping in to process the image data from the offset.
*/
Offset = ChunkPtr - ChunkBufferPtr;
Length = sprintf (String,
"\n",
CgiScriptNamePtr, UrlEncodedCgiPathInfoPtr, BookSpecifiedByFile,
ChunkArrayPtr[GetChunkNumber].VBN, ChunkArrayPtr[GetChunkNumber].VBNbyte,
ChunkArrayPtr[GetChunkNumber].Length, Offset);
ocptr = OUT_CHAR_AT(Line,Column);
ocptr->htptr = HtmlString (ocptr->htptr, Length);
strcat (ocptr->htptr, String);
ChunkPtr += DataLength;
}
/*****************************************************************************/
/*
Read the font information from the respective part of the Bookreader file.
Font 'numbers' represent the font being specified within bookreader text.
Note the point value of each font, and which font 'number' is bold, italic,
symbol, etc. This information is used when mapping the text.
*/
GetFonts ()
{
int count,
FontCount;
short Short1,
Short2,
Short3,
FontNumber;
char *fptr, *cptr;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "GetFonts()\n");
GetChunk (&FontChunk);
fptr = (char*)ChunkBufferPtr+6;
for (FontCount = 0; FontCount < NumberOfFonts; FontCount++)
{
Short1 = *(short*)fptr;
Short2 = *(short*)(fptr+2);
Short3 = *(short*)(fptr+4);
FontNumber = *(short*)(fptr+6);
if (DebugFonts)
fprintf (stdout, "Font \
#%03d 0x%04.04x 0x%04.04x 0x%04.04x 0x%04.04x\n|%s|\n",
FontNumber, Short1, Short2, Short3, FontNumber, fptr+8);
FontArrayPtr[FontNumber].FontSize = 0;
count = 0;
cptr = (char*)fptr+8;
while (*cptr && count < 8)
if (*cptr++ == '-') count++;
FontArrayPtr[FontNumber].FontSize = atoi (cptr) / 10;
/* push the font name to upper case for easy strstr() below */
for (cptr = fptr+8; *cptr; cptr++) *cptr = toupper(*cptr);
FontArrayPtr[FontNumber].FontBold =
FontArrayPtr[FontNumber].FontItalic =
FontArrayPtr[FontNumber].FontUnsupported = false;
/* "*-interim dm-*" has some interesting graphics/symbol characters */
if (strstr (fptr+8, "INTERIM DM"))
FontArrayPtr[FontNumber].FontUnsupported = true;
else
if (strstr (fptr+8, "SYMBOL"))
FontArrayPtr[FontNumber].FontUnsupported = true;
else
if (strstr (fptr+8, "BOLD"))
FontArrayPtr[FontNumber].FontBold = true;
else
if (strstr (fptr+8, "-I-"))
FontArrayPtr[FontNumber].FontItalic = true;
fptr += 8;
while (*fptr) fptr++;
fptr++;
}
}
/*****************************************************************************/
/*
This function comes from original code, so I'm a bit hazy on its functionality.
*/
GetSectionXref ()
{
int idx;
char *cptr;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "GetSectionXref()\n");
GetChunk (&SectionXrefChunk);
cptr = (char*)ChunkBufferPtr + 0x06;
for (idx = 0; idx < NumberOfSections; idx++)
{
SectionChunkNumbersArrayPtr[idx] = *(long*)cptr;
/*
if (DebugChunks)
fprintf (stdout, "Content %d. 0x%08.08lx\n", idx, *(long*)cptr);
*/
cptr += sizeof(long);
}
}
/*****************************************************************************/
/*
This function comes from original code, so I'm a bit hazy on its functionality.
*/
GetFirstChunk ()
{
int status,
SegmentNameLength;
short SegmentType,
SegmentLength;
long SegmentData1,
SegmentData2,
SegmentData3,
SegmentChunkNumber;
char *chptr,
*SegmentNamePtr;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "GetFirstChunk()\n");
CurrentSeek = 0;
ChunkNumber = 0;
FirstChunk.VBN = 1;
FirstChunk.VBNbyte = 0;
FirstChunk.Length = 1022;
GetChunk (&FirstChunk);
NumberOfChunks = *(long*)(ChunkBufferPtr+0x46);
NumberOfSections = *(long*)(ChunkBufferPtr+0x4a);
NumberOfFonts = *(long*)(ChunkBufferPtr+0x52);
LastVBN = *(long*)(ChunkBufferPtr+0x5a);
LastVBNbyte = *(short*)(ChunkBufferPtr+0x5e);
LastVBNlen = *(short*)(ChunkBufferPtr+0x60);
LastChunk.VBN = LastVBN;
LastChunk.VBNbyte = LastVBNbyte;
LastChunk.Length = LastVBNlen;
strcpy (BookTitle, (char*)ChunkBufferPtr+0x7f);
if (DebugChunks)
{
fprintf (stdout, "ChunkType: %04.04X (%d)\n",
CurrentChunkType, CurrentChunkType);
fprintf (stdout, "ChunkLength: %08.08X (%ld)\n",
CurrentChunkLength, CurrentChunkLength);
fprintf (stdout, "NumberOfChunks: %08.08X (%ld)\n",
NumberOfChunks, NumberOfChunks);
fprintf (stdout, "NumberOfSections: %08.08X (%ld)\n",
NumberOfSections, NumberOfSections);
fprintf (stdout, "NumberOfFonts: %08.08X (%ld)\n",
NumberOfFonts, NumberOfFonts);
fprintf (stdout, "LastVBN: %08.08X (%ld)\n",
LastVBN, LastVBN);
fprintf (stdout, "LastVBNbyte: %04.04X (%d)\n",
(int)LastVBNbyte, (int)LastVBNbyte);
fprintf (stdout, "LastVBNlen: %04.04X (%d)\n",
(int)LastVBNlen, (int)LastVBNlen);
fprintf (stdout, "BookTitle: |%s|\n", BookTitle);
}
ChunkArrayPtr = calloc (sizeof(struct ChunkDataStruct), NumberOfChunks);
if (!ChunkArrayPtr)
{
status = vaxc$errno;
CgiLibResponseError (FI_LI, status, ErrorCalloc);
/* definitely exit if we can't get memory!!! */
exit (SS$_NORMAL);
}
SectionChunkNumbersArrayPtr = calloc (sizeof(int), NumberOfSections);
if (!SectionChunkNumbersArrayPtr)
{
status = vaxc$errno;
CgiLibResponseError (FI_LI, status, ErrorCalloc);
/* definitely exit if we can't get memory!!! */
exit (SS$_NORMAL);
}
FontArrayPtr = calloc (sizeof(struct FontStruct), NumberOfFonts);
if (!FontArrayPtr)
{
status = vaxc$errno;
CgiLibResponseError (FI_LI, status, ErrorCalloc);
/* definitely exit if we can't get memory!!! */
exit (SS$_NORMAL);
}
chptr = (char*)ChunkBufferPtr + 0x128;
while (*(short*)chptr)
{
SegmentType = *(short*)chptr;
SegmentLength = *(short*)(chptr+2);
SegmentData1 = *(long*)(chptr+4);
SegmentData2 = *(long*)(chptr+10);
SegmentData3 = *(long*)(chptr+11);
SegmentChunkNumber = *(long*)(chptr+15);
SegmentNamePtr = chptr+20;
if (Debug && SegmentType == 12)
{
fprintf (stdout,
"Segment; Type: %04.04X Length: %04.04X SegmentNamePtr: |%s|\n\
Data1: %08.08X Data2: %08.08X Data3: %08.08X ChunkNumber: %08.08X\n",
SegmentType, SegmentLength, SegmentNamePtr,
SegmentData1, SegmentData2, SegmentData3, SegmentChunkNumber);
}
if (SegmentType == 12)
{
if (++BookDivisionCount >= MAX_DIVISIONS)
{
CgiLibResponseError (FI_LI, 0, ErrorTooManyDivisions);
return;
}
BookDivision[BookDivisionCount].DescriptionPtr =
calloc (1, SegmentNameLength = strlen(SegmentNamePtr) + 1);
if (!BookDivision[BookDivisionCount].DescriptionPtr)
{
status = vaxc$errno;
CgiLibResponseError (FI_LI, status, ErrorCalloc);
/* definitely exit if we can't get memory!!! */
exit (SS$_NORMAL);
}
memcpy (BookDivision[BookDivisionCount].DescriptionPtr,
SegmentNamePtr,
SegmentNameLength);
BookDivision[BookDivisionCount].ChunkNumber = SegmentChunkNumber;
}
chptr += SegmentLength;
}
}
/*****************************************************************************/
/*
The last "chunk" contains a series of ten-byte structures containing the
Record File Address (RFA) of each "chunk" (the VBN and byte of VBN), and the
total length of the "chunk", which can span individual records.
*/
GetLastChunk ()
{
int count,
SeekPos;
short ChunkType;
char *chptr;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "GetLastChunk()\n");
/* last record/part is the table of parts */
GetChunk (&LastChunk);
if (DebugChunks)
fprintf (stdout,
"(Xref = 2, Headings = 3, Symbols = 4, Fonts = 5)\n\
Chunks \
num VBN VBNbyte len\n");
chptr = (char*)ChunkBufferPtr + 6;
for (count = 0; count < NumberOfChunks; count++)
{
ChunkArrayPtr[count].VBN = *(long*)chptr;
ChunkArrayPtr[count].VBNbyte = *(short*)(chptr+4);
ChunkArrayPtr[count].Length = *(long*)(chptr+6);
chptr += 10;
/*
if (DebugChunks)
{
fprintf (stdout, "Chunk \
%3d 0x%08.08lx 0x%04.04x 0x%08.08lx\n",
count, ChunkArrayPtr[count].VBN, (int)ChunkArrayPtr[count].VBNbyte,
ChunkArrayPtr[count].Length);
}
*/
}
/* >>>>> THE FOLLOWING IS AN AREA FOR POSSIBLE IMPROVEMENT <<<<< */
for (count = 0; count < NumberOfChunks; count++)
{
GetChunk (&ChunkArrayPtr[count]);
if (CurrentChunkType == 0x0006)
{
SectionXrefChunk.VBN = ChunkArrayPtr[count].VBN;
SectionXrefChunk.VBNbyte = ChunkArrayPtr[count].VBNbyte;
SectionXrefChunk.Length = ChunkArrayPtr[count].Length;
}
else
if (CurrentChunkType == 0x0007)
{
SectionHeadingsChunk.VBN = ChunkArrayPtr[count].VBN;
SectionHeadingsChunk.VBNbyte = ChunkArrayPtr[count].VBNbyte;
SectionHeadingsChunk.Length = ChunkArrayPtr[count].Length;
}
else
if (CurrentChunkType == 0x0009)
{
FontChunk.VBN = ChunkArrayPtr[count].VBN;
FontChunk.VBNbyte = ChunkArrayPtr[count].VBNbyte;
FontChunk.Length = ChunkArrayPtr[count].Length;
break;
}
else
if (CurrentChunkType == 0x000d)
{
SymbolsChunk.VBN = ChunkArrayPtr[count].VBN;
SymbolsChunk.VBNbyte = ChunkArrayPtr[count].VBNbyte;
SymbolsChunk.Length = ChunkArrayPtr[count].Length;
}
}
}
/*****************************************************************************/
/*
A 'chunk' consist of 1..n bytes, read beginning a specified byte within a
specified VBN of the Bookreader file. Dynamically allocate (or reallocate)
space for the "chunk" buffer.
The record structure is a little complex, and I haven't bothered to fathom it
all out ... this works for HYPERREADER. If there is only one record in the
chunk then it is straight-forward, read the record, end of story. If there
are two or more records then ALL BUT THE LAST record have record-related data
(not Bookreader information) in the LAST 10 BYTES and ALL in the FIRST 6 BYTES
of each record. HYPERREADER concatenates all records making up a chunk and
these 16 bytes information can be safely ignored. Instead of expensive memory
copying of record data to adjust these offsets on subsequent reads, the last
10 bytes of the previous record are just written over and the first 6 bytes of
the next record positioned to overwrite the 6 bytes prior to the last 10 of
the previous record. These 6 bytes are buffered and restored.
*/
GetChunk (struct ChunkDataStruct *ChunkDataPtr)
{
int status,
RecordCount = 0,
ChunkBufferRemaining;
unsigned char Buffer6 [6];
/*********/
/* begin */
/*********/
if (Debug)
fprintf (stdout, "GetChunk() VBN: %d VBNbyte %d Length: %d\n",
ChunkDataPtr->VBN, ChunkDataPtr->VBNbyte, ChunkDataPtr->Length);
/* allocate enough memory to buffer the entire "chunk" of the book */
if (Debug)
fprintf (stdout, "PreviousChunkLength: %d ChunkDataPtr->Length: %d\n",
PreviousChunkLength, ChunkDataPtr->Length);
if (PreviousChunkLength < ChunkDataPtr->Length)
{
if (ChunkBufferPtr) free (ChunkBufferPtr);
ChunkBufferPtr = calloc (1, PreviousChunkLength =
ChunkDataPtr->Length +
BookFileLongestRecordLength);
if (!ChunkBufferPtr)
{
status = vaxc$errno;
CgiLibResponseError (FI_LI, status, ErrorCalloc);
/* definitely exit if we can't get memory!!! */
exit (SS$_NORMAL);
}
}
DynamicMemoryAllocated += ChunkDataPtr->Length;
/* prepare for reading the record via Record File Address (RFA) */
BookFileRab.rab$l_rfa0 = ChunkDataPtr->VBN;
BookFileRab.rab$w_rfa4 = ChunkDataPtr->VBNbyte;
BookFileRab.rab$b_rac = RAB$C_RFA;
BookFileRab.rab$l_ubf = (char*)ChunkBufferPtr;
ChunkBufferRemaining = ChunkDataPtr->Length +
BookFileLongestRecordLength;
if (ChunkBufferRemaining <= BookFileLongestRecordLength)
BookFileRab.rab$w_usz = ChunkBufferRemaining;
else
BookFileRab.rab$w_usz = BookFileLongestRecordLength;
/* accumulate the total bytes read in global storage to be used later */
CurrentChunkLength = 0;
for (;;)
{
if (Debug)
fprintf (stdout,
"ChunkBufferRemaining: %d BookFileRab.rab$w_usz: %d\n",
ChunkBufferRemaining, BookFileRab.rab$w_usz);
if (BookFileVarRec)
status = sys$get (&BookFileRab, 0, 0);
else
status = GetRecord (&BookFileRab);
if (VMSnok (status)) break;
if (Debug)
fprintf (stdout, "RecordCount: %d BookFileRab.rab$w_rsz: %d\n",
RecordCount, BookFileRab.rab$w_rsz);
TotalBytesRead += BookFileRab.rab$w_rsz;
/* on record 2 ... last restore the overwritten 6 bytes */
if (RecordCount++) memcpy (BookFileRab.rab$l_ubf, Buffer6, 6);
/* break if we've reached the requested chunk size */
if ((CurrentChunkLength += BookFileRab.rab$w_rsz) >=
ChunkDataPtr->Length) break;
/*
Append any subsequent record to what has been read before.
Ignore tha last 10 bytes of the record just read, and also
overwrite the 6 bytes prior to that, buffering it first
so that it may be restored afterwards. As this is not the
last record reduce the accumulated chunk length by 10 + 6.
*/
BookFileRab.rab$l_ubf += BookFileRab.rab$w_rsz - 16;
ChunkBufferRemaining -= BookFileRab.rab$w_rsz - 16;
if (ChunkBufferRemaining <= BookFileLongestRecordLength)
BookFileRab.rab$w_usz = ChunkBufferRemaining;
else
BookFileRab.rab$w_usz = BookFileLongestRecordLength;
CurrentChunkLength -= 16;
memcpy (Buffer6, BookFileRab.rab$l_ubf, 6);
/* the first read is by RFA, subsequent reads are done sequentially */
BookFileRab.rab$b_rac = RAB$C_SEQ;
}
if (VMSnok (status))
{
CgiLibResponseError (FI_LI, status, ErrorChunkRead);
exit (status | STS$M_INHIB_MSG);
}
ChunkPtr = ChunkBufferPtr;
EndChunkPtr = ChunkBufferPtr + CurrentChunkLength;
CurrentChunkType = *(short*)ChunkBufferPtr;
if (Debug)
fprintf (stdout, "ChunkPtr: %d EndChunkPtr: %d CurrentChunkLength: %d\n",
ChunkPtr, EndChunkPtr, CurrentChunkLength);
if (DebugBytes) DumpBytes ("GETCHUNK:", ChunkBufferPtr, CurrentChunkLength);
return (SS$_NORMAL);
}
/*****************************************************************************/
/*
For file formats that are non-VAR, as are (often?) NFS-served books which are
(always?) UDF (undefined), use this function to emulate VAR record format
record 'get's from the file. Uses block IO and some jiggery-pokery with the
RFA and previous RSZ fields as appropriate. Bit clunky but I guess that fits
in well with the rest of the application.
*/
int GetRecord (struct RAB *rabptr)
{
int status,
BlockCount,
BlockNumber;
unsigned long vbn;
unsigned short byte, usz, rsz;
char *ubf, *recptr;
/* maximum buffer space needed will be 32767 plus two blocks */
char BlockBuffer [(512*64)+(512*2)];
/*********/
/* begin */
/*********/
if (Debug)
fprintf (stdout, "GetRecord() rac:%d usz:%d rsz:%d rfa0:%d rfa4:%d\n",
rabptr->rab$b_rac, rabptr->rab$w_usz, rabptr->rab$w_rsz,
rabptr->rab$l_rfa0, rabptr->rab$w_rfa4);
rabptr->rab$w_usz = BookFileLongestRecordLength;
if (rabptr->rab$b_rac != RAB$C_RFA)
{
/* calculate VBN/byte of the next record using previous record size */
vbn = rabptr->rab$w_rsz / 512;
byte = rabptr->rab$w_rsz % 512;
rabptr->rab$l_rfa0 += vbn;
if (rabptr->rab$w_rfa4 + byte < 512)
rabptr->rab$w_rfa4 += byte;
else
{
rabptr->rab$l_rfa0++;
rabptr->rab$w_rfa4 = byte - 512;
}
if (Debug)
fprintf (stdout, "rfa0:%d rfa4:%d\n",
rabptr->rab$l_rfa0, rabptr->rab$w_rfa4);
}
BlockNumber = rabptr->rab$l_rfa0;
BlockCount = (rabptr->rab$w_usz / 512) + 2;
if (Debug) fprintf (stdout, "bkt:%d cnt:%d\n", BlockNumber, BlockCount);
ubf = rabptr->rab$l_ubf;
usz = rabptr->rab$w_usz;
rabptr->rab$l_bkt = BlockNumber;
rabptr->rab$l_ubf = BlockBuffer;
rabptr->rab$w_usz = BlockCount * 512;
status = sys$read (&BookFileRab, 0, 0);
if (Debug) fprintf (stdout, "sys$read() %%X%08.08X\n", status);
rabptr->rab$l_ubf = ubf;
rabptr->rab$w_usz = usz;
if (VMSnok (status)) return (status);
recptr = BlockBuffer + rabptr->rab$w_rfa4;
rsz = *(unsigned short*)recptr;
if (Debug) fprintf (stdout, "rsz:%d\n", rsz);
if (rsz > usz) return (RMS$_UBF);
recptr += 2;
memcpy (rabptr->rab$l_ubf, recptr, rsz);
rabptr->rab$w_rsz = rsz;
return (status);
}
/*****************************************************************************/
/*
*/
int OpenBookFile ()
{
int status;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "OpenBookFile() %s\n", BookFileNamePtr);
/* file access block */
BookFileFab = cc$rms_fab;
BookFileFab.fab$b_fac = FAB$M_GET | FAB$M_BRO;
BookFileFab.fab$l_dna = "DECW$BOOK:.DECW$BOOK";
BookFileFab.fab$b_dns = 20;
BookFileFab.fab$b_shr = FAB$M_SHRGET;
BookFileFab.fab$l_xab = &BookFileXabDat;
BookFileXabDat = cc$rms_xabdat;
BookFileXabDat.xab$l_nxt = &BookFileXabFhc;
BookFileXabFhc = cc$rms_xabfhc;
#ifdef ODS_EXTENDED
if (OdsExtended)
{
BookFileFab.fab$l_fna = (char*)-1;
BookFileFab.fab$b_fns = 0;
BookFileFab.fab$l_nam = (struct namdef*)&BookFileNaml;
ENAMEL_RMS_NAML(BookFileNaml)
BookFileNaml.naml$l_long_filename = BookFileNamePtr;
BookFileNaml.naml$l_long_filename_size = strlen(BookFileNamePtr);
BookFileNaml.naml$l_long_expand = ExpBookFileName;
BookFileNaml.naml$l_long_expand_alloc = sizeof(ExpBookFileName)-1;
}
else
#endif /* ODS_EXTENDED */
{
BookFileFab.fab$l_fna = BookFileNamePtr;
BookFileFab.fab$b_fns = strlen(BookFileNamePtr);
BookFileFab.fab$l_nam = &BookFileNam;
BookFileNam = cc$rms_nam;
BookFileNam.nam$l_esa = ExpBookFileName;
BookFileNam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH;
}
if (VMSok (status = sys$open (&BookFileFab, 0, 0)))
{
/* terminate 'ExpBookFileName' at the version delimiter */
#ifdef ODS_EXTENDED
if (OdsExtended)
*BookFileNaml.naml$l_long_ver = '\0';
else
#endif /* ODS_EXTENDED */
*BookFileNam.nam$l_ver = '\0';
if (*CgiFormVarRecPtr)
{
if (*CgiFormVarRecPtr == '1' ||
toupper(*CgiFormVarRecPtr) == 'Y')
BookFileVarRec = true;
else
BookFileVarRec = false;
}
else
if (BookFileFab.fab$b_rfm == FAB$C_VAR)
BookFileVarRec = true;
else
BookFileVarRec = false;
/* record access block */
BookFileRab = cc$rms_rab;
BookFileRab.rab$l_fab = &BookFileFab;
if (BookFileVarRec)
{
/* 3 buffers, read ahead performance option */
BookFileRab.rab$b_mbf = 3;
BookFileRab.rab$l_rop = RAB$M_RAH;
}
else
BookFileRab.rab$l_rop = RAB$M_BIO;
if (CgiHttpIfModifiedSincePtr[0])
{
if (VMSnok (status =
ModifiedSince (&IfModifiedSinceBinaryTime,
&BookFileXabDat.xab$q_rdt)))
{
/* book has not been modified since the date/time, don't send */
return (status);
}
}
HttpGmTimeString (LastModifiedGmDateTime, &BookFileXabDat.xab$q_rdt);
if (Debug) fprintf (stdout, "lrl: %d\n", BookFileXabFhc.xab$w_lrl);
if (!(BookFileLongestRecordLength = BookFileXabFhc.xab$w_lrl))
BookFileLongestRecordLength = 32767;
return (sys$connect (&BookFileRab, 0, 0));
}
if (CgiFormTitlePtr[0])
CgiLibResponseError (FI_LI, status, CgiFormTitlePtr);
else
if (CgiPathInfoPtr[0])
CgiLibResponseError (FI_LI, status, CgiPathInfoPtr);
else
CgiLibResponseError (FI_LI, status, CgiFormFilePtr);
return (status);
}
/*****************************************************************************/
/*
Create a series of navigation buttons, e.g. "next", "previous", "contents",
"tables", "figures", "index", etc. These are provided at the top and bottom
of each page. The buttons can be either highlighted anchors (if the button
represents an available link) or just "there" (if there is no relevant link,
i.e. on the "contents" page it is redundant to have a "contents" link) as
appropriate.
*/
ButtonBar (int Top1Bottom2)
{
#define NUMBER_OF_BUTTONS 16
static int ButtonCount = -1;
static char *ButtonLabel [NUMBER_OF_BUTTONS+1];
static char *ButtonPath [NUMBER_OF_BUTTONS+1];
int idx;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "ButtonBar()\n");
if (ButtonCount == -1)
{
char *cptr, *sptr;
if (Debug) fprintf (stdout, "|%s|\n", ButtonPtr);
cptr = ButtonPtr;
for (ButtonCount = 0;
ButtonCount <= NUMBER_OF_BUTTONS && *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 (ButtonCount < DEFAULT_MINIMUM_BUTTONS)
{
CgiLibResponseError (FI_LI, 0, ErrorButtons);
return;
}
}
if (Top1Bottom2 == 2)
{
if (PageScheme[PS_LAYOUT][0] == '2')
TotalBytesWritten += fprintf (stdout,
"\n
\n");
else
TotalBytesWritten += fprintf (stdout, "\n");
}
if (PageScheme[PS_LAYOUT][0] == '2')
TotalBytesWritten += fprintf (stdout, "\n");
else
{
TotalBytesWritten += fprintf (stdout,
"\n\
\n\
\n\
\n",
PageScheme[PS_HEADBORDER],
PageScheme[PS_HEADBORDER],
PageScheme[PS_HEADBGCOLOR],
PageScheme[PS_BUTTONBORDER]);
}
/* next */
ButtonBarButton (ButtonLabel[0], "", NextChunkNumber, DoMassage);
/* previous */
ButtonBarButton (ButtonLabel[1], "", PreviousChunkNumber, DoMassage);
/* divisions provided in the book itself */
for (idx = 1; idx <= BookDivisionCount; idx++)
{
if (GetChunkNumber == BookDivision[idx].ChunkNumber)
ButtonBarButton (BookDivision[idx].DescriptionPtr, "", 0, DoMassage);
else
ButtonBarButton (BookDivision[idx].DescriptionPtr, "",
BookDivision[idx].ChunkNumber, DoMassage);
}
/* close */
if (CgiFormRefererPtr[0])
ButtonBarButton (ButtonLabel[2], CgiFormRefererPtr, -1, false);
else
ButtonBarButton (ButtonLabel[2], "", 0, false);
/* help */
if (ButtonLabel[3][0])
ButtonBarButton (ButtonLabel[3], ButtonPath[3], -1, false);
if (Top1Bottom2 == 2)
{
/* massage on/off */
if (DoMassage)
ButtonBarButton (ButtonLabel[4], "", GetChunkNumber, false);
else
ButtonBarButton (ButtonLabel[5], "", GetChunkNumber, true);
}
/* any user-defined buttons */
for (idx = DEFAULT_MINIMUM_BUTTONS; idx < ButtonCount; idx++)
ButtonBarButton (ButtonLabel[idx], ButtonPath[idx], -2, false);
if (PageScheme[PS_LAYOUT][0] == '2')
TotalBytesWritten += fprintf (stdout, "\n");
else
TotalBytesWritten += fprintf (stdout,
" \n\
|
\n\
\n\
\n");
if (Top1Bottom2 == 1)
if (PageScheme[PS_LAYOUT][0] == '2')
TotalBytesWritten += fprintf (stdout,
"
\n");
}
/*****************************************************************************/
/*
Generate a single "button" inside the context created by ButtonBar().
*/
ButtonBarButton
(
char *ButtonLabel,
char *ButtonPath,
int ChunkNumber,
boolean NavMassage
)
{
char *MassagePtr;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "ButtonBarButton()\n");
if (NavMassage)
MassagePtr = "";
else
MassagePtr = "&massage=no";
if (PageScheme[PS_LAYOUT][0] == '2')
TotalBytesWritten += fprintf (stdout, "[");
else
TotalBytesWritten += fprintf (stdout,
" ",
PageScheme[PS_BUTTONBGCOLOR]);
if (ChunkNumber == 0)
{
/* no chunk number therefore no link, just a label */
TotalBytesWritten += fprintf (stdout, "%s", ButtonLabel);
}
else
if (ChunkNumber > 0)
{
/* link to this particular chunk of the book */
TotalBytesWritten += fprintf (stdout,
"%s",
CgiScriptNamePtr, UrlEncodedCgiPathInfoPtr, BookSpecifiedByFile,
ChunkNumber, UrlEncReferer, UrlEncTitle, MassagePtr, ButtonLabel);
}
else
{
/* -1 == SPECIAL CASE, create non-chunk button */
TotalBytesWritten += fprintf (stdout,
"%s",
ButtonPath, ButtonLabel);
}
if (PageScheme[PS_LAYOUT][0] == '2')
TotalBytesWritten += fprintf (stdout, "] \n");
else
TotalBytesWritten += fprintf (stdout, " | \n");
}
/*****************************************************************************/
/*
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, GifCompress() ... formally called 'compgif()', and GifPackBits() ...
formally called 'pack_bits()'. The original commentary and copyright notice
remains as per the code author's request.
*/
GifImage
(
int WidthPixels,
int HeightPixels,
unsigned char *ImageBytePtr,
int ByteCount
)
{
static int Background = 0,
BitsPerPixel = 1;
/* background (index 0) is white, foreground (index 1) is black */
static unsigned char Red [] = { 0xff, 0x00 },
Green [] = { 0xff, 0x00 },
Blue [] = { 0xff, 0x00 };
int idx,
Byte,
LeftOffset,
TopOffset,
Resolution,
ColorMapSize,
InitCodeSize;
char *bptr;
char Buffer [512];
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "GifImage()\n");
CgiLibResponseHeader (200, "image/gif",
"Last-Modified: %s\n", LastModifiedGmDateTime);
/* initialize the following functions */
GifNextPixel (ImageBytePtr, ByteCount);
GifPackBits (-1, -1);
ColorMapSize = 1 << BitsPerPixel;
LeftOffset = TopOffset = 0;
Resolution = BitsPerPixel;
/*
BookReader Format (BRF) images are always whole bytes.
Image width specifications (in bits) do not always appear to be so!
If the width is not an even number of bits (making up a whole byte)
then make it so by rounding up to the next whole byte number of bits.
Seems to work OK!
*/
if (WidthPixels & 0x7) WidthPixels = ((WidthPixels >> 3) + 1) << 3;
/* the initial code size */
if( BitsPerPixel <= 1 )
InitCodeSize = 2;
else
InitCodeSize = BitsPerPixel;
/**************************/
/* GIF Data Stream header */
/**************************/
/* accumulate bytes into buffer before output */
bptr = Buffer;
strcpy (bptr, "GIF89a");
bptr += 6;
/*****************************/
/* Logical Screen Descriptor */
/*****************************/
/* width and height of logical screen */
*bptr++ = WidthPixels & 0xff;
*bptr++ = (WidthPixels >> 8) & 0xff;
*bptr++ = HeightPixels & 0xff;
*bptr++ = (HeightPixels >> 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 |= (BitsPerPixel - 1);
/* write it out */
*bptr++ = Byte;
/* Background colour */
*bptr++ = Background;
/* pixel aspect ratio */
*bptr++ = 0;
/***********************/
/* Global Colour Table */
/***********************/
for (idx = 0; idx < ColorMapSize; idx++)
{
*bptr++ = Red[idx];
*bptr++ = Green[idx];
*bptr++ = Blue[idx];
}
/****************************************/
/* Graphic Control Extension descriptor */
/****************************************/
/* 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++ = 0x00;
/* 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++ = WidthPixels & 0xff;
*bptr++ = (WidthPixels >> 8) & 0xff;
*bptr++ = HeightPixels & 0xff;
*bptr++ = (HeightPixels >> 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 */
fwrite (Buffer, bptr-Buffer, 1, stdout);
/* LZW compress the data using PBM-derived algorithm and code */
GifCompress (InitCodeSize + 1);
/****************************************/
/* end of image data and GIF terminator */
/****************************************/
/* write out a zero-length packet (to end the series), and terminator */
fwrite ("\0;", 2, 1, stdout);
}
/*****************************************************************************/
/*
Get a series of pixels from the bitmap. This function is called by
GifCompress() to scan the image. Bit 0 through to bit 7 of the current byte
are returned as 1 or 0 before the next byte is pointed to. When the total
number of bytes have been transmitted return EOF. This function is
initialized by a first call, setting the values of the byte pointer and number
of bytes in the image.
*/
GifNextPixel
(
unsigned char *ImageBytePtr,
int ImageByteCount
)
{
static unsigned int BitPosition,
ByteCount;
static unsigned char *BytePtr;
/*********/
/* begin */
/*********/
if (ImageBytePtr)
{
/* initialize */
BytePtr = ImageBytePtr;
ByteCount = ImageByteCount;
BitPosition = 1;
return;
}
if (!BitPosition)
{
/* bits 0 through to 7 have been returned, move to next byte */
if (!ByteCount) return (EOF);
ByteCount--;
BytePtr++;
BitPosition = 1;
}
if (*BytePtr & BitPosition)
{
/* next call will return the next most significant bit (if <= 7) */
BitPosition = (BitPosition << 1) & 0xff;
/* the tested bit position contained a 1 (foreground) */
return (1);
}
else
{
/* next call will return the next most significant bit (if <= 7) */
BitPosition = (BitPosition << 1) & 0xff;
/* the tested bit position contained a 0 (background) */
return (0);
}
}
/****************************************************************************/
/*
* 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 GifCompress (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) printf("can't allocate heap");
for (i=0; i < MAXIMUMCODE; i++) {
heap[i] = (strTableNodePtr)malloc(sizeof(strTableNode));
if (!heap[i])
{
/*
printf("can't allocate heap");
*/
int status;
status = vaxc$errno;
CgiLibResponseError (FI_LI, status, ErrorCalloc);
/* definitely exit if we can't get memory!!! */
exit (SS$_NORMAL);
}
}
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)");
*/
int status;
status = vaxc$errno;
CgiLibResponseError (FI_LI, status, "malloc()");
/* definitely exit if we can't get memory!!! */
exit (SS$_NORMAL);
}
for(i=0; ientry.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 */
GifPackBits(compress_size, -1);
GifPackBits(compress_size, clear_code);
end_of_data = 0;
if ((cur_char = GifNextPixel(NULL, 0)) == EOF)
printf("premature end of data");
while (!end_of_data) {
prefix = cur_char;
cur_node = root[prefix];
found = 1;
if((cur_char = GifNextPixel(NULL, 0)) == 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 = GifNextPixel(NULL, 0)) == 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;
GifPackBits(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;
GifPackBits(compress_size, clear_code);
compress_size = bits_per_pix + 1;
cur_code = end_code + 1;
}
}
GifPackBits(compress_size, prefix);
GifPackBits(compress_size, end_code);
GifPackBits(compress_size, -1);
for (i=0; i < MAXIMUMCODE; i++) free(heap[i]);
free(heap);
free(root);
return (1);
}
/*
************************************************************************
* GifPackBits() 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. *
************************************************************************
*/
GifPackBits (int compress_size, int prefix)
{
static int cur_bit = 8;
static unsigned char block[BLOCKSIZE] = { 0 };
int i, left_over_bits;
if (compress_size == -1 && prefix == -1)
{
/* initialize */
cur_bit = 8;
block[0] = 0;
return;
}
/* 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<>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]) fwrite (block, block[0]+1, 1, stdout);
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<>3] |= (unsigned char)(1<<(cur_bit & 7));
/* note n>>3 == n/8 and n & 7 == n % 8 */
cur_bit++;
}
}
return (1);
}
/*****************************************************************************/
/*
*/
InsertComment
(
int Horizontal,
int Vertical,
char *Explanation,
int SourceCodeLineNumber
)
{
int Length,
Line,
Column;
char String [256];
struct OutCharStruct *ocptr;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "InsertComment()\n");
Line = Vertical / LINE_POSITIONING_UNITS;
if (!Line) Line = 1;
if (Line > LinesOnPage) PageSize (Line);
if (Line > LastLineOnPage) LastLineOnPage = Line;
Column = Horizontal / COLUMN_POSITIONING_UNITS;
if (Column >= MAX_OUTLINE_LENGTH) Column = MAX_OUTLINE_LENGTH-1;
if (Debug) fprintf (stdout, "Line: %d Column: %d\n", Line, Column);
Length = sprintf (String, "%s ",
Explanation, SourceCodeLineNumber);
ocptr = OUT_CHAR_AT(Line,Column);
ocptr->htptr = HtmlString (ocptr->htptr, Length);
strcat (ocptr->htptr, String);
}
/*****************************************************************************/
/*
For debugging purposes.
*/
DumpBytes
(
char *Comment,
char *BytePtr,
int NumberOfBytes
)
{
static char HexDigits [] = "0123456789abcdef";
int bcnt, ccnt;
char *bptr, *sptr;
char String [256];
/*********/
/* begin */
/*********/
fprintf (stdout, "\n%s Byte DUMP beginning: %08.08x Bytes: %d\n",
Comment, BytePtr, NumberOfBytes);
while (NumberOfBytes > 0)
{
sptr = String;
bptr = BytePtr;
sprintf (sptr, "\n%08.08x : ", bptr);
sptr += 12;
ccnt = 16;
bcnt = NumberOfBytes;
while (ccnt-- && bcnt--)
{
*sptr++ = HexDigits[*bptr >> 4];
*sptr++ = HexDigits[*bptr++ & 0x0f];
*sptr++ = ' ';
}
bptr = BytePtr;
strcpy (sptr, "\n ");
sptr += 12;
ccnt = 16;
bcnt = NumberOfBytes;
while (ccnt-- && bcnt--)
{
if (isprint(*bptr))
{
*sptr++ = ' ';
*sptr++ = *bptr++;
*sptr++ = ' ';
}
else
{
*sptr++ = ' ';
*sptr++ = '^';
*sptr++ = ' ';
bptr++;
}
}
*sptr++ = '\n';
*sptr = '\0';
fputs (String, stdout);
NumberOfBytes -= 16;
BytePtr += 16;
}
fputs ("\n", stdout);
}
/*****************************************************************************/
/*
For debugging purposes.
*/
DumpLongWords
(
char *Comment,
unsigned long *lwptr,
int NumberOfLongWords
)
{
int count = 1;
/*********/
/* begin */
/*********/
fprintf (stdout, "\n%s Longword DUMP beginning: %08.08x, Longwords: %d\n",
Comment, lwptr, NumberOfLongWords);
while (NumberOfLongWords-- > 0)
{
fprintf (stdout,
"%2d. %08.08x : 0x%08.08x : %10d : %5d %5d : %3d %3d %3d %3d\n",
count++, lwptr, *lwptr, *lwptr,
*lwptr >> 16, *lwptr & 0xffff,
(*lwptr & 0xff000000) >> 24,
(*lwptr & 0xff0000) >> 16,
(*lwptr & 0xff00) >> 8,
*lwptr & 0xff);
lwptr++;
}
fputs ("\n", stdout);
}
/*****************************************************************************/
/*
If the object has been modified since the specified date and time then return
a normal status indicating that the data transfer is to continue. If not
modified then send a "not modified" HTTP header and return an error status to
indicate the object should not be sent.
*/
int ModifiedSince
(
unsigned long *SinceBinaryTimePtr,
unsigned long *BinaryTimePtr
)
{
static unsigned long OneSecondDelta [2] = { -10000000, -1 };
int status;
unsigned long AdjustedBinTime [2],
ScratchBinTime [2];
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "ModifiedSince()\n");
/* if request asks for a "reload" (not cached) then give it regardless */
if (strsame (CgiHttpPragmaPtr, "no-cache", -1)) return (SS$_NORMAL);
if (strsame (CgiHttpCacheControlPtr, "no-cache", 8)) return (SS$_NORMAL);
if (strsame (CgiHttpCacheControlPtr, "no-store", 8)) return (SS$_NORMAL);
if (strsame (CgiHttpCacheControlPtr, "max-age=0", 9)) return (SS$_NORMAL);
/*
Add one second to the modified time. Ensures a negative time
for VMS where fractional seconds may result in inconclusive
results when the target time is being specified in whole seconds.
*/
if (VMSnok (status =
lib$add_times (SinceBinaryTimePtr, &OneSecondDelta, &AdjustedBinTime)))
{
CgiLibResponseError (FI_LI, status, "lib$add_times()");
return (status);
}
if (Debug) fprintf (stdout, "sys$add_times() %%X%08.08X\n", status);
/* if a positive time results the file has been modified */
if (VMSok (status =
lib$sub_times (BinaryTimePtr, &AdjustedBinTime, &ScratchBinTime)))
return (status);
if (Debug) fprintf (stdout, "sys$sub_times() %%X%08.08X\n", status);
if (status != LIB$_NEGTIM)
{
CgiLibResponseError (FI_LI, status, "sys$sub_times()");
return (status);
}
CgiLibResponseHeader (304, "text/html");
fprintf (stdout, "Not modified!\n");
return (LIB$_NEGTIM);
}
/*****************************************************************************/
/*
Create an HTTP format Greenwich Mean Time (UTC) time string in the storage
pointed at by 'TimeString', e.g. "Fri, 25 Aug 1995 17:32:40 GMT" (RFC 1123).
This must be at least 30 characters capacity. If 'BinTimePtr' is null the
time string represents the current time. If it points to a quadword, VMS time
value the string represents that time. 'TimeString' must point to storage
large enough for 31 characters.
*/
int HttpGmTimeString
(
char *TimeString,
unsigned long *BinTimePtr
)
{
static char *DayNames [] =
{ "", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
static char *MonthName [] =
{ "", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
static $DESCRIPTOR (HttpTimeFaoDsc, "!AZ, !2ZW !AZ !4ZW !2ZW:!2ZW:!2ZW GMT");
static $DESCRIPTOR (TimeStringDsc, "");
int status;
unsigned long BinTime [2],
GmTime [2];
unsigned short Length;
unsigned short NumTime [7];
unsigned long DayOfWeek;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "HttpGmTimeString()\n");
if (!BinTimePtr)
sys$gettim (&GmTime);
else
{
GmTime[0] = BinTimePtr[0];
GmTime[1] = BinTimePtr[1];
}
if (VMSnok (status = TimeAdjustGMT (true, &GmTime)))
return (status);
status = sys$numtim (&NumTime, &GmTime);
if (Debug)
fprintf (stdout, "sys$numtim() %%X%08.08X %d %d %d %d %d %d %d\n",
status, NumTime[0], NumTime[1], NumTime[2],
NumTime[3], NumTime[4], NumTime[5], NumTime[6]);
if (VMSnok (status = lib$day_of_week (&GmTime, &DayOfWeek)))
return (status);
if (Debug)
fprintf (stdout, "lib$day_of_week() %%X%08.08X is %d\n",
status, DayOfWeek);
/* set the descriptor address and size of the resultant time string */
TimeStringDsc.dsc$w_length = 30;
TimeStringDsc.dsc$a_pointer = TimeString;
if (VMSnok (status =
sys$fao (&HttpTimeFaoDsc, &Length, &TimeStringDsc,
DayNames[DayOfWeek], NumTime[2], MonthName[NumTime[1]],
NumTime[0], NumTime[3], NumTime[4], NumTime[5])))
{
if (Debug) fprintf (stdout, "sys$fao() %%X%08.08X\n", status);
TimeString[0] = '\0';
}
else
TimeString[Length] = '\0';
if (Debug) fprintf (stdout, "|%s|\n", TimeString);
return (status);
}
/*****************************************************************************/
/*
Given a string such as "Fri, 25 Aug 1995 17:32:40 GMT" (RFC 1123), or "Friday,
25-Aug-1995 17:32:40 GMT" (RFC 1036), create an internal, local, binary time
with the current GMT offset. See complementary function HttpGmTimeString().
*/
int HttpGmTime
(
char *TimeString,
unsigned long *BinTimePtr
)
{
static char *MonthName [] =
{ "", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
int status;
unsigned short Length;
unsigned short NumTime [7] = { 0,0,0,0,0,0,0 };
unsigned long DayOfWeek;
char *tptr;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "HttpGmTime() |%s|\n", TimeString);
tptr = TimeString;
/* hunt straight for the comma after the weekday name! */
while (*tptr && *tptr != ',') tptr++;
if (*tptr) tptr++;
/* span white space between weekday name and date */
while (*tptr && isspace(*tptr)) tptr++;
if (Debug) fprintf (stdout, "tptr |%s|\n", tptr);
if (!*tptr) return (STS$K_ERROR);
/* get the date and then skip to month name */
if (isdigit(*tptr)) NumTime[2] = atoi (tptr);
while (*tptr && isdigit(*tptr)) tptr++;
while (*tptr && (*tptr == '-' || isspace(*tptr))) tptr++;
if (Debug) fprintf (stdout, "tptr |%s|\n", tptr);
if (!*tptr) return (STS$K_ERROR);
/* get the month number from the name and skip to the year */
for (NumTime[1] = 1; NumTime[1] <= 12; NumTime[1]++)
if (strsame (tptr, MonthName[NumTime[1]], 3)) break;
if (NumTime[1] > 12) return (STS$K_ERROR);
while (*tptr && isalpha(*tptr)) tptr++;
while (*tptr && (*tptr == '-' || isspace(*tptr))) tptr++;
if (Debug) fprintf (stdout, "tptr |%s|\n", tptr);
if (!*tptr) return (STS$K_ERROR);
/* get the year and then skip to the hour */
if (isdigit(*tptr))
{
NumTime[0] = atoi (tptr);
if (NumTime[0] < 100) NumTime[0] += 1900;
}
while (*tptr && isdigit(*tptr)) tptr++;
while (*tptr && isspace(*tptr)) tptr++;
if (Debug) fprintf (stdout, "tptr |%s|\n", tptr);
if (!*tptr) return (STS$K_ERROR);
/* get the hour, minute and second */
if (isdigit(*tptr)) NumTime[3] = atoi (tptr);
while (*tptr && isdigit(*tptr)) tptr++;
if (*tptr == ':') tptr++;
if (isdigit(*tptr)) NumTime[4] = atoi (tptr);
while (*tptr && isdigit(*tptr)) tptr++;
if (*tptr == ':') tptr++;
if (isdigit(*tptr)) NumTime[5] = atoi (tptr);
while (*tptr && isdigit(*tptr)) tptr++;
if (*tptr == ':') tptr++;
if (!*tptr) return (STS$K_ERROR);
/* the only thing remaining should be the "GMT" */
while (*tptr && isspace(*tptr)) tptr++;
if (Debug) fprintf (stdout, "tptr |%s|\n", tptr);
if (!strsame (tptr, "GMT", 3)) return (STS$K_ERROR);
/*******************************************/
/* convert what looks like legitimate GMT! */
/*******************************************/
if (Debug)
fprintf (stdout, "NumTime[] %d %d %d %d %d %d %d\n",
NumTime[0], NumTime[1], NumTime[2], NumTime[3],
NumTime[4], NumTime[5], NumTime[6]);
status = lib$cvt_vectim (&NumTime, BinTimePtr);
if (VMSnok (status)) return (status);
if (Debug) fprintf (stdout, "lib$cvt_vectim() %%X%08.08X\n", status);
return (TimeAdjustGMT (false, BinTimePtr));
}
/*****************************************************************************/
/*
Determine the offset from GMT (UTC) using either the HTTPD$GMT or
SYS$TIMEZONE_DIFFERENTIAL logicals. If HTTPD$GMT is not defined time should be
set from the timezone differential logical. Function RequestBegin() calls
this every hour to recheck GMT offset and detect daylight saving or other
timezone changes.
*/
int TimeSetGmt ()
{
static boolean UseTimezoneDifferential = false;
int status;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "TimeSetGmt()\n");
if (!UseTimezoneDifferential)
{
status = TimeSetHttpdGmt();
/* return if error and that error was not that the name did not exist */
if (VMSok (status) || status != SS$_NOLOGNAM) return (status);
}
UseTimezoneDifferential = true;
return (TimeSetTimezone());
}
/*****************************************************************************/
/*
The SYS$TIMEZONE_DIFFERENTIAL logical contains the number of seconds offset
from GMT (UTC) as a positive (ahead) or negative (behind) number. Set the
'TimeGmtString' global storage to a "+hh:mm" or "-hh:mm" string equivalent, and
the 'TimeAheadOfGmt' global boolean and 'TimeGmtVmsString' delta-time global
string.
*/
int TimeSetTimezone ()
{
static unsigned short Length;
static $DESCRIPTOR (TimezoneLogicalNameDsc, "SYS$TIMEZONE_DIFFERENTIAL");
static $DESCRIPTOR (LnmSystemDsc, "LNM$SYSTEM");
static $DESCRIPTOR (TimeGmtStringFaoDsc, "!AZ!2ZL:!2ZL");
static $DESCRIPTOR (TimeGmtVmsStringFaoDsc, "0 !2ZL:!2ZL");
static $DESCRIPTOR (TimeGmtStringDsc, TimeGmtString);
static struct {
short int buf_len;
short int item;
void *buf_addr;
unsigned short *ret_len;
} LnmItems [] =
{
{ sizeof(TimeGmtString)-1, LNM$_STRING, TimeGmtString, &Length },
{ 0,0,0,0 }
};
int status;
long Hours,
Minutes,
Seconds;
char *SignPtr;
$DESCRIPTOR (TimeGmtVmsStringDsc, TimeGmtVmsString);
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "TimeSetTimezone()\n");
status = sys$trnlnm (0, &LnmSystemDsc, &TimezoneLogicalNameDsc, 0, &LnmItems);
if (Debug) fprintf (stdout, "sys$trnlnm() %%X%08.08X\n", status);
if (VMSnok (status)) return (status);
TimeGmtString[Length] = '\0';
if (Debug) fprintf (stdout, "TimeGmtString |%s|\n", TimeGmtString);
Seconds = atol(TimeGmtString);
if (Seconds < 0)
{
TimeAheadOfGmt = false;
Seconds = -Seconds;
SignPtr = "-";
}
else
{
TimeAheadOfGmt = true;
SignPtr = "+";
}
Hours = Seconds / 3600;
Minutes = (Seconds - Hours * 3600) / 60;
if (Debug)
fprintf (stdout, "%d %s%d:%d\n", Seconds, SignPtr, Hours, Minutes);
sys$fao (&TimeGmtStringFaoDsc, &Length, &TimeGmtStringDsc,
SignPtr, Hours, Minutes);
TimeGmtString[Length] = '\0';
if (Debug) fprintf (stdout, "TimeGmtString |%s|\n", TimeGmtString);
sys$fao (&TimeGmtVmsStringFaoDsc, &Length, &TimeGmtVmsStringDsc,
Hours, Minutes);
TimeGmtVmsString[Length] = '\0';
if (Debug) fprintf (stdout, "TimeGmtVmsString |%s|\n", TimeGmtVmsString);
TimeGmtVmsStringDsc.dsc$w_length = Length;
if (VMSnok (status =
sys$bintim (&TimeGmtVmsStringDsc, &TimeGmtDeltaBinary)))
return (status);
if (TimeGmtDeltaBinary[0] || TimeGmtDeltaBinary[1])
return (status);
/* time must have been zero, make it one, one-hundreth of a second */
TimeGmtDeltaBinary[0] = -100000;
TimeGmtDeltaBinary[1] = -1;
return (SS$_NORMAL);
}
/*****************************************************************************/
/*
Translate the logical HTTPD$GMT (defined to be something like "+10:30" or "-
01:15") and convert it into a delta time structure and store in
'TimeGmtDeltaBinary'. Store whether it is in advance or behind GMT in boolean
'TimeAheadOfGmt'. Store the logical string in 'TimeGmtString'.
*/
int TimeSetHttpdGmt ()
{
static unsigned short Length;
static $DESCRIPTOR (GmtLogicalNameDsc, "HTTPD$GMT");
static $DESCRIPTOR (LnmFileDevDsc, "LNM$FILE_DEV");
static struct {
short int buf_len;
short int item;
void *buf_addr;
unsigned short *ret_len;
} LnmItems [] =
{
{ sizeof(TimeGmtString)-1, LNM$_STRING, TimeGmtString, &Length },
{ 0,0,0,0 }
};
int status;
char *cptr, *sptr;
$DESCRIPTOR (TimeGmtVmsStringDsc, TimeGmtVmsString);
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "TimeSetHttpdGmt()\n");
status = sys$trnlnm (0, &LnmFileDevDsc, &GmtLogicalNameDsc, 0, &LnmItems);
if (Debug) fprintf (stdout, "sys$trnlnm() %%X%08.08X\n", status);
if (VMSnok (status)) return (status);
TimeGmtString[Length] = '\0';
if (Debug) fprintf (stdout, "TimeGmtString |%s|\n", TimeGmtString);
if (TimeGmtString[0] == '$') return (SS$_NORMAL);
if (*(cptr = TimeGmtString) == '-')
TimeAheadOfGmt = false;
else
TimeAheadOfGmt = true;
if (*cptr == '+' || *cptr == '-') cptr++;
sptr = TimeGmtVmsString;
*sptr++ = '0';
*sptr++ = ' ';
while (*cptr) *sptr++ = *cptr++;
*sptr = '\0';
if (Debug) fprintf (stdout, "TimeGmtVmsString |%s|\n", TimeGmtVmsString);
TimeGmtVmsStringDsc.dsc$w_length = sptr - TimeGmtVmsString;
if (VMSnok (status =
sys$bintim (&TimeGmtVmsStringDsc, &TimeGmtDeltaBinary)))
return (status);
if (TimeGmtDeltaBinary[0] || TimeGmtDeltaBinary[1])
return (status);
/* time must have been zero, make it one, one-hundreth of a second */
TimeGmtDeltaBinary[0] = -100000;
TimeGmtDeltaBinary[1] = -1;
return (SS$_NORMAL);
}
/*****************************************************************************/
/*
The GMT is generated by calculating using an offset using
'TimeGmtDeltaOffset' and boolean 'TimeAheadOfGmt'. Adjust either to or from
GMT.
*/
int TimeAdjustGMT
(
boolean ToGmTime,
unsigned long *BinTimePtr
)
{
int status;
unsigned long AdjustedTime [2];
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "TimeAdjustGMT() ToGmTime: %d\n", ToGmTime);
if ((ToGmTime && TimeAheadOfGmt) || (!ToGmTime && !TimeAheadOfGmt))
{
/* to GMT from local and ahead of GMT, or to local from GMT and behind */
status = lib$sub_times (BinTimePtr, &TimeGmtDeltaBinary, &AdjustedTime);
if (Debug) fprintf (stdout, "lib$sub_times() %%X%08.08X\n", status);
}
else
{
/* to GMT from local and behind GMT, or to local from GMT and ahead */
status = lib$add_times (BinTimePtr, &TimeGmtDeltaBinary, &AdjustedTime);
if (Debug) fprintf (stdout, "lib$add_times() %%X%08.08X\n", status);
}
if (Debug)
{
unsigned short Length;
char String [64];
$DESCRIPTOR (AdjustedTimeFaoDsc, "AdjustedTime: |!%D|\n");
$DESCRIPTOR (StringDsc, String);
sys$fao (&AdjustedTimeFaoDsc, &Length, &StringDsc, &AdjustedTime);
String[Length] = '\0';
fputs (String, stdout);
}
BinTimePtr[0] = AdjustedTime[0];
BinTimePtr[1] = AdjustedTime[1];
return (status);
}
/****************************************************************************/
/*
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;
void *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
(
char *sptr1,
char *sptr2,
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);
}
/*****************************************************************************/