/*
 * CDE - Common Desktop Environment
 *
 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these libraries and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */
/* $TOG: FormatCCDF.c /main/23 1999/10/14 15:06:28 mgreess $ */
/************************************<+>*************************************
 ****************************************************************************
 **
 **   File:	FormatCCDF.c
 **
 **   Project:    Unix Desktop Help
 **
 **   Description: This code formats an file in CCDF (Cache Creek
 **		   Distribution Format) into internal format.
 **
 **  (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 Hewlett-Packard Company
 **
 **  (c) Copyright 1993, 1994 Hewlett-Packard Company
 **  (c) Copyright 1993, 1994 International Business Machines Corp.
 **  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
 **  (c) Copyright 1993, 1994 Novell, Inc.
 **
 **
 **
 ****************************************************************************
 ************************************<+>*************************************/

/*
 * system includes
 */
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#if defined(sun)
#include <sys/utsname.h>
#endif
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

#include <X11/Xlib.h>
#include <X11/Xresource.h>

#ifdef X_NOT_STDC_ENV
extern int errno;
#endif

/*
 * Canvas Engine includes
 */
#include "CanvasP.h"
#include "CanvasSegP.h"
#include "LinkMgrP.h"

/*
 * private includes
 */
#include "bufioI.h"
#include "Access.h"
#include "AccessI.h"
#include "AccessP.h"
#include "AccessCCDFI.h"
#include "CanvasError.h"
#include "CleanUpI.h"
#include "CvStringI.h"
#include "CvtToArrayP.h"
#include "FontAttrI.h"
#include "FormatUtilI.h"
#include "FormatCCDFI.h"
#include "CCDFUtilI.h"
#include "HelpXlate.h"
#include "StringFuncsI.h"

extern char *_DtHelpGetLocale(void);

#ifdef NLS16
#endif

/******************************************************************************
 * Private variables and defines.
 *****************************************************************************/
#define	BUFF_SIZE	1024
#define	GROW_SIZE	10
#define	INFO_GROW	256

#define	CCDF_LINK_JUMP_REUSE	0
#define	CCDF_LINK_JUMP_NEW	1
#define	CCDF_LINK_DEFINITION	2
#define	CCDF_LINK_EXECUTE	3
#define	CCDF_LINK_MAN_PAGE	4
#define	CCDF_LINK_APP_DEFINE	5

#define	IsTypeHyperText(x)	(((x) & _DtCvHYPER_TEXT) ? 1 : 0)
#define	IsTypeNewLine(x)	(((x) & _DtCvNEW_LINE) ? 1 : 0)
#define	NextAvailSeg(x)		(NULL != x.list ? &(x.list[x.cnt]) : NULL)

/******************************************************************************
 * Private structures
 ******************************************************************************/
typedef	struct	_segList {
	int		 cnt;
	int		 max;
	_DtCvSegment	*list;
} SegList;

typedef	struct _formatVariables {

	int		 last_was_space;
	int		 last_was_mb;
	int		 last_was_nl;

	char		*rd_buf;
	char		*rd_ptr;
	char		*fmt_buf;
	char		*my_path;
	char		*vol_name;
	char		*topic_id;
	char		*topic_char_set;
	char		*topic_lang;

	int		 rd_size;
	int		 rd_flag;

	int		 topic_mb_max;
	int		 cur_mb_max;

	int		 fmt_buf_max;
	int		 fmt_size;
	int		 cell_cnt;

	BufFilePtr	 my_file;
	SegList		 my_list;
	_DtCvLinkDb	 my_links;
	_FrmtUiInfo	*ui_info;
} FormatVariables;


#undef NormalState

enum	_processState
  {
    NormalState,
    LabelBody
  };

typedef enum _processState	ProcessState;

/******************************************************************************
 * Private Function Declarations
 ******************************************************************************/
static	_DtCvSegment	*AllocateSegments(
				_DtCvSegment      *p_seg,
				int              original_size,
				int              new_size );
static	int		 AppendCharToInfo(
				FormatVariables  *cur_vars,
				char		**src );
static	int		 AppendOctalToInfo(
				FormatVariables *cur_vars,
				char		*src );
static	int		 AppendSpaceToInfo ( 
				FormatVariables  *cur_vars,
				char		**src,
				_DtCvFrmtOption	  type );
static	int		 AppendToInfo (
				FormatVariables  *cur_vars,
				char		**src,
				const char	 *scan_string );
static	int		 ChangeFont(
				int		  whichOne,
				unsigned long	  seg_flags,
				_DtCvFrmtOption	  frmt_type,
				_DtHelpFontHints *font_attr,
				int		  linkspec,
				FormatVariables	 *cur_vars,
				ProcessState	  cur_state,
				int		  flags,
				int		  ret_on_nl,
				int		  fnt_flag);
static	int		 CheckIdString(
				FormatVariables	 *cur_vars,
				ProcessState	  cur_state,
				unsigned long	  seg_flags,
				_DtCvFrmtOption	  frmt_type,
				_DtHelpFontHints *font_attr,
				int		  linkspec,
				int		  flags,
				int		  ret_on_nl,
				int		  fnt_flag);
static	int		 CheckSaveSegment (
				unsigned long	  seg_flags,
				_DtHelpFontHints *font_attr,
				int 		 link_spec,
				FormatVariables	 *cur_vars );
static	int		 CreateSaveGraphic (
				FormatVariables *cur_vars,
				int		 type,
				char		*file_name,
				int		 link_spec );
static	int		 CheckSegList (
				FormatVariables *cur_vars);
static	int		 Format(
				FormatVariables *cur_vars,
				_FrmtUiInfo	*ui_info,
				char		*id_string,
				_DtCvTopicPtr	*ret_handle);
static	int		 GetNextBuffer (
				FormatVariables *cur_vars);
static	int		 GetStringParameter(
				FormatVariables  *cur_vars,
				_DtCvValue	  flag,
				_DtCvValue	  eat_escape,
				_DtCvValue	  ignore_quotes,
				_DtCvValue	  less_test,
				char		**ret_string );
static	int		 GetValueParameter(
				FormatVariables *cur_vars,
				_DtCvValue	 flag,
				int		*ret_value );
static	int		 InitStructure(
				FormatVariables *cur_vars,
				_FrmtUiInfo	*ui_info,
				char		*rd_buf,
				int		 rd_size);
static	int		 Parse(
				int		  cur_cmd,
				ProcessState	  cur_state,
				FormatVariables	 *cur_vars,
				unsigned long	  seg_flags,
				_DtCvFrmtOption	  frmt_type,
				_DtHelpFontHints *font_attr,
				int		  linkspec,
				const char	 *scan_string,
				int		  allowed,
				int		  ret_on_nl,
				int		  fnt_flag);
static	int		 ProcessFigureCmd(
				FormatVariables	 *cur_vars,
				ProcessState	  cur_state,
				_DtCvFrmtOption	  frmt_type,
				_DtHelpFontHints *font_attr,
				int		  ret_on_nl,
				int		  fnt_flag);
static	int		 ProcessHypertext(
				FormatVariables	 *cur_vars,
				ProcessState	  cur_state,
				unsigned long	  seg_flags,
				_DtCvFrmtOption	  frmt_type,
				_DtHelpFontHints *font_attr,
				int		  flags,
				int		  ret_on_nl,
				int		  fnt_flag);
static	int		 ProcessInLine(
				FormatVariables	*cur_vars,
				int		 seg_type,
				int		 link_spec );
static	int		 ProcessLabelCmd(
				FormatVariables	 *cur_vars,
				unsigned long	  seg_flags,
				_DtCvFrmtOption	  frmt_type,
				_DtHelpFontHints *font_attr,
				int		  link_spec,
				int		  flags,
				int		  ret_on_nl,
				int		  fnt_flag,
				_DtCvSegment	**ret_list);
static	int		 ProcessParagraph (
				FormatVariables	*cur_vars,
				ProcessState	 cur_state,
				int		 fnt_flag);
static	int		 SaveNewLine(
				FormatVariables	 *cur_vars,
				int		  cur_cmd,
				_DtHelpFontHints *font_attr,
				int		  link_spec );
static	int		 SaveStringAsSegments (
				FormatVariables	 *cur_vars,
				unsigned long	  seg_flags,
				_DtHelpFontHints *font_attr,
				int		  link_spec);
static	int		 SkipToNextToken (
				FormatVariables	*cur_vars,
				_DtCvValue	 flag );
static	void		 TerminateSegList (
				SegList		*list,
				int		 flag);
static  int              SegmentSave(
				FormatVariables	*cur_vars,
                                unsigned long	 seg_type,
				_DtHelpFontHints *font_attr,
                                char            *string,
                                int              str_size,
                                int              num_chars,
                                int              link_spec);

/********    End Public Function Declarations    ********/

/******************************************************************************
 * Private variables
 *****************************************************************************/
static	const char	*Specials = "<\n\\ ";
static	const char	*SpaceString = "        ";
static	const char	*Period  = ".";
static	const char	*Slash   = "/";

static const FormatVariables DefVars = {
	True,		/* int		 last_was_space;	*/
	False,		/* int		 last_was_mb;		*/
	False,		/* int		 last_was_nl;		*/

	NULL,		/* char		*rd_buf;		*/
	NULL,		/* char		*rd_ptr;		*/
	NULL,		/* char		*fmt_buf;		*/
	NULL,		/* char		*my_path;		*/
	NULL,		/* char		*vol_name;		*/
	NULL,		/* char		*topic_id;		*/
	NULL,		/* char		*topic_char_set;	*/
	NULL,		/* char		*topic_lang;		*/

	0,		/* int		 rd_size;		*/
	0,		/* int		 rd_flag;		*/

	1,		/* int		 topic_mb_max;		*/
	1,		/* int		 cur_mb_max;		*/

	0,		/* int		 fmt_buf_max;		*/
	0,		/* int		 fmt_size;		*/
	0,		/* int		 cell_cnt;		*/

	NULL,		/* BufFilePtr	 my_file;		*/
	  {		/* SegList	 my_list;		*/
	    0,		    /* int		 cnt;	*/
	    0,		    /* int		 max;	*/
	    NULL,	    /* _DtCvSegment	*list;	*/
	  },		/* SegList	 my_list;		*/
	NULL,		/* _DtCvLinkDb	 my_links;		*/
	NULL,		/* _FrmtUiInfo	 ui_info;		*/
};

static	const	SegList		InitList =
  {
    0,		/* int		 cnt;	*/
    0,		/* int		 max;	*/
    NULL,	/* _DtCvSegment	*list;	*/
  };

static	const	_DtCvContainer	DefContainer =
  {
        NULL,			/* char			*id;		*/
        NULL,			/* char			*justify_char;	*/
        _DtCvDYNAMIC,		/* _DtCvFrmtOption	 type;		*/
        _DtCvBORDER_NONE,	/* _DtCvFrmtOption	 border;	*/
        _DtCvJUSTIFY_LEFT,	/* _DtCvFrmtOption	 justify;	*/
        _DtCvJUSTIFY_TOP,	/* _DtCvFrmtOption	 vjustify;	*/
        _DtCvJUSTIFY_LEFT_MARGIN,/* _DtCvFrmtOption	 orient;	*/
        _DtCvJUSTIFY_TOP,	/* _DtCvFrmtOption	 vorient;	*/
        _DtCvWRAP_NONE,		/* _DtCvFrmtOption	 flow;		*/
        10000,			/* int			 percent;	*/
        0,			/* _DtCvUnit		 leading;	*/
        0,			/* _DtCvUnit		 fmargin;	*/
        0,			/* _DtCvUnit		 lmargin;	*/
        0,			/* _DtCvUnit		 rmargin;	*/
        0,			/* _DtCvUnit		 tmargin;	*/
        1,			/* _DtCvUnit		 bmargin;	*/
	{0, NULL},		/* _DtCvLine             bdr_info;      */
        NULL,			/* _DtCvSegment		 seg_list;	*/
  };

static const FrmtPrivateInfo DefPrivInfo =
  {
	(char) False,		/* char		 top_block;	*/
	(char) False,		/* char		 dup_flag;	*/
	0,			/* int		 sdl_type;	*/
	0,			/* void		*match_info;	*/
	NULL,			/* void		*id_info;	*/
	NULL,			/* void		*tmp_info;	*/
  };

/******************************************************************************
 *
 * Private Functions
 *
 *****************************************************************************/
/******************************************************************************
 * Function:	int CreateMarker (
 *
 * Parameters:
 *              cur_vars        Specifies the current setting of
 *				formatting variables.
 *              id_string       The id string for the marker.
 *
 * Returns:	0 if successful, -1 if failure.
 *
 * Purpose:	To create a marker id.
 *
 *****************************************************************************/
static	int
CreateMarker (
    FormatVariables	*cur_vars,
    char		*id_string)
{
    int	result;

    /*
     * make sure there is room for the marker segment
     */
    result = CheckSegList(cur_vars);
    if (-1 != result)
      {
	int		 cnt  = cur_vars->my_list.cnt;
	_DtCvSegment	*list = cur_vars->my_list.list;

	/*
	 * now make a mark segment here.
	 */
	list[cnt].type = _DtCvSetTypeToMarker(list[cnt].type);
	_DtCvIdOfMarkerSeg(&list[cnt]) = id_string;
	cur_vars->my_list.cnt++;
      }

    return result;
}

/******************************************************************************
 * Function:	void GetMbLen (
 *
 * Parameters:
 *              cur_vars        Specifies the current setting of
 *				formatting variables.
 *              font_str        Specifies the CCDF string for a
 *				CHARACTERSET change.  The string is
 *				modified by this routine.
 *              lang_ptr        Returns the language and territory
 *				value.  The caller owns this memory.
 *              set_ptr         Returns the code set of the language.
 *				The caller owns this memory.
 *
 * Returns:	Nothing
 *
 * Purpose:	1) To decompose the font_string into a language and code set
 *		   portions.
 *		2) To set the mb_cur_len variable in cur_vars.
 *
 *****************************************************************************/
static	void
GetMbLen (
    FormatVariables	 *cur_vars,
    char		 *font_str,
    char		**lang_ptr,
    char		**set_ptr)
{
    int   i;
    char *ptr;
    char *langPart = NULL;
    char *codePart = NULL;
    char *mLang = NULL;

    /*
     * strip spaces
     */
    while (*font_str == ' ')
	font_str++;

    for (i = strlen(font_str) - 1; i > -1 && font_str[i] == ' '; i--)
	font_str[i] = '\0';

    /*
     * check for locale.codeset
     */
    if (_DtHelpCeStrchr(font_str, ".", 1, &ptr) == 0)
      {
	/*
	 * make font_str two separate strings by replacing
	 * the dot with a end of line.
	 */
	*ptr++ = '\0';
	langPart = strdup(font_str);
	codePart = strdup(ptr);
	goto done;
      }

    /*
     * old CCDF volume
     */
    if (font_str == NULL || *font_str == '\0') codePart = strdup("UTF-8");
    else codePart = strdup(font_str);

    mLang = _DtHelpGetLocale();

    if (_DtHelpCeStrrchr(mLang, Period, MB_CUR_MAX, &ptr) == 0)
      {
	*ptr = '\0';

	if (mLang != NULL && *mLang != '\0')
	  {
	    langPart = strdup(mLang);
	    goto done;
	  }
      }

    langPart = strdup("C");

done:
    cur_vars->cur_mb_max = MB_CUR_MAX;

    /*
     * now set the return variables
     */
    if (lang_ptr != NULL)
        *lang_ptr = langPart;
    else
	free(langPart);

    if (set_ptr != NULL)
        *set_ptr = codePart;
    else
	free(codePart);

    free(mLang);
}

/******************************************************************************
 * Function:	int ReturnLinkWinHint (int ccdf_type)
 *
 * Parameters:
 *
 * Returns:
 *
 * errno Values:
 *
 * Purpose:
 *****************************************************************************/
static	int
ReturnLinkWinHint (
    int   ccdf_type)
{
    int  winHint = _DtCvWindowHint_CurrentWindow;

    if (ccdf_type == CCDF_LINK_JUMP_NEW)
	winHint = _DtCvWindowHint_NewWindow;

    else if (ccdf_type == CCDF_LINK_DEFINITION)
	winHint = _DtCvWindowHint_PopupWindow;

    return winHint;
}

/******************************************************************************
 * Function:	int ReturnLinkType (int ccdf_type, char *spec)
 *
 * Parameters:
 *
 * Returns:
 *
 * errno Values:
 *
 * Purpose:
 *****************************************************************************/
static	int
ReturnLinkType (
    int   ccdf_type,
    char *spec)
{
    int  trueType = _DtCvLinkType_SameVolume;

    switch(ccdf_type)
      {
	case CCDF_LINK_JUMP_NEW:
	case CCDF_LINK_JUMP_REUSE:
        case CCDF_LINK_DEFINITION:
		if (strchr(spec, ' ') != NULL)
		    trueType = _DtCvLinkType_CrossLink;
		break;

	case CCDF_LINK_EXECUTE:
		trueType = _DtCvLinkType_Execute;
		break;

	case CCDF_LINK_MAN_PAGE:
		trueType = _DtCvLinkType_ManPage;
		break;
	
        case CCDF_LINK_APP_DEFINE:
		trueType = _DtCvLinkType_AppDefine;
		break;

      }

    return trueType;
}

/******************************************************************************
 * Function:	int CheckSegList (FormatVariables *cur_vars)
 *
 * Parameters:
 *
 * Returns:	0 for success, -1 for errors.
 *
 * errno Values:
 *		CEErrorReported
 *
 * Purpose:	Check the global variable 'SegCount' against 'SegMax' and
 *		realloc/malloc more memory for the global variable 'SegList'
 *		if necessary. If memory is realloc'ed/malloc'ed, the entry
 *		'para_entry->seg_list' is set to 'SegList'.
 *
 *****************************************************************************/
static	int
CheckSegList (
    FormatVariables	*cur_vars )
{
    if (cur_vars->my_list.cnt >= cur_vars->my_list.max)
      {
	cur_vars->my_list.list = AllocateSegments (cur_vars->my_list.list,
				cur_vars->my_list.max,
				(cur_vars->my_list.max + GROW_SIZE));

	if (NULL == cur_vars->my_list.list)
	    return -1;

	cur_vars->my_list.max += GROW_SIZE;
      }

    return 0;
}

/******************************************************************************
 * Function: static _DtCvSegment *AllocateSegments (_DtCvSegment p_seg,
 *					int original_size, int new_size)
 *
 * Parameters:
 *		p_seg		Specifies a pointer to the old list
 *				of _DtCvSegments.
 *		original_size	Specifies the current size of 'p_seg'.
 *		new_size	Specifies the new size of 'p_seg'.
 *
 * Returns:	A pointer to the new list of structures.
 *		A NULL indicates an error.
 *
 * Purpose:	(Re)Allocates _DtCvSegment structures and initializes
 *		them to the default segment which is type _DtCvNOOP
 *		and the container values set to default values.
 *
 *****************************************************************************/
