/*** helpif.c - help routines for user interface assistance.
*
*   Copyright <C> 1988, Microsoft Corporation
*
* Purpose:
*  These routines aid in the interpretation of help text by applications.
*  After decompression, the help text is encoded into a line oriented format
*  which includes text, highlighting and cross reference information.
*
*  Each line of text is formatted in the database as:
*
*  +--------+----------------+--------+---------------+------+---------------+
*  | cbText | - Ascii Text - | cbAttr | - Attr info - | 0xff | - Xref Info - |
*  +--------+----------------+--------+---------------+------+---------------+
*
*  Where:
*
*	cbText	    - a BYTE which contains the length of the ascii text plus
*		      one (for itself).
*	Ascii Text  - Just that, the ascii text to be displayed
*	cbAttr	    - a WORD which contains the length of the attribute
*		      information *plus* the cross reference information.
*	Attr info   - attribute/length pairs of highlighting information plus
*		      two (for itself).
*	0xff	    - Attr info terminator byte (present ONLY IF Xref
*		      information follows)
*	Xref Info   - Cross Referencing information.
*
* Notes:
*  If the LAST attributes on a line are "plain", then the attribute/length
*  pair is omitted, and the rest of the line is assumed plain.
*
*  Given a pointer to a line, a pointer to the next line is:
*
*	    Pointer + cbText + cbAttr
*
*  A line which has no cross-reference or highlighting will have a cbAttr of
*  2, and nothing else.
*
* Revision History:
*
*	25-Jan-1990 ln	locate -> hlp_locate
*	19-Aug-1988 ln	Move "locate" to assembly language hloc.asm
*   []	26-Jan-1988 LN	Created
*
*************************************************************************/
#include <stdlib.h>
#include <stdio.h>

#if defined (OS2)
#else
#include <windows.h>
#endif

#include "help.h"
#include "helpfile.h"
#include "helpsys.h"

/************************************************************************
**
** Foward Declarations
*/
uchar near pascal toupr(uchar);

/*** HelpGetLineAttr - Return attributes associated with a line of ascii text
*
*  Interprets the help files stored format and return a line at a time of
*  attribute information.
*
* Input:
*  ln		= 1 based line number to return
*  cbMax	= Max number of bytes to transfer
*  pbDst	= pointer to destination
*  pbTopic	= PB pointer to topic text
*
* Output:
*  Returns number of characters transfered (not including terminating 0xffff
*  attribute), or 0 if that line does not exist.
*
*************************************************************************/
ushort far pascal LOADDS HelpGetLineAttr(
ushort	ln,
int	cbMax,
lineattr far *pbDst,
PB	pbTopic
) {
lineattr far *pbDstBegin;
uchar far *pTopic;
/*
** Form valid (locked) pointer to topic text & working pointer to detination
*/
pTopic = PBLOCK (pbTopic);
pbDstBegin = pbDst;
/*
** Information is on present in compressed files. Locate the line in the text,
** and then point at the attribute information therein.
*/
#if ASCII
if (((topichdr far *)pTopic)->ftype & FTCOMPRESSED) {
#endif
    if (pTopic = hlp_locate(ln,pTopic)) {
	pTopic += *pTopic;
/*
** Start by giving ln the count of encoded bytes. Then while there are
** bytes, and we have enough room in the destination, AND we haven't reached
** the end of the attribute information, then for each cb/attr pair, copy
** them over, converting from our internal byte-per format to the external
** word-per format.
*/
	ln = *((ushort far UNALIGNED *)pTopic)++ - (ushort)2;
	while (   ln
	       && (cbMax >= sizeof(lineattr))
	       && (((intlineattr far *)pTopic)->attr != (uchar)0xff)
	       ) {
	    *(ushort UNALIGNED *)&(pbDst->cb)	= ((intlineattr far UNALIGNED *)pTopic)->cb;
	    *(ushort UNALIGNED *)&(pbDst->attr) = ((intlineattr far UNALIGNED *)pTopic)->attr;
	    pbDst++;
	    ((intlineattr *)pTopic)++;
	    cbMax -= sizeof(lineattr);
	    ln -= sizeof(intlineattr);
	    }
	}
#if ASCII
    }
#endif
PBUNLOCK (pbTopic);
/*
** Finally, if there is room in the destination buffer, terminate the
** attributes with "default attributes to the end of line", and then
** attribute ffff, signalling the end of the buffer.
*/
if (cbMax >= sizeof(lineattr)) {
    pbDst->cb = 0xffff;
    pbDst->attr = 0;
    cbMax -= sizeof(lineattr);
    pbDst++;
    }
if (cbMax >= sizeof(pbDst->attr))
    pbDst->attr = 0xffff;
/*
** return the number of bytes transferred, not including the terminating
** word.
*/
return (ushort)((uchar far *)pbDst - (uchar far *)pbDstBegin);

/* end HelpGetLineAttr */}

