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 packe