static _DtCvSegment *
AllocateSegments (
	_DtCvSegment	*p_seg,
	int		 original_size,
	int		 new_size )
{
    FrmtPrivateInfo *priv = NULL;

    if (NULL != p_seg)
      {
	int count = 0;

	/*
	 * reallocate the segments.
	 */
	p_seg = (_DtCvSegment *) realloc ((void *) p_seg,
					(sizeof(_DtCvSegment) * new_size));
	/*
	 * reallocate the private formatting information.
	 */
        priv = p_seg->client_use;
	priv = (FrmtPrivateInfo *) realloc ((void *) priv,
					sizeof(FrmtPrivateInfo) * new_size);
	if (NULL == priv)
	  {
	    free(p_seg);
	    p_seg = NULL;
	    return NULL;
	  }

	/*
	 * reattach the private information with their segments.
	 */
	while (count < original_size)
	    p_seg[count++].client_use = priv++;
      }
    else
	p_seg = _DtHelpAllocateSegments (new_size);

    if (NULL != p_seg)
      {
        /*
         * want to 'empty' the type.
         */
        while (original_size < new_size)
	  {
	    /*
	     * if we went through _DtHelpAllocateSegments, then the
	     * priv pointer will be NULL. But that's okay since the
	     * _DtHelpAllocateSegments routine will initalized the
	     * variables for us. The only reason we have to do it
	     * is if we've reallocated the list.
	     */
	    if (NULL != priv)
	      {
		*priv = DefPrivInfo;

	        p_seg[original_size  ].client_use   = priv++;
	        p_seg[original_size  ].type         = _DtCvNOOP;
	        p_seg[original_size  ].link_idx     = -1;
	        p_seg[original_size  ].next_seg     = NULL;
	        p_seg[original_size  ].next_disp    = NULL;
	      }

	    p_seg[original_size++].handle.container = DefContainer;
	  }
      }

    return p_seg;
}

/******************************************************************************
 * Function: static int CheckList (
 *				FormatVariables cur_vars, int count, int grow)
 *
 * Parameters:
 *		cur_vars	Specifies a structure containing the list
 *				of _DtCvSegments.
 *		count		Specifies the amount needed.
 *		grow		Specifies the amount to grow by.
 *
 * Returns:	0 if successful, -1 if failure.
 *
 * Purpose:     Makes sure there is 'grow' size number of segments
 *		available.  If not, it calls AllocateSegments to 'grow'
 *		the segment list.  cur_vars->seg_list is set to the new
 *		pointer, cur_vars->seg_max indicates the maximum number
 *		of segments in the list.
 *
 *****************************************************************************/
static int
CheckList (
    SegList	*seg_list,
    int		 count,
    int		 grow )
{
    /*
     * what's the new count?
     */
    count += seg_list->cnt;

    /*
     * is it larger than the current max?
     */
    if (count > seg_list->max)
      {
	/*
	 * grow the list by the indicated amount.
	 */
	grow += seg_list->max;

	/*
	 * check to see if it is enough. If not, force the grow value
	 * to be the required count.
	 */
	if (count > grow)
	    grow = count;

	/*
	 * (re)allocate
	 */
	seg_list->list = AllocateSegments(seg_list->list, seg_list->max, grow);

	/*
	 * return an code if no list allocated.
	 */
	if (NULL == seg_list->list)
	    return -1;

	/*
	 * set the max count
	 */
	seg_list->max = grow;
      }
			
    return 0;
}

/******************************************************************************
 * Function: int SegmentSave (int type,
 *			char **font_attr, char *string, int linkspec,
 *			_DtCvSegment *p_seg)
 * 
 * Parameters:
 *		type		Specifies the segment type.
 *		font_attr	Specifies the resource list of quarks for the
 *					font to associate with the segment.
 *		string		Specifies the string segment.
 *		link_spec	Specifies the link specification for the
 *					segment.
 *		p_seg		Specifies the _DtCvSegment structure to
 *					initialize with the data.
 *
 * Returns:	0 if successful, -1 if errors.
 *
 * errno Values:
 *		CEErrorReported
 *
 * Purpose:	Creates a new segment with the given data.
 *
 *****************************************************************************/
static int
SegmentSave (
    FormatVariables	*cur_vars,
    unsigned long	 seg_type,
    _DtHelpFontHints	*font_attr,
    char		*string,
    int			 str_size,
    int			 num_chars,
    int			 linkspec)
{
    int		  result = 0;
    _DtCvSegment *pSeg   = NextAvailSeg(cur_vars->my_list);

    pSeg->type     = _DtCvSetTypeToNoop(seg_type);
    pSeg->link_idx = linkspec;

    /*
     * We use this routine for many things.
     * If we want an newline in here, we may not have any data.
     */
    if (str_size)
      {
	pSeg->type = _DtCvSetTypeToString(pSeg->type);

	/*
	 * if a wide character representation is required,
	 * convert to wchar_t
	 */
	if (seg_type & _DtCvWIDE_CHAR)
	  {
	    wchar_t *pwcs;

	    /*
	     * include the null byte in the multibyte to widechar
	     * conversion.
	     */
	    num_chars++;

	    /*
	     * malloc the memory
	     */
	    pwcs = (wchar_t *) malloc(sizeof(wchar_t) * num_chars);
	    if (NULL != pwcs)
	      {
		/* convert */
		str_size = mbstowcs(pwcs, string, ((size_t) num_chars));

		/* check to see if it converted everything */
		if (str_size + 1 == num_chars)
		    _DtCvStringOfStringSeg(pSeg) = (void *) pwcs;
		else
		    free(pwcs);
	      }
	  }
	else
	    _DtCvStringOfStringSeg(pSeg) = (void *) strdup(string);

        if (NULL == _DtCvStringOfStringSeg(pSeg))
	    return -1;

	/*
	 * set the default font. This should really be passed in via
	 * the format entry point (or the ui_info structure).
	 */
	_DtCvFontOfStringSeg(pSeg)   = 0;

	/*
	 * load the font
	 */
	if (NULL != cur_vars->ui_info->load_font)
	    (*(cur_vars->ui_info->load_font))(
			    cur_vars->ui_info->client_data,
			    _DtHelpFontHintsLang(*font_attr),
			    _DtHelpFontHintsCharSet(*font_attr),
			    *font_attr,
			    &(_DtCvFontOfStringSeg(pSeg)));
      }

    return result;
}

/******************************************************************************
 * Function: int SaveStringAsSegments (FormatVariables, int type, char **font_attr,
 *				int link_spec)
 * 
 * Parameters:
 *		type		Specifies the segment type.
 *		font_attr	Specifies the resource list of quarks for the
 *					font to associate with the segment.
 *		link_spec	Specifies the link specification for the
 *					segment.
 *
 * Returns:	0 if successful, -1 if errors.
 *
 * errno Values:
 *
 * Purpose:	Saves a segment into the global 'SegList'. This is
 *		a wrapper around SegmentSave that adds specific
 *		information relative to this module.
 *
 *****************************************************************************/
static	int
SaveStringAsSegments (
    FormatVariables	*cur_vars,
    unsigned long	 seg_flags,
    _DtHelpFontHints	*font_attr,
    int			 link_spec)
{
    int     len;
    int     size;
    int     numChars;
    int     newLine = False;
    char    tmpChar;
    char   *ptr;

    seg_flags = _DtCvSetTypeToNoop(seg_flags);
    if (cur_vars->cur_mb_max != 1 && cur_vars->fmt_size > 1)
      {
	if (IsTypeNewLine(seg_flags))
	  {
	    newLine = True;
	    seg_flags = seg_flags & ~(_DtCvNEW_LINE);
	  }

	ptr = cur_vars->fmt_buf;
	do
	  {
	    /*
	     * what type of character is this?
	     */
	    len = mblen (ptr, cur_vars->cur_mb_max);

	    /*
	     * How many of the following characters are of the same size?
	     */
	    size = _DtHelpFmtFindBreak (ptr, len, &numChars);

	    /*
	     * save off the character that doesn't match
	     */
	    tmpChar = ptr[size];

	    /*
	     * put in the null byte for the string.
	     * and set to wide char if not doing multi-byte sequence.
	     */
	    ptr[size] = '\0';
	    if (1 != len)
	       seg_flags = _DtCvSetTypeToWideChar (seg_flags);

	    /*
	     * does this segment get the newline flag?
	     */
	    if (True == newLine && size >= cur_vars->fmt_size)
		seg_flags = _DtCvSetTypeToNewLine(seg_flags);

	    /*
	     * save the segment.
	     */
	    if (CheckSegList(cur_vars) == -1 ||
		SegmentSave (cur_vars, seg_flags, font_attr, ptr, size,
						numChars, link_spec) == -1)
		return -1;

	    /*
	     * remove the wide char flag
	     */
	    seg_flags &= ~(_DtCvWIDE_CHAR);

	    /*
	     * Bump the segment list count.
	     */
	    cur_vars->my_list.cnt++;

	    /*
	     * replace the character that didn't match.
	     * and bump the pointer to it.
	     */
	    ptr[size] = tmpChar;
	    ptr      += size;
	    cur_vars->fmt_size -= size;
	  } while (0 < cur_vars->fmt_size);
      }
    else
      {
	if (CheckSegList (cur_vars) == -1 ||
			SegmentSave (cur_vars, seg_flags, font_attr,
				cur_vars->fmt_buf, cur_vars->fmt_size,
				cur_vars->fmt_size,
				link_spec) == -1)
	    return -1;
        cur_vars->my_list.cnt++;
      }

    cur_vars->fmt_size = 0;
    if (cur_vars->fmt_buf)
      cur_vars->fmt_buf[0] = '\0';

    return 0;
}

/******************************************************************************
 * Function: int CheckSaveSegment (int type, char **font_attr, int link_spec,
 *					FormatVariables cur_vars)
 * 
 * Parameters:
 *		type		Specifies the segment type.
 *		font_attr	Specifies the resource list of quarks for the
 *					font to associate with the segment.
 *		link_spec	Specifies the link specification for the
 *					segment.
 *
 * Returns:	0 if successful, -1 if errors.
 *
 * errno Values:
 *
 * Purpose:	Checks 'cur_vars->fmt_size' for a non-zero value. If it is,
 *		calls SaveSegment.
 *
 *****************************************************************************/
static	int
CheckSaveSegment (
    unsigned long	 seg_flags,
    _DtHelpFontHints	*font_attr,
    int			 link_spec,
    FormatVariables	*cur_vars)
{
    int   result = 0;

    if (cur_vars->fmt_size)
        result = SaveStringAsSegments (cur_vars, seg_flags, font_attr, link_spec);

    return result;
}

/******************************************************************************
 * Function: void TerminateSegList (
 * 
 * Parameters:
 *
 * Returns:	0 if successful, -1 if errors.
 *
 * Purpose:	Links the segments together.
 *
 *****************************************************************************/
static	void
TerminateSegList (
    SegList	*seg_list,
    int		 flag)
{
    int	count;
    _DtCvSegment *lastDisp = NULL;
    _DtCvSegment *pSeg;

    if (True == flag && 0 == seg_list->cnt && NULL != seg_list->list)
      {
	free(seg_list->list);
	*seg_list = InitList;
      }

    /*
     * set the next segment and display segment pointers.
     */
    for (count = 1, pSeg = seg_list->list; count < seg_list->cnt;
							count++, pSeg++)
      {
	/*
	 * link this segment to the next segment
	 */
	pSeg->next_seg  = &(pSeg[1]);

	/*
	 * is this a displayable segment?
	 * If so, link it into the displayable list.
	 */
	if (_DtCvIsSegNoop(pSeg) || _DtCvIsSegRegion(pSeg)
						|| _DtCvIsSegString(pSeg))
	  {
	    /*
	     * Is there a displayable segment yet?
	     * If so, set its next displayable link to this segment.
	     */
	    if (NULL != lastDisp)
	        lastDisp->next_disp = pSeg;

	    /*
	     * this segment becomes the last displayable segment.
	     */
	    lastDisp = pSeg;
	  }
	else
	   lastDisp = NULL;
      }

    if (NULL != pSeg && NULL != lastDisp
	&& (_DtCvIsSegNoop(pSeg) || _DtCvIsSegRegion(pSeg)
						|| _DtCvIsSegString(pSeg)))
	lastDisp->next_disp = pSeg;

}

/******************************************************************************
 * Function:	int InitStructure (FormatVariables *cur_vars,
 *				char *rd_buf, int rd_size,
 * 
 * Parameters:
 *		rd_buffer	Specifies the buffer all reads use.
 *		id_string	Specifies the location ID to search for.
 *
 * Returns:	0 if successful, -1 if errors.
 *
 * errno Values:
 *
 * Purpose:     Attach formatting information to the structure that gets
 *		passed around by, among other things, setting the global
 *		variable 'cur_vars->rd_ptr' to 'rd_buffer', and
 *		remembering the location id.
 *
 *****************************************************************************/
static	int
InitStructure(
    FormatVariables	*cur_vars,
    _FrmtUiInfo		*ui_info,
    char		*rd_buf,
    int			 rd_size)
{
    cur_vars->ui_info  = ui_info;
    cur_vars->rd_size  = rd_size;
    cur_vars->rd_buf   = rd_buf;
    cur_vars->rd_ptr   = cur_vars->rd_buf;

    cur_vars->fmt_size = 0;
    if (cur_vars->fmt_buf)
	cur_vars->fmt_buf[0] = '\0';

    /*
     * allocate the link data base.
     */
    cur_vars->my_links = _DtLinkDbCreate();
    if (NULL == cur_vars->my_links)
	return -1;

    return 0;
}

/******************************************************************************
 * Function:	int AppendToInfo (FormatVariables *cur_vars,
 *					char **src, const char *scan_string)
 *
 * Parameters:
 *		src		Specifies the source string to read.
 *				Returns pointing at a special character,
 *					an invalid character or the
 *					end of string.
 *		scan_string	Specifies the special characters to
 *				look for in 'src'.
 *
 * Returns:	 0	if stopped on a special character.
 *		 1	if found the end of string.
 *		 2	if found an invalid character.
 *		-1	if errors.
 *
 * errno Values:
 * 
 * Purpose:	Appends onto 'cur_vars->fmt_buf' the number of characters
 *		found in 'src' that does not match any character in
 *		'scan_string'.
 *
 *		Sets 'cur_vars->last_was_space' to false (assumes that one
 *		of the special characters is a space).
 *
 *****************************************************************************/
static	int
AppendToInfo (
    FormatVariables	 *cur_vars,
    char		**src,
    const char		 *scan_string )
{
    int    spnResult;
    int    size;

    cur_vars->last_was_space = False;
    spnResult = _DtHelpCeStrcspn(*src, scan_string, cur_vars->cur_mb_max,
									&size);
    if (0 < size)
      {
        if (_DtHelpCeAddStrToBuf(src,&(cur_vars->fmt_buf),&(cur_vars->fmt_size),
				&(cur_vars->fmt_buf_max),size,INFO_GROW) == -1)
	    return -1;
        cur_vars->last_was_space = False;
      }

    if (spnResult == -1)
	spnResult = 2;

    if (0 == size && 1 == spnResult)
	spnResult = 0;

    return spnResult;
}

/******************************************************************************
 * Function:	int AppendOctalToInfo (FormatVariables *cur_vars, char *src)
 * 
 * Parameters:
 *		src		Specifies the source string to read.
 *
 * Returns:	 0	if successful, -1 if errors.
 *
 * errno Values:
 *		CEErrorFormattingValue
 *		CEErrorMalloc
 * 
 * Purpose:	Convert the octal representation pointed to by 'src' and
 *		change it into a character byte. The routine only allows
 *		a number between the values 1-255.
 *
 *		Append the byte onto the global variable 'Info'.
 *		Set 'cur_vars->last_was_space' to false.
 *
 *****************************************************************************/
static	int
AppendOctalToInfo(
    FormatVariables	*cur_vars,
    char		*src)
{
    /*
     * must have 0xXX
     */
    if (((int)strlen(src)) < 4 && cur_vars->my_file != NULL)
      {
	if (GetNextBuffer (cur_vars) == -1)
	    return -1;

	src = cur_vars->rd_ptr;
      }

    if (_DtHelpCeAddOctalToBuf(src,&(cur_vars->fmt_buf),&(cur_vars->fmt_size),
				&(cur_vars->fmt_buf_max),INFO_GROW) == -1)
	return -1;

    cur_vars->last_was_space = False;

    return 0;
}

/******************************************************************************
 * Function:	int AppendCharToInfo (FormatVariables *cur_vars, char **src)
 * 
 * Parameters:
 *		src		Specifies the source string to read.
 *				Returns pointing at the next character
 *					the string.
 *
 * Returns:	 0	if successful, -1 if errors.
 *
 * errno Values:
 *
 * Purpose:	Appends the character pointed to by 'src' onto the
 *		global buffer 'Info', updating the pointers associated
 *		with 'Info' accordingly.
 *
 *		Sets 'cur_vars->last_was_space' to False;
 * 
 *****************************************************************************/ static	int
AppendCharToInfo(
    FormatVariables	*cur_vars,
    char		**src)
{
    cur_vars->last_was_space = False;
    return (_DtHelpCeAddCharToBuf (src, &(cur_vars->fmt_buf),
				&(cur_vars->fmt_size),
				&(cur_vars->fmt_buf_max), INFO_GROW));
}

/******************************************************************************
 * Function:	int AppendSpaceToInfo (char **src, int type)
 *
 * Parameters:
 *		src		Specifies the source string to read.
 *				Returns pointing at the next character
 *					the string.
 *		type		Specifies the type of the segment being
 *					processed.
 *
 * Returns:	 0	if successful, -1 if errors.
 *
 * errno Values:
 *
 * Purpose:	Appends a space onto the global buffer 'Info', if
 *		the variable 'cur_vars->last_was_space' is false or the
 *		static string flag is set in 'type'.
 *
 *		Sets 'cur_vars->last_was_space' to true.
 *
 *****************************************************************************/
static	int
AppendSpaceToInfo ( 
    FormatVariables	 *cur_vars,
    char		**src,
    _DtCvFrmtOption	  type)
{
    int   result = 0;
    char *space = (char *) SpaceString;

    if (!cur_vars->last_was_space || _DtCvLITERAL == type) {

	result = _DtHelpCeAddCharToBuf (&space, &(cur_vars->fmt_buf),
					&(cur_vars->fmt_size),
					&(cur_vars->fmt_buf_max), INFO_GROW);
        cur_vars->last_was_space = True;
    }

    if (src != NULL)
        *src = *src + 1;
    return result;
}

/******************************************************************************
 * Function:	int FindEndMarker (FormatVariables *cur_vars)
 * 
 * Returns:	 0 if successful, -1 if errors.
 *
 * errno Values:
 *
 * Purpose:	Wrapper around __DtHelpFindEndMarker.
 *		Find the '>' token.
 *
 *****************************************************************************/
static int
FindEndMarker(
    FormatVariables	*cur_vars)
{
    return (_DtHelpCeGetCcdfEndMark (cur_vars->my_file, cur_vars->rd_buf,
		&(cur_vars->rd_ptr),cur_vars->rd_size,1));
}

/******************************************************************************
 * Function:	int GetNextBuffer (FormatVariables *cur_vars)
 *
 * Returns:	 0	if successful, -1 if errors.
 *
 * errno Values:
 *
 * Purpose:	Wrapper around __DtHelpGetNextBuffer.
 *		Read the next buffer's worth of information.
 * 
 *****************************************************************************/
static	int
GetNextBuffer (
    FormatVariables *cur_vars)
{
    cur_vars->rd_flag = _DtHelpCeGetNxtBuf (cur_vars->my_file,
					cur_vars->rd_buf,
					&(cur_vars->rd_ptr),
					cur_vars->rd_size);
    return (cur_vars->rd_flag);
}

