/*****************************************************************************/
/*
pass2.c
RENDER.C generates a first-pass HTML document.
PASS2.C generates the final HTML document, in particular populating headings
and links, and generating cross-referencing, tables of content, and an index.
VERSION HISTORY
---------------
29-APR-2020 MGD initial
*/
/*****************************************************************************/
#include "wasdoc.h"
#include "cgilib.h"
extern int bvbar, dbug;
/*****************************************************************************/
/*
*/
int pass2 (struct wasdoc_st *docptr)
{
int count = 0, error = 0;
char *h1ptr, *cptr, *sptr, *zptr;
if (dbug==1)
{
fputs (docptr->html, stdout);
return (error);
}
if (dbug>2) dbugThis (FI_LI, "pass2()");
if (docptr->insight >= 5) wasDocInsight (docptr, "second pass");
if (dbug>2) dbugThis (FI_LI, "1~~~~~~~~~~\n%s~~~~~~~~~~1\n", docptr->html);
/* put the first pass HTML into ->html1 */
wasDocHtmlShuffle (docptr);
pass2list (docptr);
if (!docptr->title[0])
{
/* title not explicitly set so use first heading */
cptr = pass2entry (docptr->list, 3);
cptr = pass2strip (cptr);
zptr = (sptr = docptr->title) + sizeof(docptr->title)-1;
while (*cptr && sptr < zptr) *sptr++ = *cptr++;
*sptr = '\0';
}
/* initialise the second pass HTML output buffer */
wasDocMoreHtml (docptr);
h1ptr = docptr->html1;
while (*h1ptr)
{
zptr = (sptr = docptr->html) + docptr->hsize;
sptr += docptr->hlength;
while (*h1ptr && !MATCH6(h1ptr,""))
{
pass2idx (docptr);
count = 14;
}
else
if (MATCH12 (h1ptr, ""))
{
pass2toc1 (docptr);
count = 14;
}
else
wasDocBugcheck(FI_LI);
return (count);
}
/*****************************************************************************/
/*
Heading data to be populated.
*/
int pass2heading (struct wasdoc_st *docptr, char *h1ptr)
{
static char sigNumId [48];
int count, error, offset, numeric, section;
char *cptr, *lptr, *sptr, *zptr;
char numid [48];
if (dbug>1) dbugThis (FI_LI, "pass2heading() %s", dbugMax(h1ptr));
zptr = (sptr = numid) + sizeof(numid)-1;
for (cptr = h1ptr + 8;
*cptr && *cptr != '$' && sptr < zptr;
*sptr++ = *cptr++);
*sptr = '\0';
if (!MATCH5(cptr,"$$-->") || sptr >= zptr) wasDocBugcheck(FI_LI);
sptr = (cptr += 5);
count = pass2find (docptr, numid);
if (count < 0)
{
docptr->errorValue = SS$_NOSUCHOBJECT;
RETURN_FI_LI (docptr, -1)
}
lptr = docptr->list + count;
numeric = pass2numeric (numid);
section = atoi(numid);
if (section < atoi(sigNumId)) strcpy (sigNumId, numid);
if (numeric == 1)
if (section >= 0)
{
if (docptr->chunked > 0)
if (docptr->setNavigate)
pass2navigate (docptr, section-1);
if (section >= 1)
if (docptr->chunked <= 0)
if (docptr->setPaginate)
wasDocAsIs (docptr, "
\n");
}
/* note the beginning of this section's HTML */
offset = docptr->hlength;
/* numeric target ID */
error = wasDocPrintf (docptr, "\n", numid);
if (error) wasDocBugcheck(FI_LI);
/* step to the alphanumeric ID */
count = *lptr++;
lptr += count + 1;
count = *lptr++;
/* heading numeric plus alphanumeric ID */
error = wasDocPrintf (docptr, "\n",
pass2linkid(numid,lptr));
if (error) wasDocBugcheck(FI_LI);
/* alphanumeric target ID */
error = wasDocPrintf (docptr, "\n", lptr);
if (error) wasDocBugcheck(FI_LI);
if (numeric == 1)
if (section > 0)
if (docptr->chunked)
if (docptr->title[0])
{
error = wasDocPrintf (docptr,
"%s
\n",
docptr->title);
if (error) wasDocBugcheck(FI_LI);
}
/* step to the description */
lptr += count + 1;
count = *lptr++;
if (!MATCH2(cptr,"'; cptr++);
if (!*cptr) wasDocBugcheck(FI_LI);
cptr++;
/* insert heading tag including class, style, etc. */
error = wasDocPrintf (docptr, "%*.*s", cptr-sptr, cptr-sptr, sptr);
if (error) wasDocBugcheck(FI_LI);
if (numeric <= 4)
error = wasDocPrintf (docptr,
"%s%s",
numid, lptr);
else
error = wasDocPrintf (docptr,
"%s",
lptr);
if (error) wasDocBugcheck(FI_LI);
/* step to the offset longword and record it */
lptr += count + 1;
*(ULONGPTR)lptr = offset;
while (*cptr && !MATCH3(cptr,"') wasDocBugcheck(FI_LI);
sptr = cptr;
cptr += 5;
if (*cptr == '\n') cptr++;
error = wasDocPrintf (docptr, "%*.*s", cptr-sptr, cptr-sptr, sptr);
if (error) wasDocBugcheck(FI_LI);
if (numeric == 1)
{
if (docptr->setToc2) pass2toc2 (docptr, numid);
if (docptr->setNavigate) pass2navigate (docptr, section);
}
return (cptr - h1ptr);
}
/*****************************************************************************/
/*
Insert a link to another part of the document.
*/
int pass2link (struct wasdoc_st *docptr, char *h1ptr)
{
int count, numeric, section;
char *aptr, *cptr, *lptr, *nptr, *sptr, *zptr;
char buf [256];
if (dbug>2) dbugThis (FI_LI, "pass2link() %s", dbugMax(h1ptr));
/* error report (will be emptied if there is not an error) */
zptr = (sptr = docptr->pass2At) + sizeof(docptr->pass2At)-1;
/* "") || sptr >= zptr) wasDocBugcheck(FI_LI);
aptr = (cptr += 5);
count = pass2find (docptr, buf);
if (count < 0)
{
/* see renderInsertLink() */
cptr = strstr (docptr->pass2At, """) + 6;
sptr = strstr (cptr, """);
wasDocPrintf (docptr, "\">%*.*s", sptr-cptr, sptr-cptr, cptr);
docptr->errorValue = SS$_NOSUCHOBJECT;
RETURN_FI_LI (docptr, -1)
}
docptr->pass2At[0] = '\0';
lptr = docptr->list + count;
count = *lptr++;
/* nptr now points to the numeric ID */
nptr = lptr;
section = atoi(nptr);
numeric = pass2numeric (nptr);
/* and lptr to the alphanumeric ID */
lptr += count + 1;
count = *lptr++;
wasDocPrintf (docptr, "%s#%s",
pass2chunk(docptr,section), pass2linkid(nptr,lptr));
if (MATCH6 (aptr, "\">"))
{
/* empty, so use the heading description */
aptr += 6;
lptr += count + 1;
count = *lptr++;
if (numeric <= 4)
wasDocPrintf (docptr, "\">%s %s", nptr, pass2strip(lptr));
else
wasDocPrintf (docptr, "\">%s", pass2foundin(docptr,nptr));
}
return (aptr - h1ptr);
}
/*****************************************************************************/
/*
Do something else during the second pass.
*/
int pass2extra (struct wasdoc_st *docptr, char *h1ptr)
{
int number;
char *aptr, *cptr, *sptr, *zptr;
char buf [256];
if (dbug>2) dbugThis (FI_LI, "pass2extra() %s", dbugMax(h1ptr));
zptr = (sptr = buf) + sizeof(buf)-1;
for (cptr = h1ptr + 8;
*cptr && *cptr != '$' && sptr < zptr;
*sptr++ = *cptr++);
*sptr = '\0';
if (!MATCH5(cptr,"$$-->") || sptr >= zptr) wasDocBugcheck(FI_LI);
aptr = cptr + 5;
if (MATCH12 (buf, "toc2=next=cols="))
{
number = atoi(cptr = buf + 15);
docptr->setToc2NextCols = number;
while (*cptr && isdigit(*cptr)) cptr++;
while (*cptr && !isdigit(*cptr)) cptr++;
if (isdigit(*cptr))
if ((number = atoi(cptr)) <= 100)
docptr->setToc2NextColsWidth = number;
}
return (aptr - h1ptr);
}
/*****************************************************************************/
/*
This creates an in-memory list of heading numeric IDs, with alphanumeric IDs,
and heading text. The format
stringstringstring
where is the count byte (0..255) and string is the counted string. The
string IS ALSO null-terminated allowing direct use as a null-terminated
"string". The final is an offset into the final HTML document. List
is terminated by a of zero. So a maximal list entry is three lots of
maximum 255 character plus null-termination counted-strings plus one ulong.
*/
void pass2list (struct wasdoc_st *docptr)
{
int count;
char *cptr, *h1ptr, *sptr, *zptr;
uchar *lptr;
char buf [256];
if (dbug>1) dbugThis (FI_LI, "pass2list()");
h1ptr = docptr->html1;
while (*h1ptr)
{
if (docptr->list) docptr->llength = (char*)lptr - docptr->list;
if (docptr->lsize - docptr->llength <= ((1+255+1) * 3) + sizeof(uint))
{
docptr->lsize += LIST_INCREMENT;
docptr->list = realloc (docptr->list, docptr->lsize);
if (!docptr->list) EXIT_FI_LI (vaxc$errno);
lptr = (uchar*)docptr->list + docptr->llength;
}
/* insert numeric ID */
while (*h1ptr && !MATCH4(h1ptr,"$$H:")) h1ptr++;
if (!*h1ptr) break;
for (cptr = sptr = h1ptr + 4; *cptr && *cptr != '$'; cptr++);
if (!MATCH5(cptr,"$$-->")) wasDocBugcheck(FI_LI);
/* eventually it will be! */
docptr->finalH1 = atoi(sptr);
count = cptr - sptr;
cptr += 5;
*lptr++ = count;
while (count-- > 0) *lptr++ = *sptr++;
*lptr++ = '\0';
/* insert alphanumeric ID */
if (!MATCH2(cptr,"') cptr++;
if (!*cptr) wasDocBugcheck(FI_LI);
cptr++;
/* absorb leading white-space */
while (*cptr && isspace(*cptr)) cptr++;
/* note the start of the heading text */
h1ptr = cptr;
zptr = (sptr = buf) + sizeof(buf)-1;
while (*cptr && !MATCH3(cptr,"') cptr++;
if (!*cptr) break;
cptr++;
continue;
}
if (!isalnum(*cptr))
{
cptr++;
continue;
}
*sptr++ = tolower(*cptr++);
}
*sptr = '\0';
if (!*cptr || sptr >= zptr) wasDocBugcheck(FI_LI);
/* absorb trailing white-space */
while (sptr > buf && isspace(*(sptr-1))) sptr--;
count = sptr - buf;
/* if the heading contained no alphanumerics */
if (!count)
{
strcpy (buf, "-empty-");
count = 7;
}
*lptr++ = count;
for (sptr = buf; *sptr; *lptr++ = *sptr++);
*lptr++ = '\0';
/* insert heading text */
cptr = h1ptr;
zptr = (sptr = buf) + sizeof(buf)-1;
while (*cptr && !MATCH3(cptr,"= zptr)
{
/* overly long heading text is truncated - scissors to indicate */
strcpy (sptr-8, "✂");
while (*cptr && !MATCH3(cptr,"")-1;
count = sptr - buf;
if (!count)
{
strcpy (buf, "→ EMPTY HEADING ←");
count = 27;
}
*lptr++ = count;
for (sptr = buf; *sptr; *lptr++ = *sptr++);
*lptr++ = '\0';
/* space for the offset */
*(ULONGPTR)lptr = 0;
lptr += sizeof(ulong);
}
/* terminate the list with a zero length entry (and a bit t'be sure) */
*(ULONGPTR)lptr = 0;
if (dbug>1) pass2dlist (docptr);
}
/*****************************************************************************/
/*
Debug report on ->list content.
*/
void pass2dlist (struct wasdoc_st *docptr)
{
int count, count1, count2, count3, offset;
uchar *lptr, *lptr1, *lptr2, *lptr3;
dbugThis (FI_LI, "pass2dlist() %d/%d", docptr->llength, docptr->lsize);
lptr = (uchar*)docptr->list;
count = 0;
for (;;)
{
count1 = *lptr++;
if (!count1) break;
lptr1 = lptr;
lptr += count1 + 1;
count2 = *lptr++;
lptr2 = lptr;
lptr += count2 + 1;
count3 = *lptr++;
lptr3 = lptr;
lptr += count3 + 1;
offset = *(ULONGPTR)lptr;
lptr += sizeof(ulong);
count++;
dbugThis (NULL, 0, "%d %d%c%s%c %d%c%s%c %d%c%s%c %d",
count, count1, bvbar, lptr1, bvbar,
count2, bvbar, lptr2, bvbar,
count3, bvbar, lptr3, bvbar, offset);
}
}
/*****************************************************************************/
/*
*/
void pass2offset (struct wasdoc_st *docptr)
{
int count;
char *cptr, *h1ptr, *sptr, *zptr;
uchar *lptr;
char buf [256];
if (dbug>1) dbugThis (FI_LI, "pass2offset()");
}
/*****************************************************************************/
/*
Return a string stripped of HTML tags from the heading descriptor.
*/
char* pass2strip (char *cptr)
{
static char buf [256];
char *sptr, *zptr;
while (isspace(*cptr)) cptr++;
zptr = (sptr = buf) + sizeof(buf)-1;
while (*cptr && sptr < zptr)
{
if (*cptr != '<')
{
*sptr++ = *cptr++;
continue;
}
cptr++;
while (*cptr != '>') cptr++;
if (*cptr) cptr++;
}
*sptr = '\0';
return (buf);
}
/*****************************************************************************/
/*
Finding returns the offset from the start of the list, or the if not found.
*/
int pass2find (struct wasdoc_st *docptr, char *find)
{
uint count, flen, flength, numeric;
char *cptr, *sptr;
uchar *lptr;
if (dbug>2) dbugThis (FI_LI, "pass2find() %s", dbugAll(find));
/* is a numeric ID being searched for? (n.n.n.n.n.n) */
for (cptr = find; *cptr && (isdigit(*cptr) || *cptr == '.'); cptr++);
numeric = ((cptr > find) && !*cptr);
if (!numeric) for (cptr = find; *cptr; cptr++);
flength = cptr - find;
if (!flength) return (0);
lptr = (uchar*)docptr->list;
for (;;)
{
/* note the start of entry */
sptr = (char*)lptr;
/* length of numeric ID */
count = *lptr++;
/* if end of list */
if (!count) break;
cptr = find;
flen = flength;
if (numeric)
{
/* compare the numeric ID */
if (count == flen)
while (count && flen && *lptr == *cptr)
{ count--; flen--; lptr++; cptr++; }
if (!count && !flen) break;
/* step over remainder, then alphanumeric, then text */
lptr += count + 1;
count = *lptr++;
lptr += count + 1;
count = *lptr++;
lptr += count + 1;
lptr += sizeof(ulong);
}
else
{
/* step over the numeric ID */
lptr += count + 1;
/* compare the alphanumeric ID */
count = *lptr++;
if (count == flen)
while (count && flen && *lptr == *cptr)
{ count--; flen--; lptr++; cptr++; }
if (!count && !flen) break;
/* step over remainder, then text */
lptr += count + 1;
count = *lptr++;
lptr += count + 1;
lptr += sizeof(ulong);
}
}
if (count || flen) return (-1);
return (sptr - docptr->list);
}
/*****************************************************************************/
/*
Given a pointer to the list entry, and 1 for the numeric identifier, 2 for the
alphanumeric identifier, 3 for the heading description, return the value in a
static buffer.
*/
char* pass2entry (char *lptr, int which)
{
static char buf [256];
int count;
char *cptr, *sptr;
if (which < 1 || which > 3) wasDocBugcheck(FI_LI);
count = *lptr++;
if (!count || count > 24) wasDocBugcheck(FI_LI);
if (which > 1)
{
lptr += count + 1;
count = *lptr++;
if (!count || count > 255) wasDocBugcheck(FI_LI);
if (which > 2)
{
lptr += count + 1;
count = *lptr++;
if (!count || count > 255) wasDocBugcheck(FI_LI);
}
}
for (sptr = buf; count--; *sptr++ = *lptr++);
*sptr = '\0';
return (buf);
}
/*****************************************************************************/
/*
Return a pointer to the next entry in the heading list.
*/
char* pass2next (char *lptr)
{
int count;
count = *lptr++;
if (!count || count > 24) wasDocBugcheck(FI_LI);
lptr += count + 1;
count = *lptr++;
if (!count || count > 255) wasDocBugcheck(FI_LI);
lptr += count + 1;
count = *lptr++;
if (!count || count > 255) wasDocBugcheck(FI_LI);
lptr += count + 1;
lptr += sizeof(ulong);
return (lptr);
}
/*****************************************************************************/
/*
Generate a primary Table of Content using the heading list.
A primary TOC includes all .. headings.
*/
int pass2toc1 (struct wasdoc_st *docptr)
{
int count, error = 0, numeric, section, total = 0;
char *lptr, *lptr1, *lptr2, *lptr3, *sptr;
if (dbug>1) dbugThis (FI_LI, "pass2toc1()");
lptr = docptr->list;
for (;;)
{
lptr1 = lptr;
/* point to the numeric ID */
count = *lptr1++;
if (!count) break;
/* point to the alphanumeric ID */
lptr2 = lptr1 + count + 1;
count = *lptr2++;
/* point to the heading description */
lptr3 = lptr2 + count + 1;
count = *lptr3++;
/* point at the next entry */
lptr = lptr3 + count + 1 + sizeof(ulong);
numeric = pass2numeric (lptr1);
if (numeric > 4) continue;
section = atoi(lptr1);
if (!total++)
{
error = wasDocPrintf (docptr,
"\n\n\
\n",
docptr->setTocCols);
if (error) RETURN_FI_LI (docptr, error);
}
if (docptr->setTocForm[0] >= '1' &&
docptr->setTocForm[0] <= '9')
{
if (docptr->setTocForm[1])
/* numbering and heading separated by repeated characters */
error = wasDocPrintf (docptr,
"%s%s\
| %s\n",
pass2chunk(docptr,section),
pass2linkid(lptr1,lptr2), lptr1,
docptr->setTocForm+1,
numeric == 1 ? " majr" : "",
pass2chunk(docptr,section),
pass2linkid(lptr1,lptr2), lptr3);
else
/* just empty space */
error = wasDocPrintf (docptr,
" |
%s\
| %s\n",
pass2chunk(docptr,section),
pass2linkid(lptr1,lptr2), lptr1,
numeric == 1 ? " majr" : "",
pass2chunk(docptr,section),
pass2linkid(lptr1,lptr2), lptr3);
}
else
/* anything else (e.g. HTML entity) is just used as-is */
error = wasDocPrintf (docptr,
" |
%s%s\n",
numeric == 1 ? " majr" : "",
pass2chunk(docptr,section),
pass2linkid(lptr1,lptr2),
docptr->setTocForm, lptr3);
if (error) RETURN_FI_LI (docptr, error);
}
if (total) wasDocAsIs (docptr, " |
\n
\n");
return (error);
}
/*****************************************************************************/
/*
Generate a secondary (sub-)TOC after each using the heading list.
*/
int pass2toc2 (struct wasdoc_st *docptr, char *numid)
{
int cols, count, digit1, error = 0,
numeric, section, subtotal = 0, total = 0;
char *cptr, *lptr, *lptr1, *lptr2, *lptr3, *sptr;
char width [64];
if (dbug>1) dbugThis (FI_LI, "pass2toc2() %s", dbugAll(numid));
if (!docptr->setToc2) return (0);
count = pass2find (docptr, numid);
if (count < 0) RETURN_FI_LI (docptr, count)
lptr = docptr->list + count;
digit1 = atoi (numid);
/* determine how many subsections */
lptr1 = lptr;
for (;;)
{
count = *lptr1++;
if (!count) break;
if (atoi(lptr1) != digit1) break;
numeric = pass2numeric (lptr1);
if (numeric > 1 && numeric <= 4) subtotal++;
lptr1 += count + 1;
count = *lptr1++;
lptr1 += count + 1;
count = *lptr1++;
lptr1 += count + 1;
lptr1 += sizeof(ulong);
}
for (;;)
{
lptr1 = lptr;
/* point to the numeric ID */
count = *lptr1++;
if (!count) break;
if (atoi(lptr1) != digit1) break;
/* point to the alphanumeric ID */
lptr2 = lptr1 + count + 1;
count = *lptr2++;
/* point to the heading description */
lptr3 = lptr2 + count + 1;
count = *lptr3++;
/* point at the next entry */
lptr = lptr3 + count + 1 + sizeof(ulong);
numeric = pass2numeric (lptr1);
if (numeric == 1) continue;
if (docptr->setToc2 == 1 && numeric > 4) continue;
section = atoi(lptr1);
if (!total++)
{
if (subtotal > 4)
{
if (docptr->setToc2NextColsWidth)
sprintf (width, " style=\"width:%d%%;max-width:%d%%;\"",
docptr->setToc2NextColsWidth,
docptr->setToc2NextColsWidth);
else
if (docptr->setToc2ColsWidth)
sprintf (width, " style=\"width:%d%%;max-width:%d%%;\"",
docptr->setToc2ColsWidth,
docptr->setToc2ColsWidth);
else
width[0] = '\0';
if (!(cols = docptr->setToc2NextCols)) cols = docptr->setToc2Cols;
error = wasDocPrintf (docptr,
"\n\n\
\n", cols, width);
}
else
error = wasDocPrintf (docptr, "\n\n");
if (error) RETURN_FI_LI (docptr, error);
docptr->setToc2NextCols = docptr->setToc2NextColsWidth = 0;
}
if (numeric <= 4)
error = wasDocPrintf (docptr,
"\
%s\
%s\n",
pass2chunk(docptr,section),
pass2linkid(lptr1,lptr2),
lptr1, pass2strip(lptr3));
else
error = wasDocPrintf (docptr,
" |
%s\n",
pass2chunk(docptr,section),
pass2linkid(lptr1,lptr2),
pass2strip(lptr3));
if (error) RETURN_FI_LI (docptr, error);
}
if (total) wasDocAsIs (docptr, " |
\n\n");
return (error);
}
/*****************************************************************************/
/*
Return true if the fifth or sixth integer is the "suppress index entry"
sentinal.
*/
int pass2noidx (char *cptr)
{
while (isdigit(*cptr)) cptr++;
if (*cptr++ != '.') return (0);
while (isdigit(*cptr)) cptr++;
if (*cptr++ != '.') return (0);
while (isdigit(*cptr)) cptr++;
if (*cptr++ != '.') return (0);
while (isdigit(*cptr)) cptr++;
if (*cptr++ != '.') return (0);
if (atoi(cptr) == SECTION_NO_INDEX) return (1);
while (isdigit(*cptr)) cptr++;
if (*cptr++ != '.') return (0);
return (atoi(cptr) == SECTION_NO_INDEX);
}
/*****************************************************************************/
/*
Return true if the fifth or sixth integer is the not "'heading' in section"
sentinal.
*/
int pass2notin (char *cptr)
{
while (isdigit(*cptr)) cptr++;
if (*cptr++ != '.') return (0);
while (isdigit(*cptr)) cptr++;
if (*cptr++ != '.') return (0);
while (isdigit(*cptr)) cptr++;
if (*cptr++ != '.') return (0);
while (isdigit(*cptr)) cptr++;
if (*cptr++ != '.') return (0);
if (atoi(cptr) == SECTION_NOT_IN) return (1);
while (isdigit(*cptr)) cptr++;
if (*cptr++ != '.') return (0);
return (atoi(cptr) == SECTION_NOT_IN);
}
/*****************************************************************************/
/*
Generate the document index using the heading list.
Use this pass to set the offset into the final HTML.
*/
int pass2idx (struct wasdoc_st *docptr)
{
static char sigNumId [48];
int count, entries = 0, error = 0, idx, numeric, section, total = 0;
char *abc, *cptr, *dptr, *lptr, *lptr1, *lptr2, *lptr3, *sptr, *zptr;
char alpha[16] = "";
char **sarray;
if (dbug>1) dbugThis (FI_LI, "pass2idx()");
abc = docptr->idxCollate;
for (idx = 0;; idx += 2)
{
if (!abc[idx]) break;
alpha[0] = abc[idx];
if (alpha[0] == '0')
/* Combining Short Solidus Overlay turn zero into slash-zero */
strcpy (alpha, "0̷");
else
alpha[1] = '\0';
if (dbug>2) dbugThis (FI_LI, "%c%c%c", bvbar, abc[idx], bvbar);
/* total the number of entries matching the collate character */
lptr = docptr->list;
for (total = 0;;)
{
count = *lptr++;
if (!count) break;
lptr1 = lptr;
lptr += count + 1;
count = *lptr++;
lptr += count + 1;
count = *lptr++;
if (*(cptr = lptr) == '&')
{
/* step over HTML-entified characters */
for (cptr++; *cptr && isalnum(*cptr); cptr++);
if (*cptr == ';') cptr++; else cptr = lptr;
}
/* if not this "suppress index entry" sentinal */
if (!pass2noidx (lptr1))
/* match first character of heading description */
if (*cptr == abc[idx] || *cptr == abc[idx+1])
total++;
lptr += count + 1 + sizeof(ulong);
}
/* allocate a suitably sized sort array of pointers */
sarray = calloc (total + 1, sizeof(char*));
if (!sarray) exit (vaxc$errno);
/* populate the array of pointers */
lptr = docptr->list;
for (total = 0;;)
{
/* note the current list entry */
sarray[total] = lptr;
count = *lptr++;
if (!count) break;
lptr1 = lptr;
lptr += count + 1;
count = *lptr++;
lptr += count + 1;
count = *lptr++;
if (*(cptr = lptr) == '&')
{
/* step over HTML-entified characters */
for (cptr++; *cptr && isalnum(*cptr); cptr++);
if (*cptr == ';') cptr++; else cptr = lptr;
}
/* if not this "suppress index entry" sentinal */
if (!pass2noidx (lptr1))
/* if matches increment to the next sort array element */
if (*cptr == abc[idx] || *cptr == abc[idx+1])
total++;
lptr += count + 1 + sizeof(ulong);
}
/* NULL the final element */
sarray[total] = NULL;
/* sort the array */
if (docptr->setIdxSort) qsort (sarray, total, sizeof(char*), pass2isort);
/* output the (sorted) array */
for (total = 0; sarray[total]; total++)
{
if (!entries++)
{
error = wasDocPrintf (docptr,
"\n\
\n",
docptr->setIdxCols);
if (error) RETURN_FI_LI (docptr, error);
}
lptr1 = sarray[total];
count = *lptr1++;
lptr2 = lptr1 + count + 1;
count = *lptr2++;
lptr3 = lptr2 + count + 1;
lptr3++;
section = atoi(lptr1);
numeric = pass2numeric (lptr1);
if (dbug>2) dbugThis (FI_LI, "%d %c%s%c %c%s%c %c%s%c",
numeric,
bvbar, lptr1, bvbar,
bvbar, lptr2, bvbar,
bvbar, lptr3, bvbar);
if (numeric <= 4)
{
error = wasDocPrintf (docptr,
"%s | \
\
%s %s\n",
alpha,
pass2chunk(docptr,section),
pass2linkid(lptr1,lptr2),
lptr1, pass2strip(lptr3));
if (error) RETURN_FI_LI (docptr, error);
}
else
if (pass2notin (lptr1))
{
error = wasDocPrintf (docptr,
" |
%s | \
\
%s\n",
alpha,
pass2chunk(docptr,section),
pass2linkid(lptr1,lptr2),
pass2strip(lptr3));
if (error) RETURN_FI_LI (docptr, error);
}
else
{
error = wasDocPrintf (docptr,
" |
%s | \
%s\n",
alpha,
pass2chunk(docptr,section),
pass2linkid(lptr1,lptr2),
pass2foundin(docptr,lptr1));
if (error) RETURN_FI_LI (docptr, error);
}
if (alpha[0] != '&') strcpy (alpha, " ");
}
free (sarray);
}
if (entries) wasDocAsIs (docptr, " |
\n
\n");
return (error);
}
/*****************************************************************************/
/*
Comparison function for pass2idx() qsort().
The heading description strings are compared non-case-sensitive.
*/
int pass2isort (const void *a, const void *b)
{
int count;
char *aptr, *bptr;
aptr = *((char**)a);
count = *aptr++;
aptr += count + 1;
count = *aptr++;
aptr += count + 2;
bptr = *((char**)b);
count = *bptr++;
bptr += count + 1;
count = *bptr++;
bptr += count + 2;
return (strcasecmp (aptr, bptr));
}
/*****************************************************************************/
/*
*/
void pass2navigate
(
struct wasdoc_st *docptr,
int section
)
{
if (dbug>1) dbugThis (FI_LI, "pass2navigate(%d)", section);
if (!docptr->setNavigate) return;
wasDocPrintf (docptr,
"\n\n\
%s\n",
docptr->setNavigate > 1 ? " NAVprint" : "",
docptr->iconBack);
if (section > 0)
{
wasDocPrintf (docptr, " | %s\n",
pass2chunk(docptr,section-1), section-1,
docptr->iconPrev);
wasDocPrintf (docptr, " | %s\n",
pass2chunk(docptr,0),
docptr->iconTop);
}
else
wasDocPrintf (docptr, " | %s\n | %s\n",
docptr->iconPrev, docptr->iconTop);
if (section < docptr->finalH1)
wasDocPrintf (docptr, " | %s\n",
pass2chunk(docptr,section+1), section+1,
docptr->iconNext);
else
wasDocPrintf (docptr, " | %s\n", docptr->iconNext);
wasDocPrintf (docptr,
" | %s\n\
|
\n",
docptr->iconForw);
}
/*****************************************************************************/
/*
Chunked wasDOC is where the client is provided with only one major section
() at a time. This is a three digit pseudo-section appended to the path,
leading zeroes as required. This function creates a unique path that includes
a "chunk" number based on the leading fragment ID integer so that the browser
requests using that path keeping all the (global) fragment IDs viable.
This function is NOT REENTRANT and must be used at most ONCE per print.
*/
char* pass2chunk
(
struct wasdoc_st *docptr,
int number
)
{
static char path [256];
int cnt = 0;
char *cptr, *sptr, *zptr;
if (dbug>1) dbugThis (FI_LI, "pass2chunk() %d %d", number);
if (docptr->chunked <= 0) return ("");
zptr = (sptr = path) + sizeof(path)-16;
if (docptr->oname[0])
{
/* output to file(s) */
for (cptr = docptr->oname; *cptr && sptr < zptr; *sptr++ = *cptr++);
sptr += sprintf (sptr, "%03.3d.html", number);
}
else
{
/* output via script */
for (cptr = docptr->uri; *cptr && sptr < zptr; *sptr++ = *cptr++);
if (*(cptr-1) != '/' && sptr < zptr) *sptr++ = '/';
sptr += sprintf (sptr, "%03.3d", number);
if (sptr < zptr) *sptr++ = '/';
}
*sptr = '\0';
return (path);
}
/*****************************************************************************/
/*
Where a non-major heading (5 and 6) create a link description that provides
what section the heading is found in. |lptr1| points to the numeric ID.
Return a pointer to a static buffer containing the description.
*/
char* pass2foundin (struct wasdoc_st *docptr, char *lptr1)
{
static char buf [256];
int count;
char *cptr, *lptr, *lptr2, *lptr3, *sptr, *zptr;
lptr = lptr1 - 1;
count = *lptr++;
lptr += count + 1;
count = *lptr++;
lptr2 = lptr;
lptr += count + 1;
count = *lptr++;
lptr3 = lptr;
zptr = (sptr = buf) + sizeof(buf)-1;
for (cptr = "‘"; *cptr && sptr < zptr; *sptr++ = *cptr++);
for (cptr = pass2strip(lptr3);
*cptr && sptr < zptr;
*sptr++ = *cptr++);
for (cptr = "’"; *cptr && sptr < zptr; *sptr++ = *cptr++);
/* use the default heading */
if (!*(cptr = docptr->setFoundIn)) cptr = DEFAULT_FOUND_IN;
while (*cptr && sptr < zptr) *sptr++ = *cptr++;
if (cptr = pass2parent (lptr1))
{
if (MATCH3 (cptr, "0."))
cptr = docptr->title;
else
if ((count = pass2find (docptr, cptr)) >= 0)
{
lptr = docptr->list + count;
count = *lptr++;
/* the numeric ID */
cptr = pass2parent (lptr);
while (*cptr && sptr < zptr) *sptr++ = *cptr++;
lptr += count + 1;
count = *lptr++;
lptr += count + 1;
count = *lptr++;
/* the heading description */
cptr = pass2strip (lptr);
}
else
cptr = "?";
}
else
cptr = "¿";
if (sptr < zptr) *sptr++ = ' ';
while (*cptr && sptr < zptr) *sptr++ = *cptr++;
*sptr = '\0';
return (buf);
}
/*****************************************************************************/
/*
Parse out the "parent" up to four-integer numeric ID and return.
So, "8.0.0.0.0.2" becomes "8.", and "8.1.0.0.1.0" becomes "8.1".
*/
char* pass2parent (char *numid)
{
static char buf [48];
int digit4 [1+4];
char *cptr;
if (dbug>2) dbugThis (FI_LI, "pass2parent() |%s|", dbugAll(numid));
digit4[1] = atoi (cptr = numid);
while (isdigit(*cptr)) cptr++;
if (*cptr) cptr++;
digit4[2] = atoi (cptr);
while (isdigit(*cptr)) cptr++;
if (*cptr) cptr++;
digit4[3] = atoi (cptr);
while (isdigit(*cptr)) cptr++;
if (*cptr) cptr++;
digit4[4] = atoi (cptr);
if (digit4[4])
sprintf (buf, "%d.%d.%d.%d", digit4[1], digit4[2], digit4[3], digit4[4]);
else
if (digit4[3])
sprintf (buf, "%d.%d.%d", digit4[1], digit4[2], digit4[3]);
else
if (digit4[2])
sprintf (buf, "%d.%d", digit4[1], digit4[2]);
else
sprintf (buf, "%d.", digit4[1]);
if (dbug>2) dbugThis (FI_LI, "%s", dbugAll(buf));
return (buf);
}
/*****************************************************************************/
/*
Return a pointer to a static ID for a '' anchor
target. Numeric ID is always separated from alphanumeric by a period.
Examples: "0.alnumid", "8.alnumid", "8.1.alnumid".
*/
char* pass2linkid (char *numid, char *alnumid)
{
static char buf [256];
char *cptr, *sptr, *zptr;
zptr = (sptr = buf) + sizeof(buf)-1;
cptr = pass2parent (numid);
while (*cptr && sptr < zptr) *sptr++ = *cptr++;
if (*(sptr-1) != '.' && sptr < zptr) *sptr++ = '.';
for (cptr = alnumid; *cptr && sptr < zptr; *sptr++ = *cptr++);
*sptr = '\0';
return (buf);
}
/*****************************************************************************/
/*
Return the number of section numbers represented by the numeric ID.
*/
int pass2numeric (char *numid)
{
int numeric = 0;
if (!isdigit(*numid)) return (0);
while (*numid)
{
while (isdigit(*numid)) numid++;
numeric++;
if (!*numid) return (numeric);
if (*numid != '.') return (0);
numid++;
}
return (numeric);
}
/*****************************************************************************/