/*===========================================================================
  Copyright (C) 1988-2009 European Southern Observatory (ESO)
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, 
  MA 02139, USA.
 
  Corresponding concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

.TYPE		Module
.IDENTIFICATION thelp.c
.AUTHOR    	Francois Ochsenbein [ESO-IPG]
.LANGUAGE  	C
.KEYWORDS	Hierarchical Help using TermWindows
.ENVIRONMENT	TermWindows


.COMMENTS	The module uses actions starting with `H'
		The \Help and \HelpLoad macros contain four parameters:
\begin{TeX}
\begin{enumerate}
\item The level, a number starting from 0 up to 9
\item	The topic, or filename for HelpLoad
\item	A short explanation, for survey purposes.
\item	The context specification, as a set of words separated by commas.
\end{enumerate}

A located topic starts {\em just after} the {\tt\b Help} found macro.

\end{TeX}

	The \HelpIndex has 7 parameters:
\begin{TeX}
\begin{enumerate}
\item The level, a number starting from 0 up to 9
\item	The topic, or filename for HelpLoad
\item	A short explanation, for survey purposes.
\item	The context specification, as a set of words separated by commas.
\item 	The name of the file where the help is included
\item 	The starting position just following the \b Help
\item 	The number of bytes to load.
\end{enumerate}

\end{TeX}

.VERSION 1.0	13-May-1988: Creation
.VERSION 1.1	07-Nov-1988: Possibility of an index with macro \HelpIndex
.VERSION 1.2	05-Dec-1988: Check if \Help commented out...
.VERSION 1.3	20-Jan-1989: th_set: allows NULL pointer without crash...
.VERSION 1.4	03-Mar-1989: Corrected bug signaled by Jeannine:
			\Help is always triggered, even if it's commented...
.VERSION 1.5	29-Mar-1989: Default extension for files = .tex
.VERSION 1.6	23-May-1989: Modified documentation
.VERSION 1.7	22-Jun-1989: Modified th_set: ONLY ADDRESSES are kept...
.VERSION 1.8	05-Apr-1990: Modified PromptKey: The prompt "? for subtopics"
 				appears only if subtopics do exist..
.VERSION 1.9	09-Sep-1990: Bug corrected in load_file: fname is saved
				in a independant location.
.VERSION 1.91	21-Nov-1990: Routine th_setsep to define the list
				of separators between topics.
.VERSION 2.0	04-Dec-1990: Allow Several Indexes to be Merged.
			(see th_merge)
.VERSION 2.1	07-Jun-1991: Added th_loc routine

 090901         last modif
--------------------------------------------------------------------------*/

#define	DEBUG		0	/* For debugging only		*/

#include <stdlib.h>


#define	PM_LEVEL 	LEVEL_TX
#define MAX_TOPIC	31	/* Maximal length for topic comparison	*/

#include <stesodef.h>
#include <twhelp.h>
#include <tw.h>
#include <atype.h>
#include <str.h>
#include <tex.h>
#include <buffer.h>

#include <proto_os.h>

#define reply_size	40
#define default_ext	".tex"
#define FileDate(f)	fi_date(NameFile(f,default_ext))

#define _REDISPLAY_	10
#define _PROMPT_	11
#define _SUMMARY_	12
#define _BAD_INDEX_	-3


extern int mm_free();

extern int tv_bell(), tv_dim(), tv_getcc();

extern int fi_open(), fi_gets(), fi_close(), fi_load();

extern int ty_close(), ty_display(), ty_end(), ty_mdisplay();
extern int ty_more();

extern int tw_fill(), tw_attr(), tw_uattr(), tw_r(), tw_goto(), tw_rule();
extern int tw_st(), tw_uflag(), tw_chars(), tw_copw(), tw_flags();
extern int tw_uw(), tw_write(), tw_il(), tw_clear(), tw_dc();
extern int tw_nl(), tw_upw(), tw_uc(), tw_mvc(), tw_copy();
extern int tw_mrule(), tw_cus(), tw_gc2(), tw_close(), tw_cur();
extern int tw_cline(), tw_home(), tw_tra(), tw_wa();
extern int tw_awhere(), tw_agoto(), tw_gc1(), tw_get1(), tw_wf();

extern int pm_enter(), pm_iexit(), pm_tr2(), pm_ed_i(), pm_ed_trace();

extern int eh_ed_i(), eh_ed_as(), eh_put1(), eh_put();

extern int tex_exec(), tex_unit(), tex_getparm(), tex_mexec(), tex_tell();
extern int tex_input(), tex_load(), tex_list(), tex_getvparm();

extern int tx_jr(), tx_jc(), tx_justify();
extern int tx_display(), tx_option();
extern int tx_fdisplay(), tx_file(), tx_mdisplay();


static  int     TheDoc = 0;		/* DOC Folder		*/
static 	WINDOW *helpw = NULL_WINDOW;	/* Small ^G Window	*/
static 	TeX	*htex = NULL_PTR(TeX);
TeX	*tx_tex();

typedef struct {
	char *start;
	char *end;
   }	PIECE;		/* Piece of text to use with DisplayArray */

static PIECE loaded_file;	/* Just loaded file	*/


typedef struct {
	char  	level;		/* Level 0-9		*/
	char	flag;		/* '@' to load a file	*/
	short	ltopic;		/* Proposed topic length*/
	char	*topic;		/* The topic address	*/
	char	*got_topic;	/* The got topic address*/
	char	*hpos;		/* Where exactly is \Help */
	char	*pos;		/* Where exactly in current piece */
	int 	piece;		/* Position in PIECE	*/
	int 	ofile;		/* Filenames Position 	*/
	long	fpos;		/* Position in File	*/
	int	ltext;		/* Text length		*/
   }	TOPIC;	

typedef struct {
	int 	ofile;		/* Filenames Position 	*/
	long	fpos;		/* Position in File	*/
	int	ltext;		/* Text length		*/
	char	text[1];	/* Filename + Text	*/
   }	LOADED;	

static  LOADED *loaded = NULL_PTR(LOADED);