/******************************************************************************
 * Function:	int SaveNewLine (FormatVariables *cur_vars, int cur_type,
 *					char **font_attr,
 *					int link_spec)
 *
 * Parameters:
 *		cur_cmd		Specifies the type of segment being processed.
 *		font_attr	Specifies the list of font quarks associated
 *				with the current segment.
 *		link_spec	Specifies the hypertext link specification
 *				associated with the current segment.
 *
 * Returns:	 0	if successful, -1 if errors.
 *
 * errno Values:
 *
 * Purpose:	Sets the newline flag on a segment and saves it by calling
 *			'SaveStringAsSegments'.
 *		If the current segment is non-null, save it with the
 *			newline flag set.
 *		Otherwise if there are no segments saved, create one with the
 *			a type of CE_old_NOOP.
 *		Otherwise there are other segments, but the current segment is
 *			empty. Look back at the previous segment.
 *			If it doesn't have the newline set on it,
 *			its newline flag is enabled.
 *		Otherwise the previous segment had the newline set, so
 *			create another segment just like it with a 
 *			null length and the newline flag set (if the
 *			previous segment is a graphic, create it
 *			with a type of CE_old_NOOP).
 * 
 *****************************************************************************/
static	int
SaveNewLine(
    FormatVariables	*cur_vars,
    int			 cur_type,
    _DtHelpFontHints	*font_attr,
    int			 link_spec )
{
    int         result = 0;
    _DtCvSegment   *pSeg;

    /*
     * If there is information in the buffer, save it with the newline flag
     */
    if (cur_vars->fmt_size)
	result = SaveStringAsSegments (cur_vars, _DtCvSetTypeToNewLine (cur_type),
					font_attr, link_spec);
    /*
     * check to see if there is any segments. If not create a new one
     * with the type NOOP.
     */
    else if (0 == cur_vars->my_list.cnt)
	result = SaveStringAsSegments (cur_vars, _DtCvSetTypeToNewLine(_DtCvNOOP),
					font_attr, link_spec);
    /*
     * There was not any information in the buffer and we have one or
     * more segments. Try placing the flag on the previous segment.
     */
    else 
      {
	pSeg = NextAvailSeg(cur_vars->my_list);
	pSeg--;

	/*
	 * Does the previous segment already have a newline flag?
	 * If so, create a new NOOP segment with the newline set.
	 */
	if (_DtCvIsSegNewLine (pSeg))
	    result = SaveStringAsSegments (cur_vars, _DtCvSetTypeToNewLine (_DtCvNOOP),
					font_attr, link_spec);
	else
	    pSeg->type = _DtCvSetTypeToNewLine (pSeg->type);
      }

    cur_vars->last_was_space = True;
    return result;
}

/******************************************************************************
 * Function:	int CreateSaveGraphic (FormatVariables cur_vars,
 *				int type,
 *					char *file_name, int link_spec )
 * 
 * Parameters:
 *		type		Specifies the type of graphic segment
 *				being processed.
 *		file_name	Specifies the file name of the graphic.
 *		link_spec	Specifies the hypertext link specification
 *				associated with the graphic.
 *
 * Returns:	 0	if successful, -1 if errors.
 *
 * Purpose:	Save a graphic segment.
 *		If 'file_name' is not an absolute path, resolve it to
 *		a full path by using the path to the volume.
 *
 *****************************************************************************/
static	int
CreateSaveGraphic (
    FormatVariables	*cur_vars,
    int			 type,
    char		*file_name,
    int			 link_spec)
{
    char	 *fullName;
    _DtCvSegment *cvRegion;

    if (CheckList (&(cur_vars->my_list), 1, 1) == -1)
	return -1;

    /*
     * create the path to the file name
     */
    if (*file_name == '/')
	fullName = strdup (file_name);
    else
      {
	fullName = (char *) malloc (strlen (cur_vars->my_path) +
						strlen (file_name) + 2);
	if (fullName == NULL)
	    return -1;

	strcpy (fullName, cur_vars->my_path);
	strcat (fullName, "/");
	strcat (fullName, file_name);
      }

    /*
     * load the graphic into the current segment
     */
    cvRegion = NextAvailSeg(cur_vars->my_list);
    if (NULL != cur_vars->ui_info->load_graphic &&
		(*(cur_vars->ui_info->load_graphic))(
				cur_vars->ui_info->client_data,
				cur_vars->vol_name,
				cur_vars->topic_id,
				fullName,
				NULL,
				NULL,
				&(_DtCvWidthOfRegionSeg(cvRegion)),
				&(_DtCvHeightOfRegionSeg(cvRegion)),
				&(_DtCvInfoOfRegionSeg(cvRegion))) == 0)
      {
	/*
	 * set the type and index.
	 * and indicate that this segment has been used.
	 */
	_DtCvAscentOfRegionSeg(cvRegion) = -1;
	cvRegion->type     = _DtCvSetTypeToRegion(type);
	cvRegion->link_idx = link_spec;
	cur_vars->my_list.cnt++;
      }

    /*
     * free the file name path
     */
    free (fullName);

    return 0;
}

/******************************************************************************
 * Function:	int ChangeFont (int whichOne, int segType,
 *				char **font_attr, int linkspec,
 *				FormatVariables *cur_vars, int flags)
 * 
 * Parameters:
 *		whichOne	Specifies the index into 'font_attr' to
 *				change.
 *		segType		Specifies the type of segment currently
 *				being processed.
 *		font_attr	Specifies the list of font quarks associated
 *				with the current segment.
 *		linkspec	Specifies the hypertext link specification
 *				associated with the segment.
 *		flags		Specifies the routines flags.
 *
 * Returns:	The value returned from 'routine' if successful, -1 if errors.
 *
 * errno Values:
 *
 * Purpose:	Saves any segment with the current font type and
 *		process the next segment using the new font type.
 *
 *****************************************************************************/
static	int
ChangeFont(
    int			 whichOne,
    unsigned long	 seg_flags,
    _DtCvFrmtOption	 frmt_type,
    _DtHelpFontHints	*font_attr,
    int			 linkspec,
    FormatVariables	*cur_vars,
    ProcessState	 cur_state,
    int			 flags,
    int			 ret_on_nl,
    int			 fnt_flag)
{
    int    result;
    int    oldMb_Len = cur_vars->cur_mb_max;
    char  *fontString;
    _DtHelpFontHints oldFontStruct;

    if (CheckSaveSegment (seg_flags, font_attr, linkspec, cur_vars) == -1)
	return -1;

    oldFontStruct = *font_attr;

    if (GetStringParameter(cur_vars, _DtCvTRUE, _DtCvTRUE, _DtCvFALSE,
						_DtCvFALSE, &fontString) == -1)
	return -1;

    /*
     * Is this font change allowed to go through?
     */
    if (!(fnt_flag & (1 << whichOne)))
      {
        switch (whichOne)
          {
	    case _CEFONT_SPACING:
			font_attr->spacing = _DtHelpFontSpacingProp;
			if (fontString[0] == 'm')
			    font_attr->spacing = _DtHelpFontSpacingMono;
			break;
	    case _CEFONT_SIZE:
			font_attr->pointsz = atoi(fontString);
			break;
	    case _CEFONT_ANGLE:
			font_attr->slant = _DtHelpFontSlantRoman;
			if (fontString[0] == 'i')
			    font_attr->slant = _DtHelpFontSlantItalic;
			break;
	    case _CEFONT_WEIGHT:
			font_attr->weight = _DtHelpFontWeightMedium;
			if (fontString[0] == 'b')
			    font_attr->weight = _DtHelpFontWeightBold;
			break;
	    case _CEFONT_TYPE:
			font_attr->style = _DtHelpFontStyleSanSerif;
			if (*fontString == 's')
			  {
			    if (fontString[1] == 'e')
			        font_attr->style = _DtHelpFontStyleSerif;
			    else if (fontString[1] == 'y')
			        font_attr->style = _DtHelpFontStyleSymbol;
			  }
			break;
	    case _CEFONT_CHAR_SET:
			/*
			 * Change to the correct mb_len.
			 */
			GetMbLen(cur_vars, fontString,
					&(_DtHelpFontHintsLang(*font_attr)),
					&(_DtHelpFontHintsCharSet(*font_attr)));
			break;
          }
      }

    result = FindEndMarker (cur_vars);
    if (!result)
	result = Parse (CCDF_FONT_CMD, cur_state, cur_vars,
				seg_flags, frmt_type,
				font_attr, linkspec,
				Specials, flags, ret_on_nl, fnt_flag);
    /*
     * free the lanugage and code sets strings.
     */
    if (!(fnt_flag & (1 << whichOne)) && whichOne == _CEFONT_CHAR_SET)
      {
	free(_DtHelpFontHintsLang(*font_attr));
	free(_DtHelpFontHintsCharSet(*font_attr));
      }

    /*
     * reset the old quark
     * if necessary, reset the MB_LEN
     */
    *font_attr = oldFontStruct;
    cur_vars->cur_mb_max = oldMb_Len;

    /*
     * free the memory
     */
    free (fontString);

    if (result == -1)
	return -1;

    return 0;

} /* ChangeFont */

/******************************************************************************
 * Function:	int SkipToNextToken (FormatVariables *cur_vars, int	 flag)
 * 
 * Parameters:
 *		flag	Specifies whether the routine returns a -1
 *				if '>' is the next token.
 *
 * Returns:	The value from __DtHelpSkipToNextToken:
 *			-1  If problems encountered finding the next token.
 *			 0  If no problems encountered finding the next token.
 *			 1  If flag is true and the next token is a > character.
 *
 * errno Values:
 *
 * Purpose:	Wrapper around __DtHelpSkipToNextToken.
 *		Skip the current string and any spaces or newline
 *		characters after it.
 *
 *****************************************************************************/
static	int
SkipToNextToken (
    FormatVariables	 *cur_vars,
    _DtCvValue		  flag)
{
    return (_DtHelpCeSkipToNextCcdfToken (cur_vars->my_file, cur_vars->rd_buf,
		cur_vars->rd_size, 1, &(cur_vars->rd_ptr), flag));
}

/******************************************************************************
 * Function:	int GetStringParameter (FormatVariables *cur_vars,
 *				int	 flag, int	 eat_escape,
 *				int	 ignore_quotes, int	 less_test,
 *				char **ret_string)
 * 
 * Parameters:
 *		flag            Specifies whether the routine returns
 *                                      a -1 if '>' is the next token.
 *              eat_secape      Specifies whether the backslash is not
 *                                      placed in the returned string.
 *                                      True - it is skipped.
 *                                      False - it is saved in 'ret_string'.
 *              ignore_quotes   Specifies whether quotes are to be included
 *                                      in the returned string.
 *              less_test       Specifies whether the routine should
 *                                      stop when it finds a '<' character.
 *              ret_string      Returns the string found.
 *				If NULL, throws the information away.
 *
 * Returns:	The value from __DtHelpFormatGetStringParameter:
 *			-1  If problems encountered.
 *			 0  If no problems encountered getting the string.
 *			 1  If flag is false and the no string was found.
 *
 * errno Values:
 *
 * Purpose:	Wrapper around __DtHelpFormatGetStringParameter.
 *		Skip the current string and any spaces or newline
 *		characters after it. Get the next quoted/unquoted
 *		string after that.
 *
 *****************************************************************************/
static	int
GetStringParameter(
    FormatVariables	 *cur_vars,
    _DtCvValue		  flag,
    _DtCvValue		  eat_escape,
    _DtCvValue		  ignore_quotes,
    _DtCvValue		  less_test,
    char		**ret_string)
{
    return (_DtHelpCeGetCcdfStrParam (cur_vars->my_file,
		cur_vars->rd_buf, cur_vars->rd_size, cur_vars->cur_mb_max,
		&(cur_vars->rd_ptr),
		flag, eat_escape, ignore_quotes, less_test, ret_string));
}

/******************************************************************************
 * Function:	int GetValueParameter (FormatVariables *cur_vars,
 *				int	 flag, int *ret_value)
 * 
 * Parameters:
 *              flag            Specifies whether the routine returns
 *                                      a -2 if '>' is the next token.
 *              ret_value       Returns the atoi conversion
 *                              of the string found.
 *
 * Returns:	The value from __DtHelpFormatGetValueParameter:
 *			-1  If problems encountered.
 *			 0  If no problems encountered getting the string.
 *			-2  If flag is true and the next token is a >
 *				character.
 *
 * errno Values:
 *
 * Purpose:	Wrapper around __DtHelpFormatGetValueParameter.
 *		Skip the current string and any spaces or newline
 *		characters after it. Process the next string as
 *		a numeric value.
 *
 *****************************************************************************/
static	int
GetValueParameter(
    FormatVariables	*cur_vars,
    _DtCvValue		 flag,
    int			*ret_value )
{
    return (_DtHelpCeGetCcdfValueParam (cur_vars->my_file, cur_vars->rd_buf,
			cur_vars->rd_size,
			&(cur_vars->rd_ptr), flag, cur_vars->cur_mb_max,
			ret_value));
}

/******************************************************************************
 * Function:	int GetParagraphParameters (FormatVariables *cur_vars,
 *				int seg_type, int graphic_type,
 *				char **label, char **file_name,
 *				char **link_string, int *link_type,
 *				char **description)
 *
 * Parameters:
 *              seg_type	Specifies the default type for the segment.
 *				Returns the new type for the segment.
 *		graphic_type	Specifies the default type for a graphic
 *					if a justified graphic is
 *					encountered in the paragraph options.
 *				Returns the new type for a graphic
 *					if a justified graphic was 
 *					encountered in the paragraph options.
 *		label		Returns the label if one is specified
 *					in the paragraph options.
 *		file_name	Returns the file name of a graphic if
 *					one is specified in the paragraph
 *					options.
 *		link_string	Returns the hypertext specification if one
 *					is specified in the paragraph options.
 *		link_type	Returns the hypertext link type if one is
 *					specified.
 *		description	Returns the hypertext description if one
 *					is specified.
 * 
 * Returns:	0 if successfult, -1 if errors.
 *
 * errno Values:
 *
 * Purpose:	Wrapper around __DtHelpParagraphGetOptions.
 *		Process the options found in <PARAGRAPH> syntax.
 *		Test to see if the id specified in the <PARAGRAPH>
 *		is the one we are looking at.
 *
 *****************************************************************************/
static	int
GetParagraphParameters(
    FormatVariables	 *cur_vars,
    _DtCvSegment	 *para,
    _DtCvFrmtOption	 *frmt_type,
    _DtCvFrmtOption	 *gpos,
    char		**glink,
    int			 *glinktype,
    _DtCvUnit		 *gspace,
    char		**ret_label,
    char		**file_name,
    char		**description)
{
    int		 done   = False;
    int		 result = 0;
    int		 optionCount = 0;
    int		 value;
    char	*tmpString = NULL;

    /*
     * initialize string variables if valid addresses
     */
    *ret_label   = NULL;
    *file_name   = NULL;
    *glink       = NULL;
    *description = NULL;

    *gpos       = _DtCvJUSTIFY_LEFT;
    *glinktype  = -1;
    *gspace     = 0;

    while (False == done && result != -1)
      {
	if (SkipToNextToken (cur_vars, _DtCvFALSE) == -1)
	    return -1;

	switch (_DtCvToLower(*(cur_vars->rd_ptr)))
	  {
	    /*
	     * end of paragraph spec
	     */
	    case '>':
		(cur_vars->rd_ptr)++;
		done = True;
		break;

	    /*
	     * after value
	     */
	    case 'a':
		if (GetValueParameter (cur_vars, _DtCvFALSE, &value) == -1)
		    return -1;

		_DtCvContainerBMarginOfSeg(para) = 0;
		if (value > 0 && 0 < cur_vars->ui_info->line_height)
		    _DtCvContainerBMarginOfSeg(para) = value *
					(cur_vars->ui_info->line_height / 2);
		break;

	    /*
	     * before value
	     */
	    case 'b':
		if (GetValueParameter (cur_vars, _DtCvFALSE, &value) == -1)
		    return -1;

		_DtCvContainerTMarginOfSeg(para) = 0;
		if (value > 0 && 0 < cur_vars->ui_info->line_height)
		    _DtCvContainerTMarginOfSeg(para) = value *
					(cur_vars->ui_info->line_height / 2);
		break;

	    /*
	     * description [string | "string" | 'string']
	     */
	    case 'd':
		result = GetStringParameter(cur_vars, _DtCvTRUE, _DtCvTRUE,
					_DtCvFALSE, _DtCvFALSE, description);
		break;

	    /*
	     * firstindent value
	     */
	    case 'f':
		if (GetValueParameter (cur_vars, _DtCvFALSE, &value) == -1)
		    return -1;

		_DtCvContainerFMarginOfSeg(para) =
					value * cur_vars->ui_info->avg_char;
		break;

	    /*
	     * glink      [string | "string" | 'string']
	     * gpos       value_string
	     * graphic    string
	     * gspace     value
	     * gtypelink  value
	     */
	    case 'g':
		/*
		 * to determine what token this is we must look at the
		 * second character.
		 */
		(cur_vars->rd_ptr)++;

		/*
		 * do we need to read more?
		 */
		if (*(cur_vars->rd_ptr) == '\0'
					&& GetNextBuffer(cur_vars) == -1)
		    return -1;

		/*
		 * check for the next permutation
		 */
		switch (_DtCvToLower(*(cur_vars->rd_ptr)))
		  {
		    /*
		     * glink [string | "string" | 'string']
		     */
		    case 'l':
			result = GetStringParameter(cur_vars, _DtCvTRUE,
				_DtCvTRUE, _DtCvFALSE, _DtCvFALSE, glink);
			break;

		    /*
		     * gpos value_string
		     */
		    case 'p':
			result = GetStringParameter(cur_vars, _DtCvTRUE,
				_DtCvTRUE, _DtCvFALSE, _DtCvFALSE, &tmpString);

			if (-1 != result && _DtCvToLower (*tmpString) == 'r')
			    *gpos = _DtCvJUSTIFY_RIGHT;

			if (tmpString)
			    free (tmpString);
			break;

		    /*
		     * graphic    string
		     */
		    case 'r':
			result = GetStringParameter(cur_vars, _DtCvTRUE,
				_DtCvTRUE, _DtCvFALSE, _DtCvFALSE, file_name);
			break;

		    /*
		     * gspace     value
		     */
		    case 's':
			if (GetValueParameter(cur_vars,_DtCvFALSE,&value) == -1
								|| value < 0)
			    return -1;

			*gspace = value * cur_vars->ui_info->avg_char;
			break;

		    /*
		     * gtypelink  value
		     */
		    case 't':
			if (GetValueParameter(cur_vars,_DtCvFALSE,glinktype) == -1
							|| *glinktype < 0)
			    return -1;

			break;
		  }
		break;

	    /*
	     * id string
	     */
	    case 'i':
		/*
		 * get the id string
		 */
		if (GetStringParameter(cur_vars, _DtCvTRUE, _DtCvTRUE,
				_DtCvFALSE, _DtCvFALSE, &tmpString) == -1)
		    return -1;

		_DtCvContainerIdOfSeg(para) = tmpString;
		break;

	    /*
	     * label [string | "string" | 'string']
	     * leftindent value
	     */
	    case 'l':
		/*
		 * to determine what token this is we must look at the
		 * second and possibly the third characters.
		 */
		(cur_vars->rd_ptr)++;

		/*
		 * do we need to read more?
		 */
		if (*(cur_vars->rd_ptr) == '\0'
					&& GetNextBuffer(cur_vars) == -1)
		    return -1;

		/*
		 * check for the next permutation
		 */
		if (_DtCvToLower(*(cur_vars->rd_ptr)) == 'a')
		  {
		    /*
		     * label
		     */
		    if (GetStringParameter(cur_vars, _DtCvTRUE, _DtCvFALSE,
				_DtCvFALSE, _DtCvFALSE, &tmpString) == -1)
			return -1;

		    /*
		     * If we got a label process it.
		     */
		    if (ret_label == NULL || *ret_label)
		      {
		        /*
		         * we've already processed a label!
		         * ignore this one!
		         */
			if (tmpString)
		            free (tmpString);
		      }
		    else
			*ret_label = tmpString;
		  }
		else if (_DtCvToLower(*(cur_vars->rd_ptr)) == 'e')
		  {
		    /*
		     * leftindent
		     */
		    if (GetValueParameter (cur_vars, _DtCvFALSE, &value) == -1)
			return -1;

		    _DtCvContainerLMarginOfSeg(para) =
					value * cur_vars->ui_info->avg_char;
		  }
		else
		    result = -1;
		break;

	    /*
	     * nowrap
	     */
	    case 'n':
		_DtCvContainerTypeOfSeg(para) = _DtCvLITERAL;
		*frmt_type = _DtCvLITERAL;
		break;

	    /*
	     * rightindent value
	     */
	    case 'r':
		if (GetValueParameter (cur_vars, _DtCvFALSE, &value) == -1)
		    return -1;

		_DtCvContainerRMarginOfSeg(para) =
					value * cur_vars->ui_info->avg_char;
		break;

	    /*
	     * wrap
	     */
	    case 'w':
		_DtCvContainerTypeOfSeg(para) = _DtCvDYNAMIC;
		*frmt_type = _DtCvDYNAMIC;
		break;

	    /*
	     * Found an option we don't understand.
	     */
	    default:
		result = -1;
	  }
	optionCount++;
      }

    /*
     * adjust the first margin to correctly indicate the offset from the
     * left margin. In the old CCDF, the first margin indicated left plus
     * and additional indent. For the new Canvas Engine, it is suppose
     * to be an addition on top of the left margin.
     */
    _DtCvContainerFMarginOfSeg(para) = _DtCvContainerLMarginOfSeg(para) -
					_DtCvContainerFMarginOfSeg(para);
    if (-1 == result)
	optionCount = 0;

    return (optionCount - 1);

} /* End GetParagraphParameters */