/************************************************************************
**
** HelpHlNext - Locate next cross reference
**
** Purpose:
**  Locates the next cross reference in the help topic. Locates either the
**  next physical cross reference, or the next referece beginning with a
**  particular character (case insensitive!). Locates either forward or
**  backward.
**
** Entry:
**  cLead	= leading character, or flag, indicating direction and type
**		  of search. May be:
**			NULL:	Get next sequential cross reference
**			-1:	Get previous sequential cross reference
**			char:	Get next cross reference beginning with 'char'
**			-char:	Get previous cross reference beginning with
**				'char'
**  pbTopic	= pointer to topic text.
**  photspot	= pointer to hotspot structure to recive info. (line and col
**		  indicate starting point)
**
** Exit:
**  returns TRUE if cross reference found, hotspot structure updated.
**
** Exceptions:
**  returns 0 if no such cross reference.
*/
f pascal far LOADDS HelpHlNext(cLead,pbTopic, photspot)
int	cLead;
PB	pbTopic;
hotspot far *photspot;
{
ushort	cbAttr;
ushort	col;
ushort	ln;
uchar far *pbEnd;			/* pointer to next line 	*/
uchar far *pbLineCur;			/* pointer to current line	*/
uchar far *pbFound	= 0;		/* found entry, perhaps 	*/
uchar far *pText;
uchar far *pTopic;

pTopic = PBLOCK (pbTopic);
col = photspot->col;			/* save these			*/
ln = photspot->line;
if (((topichdr far *)pTopic)->ftype & FTCOMPRESSED) {
    while (1) {
	if (ln == 0) break;			/* if not found, ret	*/
	pbLineCur = hlp_locate(ln,pTopic);	    /* find line	    */
	if (pbLineCur == 0) break;		/* if not found, ret	*/
	pText = pbLineCur;			/* point at topic text	*/
	pbLineCur += *pbLineCur;		/* skip the topic text	*/
	cbAttr = *((ushort far UNALIGNED *)pbLineCur)++ - (ushort)sizeof(ushort);
	pbEnd = pbLineCur + cbAttr;		/* next line		*/
	while (cbAttr && (((intlineattr far UNALIGNED *)pbLineCur)->attr != 0xff)) {
	    pbLineCur += sizeof(intlineattr);
	    cbAttr -=sizeof(intlineattr);
	    }
	if (cbAttr)
	    pbLineCur += sizeof(uchar); 	/* skip (0xff) attr	*/

	while (pbLineCur < pbEnd) {		/* scan rest for data	*/
/*
** in a forward scan, the first cross reference (with appropriate char) that is
** greater than our current position, is the correct one.
*/
	    if (cLead >= 0) {			/* forward scan 	*/
		if (col <= *(pbLineCur+1))	/* if found		*/
		    if ((cLead == 0)		/* and criteria met	*/
			|| (toupr(*(pText + *pbLineCur)) == (uchar)cLead)) {
		    pbFound = pbLineCur;
		    break;
		    }
		}
/*
** in a backward scan, we accept the LAST item we find which is less than
** the current position.
*/
	    else {
		if (col > *(pbLineCur))     /* if a candidate found */
		    if ((cLead == -1)	    /* and criteria met     */
			|| (toupr(*(pText + *pbLineCur)) == (uchar)-cLead))
			pbFound = pbLineCur;/* remember it	    */
		}
	    pbLineCur += 2;		    /* skip column spec     */
	    if (*pbLineCur)
		while (*pbLineCur++);	    /* skip string	    */
	    else
		pbLineCur += 3;
	    }

	if (pbFound) {			    /* if we found one	    */
	    *(ushort UNALIGNED *)&(photspot->line) = ln;
	    *(ushort UNALIGNED *)&(photspot->col)  = (ushort)*pbFound++;
	    *(ushort UNALIGNED *)&(photspot->ecol) = (ushort)*pbFound++;
	    *(uchar *UNALIGNED *)&(photspot->pXref) = pbFound;
	    PBUNLOCK (pbTopic);
	    return TRUE;
	    }
/*
** move on to next line.
*/
	if (cLead >= 0) {
	    ln++;
	    col = 0;
	    }
	else {
	    ln--;
	    col = 127;
	    }
	}
    }

PBUNLOCK (pbTopic);
return FALSE;
/* end HelpHlNext */}

/************************************************************************
**
** HelpXRef - Return pointer to Xref String
**
** Purpose:
**  Given a row, column (in a hotspot structure) and topic, return a pointer
**  to a cross reference string.
**
** Entry:
**  pbTopic	= Pointer to topic text
**  photspot	= Pointer to hotspot structure to update
**
** Exit:
**  returns far pointer into topic text of cross reference string & updates
**  hotspot structure.
**
** Exceptions:
**  returns NULL if no cross reference for that line.
**
*/
char far * pascal far LOADDS HelpXRef(pbTopic, photspot)
PB	pbTopic;
hotspot far *photspot;
{
uchar far *pTopic;
ushort	col;				/* column requested		*/
ushort	ln;				/* line requested		*/

pTopic = PBLOCK (pbTopic);
col = photspot->col;			/* save these			*/
ln = photspot->line;
if (((topichdr far *)pTopic)->ftype & FTCOMPRESSED)
    if (HelpHlNext(0,pbTopic,photspot)) 	/* if xref found	*/
	if (   (photspot->line == ln)		/* & our req. in range	*/
	    && (   (col >= photspot->col)
		&& (col <= photspot->ecol))) {
	    PBUNLOCK (pbTopic);
	    return photspot->pXref;		/* return ptr		*/
	    }

PBUNLOCK (pbTopic);
return 0;

/* end HelpXRef */}