static  TOPIC
	topics[10] = {	/* Current topic to look for */
	{'0', 0, 0, (char *)0, (char *)0, (char *)0, 0}, {'1'}, {'2'}, 
	{'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}};

static  TOPIC	topc;

static	char	*located_help = (char *)0;	/* Used by th_loc */
static  char	help_flag;	/* Used for communication with th_act */
static  char	level_found;	/* Used for communication with th_act */
static 	char 	reply[reply_size];
static 	char	blank = ' ';
static  int	subtopics = 0;	/* Used for communication with EdTop  */

static TWHELP	help;	/* Contains the Help definitions: windows + buffer */

static BUFFER	*list;		/* This buffer collects title + subtopics */
static BUFFER	*filenames;	/* This buffer collects filenames	  */
static char	*tit3 = (char *)0;
static int	ltit3 = 0;

static unsigned char sep_table[256];
static char *sep_top = (char *)0;		/* Default Separators */

#define FINISH		goto FIN

#define HELP_MACRO	"\\Help"
#define HELP_INDEX	"\\HelpIndex"
#define default_title	" Help"

	/* MONITOR(TXHELP); */


/*==========================================================================
 *			Interfaces to Doc
 *==========================================================================*/
static int myOpenDocText(str,len)
/*+++
.PURPOSE Interface to OpenDocText
.RETURNS NOK if not completely displayed / OK otherwise
---*/
	char	*str;	/* IN: TeX string to display */
	int	len;	/* IN: Length of Text	*/
{
  CloseDoc(TheDoc);
  TheDoc = OpenDocText(help.w[1], str, len);
  RaiseWindow(help.w[1]);
  return(atLastDocPage(TheDoc));
}

static int myOpenDocArray(str,n)
/*+++
.PURPOSE Interface to OpenDocText
.RETURNS NOK if not completely displayed / OK otherwise
---*/
        char    **str;	/* IN: TeX strings to display */
        int     n; 	/* IN: How many strings	*/
{
  CloseDoc(TheDoc);
  TheDoc = OpenDocArray(help.w[1], str, n);
  RaiseWindow(help.w[1]);
  return(atLastDocPage(TheDoc));
}

static int get_tit3()
/*+++
.PURPOSE Get the 3rd parameter of Title
.RETURNS Length of Text
---*/
{
	int l3;
	char *p;

  l3 = tex_getvparm(3);		/* The short explanation */
  p = (l3 > 0 ? htex->ap : "");
  if (l3 < 0)	l3 = 0;
  if (l3 >= ltit3)		/* Must expand	*/
	ltit3 = 1 + (l3|7), tit3 = MEM_EXP(char, tit3, ltit3);
  oscopy (tit3, p, l3);
  tit3[l3] = EOS;
  return(l3);
}

/*==========================================================================
 *			act0
 *==========================================================================*/
static int act0(str, len)
/*+++
.PURPOSE Action Routine to execute definitions in a help file
.RETURNS len (does nothing)
.REMARKS 
---*/
	char	*str;	/* IN: Action string, should start with H */
	int	len;	/* IN: Length of action string */
{
  return(len);
}  

/*==========================================================================
 *			CheckContext
 *==========================================================================*/
static int CheckContext()
/*+++
.PURPOSE Checks if the context is OK
.RETURNS OK / NOK
.REMARKS Context is a string in help; if the 4th parameter of \Help
	includes something, the same string must be in help.context
---*/
{
	char	*p;
	int	status;
	
  status = OK;
  
  tex_getparm(4);		/* Get 4th parameter			*/
  if (*(htex->ap) == EOS)	/* No  4th parameter => always valid 	*/
  	FINISH;
  
				/* Check if 4th parameter includes context */
  p = htex->ap + stritem(htex->ap, help.context, "\t, ");
				/* Separators may be , blank tab */
  if (*p == EOS)	status = NOK;
  
  FIN:
  return(status);
}  

/*==========================================================================
 *			th_act
 *==========================================================================*/
static int th_act(str, len)
/*+++
.PURPOSE Action Routine to interpret the `\Help' Macros
.RETURNS 0	(must stop)
.REMARKS Context checking and \HelpIndex check.
---*/
	char	*str;	/* IN: Action string, should start with H */
	int	len;	/* IN: Length of action string */
{
	
  if (*str != 'H')	FINISH;
  
  if (help.context)
	if (*help.context)
  		if_not(CheckContext())
  			FINISH;
	  
  tex_getparm(1);	/* Indicates Level */

  if ((help.indexed == 0) && (str[1] == ':'))	FINISH;
  
  level_found = '0' + atoi(htex->ap);
  help_flag   = str[1];	/* @ for HelpLoad  */

  tex_getparm(2);	/* Get argument = Topic or File Name */
  
  FIN:
  return(0);
}  

/*==========================================================================
 *			th_out
 *==========================================================================*/
static int th_out(str, len)
/*+++
.PURPOSE Output Routine to interpret the `\Help' Macros
.RETURNS 0	(must stop)
.REMARKS 
---*/
	char	*str;	/* IN: Action string, should start with H */
	int	len;	/* IN: Length of action string */
{
  return(0);		/* Must stop, should NEVER be called !!! */
}  

/*==========================================================================*/
static int next_help(str, len)
/*+++
.PURPOSE Locate next \Help
.RETURNS Offset found
.REMARKS Check if not in comment ...
---*/
	char	*str;	/* IN: Text in which locate \Help	*/
	int	len;	/* IN: Length of str	*/
{
	char	*b, *be, *bc;
	int	is_a_comment;

  b = str, be = b + len;
  while(b < be)
  {	b += oscindex(b, be-b, HELP_MACRO, sizeof(HELP_MACRO)-1);
	if (b >= be)	break;
		/* Check if it's not a comment... */
	is_a_comment = FALSE;
	for (bc = b; (bc > str) && (*bc != '\n'); bc--)
	{
		if ((*bc == '%') && (*(bc-1) != '\\'))	/* It's a comment */
		{ is_a_comment = TRUE; break; }
	}
	if (is_a_comment)	b += sizeof(HELP_MACRO) -1;
	else			break;
  }
  return(b - str);
}

/*==========================================================================
 *			Help Merging
 *==========================================================================*/
static int which_help(str, len, topic)
/*+++
.PURPOSE interpret the next Help as (level+Text)
.RETURNS Offset found
.REMARKS The actions th_act are assumed to be in effect.
---*/
	char	*str;	/* IN: Text in which locate \Help	*/
	int	len;	/* IN: Length of str	*/
	char	*topic;	/* OUT: Level + Topic, assumed size MAX_TOPIC+1	*/
{
	char	*p, *pe;

  p = str; pe = p + len;
  *topic = 0;
  p += next_help(p, pe-p);
  if (p < pe) {
	tex_exec(htex, p, pe-p);
	*topic = level_found;
	strncopy(topic+1, MAX_TOPIC, htex->ap);
  }
  return(p - str);
}

static PIECE *MergeHelp(p1, q2)
/*+++
.PURPOSE Merge two loaded helps into a single index
.RETURNS The merged piece
.REMARKS Works properly only for Sorted Indexed Helps. p1 and q2 are freed.
---*/
	PIECE	*p1;	/* IN: First Help tree	*/
	PIECE	*q2;	/* IN: Second Help tree */
{
	char	*b1, *b2, *c1, *c2;
	char	top1[MAX_TOPIC+1], top2[MAX_TOPIC+1];
	int	i;
	int	(*old_act)(), (*old_out)();
	static	PIECE	p;

	/* Allocate first the new Piece */

  old_act = htex->action, old_out = htex->output;
  htex->action = th_act,   htex->output = th_out;   
  i = (p1->end - p1->start) + (q2->end - q2->start);
  p.start = MEM_GET(char, i);	p.end = p.start;
  
  b1 = c1 = p1->start;	b2 = c2 = q2->start;
  c2 += which_help(c2, q2->end - c2, top2);	if (top2[0] != '0')	FINISH;
  c2++;
  c2 += which_help(c2, q2->end - c2, top2);	if (top2[0] != '1')	FINISH;
  b2 = c2;		/* Ignore 2nd help tree until first topic */
  c1 += which_help(c1, p1->end - c1, top1);	if (top1[0] != '0')	FINISH;
  c1++;
  c1 += which_help(c1, p1->end - c1, top1);	if (top1[0] != '1')	FINISH;
  p.end += oscopy(p.end, b1, c1-b1);
  b1 = c1; 
  while ( (c1 < p1->end) && (c2 < q2->end)) {
	i = top2[0] - top1[0];
	if (i == 0) i = studiff(top1, top2);
	if (i <= 0) {	/* top1 comes first */
		++c1;
		c1 += which_help(c1, p1->end - c1, top1);
		p.end += oscopy(p.end, b1, c1-b1);
		b1 = c1;
	}
	if (i >= 0) {	/* top2 comes first */
		++c2;
		c2 += which_help(c2, q2->end - c2, top2);
		if (i)	p.end += oscopy(p.end, b2, c2-b2);
		b2 = c2;
	}
  }

  FIN:
  p.end += oscopy(p.end, b1, p1->end - b1);	/* Append rest	*/
  p.end += oscopy(p.end, b2, q2->end - b2);	/* Append rest	*/
  MEM_FREE(p1->start);	MEM_FREE(q2->start);
  htex->action = old_act,   htex->output = old_out;   

  return(&p);
}

/*==========================================================================
 *			Reset anything at specified level
 *==========================================================================*/
static int Reset(lev)
/*+++
.PURPOSE Reset the list of subtopics to specified level:
	title and starting point of subtopics.
.RETURNS The level
.REMARKS 
---*/
	int 	lev;	/* IN: The current level */
{
	char 	*b;
	int  	i;
	
		/* First, reset the Title in the List buffer */

  for (b = list->buf, i = 0; i <= lev; i++)
	b += oscloc(b, list->offset, ' ') + 1;
  list->offset = b - list->buf;
  list->used   = list->offset;
  
  		/* Second, reset starting point of subtopic */
  
  i = lev+1;
  if (i <= 9)
  {	topics[i].piece = topics[lev].piece;
  	topics[i].pos   = topics[lev].pos;
  	topics[i].ltopic= 0;
  }

  return(lev);
}

/*==========================================================================
 *			LocHelp
 *==========================================================================*/
static int LocHelp(t)
/*+++
.PURPOSE Locate the next `\Help' macro
.RETURNS Level (t->level) as a number / -1 if end found
.REMARKS On return, t->pos AND t->hpos point to the text starting with \Help
	The context is checked.
	If the index is not loaded, \HelpIndex is ignored.
---*/
	TOPIC	*t;	/* MOD: Starting search point */
{
	PIECE 	*p;
	int	got, flag;
	int	(*old_act)(), (*old_out)();
	
  got = -1;
  t->got_topic = NULL_PTR(char);
  old_act = htex->action, old_out = htex->output;
  htex->action = th_act,   htex->output = th_out;   


  flag = 0;			/* Indicates starting of a new piece */
  
  while(1)			/* Loop over pieces */
  {	if (t->piece >= help.buf->used)	FINISH;
	p = (PIECE *)(help.buf->buf + t->piece);
	if (flag)	t->pos = p->start;
	flag = 0;
	if (t->pos >= p->end)
	{	t->piece += sizeof(PIECE);
		flag = 1;
		continue;
	}
  	t->pos += next_help(t->pos, p->end - t->pos);
  	if (t->pos >= p->end)	continue;	/* Not found */

  		/* Something starting with \Help found 	
		 * Interpret with the TeX parser -- 
		 * but be sure it's not a comment!	*/

#if 0
	for (st = t->pos; st > p->start; st--)
		if ( (*st == '\n') || (*st == '%'))	break;

	if (*st == '%')		/* It's a comment	*/
		{ t->pos += 1; continue; }
#endif
	level_found = '\0';
	tex_exec(htex, t->pos, p->end - t->pos);
	if (level_found)		break;	
	t->pos += 1;
  }

	/* Here if a \Help macro found: level is stored as level_found,
	 * and the found topic / filename as htex->ap.
	 */

  t->level = level_found;
  t->flag  = help_flag;
  t->hpos  = t->pos;
  t->got_topic = htex->ap;
  got = level_found - '0';
  
  FIN:
  htex->action = old_act,   htex->output = old_out;   
  return(got);
}

/*==========================================================================
 *			Sorry (no help available...)
 *==========================================================================*/
static int Sorry(asked, exit_option)
/*+++
.PURPOSE Display the "Sorry" message ...
.RETURNS OK 
.REMARKS 
---*/
	char	*asked;		/* IN: What was asked for and not found */
	int	exit_option;	/* IN: 1 to log error, 0 to output on window */
{
	static char *sp[] = {"Sorry, no such", NULL_PTR(char)};
	int 	i;

  if (exit_option)
  	ERR_ED_STRING("Topic not found: ", asked);
  else
  {	sp[1] = help.title;
  	ClearWindow(help.w[0]);
  	SetAttr(help.w[0], _BOLD_|_BLINK_|_REVERSE_);
  	for (i = 0; i < ITEMS(sp); i++)
  		Put(help.w[0], sp[i]);
  	SetAttr(help.w[0], _NORMAL_);
  	WriteBinary(help.w[0], list->buf, list->offset);
  	SetAttr(help.w[0], _UNDERSCORE_);
  	Put(help.w[0], asked);
  	SetAttr(help.w[0], _NORMAL_);
	Bell();
  	RaiseWindow(help.w[0]);
  }

  return(OK);
}

/*==========================================================================
 *			BadIndex
 *==========================================================================*/
static int BadIndex()
/*+++
.PURPOSE Display a message if index is too old. Execute the bad_index.
.RETURNS _BAD_INDEX_
.REMARKS Replace \HelpIndex by \HelpLoad and replace arg. 2,3,4 by blanks
---*/
{
	int	k;

  ERR_ED_STRING("File was modified: ", htex->ap);

  ClearWindow(help.w[0]);
  SetAttr(help.w[0], _BOLD_|_BLINK_|_REVERSE_);
  PutCentered(help.w[0], " Index Invalid ");
  SetAttr(help.w[0], _NORMAL_);
  RaiseWindow(help.w[0]);
  
	/* Modify \HelpIndex{0} to \HelpLoad{0}	*/

  topc.piece = 0;
  topc.pos = ((PIECE *)(help.buf->buf))->start;
  LocHelp(&topc);
  oscopy(topc.pos+5, "Load ", 5);
  topc.pos += 1 + oscloc(topc.pos, 200, '{');
  for (k=3; --k>=0; )
  {	topc.pos += oscloc(topc.pos, 200, '{');
  	*(topc.pos) = ' ';
  	oscfill(topc.pos, oscloc(topc.pos, 200, '{'), ' ');
  }
  
  if (help.bad_index)	oshcmd(help.bad_index, 
  	NULL_PTR(char), NULL_PTR(char), NULL_PTR(char));

  return(_BAD_INDEX_);
}

/*==========================================================================
 *			oFile
 *==========================================================================*/
static int oFile(filename)
/*+++
.PURPOSE Save filename in `filenames' buffer.
.RETURNS Position in `filenames' buffer where file name is saved.
.REMARKS Check if filname already exists. The default extension is added.
---*/
	char	*filename;	/* IN: File to save */
{
	int 	l, o;
	char	*p, *complete_filename;

  complete_filename = NameFile(filename, default_ext);
  l = strlen(complete_filename) + 1;
  
  for (o=0; o < filenames->used; )
  {	p = filenames->buf + o;
  	if (oscomp(p, complete_filename, l) == 0)	break;
  	o += 1 + strlen(p);
  }

	/* If not yet existing, add in buffer	*/
	
  if (o >= filenames->used)
  {	o = filenames->used;
  	BUF_AppendItems(filenames, char, complete_filename, l);
  }

  return(o);
}

/*==========================================================================
 *			load_file
 *==========================================================================*/
static int load_file(filename)
/*+++
.PURPOSE Load a file in a new allocated piece of memory.
	This piece is detailed in the static loaded_file.
.RETURNS --- OK (no remaining text)
	--- NOK (file not found)
.REMARKS Execute definitions in the loaded file, until the first \Help.
	Default extension is .tex
---*/
	char	*filename;	/* IN: File to load */
{
	char 	*b, *fname;
	int  	status, fid, lb;
	unsigned int l;
	long 	fs;
	int  	(*old_act)(), (*old_out)();

	/* Open first the File. If fails, return */

  status = NOK;
  fname = strsave(NameFile(filename, default_ext));

  fs = fi_size(fname);		/* Size of File */

  if_not(fid = fi_open(fname, READ|RECORD_MODE))
  	FINISH;

  l  = fs;
  if (l == 0)	FINISH;

	/* Allocate memory to copy file	*/
	
  if_not ( b = MEM_GET(char, l))
  	FINISH;

  loaded_file.start = b;
  loaded_file.end   = b + l;
	
	/* Read file until first \Help	*/
  
  for (fs=0; fi_gets(fid, b, loaded_file.end - b) > 0; fs = fi_tell(fid))
  {	if (*b == '%')	continue;	/* Comment line...	*/
  	lb = strlen(b);
	if (*(b + oscindex(b, lb, HELP_MACRO, sizeof(HELP_MACRO)-1)))
		break;
  	b += lb;
  	*(b++) = '\n';
  }
  fi_close(fid);
		
	/* Execute the TeX definitions before the first \Help */
	
  old_act = htex->action, old_out = htex->output;
  htex->action = act0,   htex->output = act0;   
  TeX_Execute(htex, loaded_file.start, b - loaded_file.start);
  htex->action = old_act,   htex->output = old_out;   
  
	/* Load the Remaining part of the File */

  l = (loaded_file.end - loaded_file.start) - fs;
  MEM_FREE(loaded_file.start);
  b = MEM_GET(char, l);
  loaded_file.start = b;
  loaded_file.end   = b + fi_load(fname, fs, b, l);

  status = OK;
  
  FIN:
  strfree(fname);		/* Release the memory */
  return(status);
}

/*==========================================================================
 *			load_help
 *==========================================================================*/
static int load_help(filename, start_level)
/*+++
.PURPOSE Load a new help file (triggered by \HelpLoad).
	It modifies the levels of the loaded file.
.RETURNS OK  / NOK (something failed)
.REMARKS 
---*/
	char	*filename;	/* IN: File to load */
	char	start_level;	/* IN: Lowest level	*/
{
	int 	status, i;
	char	*b;

  status = NOK;
  if_not(load_file(filename))	FINISH;

  status = OK;
  if (start_level <= '0')	FINISH;
  
  b = loaded_file.start; 
  i = start_level - '0';
  
  while (b < loaded_file.end)
  {	b += next_help(b, loaded_file.end - b);
	b += oscspan((unsigned char *)b, loaded_file.end - b, _ALPHA_|_PUNCT_|_SPACE_,
		main_ascii);		/* Skip until digit */
	if (b >= loaded_file.end)	break;
	*b += i;		/* Modify the Level	*/
  }	

  FIN:
  return(status);
}

/*==========================================================================
 *			help_load
 *==========================================================================*/
static int help_load(t)
/*+++
.PURPOSE Treats the `\HelpLoad{lev}{filename}' macro
.RETURNS OK  / NOK (something failed)
.REMARKS Filename is given as t->got_topic, level as t->level
---*/
	TOPIC	*t;	/* IN: The current topic	*/
{
	PIECE 	*p;
	char	*b;
	int	status, l;
	
	/* 1. The filename is in t->got_topic; load it */

  status = NOK;

  b = t->pos + tex_tell();	/* Position just following the \Help */
  t->flag = '.';			/* File is now loaded		*/

  if_not(load_help(t->got_topic, t->level))	FINISH;
  
	/* 2. Allocate two new PIECE items in help:
	 * 	- 1 for the new file
	 *	- 1 for the second part following the \HelpLoad
	 * These items must be located where we are.
	 */
	 
  help.buf->offset = t->piece;
  l = help.buf->used - help.buf->offset;	/* Length of 2nd part */
  
  if_not(BUF_AllocateItems(help.buf, PIECE, 2))	FINISH;

  p = BUF_ItemPosition(help.buf, PIECE);
  oscopy((char *)(p+2), (char *)p, l);		/* Duplicates current piece */

  	/* 3. Modify the current length (stops just before \HelpLoad	*/
  
  p->end = t->pos;
  
  	/* 4. Insert the loaded file	*/
  	
  *++p = loaded_file;
  
  	/* 5. Modify the 2nd part (starts just after \HelpLoad	*/

  ++p;
  p->start = b;

  status = OK;
  
  FIN:
  return(status);
}


/*==========================================================================
 *			LocTop
 *==========================================================================*/
static int LocTop(lev, opt, optl)
/*+++
.PURPOSE Locate the topic of specified level
.RETURNS Lev if found - another number otherwise
.REMARKS Comparison is case insensitive. On return, the current text
	starts just after the \Help.
---*/
	int	lev;	/* IN: Level to look for	*/
	int	opt;	/* IN: 1 to add topic in title / 2 for Explanation */
	int	optl;	/* IN: 1 to locate file with text / 0 otherwise */
{
	int	i;

  for (topc = topics[lev]; (i = LocHelp(&topc)) >= lev; topc.pos += 1)
  {	if (i > lev)	continue;
		/* Check if a file was found. If yes, load it	*/
	if (topc.flag == '@')
	{	help_load(&topc);
		continue;
	}
	if (osccomp(topc.topic, topc.got_topic, topc.ltopic) == 0)
		break;		/* Topic is found */
  }

  if (i != lev)		return(i);	/* Not Found	*/
  
  if (opt & 2)	get_tit3();
  if (opt & 1)  {		/* Modify the title	*/
  	list->used = list->offset;
  	BUF_AppendString(list, topc.got_topic);
  	BUF_AppendItem(list, char, &blank);
  	list->offset = list->used ;
  }

  if ((topc.flag == ':') && (optl))	/* From Index: Load Parameters	*/
  {	tex_getparm(6);	topc.fpos  = atol(htex->ap);  /*  Start of file	*/
  	tex_getparm(7);	topc.ltext = atoi(htex->ap);  /* Length in file */
  	tex_getparm(5);	topc.ofile = oFile(htex->ap); /* Filename	*/
  }

  topc.pos += tex_tell();	/* Position after the \Help macro	*/
  topics[lev] = topc;		/* Copy found topic	*/

  return(lev);
}

/*==========================================================================
 *			GetSubTop
 *==========================================================================*/
static int GetSubTop(lev)
/*+++
.PURPOSE Locate the subtopics of specified level
.RETURNS Number of subtopics
.REMARKS 
---*/
	int	lev;	/* IN: Level of Topic	*/
{
	int	i, n, len;
	static  char ed[] = "\\CheckCols{00}\05\01";

  Reset(lev);
  
  BUF_AppendString(list,"\\vfill\n\\Rule\n");
  i = lev+1;
  
  for (n = 0; LocTop(i, 0, 0) == i; n++)
  {	len = strlen(topics[i].got_topic);
  	ed[11] = '0' + len/10;
  	ed[12] = '0' + len%10;
  	BUF_AppendItems(list, char, ed, sizeof(ed)-1);
  	BUF_AppendItems(list, char, topics[i].got_topic, len);
  	BUF_AppendItems(list, char, "\05\02 \\tab", 7);
  }

  if (n == 0)	list->used = list->offset;	/* No subtopics	*/
  else  	BUF_AppendItems(list, char, "\n\\SkipLine", 10);

  return(n);
}
  
/*==========================================================================
 *			Prompt
 *==========================================================================*/

static WINDOW *CreateHelpWindow()
/*+++
.PURPOSE Create the customized help window
.RETURNS The window created
.REMARKS 
---*/
{
	WINDOW  *helpw;
#if 0
	static  char help_1[] = "Answer by non-ambiguous (sub)topic(s)\
 separated with blanks, or the keys:\n\\SkipLine\n\\begin{table}{c p}{3}\
{\\bf?}&to redisplay previous topic\\\\\
{\\bf\\b}&to list a summary\\\\\
{\\bf Ret}&to return to previous topic\\\\ ";
	static  char help_2[] = "{\\bf \\^A} & to Exit\\\\ ";
	static  char help_3[] = "\
\\end{table}\n\\SkipLine\nTopic names are {\\Vu case insensitive}.";
	static  char *help_text[] = { 
		help_1, help_1+sizeof(help_1)-1,
		help_2, help_2+sizeof(help_2)-1,
		help_3, help_3+sizeof(help_3)-1
		};

  			/* Create Help Window */
  help_2[7] = '@' + GetControl(TW_cc_EOF);
  if (helpw = OpenWindow("Help Topics", 0, -29, -18, 29, _REVERSE_,
	_TITLE_|_BORDER_|_BORDER2_|_DISPLAY_, 0))
  	DisplayArray(helpw, help_text, ITEMS(help_text)/2);
#else
	static char help_text[] = "\
 Answer by non-ambiguous (sub)topics\n\
 separated with blanks, or the keys\n\n\
  \017^\04\016 to Exit\n\
 \017Ret\016 to return to previous topic\n\
   \017\\\016 to list a Summary\n\
   \017?\016 to redisplay previous topic\n\
  \017^B\016 to redisplay previous page\n\
   \017.\016 to display one more line\n\n\
 Topic names are \017case insensitive\016";

  help_text[strloc(help_text, '\04')] = '@' | GetControl (TW_cc_EOF );
  helpw = AdjustedWindow("Help on Help", help_text, 
	_REVERSE_, _BORDER_|_TITLE_, _UPRIGHT_);
#endif

  return(helpw);
}

static int Prompt1(opt)
/*+++
.PURPOSE Prompt for Continuation
.RETURNS -1 to stop / _SUMMARY_ to list the Summary (triggered by  \) /
		_REDISPLAY_ to redisplay the parent topic /
		_PROMPT_ when just Return was entered /
		OK if a subtopic was typed in
.REMARKS 
---*/
	int	opt;	/* IN: 1 if Bottom Reached  / 2 for Exit with Q */
{
	int	status, at_bottom, old_pos;
	static	char specials[] = " \02\04\06\025.\r?\\"; /* Those stop input */

			/* Special Keys are ^B ^D ^F ^U	*/
  at_bottom = opt & 1;
  status = 0;
  old_pos = GetPosition(help.w[2]);
  while (status == 0) {
  	SetPosition(help.w[2], old_pos);	
  	ClearRight(help.w[2]);
	if (opt & 2) {
		status = GetKey1(help.w[2], reply, specials);
		if (status > 0)	status = 0;
	}
  	else	status = Gets1(help.w[2], reply, sizeof(reply), specials);
	if ((status == OK) && (reply[0] == EOS))
		status = 0, reply[0] = ' ';
  	if (status == 0)  switch(reply[0])   { 
    	  case '\\': 	status = _SUMMARY_;	break;
    	  case '?': 	status = _REDISPLAY_;	break;
    	  case '\r':  case EOS:
		if (at_bottom)	status = _PROMPT_;
		else		reply[0] = ' ';	break;
	}
	if (status)	continue;
	status = MoreDoc(TheDoc, reply[0]);
	at_bottom = atLastDocPage(TheDoc);
	if (status == NOK)	Bell();
	if (status == OK)	status = 0;
  }

  FIN:
  ClearWindow(help.w[2]);
  return(status == 0 ? OK : status);
}

static int Prompt(lev)
/*+++
.PURPOSE Inquire for a Topic
.RETURNS -1 to stop / _SUMMARY_ to list the Summary (triggered by  \),
		_REDISPLAY_ to redisplay the parent topic.
.REMARKS A special Help window is used, in case of Control-K
---*/
	int	lev;	/* IN: Level / -1 for Continuation 	*/
{
	int	status;

  RaiseWindow(help.w[2]);
  ActiveWindow(help.w[2]);
  SetAttr(help.w[2], _REVERSE_);

  if (lev > 0)	
  {	WriteBinary(help.w[2], list->buf, list->offset);
	Put(help.w[2], "Subtopic ? ");
  }
  else	Put(help.w[2], " Topic ? ");

  SetAttr(help.w[2], _NORMAL_);
  WriteBinary(help.w[2], &blank, 1);
  status = Prompt1(1);

  return(status);
}

static int PromptKey(opt)
/*+++
.PURPOSE Prompt for Continuation
.RETURNS -1 to stop / _SUMMARY_ to list the Summary (triggered by  \),
		_REDISPLAY_ to redisplay the parent topic.
.REMARKS 
---*/
	int	opt;	/* IN: non-zero to display the ?	*/
{
	int	status, opt1;

  RaiseWindow(help.w[2]);
  ActiveWindow(help.w[2]);
  SetAttr(help.w[2], _REVERSE_);
  Put(help.w[2], " <Return> to continue");
  Put(help.w[2], ", ");
  SetAttr(help.w[2], _REVERSE_|_BOLD_);
  opt1 = 0;		/* Option for Prompt1 */
  if (opt) {
  	Put(help.w[2], "?");
  	SetAttr(help.w[2], _REVERSE_);
  	Put(help.w[2], " for list of subtopics");
  } 
  else {
  	Put(help.w[2], "q");
  	SetAttr(help.w[2], _REVERSE_);
  	Put(help.w[2], " to quit");
	opt1 = 2;	/* Allow to Exit with Q */
  }

  Put(help.w[2], " ");
  SetAttr(help.w[2], _NORMAL_);
  status = Prompt1(opt1);

  return(status);
}

/*==========================================================================
 *			GetSummary
 *==========================================================================*/
static int GetSummary(lev)
/*+++
.PURPOSE Prepare complete list of all subtopics
.RETURNS OK
.REMARKS 
---*/
	int	lev;	/* IN: Level of Topic	*/
{
	int	i, l, l3, m;
	char	*b, buf[80];

  Reset(lev);
  topc = topics[lev];
  BUF_AppendString(list, "\\begin{table}{ l p }{$$}");
  m = 0;		/* Size of first column	*/
  
  while ( (i = LocHelp(&topc)) > lev)
  {	if (topc.flag == '@')
	{	help_load(&topc);
		continue;
	}
	l = (i-lev-1)*4;	/* Length of first column	*/
  	b = buf + oscfill(buf, l, '~');
	*(b++) = '{';
	if (l == 0)	b += strcopy(b, "\\bf");
	*(b++) = '\05'; *(b++) = '\01';	/* begin{verbatim} */
	BUF_AppendItems(list, char, buf, b-buf);
	BUF_AppendString(list, topc.got_topic);
	BUF_AppendItems(list, char, "\05\02}&", 4);
	l += 1 + strlen(topc.got_topic);
	m = MAX(m, l);
	l3 = get_tit3();		/* The short explanation */
	if (l3 > 0)	BUF_AppendItems(list, char, tit3, l3);
	BUF_AppendItems(list, char, "\\\\", sizeof("\\\\")-1);
	topc.pos += 1;
  }

  BUF_AppendString(list, "\n\\end{table}");
  b  = list->buf + list->offset;
  b += oscloc(b, 80, '$');
  *(b++) = '0' + m/10, *(b++) = '0' + m%10;

  return(OK);
}
  
/*==========================================================================
 *			EdTop
 *==========================================================================*/
static int EdTop(lev)
/*+++
.PURPOSE Edit the found Topic and list of Subtopics
.RETURNS _EOF_ / _INTERRUPT_ / _PROMPT_ / OK (something was typed) / 
	 _REDISPLAY_ (no subtopic, and ? was typed) / _SUMMARY_
.REMARKS The static subtopics contains in return the number of found subtopics.
	The relevant file is loaded if help is indexed.
---*/
	int	lev;	/* IN: Level of Topic	*/
{
	PIECE	*p;
	static  char *as[4];
	int	status;

	/* Locate Text	*/
 
  p = (PIECE *)(help.buf->buf + topics[lev].piece);

	/* If help is indexed, load the file	*/

  if (topics[lev].flag == ':')
  {
	if ((loaded) && (loaded->fpos == topics[lev].fpos)
	 	     && (loaded->ofile== topics[lev].ofile))
		;			/* Topic already loaded */
	else
	{	loaded = (LOADED *)mm_expand(loaded, sizeof(LOADED)+
			topics[lev].ltext);
		loaded->ofile = topics[lev].ofile;
		if (fi_date(filenames->buf + loaded->ofile) > help.date)  
			return(BadIndex());	/* File was modified... */
		loaded->fpos  = topics[lev].fpos;
		loaded->ltext = topics[lev].ltext;
	  	as[0] = loaded->text;
		loaded->ltext = fi_load(filenames->buf + loaded->ofile,
  					loaded->fpos, 	/* Pos in file	*/
  					as[0], loaded->ltext);
  		as[1] = as[0] + loaded->ltext;
	}
  }
  else	as[0] = topics[lev].pos, as[1] = p->end;

	/* Edit the title in the help.w[0] window */
 
  ClearWindow(help.w[0]);
  SetAttr(help.w[0], _BOLD_),
  Put(help.w[0],help.title),
  Put(help.w[0],": "),
  SetAttr(help.w[0], _NORMAL_);

  if (help.flags & 2) 		/* Use Short Title */
	ShowString(help.w[0], tit3);
  else	WriteBinary(help.w[0], list->buf, list->offset);
  TouchWindow(help.w[0]);	/* Display the Title */

	/* Locate the Subtopics	*/
 
  subtopics = GetSubTop(lev);

	/* Now, edit the text + list of subtopics	*/

  as[2] = list->buf + list->offset, as[3] = list->buf + list->used;
  
  status = myOpenDocArray(as, 2);
  while(status == 0) {
  	status = PromptKey(subtopics);
	if (status < 0)		FINISH;
	switch(status) {
  	  case _REDISPLAY_ :	/* Display subtopics */
		if (subtopics == 0)
			{ Bell(); FINISH;}
  		status = myOpenDocText(as[2], as[3] - as[2]);
		break;
  	  case _PROMPT_ :	/* Display Continuation */
		break;
  	  case _SUMMARY_ :	
  	  	status = _SUMMARY_;
  	  	FINISH;
	  default:		/* A text was typed ahead */
	  	status = OK;
	  	FINISH;
	}
	RaiseWindow(help.w[1]);
  }
  status = _PROMPT_;

  FIN:
  return(status);
}
  
/*==========================================================================
 *			EdSummary
 *==========================================================================*/
static int EdSummary(lev)
/*+++
.PURPOSE Display the Summary
.RETURNS _EOF_ / _INTERRUPT_ / _PROMPT_ / OK (something was typed) / 
	_REDISPLAY_ (no subtopic, and ? was typed)
.REMARKS 
---*/
	int	lev;	/* IN: Level of Topic	*/
{
	int	status;

	/* First, edit the title in the help.w[0] window */
 
  ClearWindow(help.w[0]);
  SetAttr(help.w[0], _BOLD_);
  Put(help.w[0], help.title);
  Put(help.w[0], " Summary:");
  SetAttr(help.w[0], _NORMAL_);
  WriteBinary(help.w[0], list->buf, list->offset);
  TouchWindow(help.w[0]);	/* Display the Title */

  status = myOpenDocText(list->buf + list->offset, list->used - list->offset);

  while(status == 0) {
  	status = PromptKey(1);
	if (status < 0)		FINISH;
	switch(status) {
  	  case _REDISPLAY_ :	/* Redisplay subtopics */
		GetSubTop(lev);
  		status = myOpenDocText(list->buf + list->offset,
	  		list->used - list->offset);
		break;
  	  case _PROMPT_ :	/* Terminated (at bottom) */
		break;
  	  case _SUMMARY_ :	
		MoreDoc(TheDoc, 'g');	/* Display Top	*/
		status = 0;
		break;
	  default:		/* A text was typed ahead */
	  	status = OK;
	  	FINISH;
	}
	RaiseWindow(help.w[1]);
  }
  status = _PROMPT_;

  FIN:
  return(status);
}
  
/***************************************************************
 *		P U B L I C     R O U T I N E S
 ****************************************************************/

/*==========================================================================
 *			th_topsep
 *==========================================================================*/
int th_topsep(list)
/*+++
.PURPOSE Define the list of topic separator characters
.RETURNS OK
.REMARKS Default is blank.
---*/
	char *list;	/* IN: Available characters as separators */
{
  strset(sep_table, list);
  sep_top = list;		/* Tells it's initialized */
  return(OK);
}	

/*==========================================================================
 *			th_init
 *==========================================================================*/
TWHELP *th_init(filename, wt, w, wd)
/*+++
.PURPOSE Initialize Help procedure: definition of \Help macro,
	set the three windows.
.RETURNS --- OK 
	--- NOK (error of file doesn't exist)
.REMARKS Default extension for filename = .tex
---*/
	char   *filename;	/* IN: Name of help file */
	WINDOW *wt;	/* IN: Window for Help titles	*/
	WINDOW *w;	/* IN: Window for Help display	*/
	WINDOW *wd;	/* IN: Window for dialogue	*/
{
	TWHELP 	*h;
	int	(*old_act)(), (*old_out)();
	short int screen_size[2];
	static char definitions[] = 
		"\\def\\Help#4{\\action{H.}}\\def\\HelpLoad#4{\\action{H@}}\
\\def\\HelpIndex#7{\\action{H:}}\\def\\fromHelpFiles#1{}";

  ENTER("*th_init");

  TRACE_ED_STRING("Help file:", filename);

	/* Load the definitions	*/
	
  if_not(htex)
  {	htex = tx_tex();

	if (!sep_top)	th_topsep(" ");		/* Topic Separators */
	
  	old_act = htex->action, old_out = htex->output;
  	htex->action = act0,   htex->output = act0;   
	TeX_Execute(htex, definitions, sizeof(definitions)-1);
  	htex->action = old_act,   htex->output = old_out;   
  	list = BUF_Open(char, 256, 128);
  	filenames = BUF_Open(char, 0, 128);
	tit3 = strsave("");
  }

	/* Initialize the TWHELP structure */

  h = NULL_PTR(TWHELP);
  help.w[0] = wt;
  help.w[1] = w;
  help.w[2] = wd;
  help.buf  = NULL_PTR(BUFFER);
  help.date = FileDate(filename);
  help.title 	= default_title;
  help.context 	= NULL_PTR(char);
  help.bad_index = NULL_PTR(char);
  help.indexed	= 0;

	/* Load Help file	*/

  if_not (load_file(filename))	FINISH;

  if_not(help.buf = BUF_Open(PIECE, 1, 16))	/* Allocate buffer */
	FINISH;
	

	/* Check if Help is Indexed	*/
  if (oscomp(loaded_file.start, HELP_INDEX, sizeof(HELP_INDEX)-1) == 0)
  	help.indexed = 1;

  BUF_AppendItem(help.buf, PIECE, &loaded_file);
  					/* Add this piece to the buffer	*/


	/* If windows are NULL, use defaults	*/

  ScreenSize(screen_size);

  if_not(help.w[1])		/* No main Window for Help Display	*/
  {	help.w[1] = OpenWindow("$help", 0, 0, screen_size[0]-2, 0, 
  		_NORMAL_, _DISPLAY_, 0);
  	CursorTo(help.w[1], 1, 0);
	SetAttr(help.w[1], _GRAPHICS_);
  	Fill(help.w[1], RuleChar(_HORIZONTAL_), screen_size[1]);
	SetAttr(help.w[1], _NORMAL_);
	help.w[0] = OpenSubWindow(help.w[1], ".help", 0, 0, 1, 0, 
		_NORMAL_, _DISPLAY_, 0);	/* Title of Help Display */
  	help.w[1] = OpenSubWindow(help.w[1], "=help", 2, 0, 0, 0, 
  		_NORMAL_, _DISPLAY_, 0);
  }


  if_not(help.w[2])		/* No Dialogue Window for Help Display	*/
  	help.w[2] = OpenWindow("?help", -2, 0, 2, 0, _NORMAL_, 0, 5);

  h = MEM_GET(TWHELP, 1);
  if (h)	*h = help;

  FIN:
  EXIT_PTR(TWHELP, h);
}

int th_merge(help, filename)
/*+++
.PURPOSE Merge a Help with another one. The help must just have been
	opened, and the two help files MUST BE INDEXED.
.RETURNS --- OK / NOK
.REMARKS Default extension for filename = .tex
---*/
	TWHELP 	*help;		/* MOD: Main Help	*/
	char   *filename;	/* IN: Name of help file */
{
	int	status;
	BUFFER	*b;
	PIECE	*p;

  ENTER("th_merge");

  status = NOK;
  if_not (help->indexed)	{ ERROR("Help not Indexed"); FINISH; }
  b = help->buf;
  if (b->used != sizeof(PIECE))	{ ERROR("Help was used..."); FINISH; }

  if_not((load_file(filename)))	FINISH;

  if ((p = MergeHelp((PIECE *)(b->buf), &loaded_file))) {
	help->date = oshtime();	/* Be sure no Bad Index Error */
	BUF_Clear(b);
	BUF_AppendItem(b, PIECE, p);
	status = OK;
  }

  FIN:
  EXIT(status);
}

/*==========================================================================
 *			th_set
 *==========================================================================*/
char *th_set(helpin, context, opt)
/*+++
.PURPOSE Defines the Help Context / Title / What to do if index too old
.RETURNS The old Context.
.REMARKS Only address is kept... Use therefore e.g. strsave before calling...
---*/
	TWHELP 	*helpin;	/* IN: The loaded Help	*/
	char	*context;	/* IN: Context */
	int	opt;		/* IN: 0 for Title / 1 for Context */
{
	char 	*old_context, **aa;

  ENTER("*th_set");

	/* Copy context to a new piece of memory */

  old_context = NULL_PTR(char);
  
  switch(opt)
  { case 0: case 1: case 2:	aa = &(helpin->title);
  	break;
    default: 	ERR_ED_I("Bad option: ", opt);
    	FINISH;
  }

  old_context = aa[opt];

  aa[opt] = context;
  
  FIN:
  EXITp(old_context);
}
  
int th_oset(helpin, mask, opt)
/*+++
.PURPOSE Define the Option Context
.RETURNS The old Option Context.
---*/
	TWHELP 	*helpin;	/* IN: The loaded Help	*/
	int	mask;		/* IN: Which option	*/
	int	opt;		/* IN: 0 for Clear / 1 for Set */
{
	int	old_option;

  old_option = helpin->flags & mask;
  if (opt)
	helpin->flags |= mask;
  else	helpin->flags &= ~mask;

  return(old_option);
}

/*==========================================================================
 *			th_help
 *==========================================================================*/
int th_help(helpin, topic, exit_option)
/*+++
.PURPOSE Display help on `topic'
.RETURNS OK / NOK (error reported) / EOF (the user typed EOF) / -3 (Bad Index)
.REMARKS 
---*/
	TWHELP 	*helpin;	/* IN: The loaded Help	*/
	char	*topic;	/* IN: Topics (separated by blanks) to display */
	int	exit_option;	/* IN: non-zero to exit immediately after \
					display, -1 for locate only */
{
	char *b, x;
	int level, level_step, i, status;
	WINDOW	*old_helpw;

  ENTER("th_help");

  strfree(located_help), located_help = (char *)0;
  if (topic)
  {	TRACE_ED_STRING("Topic: ", topic);
	i = strlen(topic);
	i = MIN(i, sizeof(reply)-1);
	oscopy(reply, topic, i);
  }
  else	i = 0;
  reply[i] = EOS;

  if (!helpw)	helpw = CreateHelpWindow();
  old_helpw = help.w[2]->help;
  AttachHelpWindow(help.w[2],helpw);

  TheDoc = 0;
  help   = *helpin;	
  help.flags |= 1;		/* Edit in Top Window	*/
  status = 1;
  level  = 0;			/* Starting Level	*/
  level_step = 1;		/* Decrease by this amount for _PROMPT_ */
  topics[0].piece = 0;
  topics[0].pos = ((PIECE *)(help.buf->buf))->start;

  BUF_Clear(filenames); 
  LocTop(0, 0, 1);		/* Find level 0 (start)	*/

  if_not(exit_option)	ClearWindow(help.w[2]);

  BUF_Clear(list); 
  BUF_AppendItem(list, char, &blank);	/* Initialize list buffer */
  
  while ((status >= 0) && (level >= 0)) {
	Reset(level);
	if (level_step < 1)	level_step = 1;

	switch(status) {

	  case _REDISPLAY_:
		Reset(level);
		goto EDIT_TOP;

  	  case _PROMPT_:	/* Get the topic */
  		status = Prompt(level);
  		if (status == _PROMPT_)	level-= level_step, level_step = 1;
  		continue;
  	
  	  case _SUMMARY_:	/* List the Summary */
  		GetSummary(level);
  		status = EdSummary(level);
  		continue;
  	}

	  	/* Locate topics separated by blanks	*/

	TRACE_ED_STRING("Reply is: ", reply);
	level_step = 1;
	for (b = reply; *b; ) {
		i = strspan_(b, 1, sep_table);
		if (i)	{
			if (*b != ' ')	level_step += 1;
			b += i;
		}
	  	if_not(*b)	break;
		Reset(level);
		if (++level > 9)	level = 9;
	  	topics[level].topic = b;
	  	topics[level].ltopic = strscan_(b, 1, sep_table);
		if (LocTop(level, help.flags, 1) != level) {
			if (exit_option >=  0)	Sorry(b, exit_option);
			if (exit_option)	FINISH;
			level -= level_step, level_step = 1;
			status = _PROMPT_;
			break;
		}
		b += topics[level].ltopic;
	}
	if (status == _PROMPT_)	continue;


		/* Edit located Topic	*/

	EDIT_TOP:
	if (exit_option >=  0)	status = EdTop(level);	
  	else {
		b = topics[level].pos;	x = *b; *b = 0;
		located_help = strsave(topics[level].hpos);
		*b = x;
	}
	if (exit_option)	FINISH;
	if (status < 0)		FINISH;
	if (status == _REDISPLAY_) continue;
	if (subtopics == 0) 	level -= level_step, level_step = 1;
  }

  FIN:
  AttachHelpWindow(help.w[2],old_helpw);
  CloseDoc(TheDoc), TheDoc = 0;

  if (status >= 0)	status = OK;
  else if (status == _BAD_INDEX_)	helpin->indexed = 0;
  if (loaded)		/* Free allocated memory if Index	*/
	MEM_FREE(loaded), loaded = NULL_PTR(LOADED);
  EXIT(status);
}

/*==========================================================================
 *			th_loc
 *==========================================================================*/
char *th_loc(helpin, topic, option)
/*+++
.PURPOSE Retrieve the named topic
.RETURNS String starting at the \Help keyword \ NULL
.REMARKS 
---*/
	TWHELP 	*helpin;	/* IN: The loaded Help	*/
	char	*topic;	/* IN: Topics (separated by blanks) to display */
	int	option;	/* For future use option 	*/
{
  ENTER("*th_help");
	strfree(located_help); located_help = (char *)0;
	if (th_help(helpin, topic, -1) != OK)
		strfree(located_help), located_help = (char *)0;
  EXITp(located_help);
}