/******************************************************************************
 * Function:	int FlowingParagraph (
 *
 * Parameters:
 *
 * Returns:
 *
 * errno Values:
 *
 * Purpose:	Process the <PARAGRAPH> specification.
 *
 *****************************************************************************/
static	int
FlowingParagraph(
    FormatVariables	*cur_vars,
    ProcessState	 cur_state,
    _DtCvFrmtOption 	 frmt_flags,
    int			 allowed,
    int			 link_idx,
    int			 ret_on_nl,
    int			 fnt_flag,
    _DtCvFrmtOption	 gpos,
    _DtCvUnit		 gspace,
    char		*file_name,
    _DtHelpFontHints	*font_attr)
{
    int		type     = 0;
    int		result   = -1;
    _DtCvSegment *graphSeg;
    _DtCvSegment *bodySeg;
    SegList	flowList = InitList;

    /*
     * reset current list
     */
    cur_vars->my_list = InitList;

    /*
     * create two containers.
     */
    if (0 == CheckList(&(flowList), 2, 2))
      {
	/*
	 * point to the containers for the graphic and body.
	 */
	graphSeg = flowList.list;
	bodySeg  = flowList.list;
	bodySeg++;

	/*
	 * set the bottom margins to zero.
	 */
	graphSeg->type = _DtCvSetTypeToContainer(graphSeg->type);
	bodySeg->type  = _DtCvSetTypeToContainer(bodySeg->type);
	_DtCvContainerBMarginOfSeg(graphSeg) = 0;
	_DtCvContainerBMarginOfSeg(bodySeg) = 0;

	/*
	 * so the first segment is the container for the graphic.
	 * set the controller flag and values.
	 */
	graphSeg->type = _DtCvSetTypeToController(graphSeg->type);
	_DtCvContainerFlowOfSeg(graphSeg)    = _DtCvWRAP;
	_DtCvContainerPercentOfSeg(graphSeg) = 0;
	_DtCvContainerOrientOfSeg(graphSeg)  = gpos;
	if (_DtCvJUSTIFY_LEFT == gpos)
	  {
	    _DtCvContainerJustifyOfSeg(graphSeg) = gpos;
	    _DtCvContainerRMarginOfSeg(graphSeg) = gspace;
	  }
	else
	  {
	    _DtCvContainerJustifyOfSeg(graphSeg) = gpos;
	    _DtCvContainerLMarginOfSeg(graphSeg) = gspace;
	  }

	/*
	 * mark the first segment as used.
	 */
	flowList.cnt++;

	/*
	 * check for hypertext link.
	 */
	if (link_idx > -1)
	    type = _DtCvHYPER_TEXT;

	/*
	 * re-set the segment list and create the graphic.
	 */
	if (0 == CreateSaveGraphic(cur_vars, type, file_name, link_idx))
	  {
	    /*
	     * so the first segment in cur_vars is a region.
	     * attach it to the graphic container.
	     */
	    _DtCvContainerListOfSeg(graphSeg) = cur_vars->my_list.list;

	    /*
	     * now process the following information as the body of
	     * the paragraph as the list for the non-controller
	     * container.
	     */
	    cur_vars->my_list = InitList;
	    if (-1 != Parse (CCDF_PARAGRAPH_CMD, cur_state, cur_vars,
						0, frmt_flags,
						font_attr, -1,
						Specials, allowed,
						ret_on_nl, fnt_flag))
	      {
		/*
		 * establish the links between the segments
		 */
		TerminateSegList(&(cur_vars->my_list), True);

		/*
		 * if there was a segment list generated,
		 * attach the it to the non-controller
		 * container and mark it as used.
		 */
		if (NULL != cur_vars->my_list.list)
		  {
	            _DtCvContainerListOfSeg(bodySeg) = cur_vars->my_list.list;
		    flowList.cnt++;
		  }
		result = 0;
	      }
	  }
      }

    cur_vars->my_list        = flowList;
    TerminateSegList(&(cur_vars->my_list), True);
    return result;
}

/******************************************************************************
 * Function:	int ProcessParagraph (
 *
 * Parameters:
 *
 * Returns:
 *
 * errno Values:
 *
 * Purpose:	Process the <PARAGRAPH> specification.
 *
 *****************************************************************************/
static	int
ProcessParagraph(
    FormatVariables	*cur_vars,
    ProcessState	 cur_state,
    int			 fnt_flag)
{
    int		 result   = 0;
    int		 labelFnd = False;
    int		 allowed = ~(CCDF_TOPIC_CMD | CCDF_TITLE_CMD | CCDF_ABBREV_CMD);
    int		 oldMbLenMax = cur_vars->cur_mb_max;
    int		 glinktype   = -1;
    int		 linkIndex   = -1;
    _DtCvUnit	 gspace      = 0;
    char	*glinkSpec   = NULL;
    char	*label       = NULL;
    char	*fileName    = NULL;
    char	*description = NULL;
    char	 numChar[16];

    ProcessState	 myState = NormalState;
    _DtCvFrmtOption	 gpos;
    _DtCvFrmtOption	 frmtType = _DtCvDYNAMIC;
    _DtCvSegment	*paraSeg;
    _DtCvSegment	*labelSeg = NULL;
    _DtCvSegment	*col1     = NULL;
    _DtCvSegment	*col2     = NULL;
    _DtCvSegment	**childList;
    SegList		 oldList;
    SegList		 tableList = InitList;
    _DtHelpFontHints	 fontAttrs;

    /*
     * remember the old font list.
     * initialize the font quark list
     * and use the char set specified for this topic.
     */
    _DtHelpCeCopyDefFontAttrList (&fontAttrs);
    cur_vars->cur_mb_max               = cur_vars->topic_mb_max;
    _DtHelpFontHintsLang(fontAttrs)    = cur_vars->topic_lang;
    _DtHelpFontHintsCharSet(fontAttrs) = cur_vars->topic_char_set;

    /*
     * Make next segment in my parent's list for a container for this
     * paragraph.
     */
    if (-1 == CheckSegList(cur_vars))
	return -1;

    /*
     * remember the parent list
     * make sure the defaults are set
     */
    oldList           = cur_vars->my_list;
    paraSeg           = NextAvailSeg(cur_vars->my_list);
    paraSeg->handle.container = DefContainer;
    paraSeg->type     = _DtCvSetTypeToContainer(paraSeg->type);
    _DtCvContainerLeadingOfSeg(paraSeg) = cur_vars->ui_info->leading;
    cur_vars->my_list = InitList;

    /*
     * get the parameters on the paragraph
     * and set the default for the bottom.
     */
    result = GetParagraphParameters (cur_vars, paraSeg, &frmtType,
				&gpos, &glinkSpec, &glinktype, &gspace,
				&label, &fileName, &description);

    /*
     * for lists, we will inherit our parent's bottom margin
     * when we return to the loop processing the list. Therefore,
     * set our bottom margin to zero if we are part of a label body.
     */
    if (0 == result && LabelBody == cur_state)
	_DtCvContainerBMarginOfSeg(paraSeg) = 0;
    else if (result > 0)
	result = 0;

    /*
     * check for a label specification. Either as a 'label' parameter
     * or a the <LABEL> command.
     */
    if (-1 != result)
      {
	/*
	 * check for the parameter
	 */
	if (NULL != label)
	  {
	    FormatVariables   oldVars = *cur_vars;

	    cur_vars->rd_buf  = label;
	    cur_vars->rd_flag = 0;
	    cur_vars->rd_ptr  = label;
	    cur_vars->my_file = NULL;

	    result = Parse (CCDF_LABEL_CMD, NormalState, cur_vars,
					0, frmtType,
					&fontAttrs, -1,
					Specials,
					(CCDF_FONT_CMD | CCDF_OCTAL_CMD),
					False, fnt_flag);

	    if (result != -1)
		result = CheckSaveSegment (_DtCvSTRING, &fontAttrs,
								-1, cur_vars);

	    TerminateSegList(&(cur_vars->my_list), True);
	    cur_vars->rd_buf  = oldVars.rd_buf;
	    cur_vars->rd_flag = oldVars.rd_flag;
	    cur_vars->rd_ptr  = oldVars.rd_ptr;
	    cur_vars->my_file = oldVars.my_file;
	    cur_vars->last_was_space = True;

	    labelSeg = cur_vars->my_list.list;
	    cur_vars->my_list = InitList;
	    labelFnd = True;
	  }
	/*
	 * check for the command.
	 */
	else if (0 == _DtHelpCeCheckNextCcdfCmd("lab", cur_vars->my_file,
						cur_vars->rd_buf,
						cur_vars->rd_size, 1,
						&(cur_vars->rd_ptr)))
	  {
	    result = ProcessLabelCmd(cur_vars, 0, frmtType,
						&fontAttrs, -1,
						allowed, False, fnt_flag,
						&labelSeg);
	    labelFnd = True;
	  }

	/*
	 * if a label was processed, create the containers for it.
	 */
	if (-1 != result && True == labelFnd)
	  {
	    /*
	     * set the correct state.
	     */
	    myState = LabelBody;

	    /*
	     * create containers for the label and it's body
	     */
	    result = CheckList(&tableList, 2, 2);
	    if (-1 != result)
	      {
		/*
		 * set the top and bottom margins on both the child
		 * containers (so they are the same) to reflect
		 * the list's before/after values.
		 */
		/*
		 * set the pointer to the first column
		 * overwrite the left and right margin values.
		 */
		col1       = NextAvailSeg(tableList);
		col1->type = _DtCvSetTypeToContainer(col1->type);
		_DtCvContainerTMarginOfSeg(col1) =
					_DtCvContainerTMarginOfSeg(paraSeg);
		_DtCvContainerBMarginOfSeg(col1) =
					_DtCvContainerBMarginOfSeg(paraSeg);
		_DtCvContainerLMarginOfSeg(col1) = 0;
		_DtCvContainerRMarginOfSeg(col1) = cur_vars->ui_info->avg_char;
		tableList.cnt++;

		/*
		 * set the pointer for the second column
		 * and set the top/bottom values.
		 */
		col2       = NextAvailSeg(tableList);
		col2->type = _DtCvSetTypeToContainer(col2->type);
		_DtCvContainerTMarginOfSeg(col2) =
					_DtCvContainerTMarginOfSeg(paraSeg);
		_DtCvContainerBMarginOfSeg(col2) =
					_DtCvContainerBMarginOfSeg(paraSeg);
		tableList.cnt++;

		/*
		 * create an id for this label
		 */
		sprintf(numChar, "%d", cur_vars->cell_cnt++);
		_DtCvContainerIdOfSeg(col1) = (char *) malloc (
				strlen("&CCDF_RES_") + strlen(numChar) + 1);
		if (NULL != _DtCvContainerIdOfSeg(col1))
		  {
		    /*
		     * copy over the id and attach the list.
		     */
		    strcpy(_DtCvContainerIdOfSeg(col1), "&CCDF_RES_");
		    strcat(_DtCvContainerIdOfSeg(col1), numChar);
		    _DtCvContainerListOfSeg(col1) = labelSeg;

		    /*
		     * set the id for the second column.
		     */
		    sprintf(numChar, "%d", cur_vars->cell_cnt++);
		    _DtCvContainerIdOfSeg(col2) = (char *) malloc (
				strlen("&CCDF_RES_") + strlen(numChar) + 1);
		    if (NULL != _DtCvContainerIdOfSeg(col2))
		      {
			strcpy(_DtCvContainerIdOfSeg(col2), "&CCDF_RES_");
			strcat(_DtCvContainerIdOfSeg(col2), numChar);
		      }
		    else
			result = -1;
		  }
		else
		    result = -1;
	      }
	  }
      }

    /*
     * disallow labels from here on out. Either one was found and processed
     * or not.
     */
    allowed &= (~(CCDF_LABEL_CMD));

    /*
     * Now check and process the graphic specification.
     */
    if (-1 != result)
      {
	/*
	 * initialize the segment list for the children of the paragraph.
	 */
	cur_vars->my_list = InitList;

	/*
	 * check for a graphic in the specification.
	 * If so, then we need to create, as the list for the
	 * paragraph container, two containers; one container
	 * that is a controller and has the graphic and the
	 * other contains the rest of the content of the
	 * paragraph.
	 *
	 * First, create the link index for the graphic.
	 */
	if (NULL != glinkSpec)
	  {
	    /*
	     * but if there isn't a graphic, throw away the link
	     * specifications since they are useless!
	     */
	    if (NULL != fileName && strlen (glinkSpec))
	      {
		linkIndex = _DtLinkDbAddLink (cur_vars->my_links,
					NULL,
					glinkSpec,
					ReturnLinkType(glinktype, glinkSpec),
					ReturnLinkWinHint(glinktype),
					description);

		/*
		 * had problems creating the link index, bail
		 */
		if (linkIndex == -1)
		    result = -1;
	      }
	  }

	/*
	 * now create the graphic and process the graphic body..
	 * or just process the rest of the paragraph....
	 */
	if (0 == result)
	  {
	    if (NULL != fileName)
	        result = FlowingParagraph(cur_vars,
					myState,
					frmtType,
					allowed, linkIndex,
					False, fnt_flag,
					gpos, gspace, fileName,
					&fontAttrs);
	    else
	      {
		result = Parse (CCDF_PARAGRAPH_CMD, myState, cur_vars,
					0, frmtType,
					&fontAttrs, -1,
					Specials, allowed,
					False, fnt_flag);
		if (-1 != result)
		  {
		    /*
		     * establish the links
		     */
		    TerminateSegList (&(cur_vars->my_list), True);
		  }
	      }
	  }

	if (-1 != result)
	  {
	     char	**colW;
	     _DtCvFrmtOption	*colJ;

	    if (True == labelFnd)
	      {
		char *colW1 = "1";		/* default bulletted list */
		char *colW2 = "99,0,98";	/* default bulletted list */
		char *ids;

		/*
		 * set the body of the label in it's container.
		 */
		_DtCvContainerListOfSeg(col2) = cur_vars->my_list.list;

		/*
		 * figure out which column widths should be used.
		 */
		if (14 * cur_vars->ui_info->avg_char
					== _DtCvContainerLMarginOfSeg(paraSeg))
		  {
		    /* Labeled lists */
		    colW1 = "20,20,10";
		    colW2 = "80,10,20";
		  }
		else if (3 * cur_vars->ui_info->avg_char
					== _DtCvContainerLMarginOfSeg(paraSeg))
		    /* Ordered lists */
		    colW1 = "1,98,0";
		else if (0 == _DtCvContainerLMarginOfSeg(paraSeg))
		    /* Plain lists */
		    colW1 = "0";

		/*
		 * create the column width strings.
		 */
		colW = NULL;
		colW = (char **) _DtCvAddPtrToArray((void **) colW,
						((void *) strdup(colW1)));
		if (NULL != colW)
		    colW = (char **) _DtCvAddPtrToArray((void **) colW,
						((void *) strdup (colW2)));

		/*
		 * create the array for column justification.
		 */
		colJ = (_DtCvFrmtOption *) malloc (sizeof(_DtCvFrmtOption) * 2);
		if (NULL != colJ)
		  {
		    colJ[0] = _DtCvJUSTIFY_LEFT;
		    colJ[1] = _DtCvJUSTIFY_LEFT;
		  }

		/*
		 * set this segment's type to a table and fill in all
		 * the relevant information.
		 */
		paraSeg->type                        = _DtCvTABLE;
		_DtCvNumColsOfTableSeg(paraSeg)      = 2;
		_DtCvColWOfTableSeg(paraSeg)         = colW;
		_DtCvColJustifyOfTableSeg(paraSeg)   = colJ;
		_DtCvJustifyCharsOfTableSeg(paraSeg) = NULL;

		/*
		 * now create the list of ids in this table.
		 */
		ids = (char *) malloc (
				strlen(_DtCvContainerIdOfSeg(col1)) +
				strlen(_DtCvContainerIdOfSeg(col2)) + 2);
		if (NULL != ids)
		  {
		    strcpy(ids, _DtCvContainerIdOfSeg(col1));
		    strcat(ids, " ");
		    strcat(ids, _DtCvContainerIdOfSeg(col2));
		  }
		_DtCvCellIdsOfTableSeg(paraSeg) = NULL;
		_DtCvCellIdsOfTableSeg(paraSeg) = (char **)
			_DtCvAddPtrToArray(
				(void **) _DtCvCellIdsOfTableSeg(paraSeg),
				(void *) ids);

		/*
		 * now create the list of cells in the table.
		 */
		_DtCvCellsOfTableSeg(paraSeg) = NULL;
		_DtCvCellsOfTableSeg(paraSeg) = (_DtCvSegment **)
				_DtCvAddPtrToArray(
					(void **) _DtCvCellsOfTableSeg(paraSeg),
					(void  *) col1);
		if (NULL != _DtCvCellsOfTableSeg(paraSeg))
		    _DtCvCellsOfTableSeg(paraSeg) = (_DtCvSegment **)
				_DtCvAddPtrToArray(
					(void **) _DtCvCellsOfTableSeg(paraSeg),
					(void  *) col2);

		if (NULL == colW || NULL == colJ || NULL == ids
				|| NULL == _DtCvCellIdsOfTableSeg(paraSeg)
				|| NULL == _DtCvCellsOfTableSeg(paraSeg))
		    result = -1;
	      }
	    else
	      {
		_DtCvSegment *pSeg;

		/*
		 * attach the list to my container.
		 */
	        _DtCvContainerListOfSeg(paraSeg) = cur_vars->my_list.list;

		/*
		 * consolidate any table children I have.
		 */
		for (pSeg = cur_vars->my_list.list, labelSeg = NULL;
				-1 != result && NULL != pSeg;
							pSeg = pSeg->next_seg)
		  {
		    if (_DtCvIsSegTable(pSeg))
		      {
			if (NULL != labelSeg)
			  {
			    /*
			     * bypass this segment since it will no
			     * longer be 'used' by consolidating it
			     * with another label set.
			     */
			    labelSeg->next_seg = pSeg->next_seg;

			    /*
			     * free the other justification and width info.
			     */
			    free((void *) _DtCvColJustifyOfTableSeg(pSeg));
			    colW = _DtCvColWOfTableSeg(pSeg);
			    free(*colW++);
			    free(*colW);
			    free(_DtCvColWOfTableSeg(pSeg));

			    /*
			     * add the segments to the list
			     */
			    childList = _DtCvCellsOfTableSeg(pSeg);
			    _DtCvCellsOfTableSeg(labelSeg) = (_DtCvSegment **)
				_DtCvAddPtrToArray(
				    (void **) _DtCvCellsOfTableSeg(labelSeg),
					*childList);
			    childList++;
			    if (NULL != _DtCvCellsOfTableSeg(labelSeg))
				_DtCvCellsOfTableSeg(labelSeg) = (_DtCvSegment **)
				    _DtCvAddPtrToArray(
					(void **)_DtCvCellsOfTableSeg(labelSeg),
					*childList);
			    free((void *) _DtCvCellsOfTableSeg(pSeg));

			    /*
			     * add the ids to the list
			     */
			    _DtCvCellIdsOfTableSeg(labelSeg) = (char **)
				_DtCvAddPtrToArray(
				    (void **)_DtCvCellIdsOfTableSeg(labelSeg),
				    (void  *)(*(_DtCvCellIdsOfTableSeg(pSeg))));

			    free((void *) _DtCvCellIdsOfTableSeg(pSeg));

			    if (NULL == _DtCvCellIdsOfTableSeg(labelSeg)
				|| NULL == _DtCvCellsOfTableSeg(labelSeg))
				result = -1;
			  }
			else
			  {
			    /*
			     * this segment becomes the segment holding
			     * the consolidated table.
			     */
			    labelSeg = pSeg;
			  }
		      }
		    else
			labelSeg = NULL;
		  }
	      }
	  }
      }

    /*
     * restore the font MB_CUR_MAX
     */
    cur_vars->cur_mb_max = oldMbLenMax;

    /*
     * free all the strings
     */
    if (label != NULL)
        free (label);
    if (fileName != NULL)
        free (fileName);

    /*
     * free the linkSpec and description,
     */
    if (NULL != description)
	free(description);
    if (NULL != glinkSpec)
	free(glinkSpec);

    if (result == -1)
	return -1;

    oldList.cnt++;
    cur_vars->my_list        = oldList;
    cur_vars->last_was_space = True;
    return 0;

} /* End ProcessParagraph */

/******************************************************************************
 * Function:	int ProcessHypertext (
 *					FormatVariables *cur_vars, int segType,
 *					char **font_attr, int flags )
 *
 * Parameters:
 *		segType		Specifies the type of segment currently
 *				being processed.
 *		font_attr		Specifies the list of font quarks to
 *				associate with the string.
 *		flags		Specifies the formatting commands allowed.
 * 
 * Returns:	0 if successful, -1 if errors.
 *
 * errno Values:
 *
 * Purpose:	Process the <LINK> specification.
 *
 *****************************************************************************/
static	int
ProcessHypertext(
    FormatVariables	*cur_vars,
    ProcessState	 cur_state,
    unsigned long	 seg_flags,
    _DtCvFrmtOption	 frmt_type,
    _DtHelpFontHints	*font_attr,
    int			 flags,
    int			 ret_on_nl,
    int			 fnt_flag)
{
    int    result = 0;
    int    hyperType;
    int    hyperIndex;
    char  *description = NULL;
    char  *hyperlink = NULL;

    /*
     * is a <LINK> command allowed here?
     */
    if (CCDF_NOT_ALLOW_CMD(flags, CCDF_LINK_CMD))
	return -1;

    /*
     * get the hypertext type
     */
    if (GetValueParameter (cur_vars, _DtCvTRUE, &hyperType) < 0)
	return -1;

    if (hyperType < 0)
      {
	errno = CEErrorHyperType;
	return -1;
      }

    /*
     * get the hypertext link spec.
     */
    result = GetStringParameter(cur_vars, _DtCvTRUE, _DtCvTRUE, _DtCvFALSE,
							_DtCvFALSE, &hyperlink);
    if (0 == result)
      {
        /*
         * See if we have the optional description string
         */
        result = GetStringParameter (cur_vars, _DtCvFALSE, _DtCvTRUE,
					_DtCvFALSE, _DtCvFALSE, &description);
        if (result == 1)
	    description = NULL;

	result = _DtLinkDbAddLink (cur_vars->my_links,
					NULL,
					hyperlink,
					ReturnLinkType(hyperType, hyperlink),
					ReturnLinkWinHint(hyperType),
					description);
	if (NULL != description)
	    free(description);

	free(hyperlink);
      }

    /*
     * if no problems encountered, start parsing with this
     * hypertext link.
     */
    if (result != -1)
      {
	hyperIndex = result;
        result = FindEndMarker (cur_vars);
        if (!result)
	  {
	    /*
	     * set the hypertext flag and type.
	     */
	    seg_flags = _DtCvSetTypeToHyperText(seg_flags);

	    result = Parse (CCDF_LINK_CMD, cur_state, cur_vars,
						seg_flags, frmt_type,
						font_attr,
						hyperIndex,
						Specials, flags,
						ret_on_nl, fnt_flag);
	  }
      }

    /*
     * Don't free the hypertext string or description.
     * It is now owned by the Link List.
     */

    if (result == -1)
	return -1;

    return 0;

} /* End ProcessHypertext */

/******************************************************************************
 * Function:	int ProcessLabelCmd (
 *				FormatVariables *cur_vars, int seg_type,
 *				char **font_attr, int link_spec,
 *				int flags )
 *
 * Parameters:
 *		font_attr	Specifies the list of font quarks to
 *				associate with the string.
 *		link_spec	Specifies the hypertext link to associate
 *				with the string.
 *		flags		Specifies the formatting commands allowed.
 * 
 * Returns:	0 if successful, -1 if errors.
 *
 * errno Values:
 *
 * Purpose:	Process the <LABEL> specification.
 *
 *****************************************************************************/
static	int
ProcessLabelCmd(
    FormatVariables	*cur_vars,
    unsigned long	 seg_flags,
    _DtCvFrmtOption	 frmt_type,
    _DtHelpFontHints	*font_attr,
    int			 link_spec,
    int			 flags,
    int			 ret_on_nl,
    int			 fnt_flag,
    _DtCvSegment	**ret_list)
{
    SegList	oldList = cur_vars->my_list;

    /*
     * is a <LABEL> command allowed?
     */
    if (CCDF_NOT_ALLOW_CMD(flags, CCDF_LABEL_CMD))
	return -1;

    /*
     * can't have another <LABEL> command inside this one
     */
    flags &= (~(CCDF_LABEL_CMD));

    /*
     * Find the end marker
     */
    if (FindEndMarker (cur_vars) != 0)
	return -1;

    cur_vars->my_list = InitList;
    if (Parse (CCDF_LABEL_CMD, NormalState, cur_vars, 
					seg_flags,
					frmt_type,
					font_attr,
					link_spec,
					Specials, flags,
					ret_on_nl, fnt_flag) == -1)
	return -1;

    /*
     * set the links, return the segment list and restore the old list.
     */
    TerminateSegList(&(cur_vars->my_list), True);
    *ret_list = cur_vars->my_list.list;
    cur_vars->my_list = oldList;

    /*
     * Indicate that preceding space on the next text should be ignored
     */
    cur_vars->last_was_space = True;

    return 0;

} /* End ProcessLabelCmd */

/******************************************************************************
 * Function:	int ProcessFigureCmd (
 *					FormatVariables *cur_vars,
 *					char **font_attr)
 *
 * Parameters:
 *		font_attr		Specifies the list of font quarks to
 *				associate with the string.
 * 
 * Returns:	0 if successful, -1 if errors.
 *
 * errno Values:
 *
 * Purpose:	Process the <FIGURE> specification.
 *
 *****************************************************************************/
static	int
ProcessFigureCmd(
    FormatVariables	*cur_vars,
    ProcessState	 cur_state,
    _DtCvFrmtOption	 frmt_type,
    _DtHelpFontHints	*font_attr,
    int			 ret_on_nl,
    int			 fnt_flag)
{
    int		cnt;
    int		done         = False;
    int		result       = 0;
    int		linkType     = CCDF_LINK_JUMP_REUSE;
    int		linkIndex    = -1;
    int		segType      = _DtCvREGION;
    char	*description = NULL;
    char	*idString    = NULL;
    char	*filename    = NULL;
    char	*linkspec    = NULL;
    _DtCvFrmtOption	vertOrientCap  = _DtCvJUSTIFY_BOTTOM;
    _DtCvFrmtOption	horzOrientCap  = _DtCvJUSTIFY_CENTER;
    _DtCvFrmtOption	justifyCap     = _DtCvJUSTIFY_CENTER;
    _DtCvFrmtOption	justifyGraphic = _DtCvJUSTIFY_CENTER;
    _DtCvSegment	*figContainer;
    SegList		oldList;
    SegList		capList;
    SegList		figList = InitList;

    /*
     * create a container for this figure (and possibly a caption)
     */
    if (-1 == CheckSegList(cur_vars))
	return -1;

    figContainer       = NextAvailSeg(cur_vars->my_list);
    figContainer->handle.container = DefContainer;
    figContainer->type = _DtCvSetTypeToContainer(figContainer->type);
    _DtCvContainerLeadingOfSeg(figContainer) = cur_vars->ui_info->leading;

    /*
     * remember the old list and initialize for the figure.
     */
    oldList = cur_vars->my_list;
    cur_vars->my_list = InitList;

    /*
     * process the parameters of the figure cmd.
     */
    while (!done && result != -1)
      {
	if (SkipToNextToken (cur_vars, _DtCvFALSE) == -1)
	  {
	    result = -1;
	    continue;
	  }

	switch (_DtCvToLower(*(cur_vars->rd_ptr)))
	  {
	    /*
	     * end of figure spec
	     */
	    case '>':
		/*
		 * move past the end of token marker
		 * and skip the leading blanks in the caption.
		 */
		(cur_vars->rd_ptr)++;
		while (result != -1 && !done)
		  {
		    if (*(cur_vars->rd_ptr) == '\0')
			result = GetNextBuffer (cur_vars);
		    else if ((cur_vars->cur_mb_max == 1 ||
			mblen(cur_vars->rd_ptr, cur_vars->cur_mb_max) == 1)
						&& *(cur_vars->rd_ptr) == ' ')
			(cur_vars->rd_ptr)++;
		    else
			done = True;
		  }
		break;

	    /*
	     * ccenter
	     * center
	     * cbottom
	     * cleft
	     * cright
	     * ctop
	     */
	    case 'c':
		/*
		 * Go to the next character.
		 */
		(cur_vars->rd_ptr)++;

		/*
		 * Do we need to read more information?
		 */
		if (*(cur_vars->rd_ptr) == '\0' && GetNextBuffer (cur_vars) == -1)
		    result = -1;

		if (result != -1)
		  {
		    /*
		     * look at the next charager to determine the token.
		     */
		    switch (_DtCvToLower(*(cur_vars->rd_ptr)))
		      {
		        case 'c': /* caption center */
			    justifyCap    = _DtCvJUSTIFY_CENTER;
			    horzOrientCap = _DtCvJUSTIFY_CENTER;
			    break;

		        case 'e': /* graphic centered */
			    justifyGraphic = _DtCvJUSTIFY_CENTER;
			    break;

		        case 'b': /* caption below graphic */
			    vertOrientCap = _DtCvJUSTIFY_BOTTOM;
			    break;

		        case 'l': /* caption justified left */
			    justifyCap    = _DtCvJUSTIFY_LEFT;
			    horzOrientCap = _DtCvJUSTIFY_LEFT_MARGIN;
			    break;

		        case 'r': /* caption right justified */
			    justifyCap    = _DtCvJUSTIFY_RIGHT;
			    horzOrientCap = _DtCvJUSTIFY_RIGHT_MARGIN;
			    break;

		        case 't': /* caption at top */
			    vertOrientCap = _DtCvJUSTIFY_TOP;
			    break;
		      }
		  }
		break;

	    /*
	     * description [string | "string" | 'string']
	     */
	    case 'd':
		result = GetStringParameter (cur_vars, _DtCvTRUE, _DtCvTRUE,
					_DtCvFALSE, _DtCvFALSE, &description);
		break;

	    /*
	     * file
	     */
	    case 'f':
		result = GetStringParameter (cur_vars, _DtCvTRUE, _DtCvTRUE,
					_DtCvFALSE, _DtCvFALSE, &filename);
		break;

	    /*
	     * id string
	     */
	    case 'i':
		/*
		 * get the id string
		 */
		result = GetStringParameter (cur_vars, _DtCvTRUE, _DtCvTRUE,
					_DtCvFALSE, _DtCvFALSE, &idString);
		/*
		 * if this figure had an id associated with it, create
		 * marker segment for it.
		 */
		if (-1 != result && NULL != idString && '\0' != *idString)
		  {
		    /*
		     * create the marker. If there is an error, free the
		     * string.
		     */
		    result = CreateMarker(cur_vars, idString);
		    if (-1 == result)
		        free (idString);
		  }
		break;

	    /*
	     * left
	     * link [string | "string" | 'string']
	     */
	    case 'l':
		/*
		 * Go to the next character.
		 */
		(cur_vars->rd_ptr)++;

		/*
		 * Do we need to read more information?
		 */
		if (*(cur_vars->rd_ptr) == '\0' && GetNextBuffer (cur_vars) == -1)
		    result = -1;

		if (result != -1)
		  {
		    /*
		     * look at the next charager to determine the token.
		     */
		    switch (_DtCvToLower(*(cur_vars->rd_ptr)))
		      {
		        case 'e':
			    justifyGraphic = _DtCvJUSTIFY_LEFT;
			    break;

		        case 'i':
			    segType = _DtCvSetTypeToHyperText (segType);
			    result = GetStringParameter (cur_vars, _DtCvTRUE,
					_DtCvTRUE, _DtCvFALSE, _DtCvFALSE,
					&linkspec);
			    break;

		        default:
			    result = -1;
		      }
		  }
		break;

	    /*
	     * right
	     */
	    case 'r':
		justifyGraphic = _DtCvJUSTIFY_RIGHT;
		break;

	    /*
	     * typelink value
	     */
	    case 't':
		if (GetValueParameter (cur_vars, _DtCvTRUE, &linkType) == -1)
		    result = -1;
		else if (linkType < 0)
		  {
		    errno = CEErrorHyperType;
		    result = -1;
		  }
		break;

	    default:
		result = -1;
	  }
      }

    if (result != -1 && (filename == NULL || strlen(filename) == 0))
	result = -1;

    if (result != -1)
      {
	/*
	 * check to see if the hypertext flag is set but we don't
	 * have a link specification.
	 */
        if (IsTypeHyperText(segType) && !linkspec)
	  {
	    segType = segType & ~(_DtCvHYPER_TEXT);

	    if (NULL != description)
	        free (description);
	    description = NULL;
	  }

	/*
	 * do we still have a good hypertext?
	 * if so, create a link index for it.
	 */
	if (IsTypeHyperText(segType))
	  {
	    linkIndex = _DtLinkDbAddLink (cur_vars->my_links,
					NULL,
					linkspec,
					ReturnLinkType(linkType, linkspec),
					ReturnLinkWinHint(linkType),
					description);
	    if (linkIndex == -1)
		result = -1;
	  }
      }

    if (result != -1)
      {
	/*
	 * parse the caption.  If there is a caption, we will need to
	 * wrap it and the figure in containers with the appropriate
	 * controller flags set to get the correct layout.
	 */
	result = Parse (CCDF_FIGURE_CMD, cur_state, cur_vars,
	   			 0, frmt_type,
				 font_attr, -1,
				Specials,
				(CCDF_NEWLINE_CMD | CCDF_LINK_CMD |
				 CCDF_FONT_CMD    | CCDF_ID_CMD),
				 ret_on_nl, fnt_flag);

	/*
	 * save the caption List.
	 */
	TerminateSegList(&(cur_vars->my_list), True);
	capList = cur_vars->my_list;

	/*
	 * is there a caption? If so, create two segments instead
	 * of one.
	 */
	cnt = 1;
	if (-1 != result && 0 != capList.cnt)
	    cnt = 2;

	/*
	 * allocate room for the figure (and caption)
	 */
	if (-1 != result)
	    result = CheckList(&figList, cnt, cnt);

	if (-1 != result)
	  {
	    /*
	     * process any caption that was specified
	     */
	    if (0 != capList.cnt)
	      {
	        _DtCvSegment *pSeg = figList.list;

		/*
		 * create a controller for the caption.
		 */
		pSeg->type = _DtCvSetTypeToController(
					_DtCvSetTypeToContainer(pSeg->type));

		/*
		 * set its orientation and justification.
		 */
		_DtCvContainerOrientOfSeg(pSeg)  = horzOrientCap;
		_DtCvContainerVOrientOfSeg(pSeg) = vertOrientCap;
		_DtCvContainerJustifyOfSeg(pSeg) = justifyCap;
		_DtCvContainerBMarginOfSeg(pSeg) = 0;

		/*
		 * attach the caption to the container.
		 */
		_DtCvContainerListOfSeg(pSeg) = capList.list;

		/*
		 * indicate this segment has been used.
		 */
		figList.cnt++;
	      }

	    /*
	     * now load the graphic into the next available segment
	     * allocated for the figure.
	     */
	    cur_vars->my_list = figList;
	    result = CreateSaveGraphic (cur_vars, segType, filename, linkIndex);

	    /*
	     * if no problems, attach the figure (and caption) to the
	     * wrapper container
	     */
	    if (-1 != result)
	      {
		/*
		 * set the links
		 */
		TerminateSegList(&(cur_vars->my_list), True);

		/*
		 * attach the figure (and caption) to the wrapper
		 */
		_DtCvContainerListOfSeg(figContainer) = cur_vars->my_list.list;

		/*
		 * set the justify to the correct value to act on the
		 * figure.
		 */
		_DtCvContainerJustifyOfSeg(figContainer) = justifyGraphic;

		/*
		 * indicate that this segment has been used 
		 */
		oldList.cnt++;
	      }
	  }
      }

    /*
     * restore the segment list.
     * and free memory.
     */
    cur_vars->my_list = oldList;
    free (filename);

    /*
     * don't free the link string or description,
     * the link list owns them now.
     */

    return result;

} /* End ProcessFigureCmd */

/******************************************************************************
 * Function:	int ProcessInLine (FormatVariables cur_vars,
 *					int seg_type, int link_spec )
 *
 * Parameters:
 *		seg_type	Specifes the type of segment currently
 *				being processed.
 *		link_spec	Specifies the hypertext link associated
 *				with this segment.
 * 
 * Returns:	0 if successful, -1 if errors.
 *
 * errno Values:
 *
 * Purpose:	Process a <GRAPHIC> specification.
 *
 *****************************************************************************/
static	int
ProcessInLine(
    FormatVariables	*cur_vars,
    int			seg_type,
    int			link_spec )
{
    int		done = False;
    int		result = 0;
    char	*idString = NULL;
    char	*filename = NULL;

    /*
     * process the graphic parameters
     */
    while (!done && result != -1)
      {
	if (SkipToNextToken (cur_vars, _DtCvFALSE) == -1)
	    return -1;

	switch (_DtCvToLower(*(cur_vars->rd_ptr)))
	  {
	    /*
	     * end of in line spec
	     */
	    case '>':
		/*
		 * move past the end of token marker
		 */
		(cur_vars->rd_ptr)++;
		done = True;
		break;

	    /*
	     * file
	     */
	    case 'f':
		result = GetStringParameter (cur_vars, _DtCvTRUE, _DtCvTRUE,
					_DtCvFALSE, _DtCvFALSE, &filename);
		break;

	    /*
	     * id string
	     */
	    case 'i':
		/*
		 * get the id string
		 */
		result = GetStringParameter (cur_vars, _DtCvTRUE, _DtCvTRUE,
					_DtCvFALSE, _DtCvFALSE, &idString);
		/*
		 * if this graphic had an id associated with it, create
		 * marker segment for it.
		 */
		if (-1 != result && NULL != idString && '\0' != *idString)
		  {
		    /*
		     * create the marker. If there is an error, free the
		     * string.
		     */
		    result = CreateMarker(cur_vars, idString);
		    if (-1 == result)
		        free (idString);
		  }

		break;

	    default:
		result = -1;
	  }
      }

    if (result != -1 && (filename == NULL || strlen(filename) == 0))
	result = -1;

    if (result != -1)
      {
	/*
	 * create the graphic.
	 */
	result = CreateSaveGraphic (cur_vars,
					_DtCvSetTypeToInLine(seg_type),
					filename, link_spec);
	cur_vars->last_was_space = False;
      }

    free (filename);

    return result;

} /* End ProcessInLine */

/******************************************************************************
 * Function:	int CheckIdString (
 *				FormatVariables *cur_vars, int segType,
 *				char **font_attr, int linkspec,
 *				int flags)
 * 
 * Parameters:
 *		segType		Specifies the type of segment currently
 *				being processed.
 *		font_attr		Specifies the list of font quarks to
 *				associate with the string.
 *		linkspec	Specifies the hypertext link associated
 *				with the segment.
 *		flags		Specifies the formatting commands allowed.
 * 
 * Returns:	0 if successful, -1 if errors.
 *
 * errno Values:
 *
 * Purpose:	Create a marker at this location.
 * 
 *****************************************************************************/
static	int
CheckIdString(
    FormatVariables	*cur_vars,
    ProcessState	 cur_state,
    unsigned long	 seg_flags,
    _DtCvFrmtOption	 frmt_type,
    _DtHelpFontHints	*font_attr,
    int			 linkspec,
    int			 flags,
    int			 ret_on_nl,
    int			 fnt_flag)
{
    int    result = -1;
    char  *ptr    = NULL;

    /*
     * is it legal to have a <ID> command at this point?
     */
    if (CCDF_NOT_ALLOW_CMD (flags, CCDF_ID_CMD))
	return -1;

    /*
     * get the id string
     * clear out any current information in the buffer
     * and make sure there is a segment available.
     */
    if (-1 != GetStringParameter (cur_vars, _DtCvTRUE, _DtCvTRUE,
						_DtCvFALSE, _DtCvFALSE, &ptr)
				&&
	-1 != CheckSaveSegment (seg_flags, font_attr, linkspec, cur_vars))
      {
	/*
	 * create the marker. If there is an error, free the
	 * string.
	 */
	result = CreateMarker(cur_vars, ptr);
	if (-1 == result)
	    free (ptr);

	/*
	 * find the end of the <ID> syntax
	 * and parse the data between the begin <ID> and </ID>.
	 */
	if (0 == result && -1 != FindEndMarker (cur_vars) &&
		-1 != Parse (CCDF_ID_CMD, cur_state, cur_vars,
						seg_flags, frmt_type,
						font_attr,
						linkspec,
						Specials, flags,
						ret_on_nl, fnt_flag))
	    result = 0;
      }

    /*
     * return an error code if necessary
     */
    return result;

} /* End CheckIdString */

/******************************************************************************
 * Function:	int	Parse (int cur_cmd, 
 *				FormatVariables *cur_vars, int segType,
 *				char **font_attr,
 *				int  linkspec, int allowed)
 * 
 * Parameters:
 *		cur_cmd		Specifies the current formatting command
 *				being processed.
 *		parent		Specifies the parent paragraph.
 *		segType		Specifies the type of segment currently
 *				being processed.
 *		font_attr		Specifies the list of font quarks to
 *				associate with the string.
 *		linkspec	Specifies the hypertext link associated
 *				with the segment.
 *		allowed		Specifies the formatting commands allowed.
 * 
 * Returns:
 *		1 if ran into a </> specification.
 *		-1 if errors.
 *
 * errno Values:
 *
 * Purpose:	Parse the data.
 *
 *****************************************************************************/
static	int
Parse(
    int			  cur_cmd,
    ProcessState	  cur_state,
    FormatVariables	 *cur_vars,
    unsigned long	  seg_flags,
    _DtCvFrmtOption	  frmt_type,
    _DtHelpFontHints	*font_attr,
    int			  linkspec,
    const char		 *scan_string,
    int			  allowed,
    int			  ret_on_nl,
    int			  fnt_flag)
{
    int      charSize = 1;
    int	     leftOver = 0;
    int	     done = False;
    int	     fontType = False;
    int	     cmdType  = False;
    char    *ptr;

    while (!done)
      {
	if (cur_vars->cur_mb_max != 1)
	    charSize = mblen(cur_vars->rd_ptr, cur_vars->cur_mb_max);

	if (charSize == 1)
	  {
	    /*
	     * check to see if a newline was the previous character.
	     * If so, it may need to be replaced with a space.
	     */
	    if (cur_vars->last_was_nl == True &&
			AppendSpaceToInfo(cur_vars, NULL, frmt_type) == -1)
		return -1;

	    cur_vars->last_was_nl = False;
	    cur_vars->last_was_mb = False;

	    switch (*(cur_vars->rd_ptr))
	      {
		case '<':
		    /*
		     * Go to the next character.
		     */
		    (cur_vars->rd_ptr)++;

		    /*
		     * determine the cmd
		     */
		    cmdType = _DtHelpCeGetCcdfCmd (cur_cmd, cur_vars->rd_buf,
							&(cur_vars->rd_ptr),
							cur_vars->my_file,
							cur_vars->rd_size,
							allowed);
		    switch (cmdType)
		      {
			/*
			 * <figure>
			 */
			case CCDF_FIGURE_CMD:
			    	if (CheckSaveSegment(seg_flags, font_attr,
					linkspec, cur_vars) == -1
						||
					ProcessFigureCmd(cur_vars,
						cur_state,
						frmt_type,
						font_attr,
						ret_on_nl, fnt_flag) == -1)
				    return -1;
				break;

			/*
			 * <angle>
			 * <characterset>
			 * <size>
			 * <spacing>
			 * <type>
			 * <weight>
			 */
			case CCDF_FONT_CMD:
				fontType = _DtHelpCeGetCcdfFontType (cur_vars->rd_ptr);
				done     = ChangeFont(fontType, seg_flags,
							frmt_type,
							font_attr , linkspec,
							cur_vars,
							cur_state,
							allowed,
							ret_on_nl,
							fnt_flag);

				break;

			/*
			 * </>
			 */
			case CCDF_FORMAT_END:
				if (CheckSaveSegment(seg_flags, font_attr,
						linkspec, cur_vars) == -1 ||
					FindEndMarker (cur_vars) == -1)
				    return -1;

				return 1;

			/*
			 * <graphic>
			 */
			case CCDF_GRAPHIC_CMD:
				/*
				 * clear out any information
				 * in the buffer and then
				 * process the figure.
				 */
				if (CheckSaveSegment (seg_flags, font_attr,
						linkspec, cur_vars) == -1
							||
					ProcessInLine (cur_vars, seg_flags,
								linkspec) == -1)
				    done = -1;
				break;

			/*
			 * <id>
			 */
			case CCDF_ID_CMD:
				done = CheckIdString (cur_vars,
						      cur_state,
						      seg_flags,
						      frmt_type,
						      font_attr,
						      linkspec,
						      allowed,
						      ret_on_nl, fnt_flag);
				break;

			/*
			 * <link>
			 */
			case CCDF_LINK_CMD:
				if (CheckSaveSegment (seg_flags, font_attr,
						linkspec, cur_vars) == -1
					||
				     ProcessHypertext(cur_vars, cur_state,
							seg_flags, frmt_type,
						  font_attr, allowed,
						  ret_on_nl, fnt_flag) == -1)
				    done = -1;
				break;

			/*
			 * <newline>
			 */
			case CCDF_NEWLINE_CMD:
				if (SaveNewLine (cur_vars, seg_flags, font_attr,
						linkspec) == -1
						||
					FindEndMarker (cur_vars) == -1)
				    done = -1;
				break;

			case CCDF_OCTAL_CMD:
				if (AppendOctalToInfo(cur_vars,
							cur_vars->rd_ptr) == -1
					|| FindEndMarker (cur_vars) == -1)
				    done = -1;
				break;

			/*
			 * <paragraph>
			 */
			case CCDF_PARAGRAPH_CMD:
				if (CheckSaveSegment (seg_flags, font_attr,
						linkspec, cur_vars) == -1
						||
				      ProcessParagraph(cur_vars, cur_state,
							fnt_flag) == -1)
				    done = -1;
				break;

			/*
			 * <label>
			 * this should never be hit if the markup is
			 * correct. Processing labels is through the
			 * the paragraph processor now.
			 */
			case CCDF_LABEL_CMD:

			/*
			 * unknown command.
			 */
			default:
				return -1;
		      }
		    break;

		case '\n':
			/*
			 * Go to the next character
			 */
			(cur_vars->rd_ptr)++;

			/*
			 * If processing a static segment, we want to
			 * keep the author defined end-of-lines.
			 *
			 * Otherwise, we throw them away.
			 */
			if (_DtCvLITERAL == frmt_type || True == ret_on_nl)
			  {
			    done = SaveStringAsSegments (cur_vars,
					_DtCvSetTypeToNewLine(seg_flags),
					font_attr, linkspec);

			    if (ret_on_nl)
				return 0;
			  }
			else if (cur_vars->last_was_space == False)
			    cur_vars->last_was_nl = True;

			break;

		case '\t':
			/*
			 * go past this character.
			 */
			(cur_vars->rd_ptr)++;

			/*
			 * append 1-8 characters on the end of the buffer.
			 */
			_DtHelpCeCountChars(cur_vars->fmt_buf, MB_CUR_MAX,
								&leftOver);
			leftOver = leftOver % 8;
			ptr = ((char *) SpaceString) + leftOver;
			done = _DtHelpCeAddStrToBuf (&ptr,
						&(cur_vars->fmt_buf),
						&(cur_vars->fmt_size),
						&(cur_vars->fmt_buf_max),
						(8 - leftOver),
						INFO_GROW);
			break;

		case '\\':
			/*
			 * The author has escaped a character.
			 * Increment to the escaped character.
			 */
			(cur_vars->rd_ptr)++;

			/*
			 * Do we need to read more information?
			 */
			if (*(cur_vars->rd_ptr) == '\0' &&
						GetNextBuffer (cur_vars) == -1)
			    return -1;
			/*
			 * If we didn't read more information or
			 * was successful on the read, save the
			 * escaped character.
			 */
			done = AppendCharToInfo (cur_vars, &(cur_vars->rd_ptr));
			break;

		case ' ':
			/*
			 * Put a space in the segment.
			 */
			done = AppendSpaceToInfo (cur_vars,
						&(cur_vars->rd_ptr), frmt_type);
			break;

		default:
			/*
			 * put the information in the buffer
			 */
			if (AppendToInfo (cur_vars, &(cur_vars->rd_ptr),
							scan_string) == -1)
			    return -1;
	      }

	  }
	else if (charSize > 1)
	  {
	    if ((cur_vars->ui_info->nl_to_space == 1 ||
			(cur_vars->last_was_mb == False
					&& cur_vars->last_was_nl == True))
	         && AppendSpaceToInfo(cur_vars, NULL, frmt_type) == -1)
		done = -1;
		
	    cur_vars->last_was_nl = False;
	    cur_vars->last_was_mb = True;
	    if (AppendToInfo (cur_vars, &(cur_vars->rd_ptr), scan_string) == -1)
		return -1;
	  }
	else if (charSize < 0)
	  {
	    /*
	     * we have either invalid characters or part of a multi-byte
	     * character. Read the next buffer for more info.
	     */
	     leftOver = strlen (cur_vars->rd_ptr);
	     if (leftOver < ((int) MB_CUR_MAX))
	       {
		if (GetNextBuffer (cur_vars) == -1)
		    done = -1;
	       }
	     else
	       {
		 /*
		  * In trouble brothers and sisters. We have garbage in the
		  * buffer - BAIL OUT!
		  */
		 done = -1;
	       }
	  }
	if (!done && *(cur_vars->rd_ptr) == '\0')
	  {
	    /*
	     * We're at the end of the buffer, can we read more?
	     */
	    if (cur_vars->rd_flag > 0 && GetNextBuffer (cur_vars) == -1)
		return -1;

	    if (cur_vars->rd_flag == 0)
		done = True;
          }
      }

    /*
     * DO NOT MODIFY 'done' HERE
     * If you do, the caller is unable to test for return value == 1!!!!
     */
    if (done == -1 ||
	CheckSaveSegment (seg_flags, font_attr, linkspec, cur_vars) == -1)
	return -1;

    return done;
}

/*****************************************************************************
 * Function:	int ParseTitle (FormatVariables cur_vars)
 *
 * Parameters:
 *		cur_vars	Specifies the current values for formatting.
 *
 * Returns:	0 if successful, -1 if failure.
 *
 * errno Values:
 *
 * Purpose:	ParseTitle gets the data between a <TITLE> and </TITLE>
 *		pair; putting it in a controlling container.
 *
 *****************************************************************************/
static	int
ParseTitle(
    FormatVariables	*cur_vars,
    int			 cont_flag,
    int			 skip_abbrev,
    int			 fnt_flag,
    _DtHelpFontHints	*fontAttrs)
{
    int	result = -1;
    SegList oldList = cur_vars->my_list;

    /*
     * rest the current segment list
     */
    cur_vars->my_list  = InitList;

    /*
     * check for the <TITLE> directive.
     */
    if (_DtHelpCeCheckNextCcdfCmd ("ti", cur_vars->my_file, cur_vars->rd_buf,
			cur_vars->rd_size, 1, &(cur_vars->rd_ptr)) != 0
		|| FindEndMarker (cur_vars) != 0)
	return -1;

    /*
     * Parse will return
     *    1 when a </> is found,
     *    0 if the data runs out before we finish parsing,
     *   -1 if errors.
     *
     * A return of 1 is required from Parse
     */
    if (1 == Parse (CCDF_TITLE_CMD, NormalState, cur_vars,
			0, _DtCvDYNAMIC,
			fontAttrs, -1, Specials,
			~(CCDF_TOPIC_CMD | CCDF_TITLE_CMD | CCDF_ABBREV_CMD |
			CCDF_PARAGRAPH_CMD | CCDF_FIGURE_CMD | CCDF_LABEL_CMD),
			False, fnt_flag))
      {
	/*
	 * skip the any abbreviation that might be there
	 */
	result = 0;
	if (skip_abbrev)
	    result = _DtHelpCeSkipCcdfAbbrev (cur_vars->my_file,
				cur_vars->rd_buf,
				&(cur_vars->rd_ptr), cur_vars->rd_size,
				cur_vars->cur_mb_max);

	/*
	 * if we successfully skipped the abbreviation and there is
	 * a title - then attach the title body to the controlling
	 * container (and make the container a controller).
	 */
	if (result != -1 && cur_vars->my_list.list)
	  {
	    _DtCvSegment	*list = oldList.list;
	    int			 cnt  = oldList.cnt;

	    TerminateSegList (&(cur_vars->my_list), True);

	    /*
	     * when this segment was allocated, it was initialized
	     * to the correct vertical and horizontal orientation
	     * for a CCDF title.  Simply set the type to container
	     * and controller to get it to act properly.
	     */
	    if (NULL != list)
	      {
		list[cnt].type = _DtCvSetTypeToContainer(list[cnt].type);
		if (True == cont_flag)
		    list[cnt].type = _DtCvSetTypeToController(list[cnt].type);

		/*
		 * attach the title body to the controller container.
		 */
		_DtCvContainerListOfSeg(&list[cnt]) = cur_vars->my_list.list;

		/*
		 * and count this segment as used.
		 */
		oldList.cnt++;
              }
	    /*
	     * otherwise, a container hasn't been created for this list.
	     * but we don't want to loose the information.
	     */
	    else
		oldList.list = cur_vars->my_list.list;
          }
      }

    /*
     * restore segment usage information.
     */
    cur_vars->my_list = oldList;

    return result;
}

/*****************************************************************************
 * Function:	int Format (FormatVariables cur_vars, char *id_string,
 *
 * Parameters:
 *
 * Returns:
 *
 * errno Values:
 *
 * Purpose:	Format is the top entry point for formating Help Files with
 *		formatting information into a form understood by a display area.
 *		It will keep calling Parse, until the entire topic is read
 *		or an error  occurs.
 *
 *****************************************************************************/
static	int
Format(
    FormatVariables	*cur_vars,
    _FrmtUiInfo		*ui_info,
    char		*id_string,
    _DtCvTopicPtr	*ret_topic )
{
    int			 result  = 0;
    int			 done    = 0;
    char		*charSet = NULL;
    char		 readBuf[BUFF_SIZE];
    _DtHelpFontHints     fontAttrs;
    _DtCvTopicInfo	*topicHandle;
    SegList		 myList = InitList;

    /*
     * malloc space for the topic handle.
     */
    topicHandle = (_DtCvTopicInfo *) malloc (sizeof(_DtCvTopicInfo));
    if (topicHandle == NULL)
	return -1;

    /*
     * initialize the font attributes to the default hints
     */
    _DtHelpCeCopyDefFontAttrList (&fontAttrs);

    /*
     * set up the formatting variable structure
     */
    if (InitStructure (cur_vars, ui_info, readBuf, BUFF_SIZE) == -1)
	return -1;

    /*
     * read the first buffer's worth of the topic.
     */
    cur_vars->rd_flag = _DtHelpCeReadBuf (cur_vars->my_file, cur_vars->rd_buf,
						cur_vars->rd_size);
    if (cur_vars->rd_flag == -1)
	return -1;

    /*
     * The first command in the buffer MUST be the <TOPIC> command.
     * It is in a one byte charset.
     */
    if (_DtHelpCeGetCcdfTopicCmd (((void *) NULL), cur_vars->my_file,
			cur_vars->rd_buf, &(cur_vars->rd_ptr),
			cur_vars->rd_size, 1, &charSet) == -1)
	return -1;

    /*
     * change the character set to the returned character set
     * Assume that the charset is 'UTF-8' if not specified.
     */
    cur_vars->cur_mb_max = 1;
    if (NULL != charSet)
	GetMbLen(cur_vars, charSet, &(_DtHelpFontHintsLang(fontAttrs)),
					&(_DtHelpFontHintsCharSet(fontAttrs)));

    /*
     * Remember the topic code set and MB_CUR_MAX.
     */
    cur_vars->topic_char_set = _DtHelpFontHintsCharSet(fontAttrs);
    cur_vars->topic_lang     = _DtHelpFontHintsLang(fontAttrs);
    cur_vars->topic_mb_max   = cur_vars->cur_mb_max;

    /*
     * allocate two segments for the topic - one for the <title>
     * and one for the body of the topic.
     */
    if (0 != CheckList(&(cur_vars->my_list), 2, 2))
      {
	if (NULL != charSet)
	  {
	    free(_DtHelpFontHintsLang(fontAttrs));
	    free(_DtHelpFontHintsCharSet(fontAttrs));
	    free(charSet);
	  }
	return -1;
      }

    /*
     * get the title and skip the abbreviation.
     */
    if (0 != ParseTitle(cur_vars, True, True, 0, &fontAttrs))
      {
	if (NULL != charSet)
	  {
	    free(_DtHelpFontHintsLang(fontAttrs));
	    free(_DtHelpFontHintsCharSet(fontAttrs));
	    free(charSet);
	  }
	return -1;
      }

    /*
     * remember this current list since we will be attaching any more
     * information as a child of these segments.
     */
    myList = cur_vars->my_list;

    /*
     * process the rest of the information
     */
    while (0 == result && !done)
      {
	/*
	 * reset the segment count before processing the information
	 * for this paragraph
	 */
	cur_vars->my_list = InitList;

	/*
	 * make sure we've got a container for this paragraph
	 */
	done = CheckList(&myList, 1, GROW_SIZE);

	/*
	 * now process the information while finding paragraphs.
	 */
	if (0 == done)
	    do {
	        done = Parse (CCDF_PARAGRAPH_CMD, NormalState, cur_vars,
			0, _DtCvDYNAMIC,
			&fontAttrs, -1, Specials,
			~(CCDF_TOPIC_CMD | CCDF_TITLE_CMD | CCDF_ABBREV_CMD),
			False, 0);
	    } while (0 == done);

	/*
	 * if no errors, attach the resulting child information to the
	 * paragraph container.
	 */
	if (done != -1)
	  {
	    /*
	     * clean up the last segment.
	     */
	    if (cur_vars->my_list.list)
	      {
		_DtCvSegment	*list = NextAvailSeg(myList);

	        TerminateSegList (&(cur_vars->my_list), True);

		/*
		 * make it a container.
		 */
		list->type = _DtCvSetTypeToContainer(list->type);

		/*
		 * attach the information to the container
		 */
		_DtCvContainerListOfSeg(list) = cur_vars->my_list.list;

		/*
		 * and count this container as used.
		 */
		myList.cnt++;
	      }
	  }
	else
	    result = -1;
      }

    /*
     * If we had errors, deallocate the memory.
     */
    if (result == -1)
	_DtHelpFreeSegments(myList.list, _DtCvFALSE, ui_info->destroy_region, 
							ui_info->client_data);
    else
      {
	/*
	 * Tighten up the paragraph structures if too many allocated.
	 */
	if (0 != myList.cnt)
	    TerminateSegList (&(myList), True);
	else
	  {
	    free ((char *) myList.list);
	    myList.list = NULL;
	  }
      }

    /*
     * return the values, even if they are null and zero.
     */

    if (id_string == NULL)
        topicHandle->id_str = id_string;
    else
        topicHandle->id_str = strdup(id_string);

    topicHandle->seg_list  = myList.list;
    topicHandle->mark_list = NULL;
    topicHandle->link_data = cur_vars->my_links;

    *ret_topic = (void *) topicHandle;

    /*
     * free memory
     */
    if (NULL != charSet)
      {
	free(_DtHelpFontHintsLang(fontAttrs));
	free(_DtHelpFontHintsCharSet(fontAttrs));
	free(charSet);
      }

    if (result == -1)
	return -1;

    return 0;
}

/*****************************************************************************
 * Function:	int FormatCCDFTitle (FormatVariables cur_vars
 *
 * Parameters:
 *		cur_vars	Specifies the current values for formatting.
 *		id_string	Specifies the id to look for or NULL.
 *		ret_para	Returns a pointer to a list of _DtCvSegment
 *				structures.
 *		ret_num		Specifies the number of structures in
 *				'ret_para'.
 *		ret_seg		Specifies the segment containing 'id_string'
 *				or NULL.
 *
 * Returns:
 *
 * errno Values:
 *
 * Purpose:	Format is the top entry point for formating Help Files with
 *		formatting information into a form understood by a display area.
 *		It will keep calling Parse, until the entire topic is read
 *		or an error  occurs.
 *
 *****************************************************************************/
static	int
FormatCCDFTitle(
    FormatVariables	*cur_vars,
    _DtHelpFontHints	 fontAttrs,
    char		*filename,
    int			 offset,
    int			 level,
    int			 fnt_flags,
    int			 want_abbrev)
{
    char    *strPtr;
    char    *charSet   = NULL;
    int      result    = 0;
    int      origCnt   = cur_vars->my_list.cnt;
    int      len;
    _DtCvSegment	*titleSeg;
    _DtCvSegment	*list;

    cur_vars->rd_buf[0] = '\0';
    cur_vars->rd_ptr    = cur_vars->rd_buf;

    if (_DtHelpCeFileOpenAndSeek(filename, offset, -1,
					&(cur_vars->my_file), NULL) != 0)
	return -1;

    cur_vars->rd_flag = _DtHelpCeReadBuf (cur_vars->my_file, cur_vars->rd_buf,
						cur_vars->rd_size);

    if (cur_vars->rd_flag == -1)
      {
	_DtHelpCeBufFileClose (cur_vars->my_file, True);
	return -1;
      }

    /*
     * The first command in the buffer MUST be the <TOPIC> command.
     * It is in a one byte charset.
     */
    if (_DtHelpCeGetCcdfTopicCmd (((void *) NULL), cur_vars->my_file,
			cur_vars->rd_buf, &(cur_vars->rd_ptr),
			cur_vars->rd_size, 1, &charSet) == -1)
      {
	_DtHelpCeBufFileClose (cur_vars->my_file, True);
	return -1;
      }

    /*
     * change the character set to the returned character set
     * Assume that the charset is 'UTF-8' if not specified.
     */
    cur_vars->cur_mb_max = 1;
    if (NULL != charSet)
	GetMbLen(cur_vars, charSet, &(_DtHelpFontHintsLang(fontAttrs)),
					&(_DtHelpFontHintsCharSet(fontAttrs)));

    /*
     * Remember the topic code set and MB_CUR_MAX.
     */
    cur_vars->topic_char_set = _DtHelpFontHintsCharSet(fontAttrs);
    cur_vars->topic_lang     = _DtHelpFontHintsLang(fontAttrs);
    cur_vars->topic_mb_max   = cur_vars->cur_mb_max;

    /*
     * allocate another segment for this title.
     */
    if (0 != CheckSegList(cur_vars))
      {
	if (NULL != charSet)
	  {
	    free(_DtHelpFontHintsLang(fontAttrs));
	    free(_DtHelpFontHintsCharSet(fontAttrs));
	    free(charSet);
	  }
	_DtHelpCeBufFileClose(cur_vars->my_file, True);
	return -1;
      }

    /*
     * set the left margin correctly.
     */
    titleSeg  = NextAvailSeg(cur_vars->my_list);
    titleSeg->handle.container = DefContainer;
    titleSeg->type = _DtCvSetTypeToContainer(titleSeg->type);
    _DtCvContainerLMarginOfSeg(titleSeg) = level*2*cur_vars->ui_info->avg_char;
    _DtCvContainerBMarginOfSeg(titleSeg) = 0;
    _DtCvContainerTypeOfSeg(titleSeg)    = _DtCvLITERAL;
    _DtCvContainerLeadingOfSeg(titleSeg) = cur_vars->ui_info->leading;

    /*
     * Parse the title.
     */
    if (-1 == ParseTitle(cur_vars, False, False, fnt_flags, &fontAttrs))
      {
	if (NULL != charSet)
	  {
	    free(_DtHelpFontHintsLang(fontAttrs));
	    free(_DtHelpFontHintsCharSet(fontAttrs));
	    free(charSet);
	  }
	_DtHelpCeBufFileClose(cur_vars->my_file, True);
	return -1;
      }

    /*
     * We've parsed the title. see if we want the abbrev.
     */
    /*
     * if the title is null or we want the abbrev,
     * process the abbreviation.
     */
    if (NULL == _DtCvContainerListOfSeg(titleSeg) || want_abbrev)
      {
	SegList	titleList = cur_vars->my_list;

	/*
	 * reset the buffer to get the abbreviation
	 */
	if (cur_vars->fmt_buf != NULL)
	  {
	    free(cur_vars->fmt_buf);
	    cur_vars->fmt_buf     = NULL;
	    cur_vars->fmt_size    = 0;
	    cur_vars->fmt_buf_max = 0;
	  }

	result = _DtHelpCeGetCcdfAbbrevCmd (cur_vars->my_file, cur_vars->rd_buf,
				cur_vars->rd_size, cur_vars->cur_mb_max,
				&(cur_vars->rd_ptr), &(cur_vars->fmt_buf));

	/*
	 * If no errors getting the abbreviation, save it
	 */
	if (result != -1)
	  {
	    /*
	     * save the abbreviation
	     */
	    if (cur_vars->fmt_buf != NULL)
	      {
		/*
		 * If there was a title, free it
		 */
		if (NULL != _DtCvContainerListOfSeg(titleSeg))
		    _DtHelpFreeSegments(_DtCvContainerListOfSeg(titleSeg),
					_DtCvFALSE, 
					cur_vars->ui_info->destroy_region,
						cur_vars->ui_info->client_data);

		/*
		 * save the abbreviation
		 */
		cur_vars->my_list      = InitList;
		cur_vars->fmt_size     = strlen (cur_vars->fmt_buf);
		cur_vars->fmt_buf_max  = cur_vars->fmt_size + 1;
		if (cur_vars->fmt_size > 0)
		    result = SaveStringAsSegments (cur_vars, 0, &fontAttrs, -1);

		if (result != -1)
		    TerminateSegList(&(cur_vars->my_list), True);

		titleSeg->type = _DtCvSetTypeToContainer(titleSeg->type);
		_DtCvContainerListOfSeg(titleSeg) = cur_vars->my_list.list;
		titleList.cnt++;
	      }
	  }

	/*
	 * eliminate this segment if no title or abbrev was found.
	 */
	if (NULL == _DtCvContainerListOfSeg(titleSeg))
	  {
	    titleList.cnt = origCnt;
	    _DtCvContainerTypeOfSeg(titleSeg) = _DtCvDYNAMIC;
	  }

	/*
	 * reset the title list
	 */
	cur_vars->my_list = titleList;
      }
    else
	result = _DtHelpCeSkipCcdfAbbrev (cur_vars->my_file, cur_vars->rd_buf,
				&(cur_vars->rd_ptr), cur_vars->rd_size,
				cur_vars->cur_mb_max);

    if (cur_vars->fmt_buf != NULL)
	cur_vars->fmt_buf[0] = '\0';
    cur_vars->fmt_size     = 0;

    /*
     * remove the newline from the lists and
     * change all the newlines on the segments into a space.
     */
    for (list = _DtCvContainerListOfSeg(titleSeg);
					NULL != list; list = list->next_seg)
      {
	if (_DtCvIsSegNewLine(list))
	  {
	    /*
	     * clear the newline flag.
	     */
	    list->type &= ~(_DtCvNEW_LINE);

	    /*
	     * check to see if the last character of this segment or
	     * the first character of the next segment is a space.
	     * if not, add one.
	     */
	    if (NULL != list->next_disp && _DtCvIsSegString(list)
						&& _DtCvIsSegRegChar(list))
	      {
		/*
		 * is the last character of the string a space?
		 * if so do nothing.
		 */
		len = strlen((char *) _DtCvStringOfStringSeg(list)) - 1;
		if (' ' != ((char *) _DtCvStringOfStringSeg(list))[len])
		  {
		    /*
		     * is the next segment a one byte string?
		     * and if so, is the first character a space?
		     * if so, do nothing.
		     */
		    if (_DtCvIsSegString(list->next_disp)
			&& _DtCvIsSegRegChar(list->next_disp)
			&& ' ' != *((char *)_DtCvStringOfStringSeg(list->next_disp)))
		      {
			/*
			 * need to add a space to one of these strings.
			 */
			len += 3;
			_DtCvStringOfStringSeg(list) = (char *) realloc(
					(void *) _DtCvStringOfStringSeg(list),
					sizeof(char *) * len);
			if (NULL == _DtCvStringOfStringSeg(list))
			    return -1;

			strPtr = (char *) _DtCvStrPtr(
						_DtCvStringOfStringSeg(list),
						0,
						len - 3);
			*strPtr++ = ' ';
			*strPtr   = '\0';
		      }
		  }
	      }
	  }
      }

    _DtHelpCeBufFileClose (cur_vars->my_file, True);

    /*
     * free memory
     */
    if (NULL != charSet)
      {
	free(_DtHelpFontHintsLang(fontAttrs));
	free(_DtHelpFontHintsCharSet(fontAttrs));
	free(charSet);
      }

    if (result == -1)
	return -1;

    return 0;
}

/******************************************************************************
 * Function:	int FormatEntryInToc (
 *
 * Parameters:
 *
 * Returns:	0 if successful, -1 if errors
 *
 * errno Values:
 *
 * Purpose:
 *
 ******************************************************************************/
static int
FormatEntryInToc(
    _DtHelpVolumeHdl	  volume,
    char		 *loc_id,
    FormatVariables	 *cur_vars,
    _DtHelpFontHints	  font_attrs,
    int			  level)
{
    int		  result    = -1;
    int		  tocLen    = 0;
    int		  offset;
    int		  num;
    char	 *strPtr = NULL;
    _DtCvSegment *pSeg;
    _DtCvSegment *titleSeg;
    SegList       titleList;

    /*
     * check to see that there is a segment for this title.
     */
    if (0 != CheckSegList(cur_vars))
	return -1;

    /*
     * The next segment in the list will become the next title.
     * Get a pointer to it for later use.
     */
    titleList = cur_vars->my_list;
    titleSeg  = NextAvailSeg(titleList);
    titleSeg->handle.container = DefContainer;
    titleSeg->type = _DtCvSetTypeToContainer(titleSeg->type);
    _DtCvContainerBMarginOfSeg(titleSeg) = 0;
    _DtCvContainerLeadingOfSeg(titleSeg) = cur_vars->ui_info->leading;

    /*
     * Get the file and offset for the topic.
     * and process the topic's title, ignoring most font changes.
     */
    if (True == _DtHelpCeFindId(volume, loc_id, -1, &strPtr, &offset) &&
	-1 != FormatCCDFTitle(cur_vars, font_attrs, strPtr, offset, level,
  ((1<<_CEFONT_SIZE)|(1<<_CEFONT_WEIGHT)|(1<<_CEFONT_ANGLE)|(1<<_CEFONT_TYPE)),
				    False))
      {
	/*
	 * now create the hypertext link index for the ghostlink.
	 */
	num    = _DtLinkDbAddLink(cur_vars->my_links, NULL, loc_id,
					    _DtCvLinkType_SameVolume,
					    _DtCvWindowHint_CurrentWindow,
					    NULL);
	/*
	 * if a link index was successfully created, process the item.
	 */
	if (num != -1)
	  {
	    result = 0;

	    /*
	     * check to see if this title had any segments.
	     */
	    pSeg = _DtCvContainerListOfSeg(titleSeg);
	    if (NULL != pSeg)
	      {
		while (NULL != pSeg)
		  {
		    /*
		     * If this segment has a hypertext link, remove it.
		     */
		    if (pSeg->link_idx != -1)
			_DtLinkDbRemoveLink(cur_vars->my_links, pSeg->link_idx);
    
		    /*
		     * change the link index and flag to the ghost link.
		     */
		    pSeg->link_idx  = num;
		    pSeg->type     &= ~(_DtCvHYPER_TEXT);
		    pSeg->type      = _DtCvSetTypeToGhostLink(pSeg->type);

		    /*
		     * go to the next segment.
		     */
		    pSeg = pSeg->next_seg;
		  }
	      }
	    else
	      {
		/*
		 * no title or abbreviation, so use the location id.
		 * allocate room for it and put it between parens.
		 */
		tocLen = strlen (loc_id) + 9;
		if (tocLen > cur_vars->fmt_buf_max)
		  {
		    if (NULL == cur_vars->fmt_buf)
			cur_vars->fmt_buf = (char *) malloc (tocLen);
		    else
			cur_vars->fmt_buf = (char *) realloc(
					    cur_vars->fmt_buf,
					    tocLen);
		    cur_vars->fmt_buf_max = tocLen;
		  }
    
		cur_vars->fmt_size = tocLen - 1;
		strcpy (cur_vars->fmt_buf, "...(");
		strcat (cur_vars->fmt_buf, loc_id);
		strcat (cur_vars->fmt_buf, ")...");
    
		/*
		 * don't overwrite the title's
		 */
		cur_vars->my_list = InitList;

		/*
		 * save the string off and then attach to the title
		 * container.
		 */
		result = SaveStringAsSegments(cur_vars, _DtCvGHOST_LINK,
							&font_attrs, num);
		if (-1 != result)
		    _DtCvContainerListOfSeg(titleSeg) = cur_vars->my_list.list;

		/*
		 * restore the title list.
		 */
		cur_vars->my_list = titleList;
	      }
	  }
      }

   if (NULL != strPtr)
       free(strPtr);

   return result;

}  /* End FormatEntryInToc */

/******************************************************************************
 * Function:	int FormatExpandToc (
 *				_DtHelpVolumeHdl volume,
 *
 * Purpose:
 ******************************************************************************/
static int
FormatExpandedToc(
    _DtHelpVolumeHdl	  volume,
    char		  *cur_id,
    char		 **path_lst,
    FormatVariables	 *cur_vars,
    _DtHelpFontHints	  font_attrs,
    int			  level)
{
    int    result = 0;

    if (*path_lst != NULL)
      {
	font_attrs.weight = _DtHelpFontWeightMedium;
	if (_DtCvStrCaseCmpLatin1(*path_lst, cur_id) == 0)
	    font_attrs.weight = _DtHelpFontWeightBold;

        result = FormatEntryInToc(volume, *path_lst, cur_vars,
				font_attrs, level);

        if (result != -1)
          {
	    char **children;
	    char **childLst;
	    int    count = _DtHelpCeGetCcdfTopicChildren(volume,
							*path_lst, &children);

	    /*
	     * if there are children, format them
	     */
	    level++;
	    path_lst++;
	    childLst = children;
	    font_attrs.weight = _DtHelpFontWeightMedium;
	    while (result != -1 && count > 0)
	      {
		/*
		 * found the next item in the list
		 */
		if (*path_lst != NULL &&
			_DtCvStrCaseCmpLatin1(*path_lst, *childLst) == 0)
		    result = FormatExpandedToc(volume,
					cur_id, path_lst,
					cur_vars, font_attrs, level);
		else
		    result = FormatEntryInToc(volume, *childLst,
					cur_vars, font_attrs, level);

		childLst++;
		count--;
	      }

	    if (children != NULL)
		_DtCvFreeArray((void **) children);
          }
      }

    return result;
}

/******************************************************************************
 *
 * Semi-Public Functions
 *
 *****************************************************************************/
/******************************************************************************
 * Function:	VarHandle *__DtHelpCeSetUpVars (_FrmtUiInfo *ui_info)
 *
 * Parameters:
 *
 * Returns:	NULL if unsuccessful.
 *
 * errno Values:
 *
 * Purpose:	
 *
 ******************************************************************************/
VarHandle
__DtHelpCeSetUpVars(
    _FrmtUiInfo	*ui_info)
{
    FormatVariables    *newVars;

    newVars = (FormatVariables *) malloc (sizeof(FormatVariables));
    *newVars = DefVars;
    newVars->ui_info = ui_info;

    newVars->topic_mb_max = MB_CUR_MAX;
    newVars->cur_mb_max   = newVars->topic_mb_max;

    return ((VarHandle) newVars);

}  /* End __DtHelpCeSetUpVars */

/******************************************************************************
 * Function:	int __DtHelpCeProcessString (char *in_string, int seg_type,
 *						char **font_attr)
 *
 * Parameters:
 *
 * Returns:	0 if successful, -1 if errors.
 *
 * errno Values:
 *
 * Purpose:	
 *
 ******************************************************************************/
int
__DtHelpCeProcessString(
    VarHandle		 var_handle,
    BufFilePtr		 my_file,
    _DtCvFrmtOption	 frmt_type,
    char		*scan_string,
    char		*in_string,
    int			 in_size,
    int			 fnt_flag,
    int			 ret_on_nl,
    _DtHelpFontHints	*font_attr )
{
    int			 done = 0;
    FormatVariables	*myVars = (FormatVariables *) var_handle;
    SegList		 oldList = myVars->my_list;

    myVars->rd_buf  = in_string;
    myVars->rd_size = in_size;
    myVars->rd_ptr  = in_string;
    myVars->my_file = my_file;

    if (my_file == NULL)
	myVars->rd_flag = 0;
    else
	myVars->rd_flag = strlen(in_string);

    while (!done)
      {
	if (True == ret_on_nl)
	    myVars->my_list = InitList;

        done = Parse (CCDF_FONT_CMD, NormalState, myVars,
		0, frmt_type,
		font_attr,
		-1, scan_string, (CCDF_FONT_CMD | CCDF_OCTAL_CMD), ret_on_nl,
		fnt_flag);

	if (done != -1 && True == ret_on_nl)
	  {
	    TerminateSegList (&(myVars->my_list), True);
	    if (NULL != myVars->my_list.list)
	      {
		done = CheckList (&oldList, 1, GROW_SIZE);
		if (-1 != done)
		  {
		    _DtCvSegment *newSeg = NextAvailSeg(oldList);

		    newSeg->type = _DtCvSetTypeToContainer(newSeg->type);
		    _DtCvContainerListOfSeg(newSeg) = myVars->my_list.list;
		    _DtCvContainerTypeOfSeg(newSeg) = _DtCvDYNAMIC;

		    oldList.cnt++;

		    TerminateSegList(&oldList, False);
		  }
	      }
	  }
      }

    if (False == ret_on_nl)
	oldList = myVars->my_list;

    myVars->my_list = oldList;
    return done;

}  /* End __DtHelpCeProcessString */

/*****************************************************************************
 * Function:    int __DtHelpCeGetParagraphList (
 *
 * Parameters:
 *
 * Returns:     0 if successful, -1 if errors.
 *
 * errno Values:
 *
 * Purpose:     _DtHelpFormatGetParagraphList places a terminator on the
 *              last segment in the current paragraph and returns the
 *              segment list included in the topic information structure.
 *
 *		If make_cont is true, the segment list is first included
 *		in a container with the specified type.
 *
 *****************************************************************************/
int
__DtHelpCeGetParagraphList (
    VarHandle		 var_handle,
    int			 make_cont,
    _DtCvFrmtOption	 type,
    _DtCvTopicPtr	*ret_handle)
{
    int			 result = -1;
    FormatVariables	*myVars = (FormatVariables *) var_handle;
    _DtCvTopicInfo	*topicHandle;

    /*
     * check the parameters.
     */
    if (ret_handle == NULL || myVars == NULL)
      {
        errno = EINVAL;
        return -1;
      }

    TerminateSegList(&(myVars->my_list), True);

    topicHandle = (_DtCvTopicInfo *) malloc (sizeof(_DtCvTopicInfo));
    if (topicHandle != NULL)
      {
	result = 0;

	if (True == make_cont)
	  {
	    _DtCvSegment *myCont = AllocateSegments(NULL, 0, 1);

	    if (NULL != myCont)
	      {
		myCont->type = _DtCvSetTypeToContainer(myCont->type);
	        _DtCvContainerTypeOfSeg(myCont) = type;
	        _DtCvContainerListOfSeg(myCont) = myVars->my_list.list;

	        myVars->my_list.list = myCont;
	        myVars->my_list.cnt  = 1;
	        myVars->my_list.max  = 1;
	     }
	    else
	        result = -1;
	  }

	topicHandle->id_str    = NULL;
        topicHandle->seg_list  = myVars->my_list.list;
        topicHandle->mark_list = NULL;
        topicHandle->link_data = myVars->my_links;

        *ret_handle = (_DtCvTopicPtr) topicHandle;
      }

    if (myVars->fmt_buf != NULL)
	free(myVars->fmt_buf);

    return result;
}

/******************************************************************************
 * Function:	int _DtHelpCeFrmtCcdfTopic (_DtHelpVolumeHdl volume, char *filename,
 *				int offset, char *id_string,
 *				_DtCvTopicPtr *ret_handle)
 *
 * Parameters:
 *		volume		Specifies the Help Volume the information
 *				is associated with.
 *		filename	Specifies the file containing the Help Topic
 *				desired.
 *		offset		Specifies the offset into 'filename' to
 *				the Help Topic desired.
 *		id_string	Specifies the location id to look for or NULL.
 *		ret_handle	Returns a handle to the topic information
 *				including the number of paragraphs and the
 *				id match segment.
 *
 * Returns:	0 if successful, -1 if errors
 *
 * errno Values:
 *
 * Purpose:	_DtHelpCeFrmtCcdfTopic formats Help Files with formatting
 *		information into a Canvas Engine structure.
 *
 ******************************************************************************/
int
_DtHelpCeFrmtCcdfTopic(
    _DtHelpVolumeHdl	 volume,
    char		*filename,
    int			 offset,
    char		*id_string,
    _FrmtUiInfo		*ui_info,
    _DtCvTopicPtr	*ret_handle )
{
    int		 result = -1;
    char	*ptr;
    FormatVariables	variables;

    /*
     * Check the parameters
     */
    if (volume == NULL || filename == NULL || offset < 0 || ret_handle == NULL)
      {
	errno = EINVAL;
	return -1;
      }

    /*
     * get the current file path.
     */
    variables = DefVars;
    variables.topic_id = id_string;
    if (volume)
      {
	variables.vol_name = _DtHelpCeGetVolumeName(volume);
	variables.my_path  = strdup (variables.vol_name);
	if (variables.my_path == NULL)
	    return -1;

	result = _DtHelpCeStrrchr (variables.my_path, Slash, MB_CUR_MAX, &ptr);
	if (result == -1)
	    ptr = strrchr (variables.my_path, '/');

	if (ptr)
	    *ptr = '\0';
      }

    /*
     * open the file and seek to the correct place.
     */
    result = _DtHelpCeFileOpenAndSeek (filename, offset, -1,
						&(variables.my_file), NULL);
    if (result != -1)
      {
	/*
	 * Initialize the X variables.
	 */
	result = Format (&variables, ui_info, id_string, ret_handle);

	_DtHelpCeBufFileClose (variables.my_file, True);
      }

    if (variables.fmt_buf != NULL)
	free (variables.fmt_buf);

    if (variables.my_path != NULL)
	free (variables.my_path);

    return result;

}  /* End _DtHelpCeFrmtCcdfTopic */

/******************************************************************************
 * Function:	int _DtHelpCeFrmtCcdfPathAndChildren (
 *				_DtHelpVolumeHdl volume,
 *				_DtCvTopicPtr *ret_handle)
 *
 * Parameters:
 *		ret_handle	Returns a handle to the topic information
 *				including the number of paragraphs and the
 *				id match segment.
 *
 * Returns:	0 if successful, -1 if errors, 1 if there the path is empty.
 *
 * errno Values:
 *
 * Purpose:	_DtHelpCeFormatCcdfToc formats Table of Contents for
 *		a CCDF Help File.
 *
 ******************************************************************************/
int
_DtHelpCeFrmtCcdfPathAndChildren(
    _DtHelpVolumeHdl	  volume,
    char		 *loc_id,
    _FrmtUiInfo		 *ui_info,
    _DtCvTopicPtr	 *ret_handle )
{
    int		  result    = 0;
    int		  pathCnt;
    char	 *ptr;
    char	**pathHead;
    char	  readBuf[BUFF_SIZE];
    FormatVariables     myVars;
    _DtHelpFontHints    fontAttrs;

    _DtHelpCeCopyDefFontAttrList (&fontAttrs);
    fontAttrs.pointsz = 10;
    fontAttrs.weight  = _DtHelpFontWeightMedium;
    fontAttrs.style   = _DtHelpFontStyleSanSerif;

    /*
     * Check the parameters
     */
    if (volume == NULL || ret_handle == NULL)
      {
	errno = EINVAL;
	return -1;
      }

    readBuf[0] = '\0';
    myVars     = DefVars;
    if (InitStructure (&myVars, ui_info, readBuf, BUFF_SIZE) == -1)
	return -1;

    /*
     * get the list of topics in the volume.
     */
    pathCnt = _DtHelpCeGetCcdfIdPath(volume, loc_id, &pathHead);
    if (pathCnt == -1)
	return -1;

    /*
     * Check to see if this topic is in the topic list.
     * I.e. if a count of 1 is returned this should match the
     * top topic, otherwise this is an hidden topic.
     */
    if (pathCnt == 1)
      {
	if (_DtHelpCeGetCcdfTopTopic(volume, &ptr) == -1)
	    result = -1;
	else if (_DtCvStrCaseCmpLatin1(ptr, *pathHead) != 0)
	    result = 1;
      }

    /*
     * if result is non-zero, we had problems or this is a hidden topic
     */
    if (result == 0)
      {
        fontAttrs.slant   = _DtHelpFontSlantRoman;

        myVars.topic_id = loc_id;
	myVars.vol_name = _DtHelpCeGetVolumeName(volume);
	myVars.my_path  = strdup (myVars.vol_name);
        if (myVars.my_path == NULL)
	    return -1;

        if (-1 == _DtHelpCeStrrchr (myVars.my_path, Slash, MB_CUR_MAX, &ptr))
	    ptr = strrchr (myVars.my_path, '/');

        if (ptr)
	    *ptr = '\0';

        result = FormatExpandedToc(volume, loc_id, pathHead,
						&myVars, fontAttrs, 0);
	_DtCvFreeArray((void **) pathHead);
      }

    if (result != -1)
	result = __DtHelpCeGetParagraphList (&myVars, False, _DtCvDYNAMIC,
								ret_handle);

    /*
     * check for an empty path.
     */
    if (-1 != result && NULL == ((_DtCvTopicInfo *) *ret_handle)->seg_list)
	result = 1;

    if (myVars.my_path != NULL)
        free(myVars.my_path);

    return result;

}  /* End _DtHelpCeFrmtCcdfPathAndChildren */

/******************************************************************************
 * Function:	int _DtHelpCeGetCcdfTitleChunks (
 *				_DtHelpVolumeHdl volume,
 *				_DtCvTopicPtr *ret_handle)
 *
 * Parameters:
 *		ret_handle	Returns a handle to the topic information
 *				including the number of paragraphs and the
 *				id match segment.
 *
 * Returns:	0 if successful, -1 if errors
 *
 * errno Values:
 *
 * Purpose:	_DtHelpCeFormatCcdfToc formats Table of Contents for
 *		a CCDF Help File.
 *
 ******************************************************************************/
int
_DtHelpCeGetCcdfTitleChunks(
    _DtHelpVolumeHdl	   volume,
    char		  *loc_id,
    _FrmtUiInfo		  *ui_info,
    void		***ret_chunks)
{
    long	  type   = 0;
    int		  result = 0;
    int		  offset;
    char	 *fileName;
    char	 *ptr;
    char	  readBuf[BUFF_SIZE];
    _DtCvSegment	*titleSeg;
    _DtCvSegment	*list    = NULL;
    FormatVariables     myVars;
    _DtHelpFontHints    fontAttrs;
    _DtHelpCeLockInfo lockInfo;

    /*
     * Check the parameters
     */
    if (volume == NULL || ret_chunks == NULL)
      {
	errno = EINVAL;
	return -1;
      }

    /*
     * init the return value
     */
    *ret_chunks = NULL;

    /*
     * get the topic in the volume.
     */
    if (_DtHelpCeLockVolume(volume, &lockInfo) != 0)
	return -1;

    if (_DtHelpCeFindId(volume, loc_id, -1, &fileName, &offset) == False)
      {
	_DtHelpCeUnlockVolume(lockInfo);
	return -1;
      }

    _DtHelpCeCopyDefFontAttrList (&fontAttrs);
    myVars = DefVars;
    if (InitStructure (&myVars, ui_info, readBuf, BUFF_SIZE) == -1)
      {
	_DtHelpCeUnlockVolume(lockInfo);
	return -1;
      }

    /*
     * initialize my variables.
     */
    myVars.topic_id = loc_id;
    myVars.vol_name = _DtHelpCeGetVolumeName(volume);
    myVars.my_path  = strdup (myVars.vol_name);
    if (myVars.my_path == NULL)
      {
	_DtHelpCeUnlockVolume(lockInfo);
	return -1;
      }

    if (-1 == _DtHelpCeStrrchr (myVars.my_path, Slash, MB_CUR_MAX, &ptr))
	ptr = strrchr (myVars.my_path, '/');

    if (ptr)
	*ptr = '\0';

    result = FormatCCDFTitle(&myVars, fontAttrs, fileName,
				offset, 0, (1 << _CEFONT_SIZE), True);
    free(fileName);

    if (-1 != result && 0 < myVars.my_list.cnt)
      {
	titleSeg = &(myVars.my_list.list[myVars.my_list.cnt - 1]);
	list     = _DtCvContainerListOfSeg(titleSeg);
      }

    if (result != -1 && NULL != list)
      {
	_DtCvSegment *pSeg = list;

	while (-1 != result && NULL != pSeg)
	  {
	    /*
	     * create the types and add the informatio to the array.
	     */
	    type = DT_HELP_CE_FONT_PTR | DT_HELP_CE_STRING;
	    if (_DtCvIsSegNewLine(pSeg))
		type |= DT_HELP_CE_NEWLINE;

	    if (_DtCvIsSegString(pSeg))
	      {
		*ret_chunks = (void **) _DtCvAddPtrToArray(*ret_chunks,
					(void *) type);
		if (NULL != *ret_chunks)
		    *ret_chunks = (void **) _DtCvAddPtrToArray(*ret_chunks,
					(void *) _DtCvFontOfStringSeg(pSeg));

		if (NULL != *ret_chunks)
		  {
		    if (_DtCvIsSegWideChar(pSeg))
		      {
		        offset = _DtCvStrLen(_DtCvStringOfStringSeg(pSeg), 1)
					* MB_CUR_MAX + 1;
			ptr    = (char *) malloc (sizeof(char) * offset);
			if (NULL != ptr)
			    wcstombs(ptr,
					(wchar_t *)_DtCvStringOfStringSeg(pSeg),
					offset);
		      }
		    else
			ptr = strdup((char *) _DtCvStringOfStringSeg(pSeg));

		    if (NULL != ptr)
		        *ret_chunks = (void **)_DtCvAddPtrToArray(
					*ret_chunks, (void *) ptr);
		  }

		if (NULL == *ret_chunks || NULL == ptr)
		    result = -1;
	      }

	    /*
	     * go to the next segment
	     */
	    pSeg = pSeg->next_seg;
	  }
      }
    else
      {
	char  buf[128];
	char *idStr;
	int   idLen = strlen (loc_id) + 9;

	/*
	 * create the location id string.
	 */
	idStr  = (char *) malloc (idLen);
	if (NULL != idStr)
	  {
	    /*
	     * format the location id
	     */
	    strcpy (idStr, "...(");
	    strcat (idStr, loc_id);
	    strcat (idStr, ")...");
	
	    /*
	     * format the language and codeset
	     */
	    strcpy(buf, _DtHelpFontHintsLang(fontAttrs));
	    strcat(buf, ".");
	    strcpy(buf, _DtHelpFontHintsCharSet(fontAttrs));

	    /*
	     * creat a chunk table
	     */
	    *ret_chunks = (void **) _DtCvAddPtrToArray(*ret_chunks,
	    		(void *) (DT_HELP_CE_CHARSET | DT_HELP_CE_STRING));
	    if (NULL != *ret_chunks)
	        *ret_chunks = (void **) _DtCvAddPtrToArray(*ret_chunks,
						(void *) strdup(buf));
	    if (NULL != *ret_chunks)
	        *ret_chunks = (void **) _DtCvAddPtrToArray(*ret_chunks,
						(void *) idStr);
          }
      }

    if (NULL != *ret_chunks)
	*ret_chunks = (void **) _DtCvAddPtrToArray(*ret_chunks,
						(void *) DT_HELP_CE_END);

    /*
     * free the list
     */
    if (0 < myVars.my_list.cnt)
	_DtHelpFreeSegments(myVars.my_list.list, _DtCvFALSE,
						ui_info->destroy_region, 
						ui_info->client_data);

    /*
     * free the link database
     */
    _DtLinkDbDestroy(myVars.my_links);

    /*
     * free memory
     */
    if (myVars.my_path != NULL)
        free(myVars.my_path);
    if (myVars.fmt_buf != NULL)
        free(myVars.fmt_buf);

    _DtHelpCeUnlockVolume(lockInfo);

    /*
     * indicate an error if problems.
     */
    result = 0;
    if (NULL == *ret_chunks)
	result = -1;

    return result;

}  /* End _DtHelpCeGetCcdfTitleChunks */

/******************************************************************************
 * Function:	int _DtHelpCeGetCcdfVolTitleChunks (
 *				_DtHelpVolumeHdl volume,
 *				void ***ret_chunks)
 *
 * Parameters:
 *		ret_chunks	Returns a ptr to the title information chunks
 *				The title chunks are stored in allocated memory
 *				that is owned by the caller and should be
 *                              freed when no longer needed.
 *				id match segment.
 *
 * Returns:	0 if successful, -1 if errors
 *
 * errno Values:
 *
 * Purpose:	_DtHelpCeFormatCcdfVolTitleChunks formats volume title for
 *		a CCDF Help File.
 *
 ******************************************************************************/
int
_DtHelpCeGetCcdfVolTitleChunks(
    _DtHelpVolumeHdl	   volume,
    _FrmtUiInfo		  *ui_info,
    void		***ret_chunks)
{
    char    *charSet;
    char    *titleStr;

    /*
     * get the title of the volume (we own the memory).
     */
    charSet = _DtHelpCeGetCcdfVolLocale(volume);
    if (charSet == NULL)
	charSet = strdup("C.UTF-8");

    titleStr = _DtHelpCeGetCcdfVolTitle(volume);
    if (titleStr != NULL)
      {
        *ret_chunks = (void **) malloc(sizeof(void *) * 4);
        if (*ret_chunks == NULL)
	    return -1;

        (*ret_chunks)[0] = (void *) (DT_HELP_CE_CHARSET | DT_HELP_CE_STRING);
        (*ret_chunks)[1] = (void *) strdup(charSet);
        (*ret_chunks)[2] = (void *) strdup(titleStr);
        (*ret_chunks)[3] = (void *) DT_HELP_CE_END;
      }
    else 
      {
        free(charSet);
        if (_DtHelpCeGetCcdfTitleChunks(volume, "_title",
						ui_info, ret_chunks) != 0
		&&
	    _DtHelpCeGetCcdfTitleChunks(volume, "_hometopic",
						ui_info, ret_chunks) != 0)
 	    return -1;
      }

    return 0;

}  /* End _DtHelpCeGetCcdfVolTitleChunks */
