
/*
 * helpcom.h
 *
 *
 * Common #defines, structures and code for HC.C and HELP.C
 *
 */

#ifndef HELPCOM_H
#define HELPCOM_H

#include "port.h"

/*
 * help file signature
 * If you get a syntax error, remove the LU from the end of the number.
 */

#define HELP_SIG	   (0xAFBC1823LU)


/*
 * commands imbedded in the help text
 */

#define CMD_LITERAL	  1   /* next char taken literally */
#define CMD_PARA	  2   /* paragraph start code */
#define CMD_LINK	  3   /* hot-link start/end code */
#define CMD_FF		  4   /* force a form-feed */
#define CMD_XONLINE	  5   /* exclude from online help on/off */
#define CMD_XDOC	  6   /* exclude from printed document on/off */
#define CMD_CENTER	  7   /* center this line */
#define CMD_SPACE	  8   /* next byte is count of spaces */

#define MAX_CMD 	  8


/*
 * on-line help dimensions
 */

#define SCREEN_WIDTH	  (78)
#define SCREEN_DEPTH	  (22)
#define SCREEN_INDENT	  (1)


/*
 * printed document dimensions
 */

#define PAGE_WIDTH	   (72)  /* width of printed text */
#define PAGE_INDENT	   (2)	 /* indent all text by this much */
#define TITLE_INDENT	   (1)	 /* indent titles by this much */

#define PAGE_RDEPTH	   (59)  /* the total depth (inc. heading) */
#define PAGE_HEADING_DEPTH (3)	 /* depth of the heading */
#define PAGE_DEPTH	   (PAGE_RDEPTH-PAGE_HEADING_DEPTH) /* depth of text */


/*
 * Document page-break macros.	Goto to next page if this close (or closer)
 * to end of page when starting a CONTENT, TOPIC, or at a BLANK line.
 */

#define CONTENT_BREAK (7)  /* start of a "DocContent" entry */
#define TOPIC_BREAK   (4)  /* start of each topic under a DocContent entry */
#define BLANK_BREAK   (2)  /* a blank line */


/*
 * tokens returned by find_token_length
 */

#define TOK_DONE    (0)   /* len == 0		  */
#define TOK_SPACE   (1)   /* a run of spaces	  */
#define TOK_LINK    (2)   /* an entire link	  */
#define TOK_PARA    (3)   /* a CMD_PARA 	  */
#define TOK_NL	    (4)   /* a new-line ('\n')    */
#define TOK_FF	    (5)   /* a form-feed (CMD_FF) */
#define TOK_WORD    (6)   /* a word		  */
#define TOK_XONLINE (7)   /* a CMD_XONLINE	  */
#define TOK_XDOC    (8)   /* a CMD_XDOC 	  */
#define TOK_CENTER  (9)   /* a CMD_CENTER	  */


/*
 * modes for find_token_length() and find_line_width()
 */

#define ONLINE 1
#define DOC    2


/*
 * struct PD_INFO used by process_document()
 */

typedef struct
   {

   /* used by process_document -- look but don't touch! */

   int	     pnum,
	     lnum;

   /* PD_GET_TOPIC is allowed to change these */

   char far *curr;
   unsigned  len;

   /* PD_GET_CONTENT is allowed to change these */

   char far *id;
   char far *title;
   int	     new_page;

   /* general parameters */

   char far *s;
   int	     i;


   } PD_INFO;


/*
 * Commands passed to (*get_info)() and (*output)() by process_document()
 */

enum  PD_COMMANDS
   {

/* commands sent to pd_output */

   PD_HEADING,	       /* call at the top of each page */
   PD_FOOTING,		/* called at the end of each page */
   PD_PRINT,		/* called to send text to the printer */
   PD_PRINTN,		/* called to print a char n times */
   PD_PRINT_SEC,	/* called to print the section title line */
   PD_START_SECTION,	/* called at the start of each section */
   PD_START_TOPIC,	/* called at the start of each topic */
   PD_SET_SECTION_PAGE, /* set the current sections page number */
   PD_SET_TOPIC_PAGE,	/* set the current topics page number */
   PD_PERIODIC, 	/* called just before curr is incremented to next token */

/* commands sent to pd_get_info */

   PD_GET_CONTENT,
   PD_GET_TOPIC,
   PD_RELEASE_TOPIC,
   PD_GET_LINK_PAGE

   } ;


typedef int (*PD_FUNC)(int cmd, PD_INFO *pd, VOIDPTR info);


int _find_token_length(char far *curr, unsigned len, int *size, int *width);
int find_token_length(int mode, char far *curr, unsigned len, int *size, int *width);
int find_line_width(int mode, char far *curr, unsigned len);
int process_document(PD_FUNC get_info, PD_FUNC output, VOIDPTR info);


/*
 * Code common to both HC.C and HELP.C (in Fractint).
 * #include INCLUDE_COMMON once for each program
 */


#endif
#ifdef INCLUDE_COMMON


#ifndef XFRACT
#define getint(ptr) (*(int far *)(ptr))
#define setint(ptr,n) (*(int far *)(ptr)) = n
#else
/* Get an int from an unaligned pointer
 * This routine is needed because this program uses unaligned 2 byte
 * pointers all over the place.
 */
getint(ptr)
char *ptr;
{
    int s;
    bcopy(ptr,&s,sizeof(int));
    return s;
}

/* Set an int to an unaligned pointer */
void setint(ptr, n)
int n;
char *ptr;
{
    bcopy(&n,ptr,sizeof(int));
}
#endif


static int is_hyphen(char far *ptr)   /* true if ptr points to a real hyphen */
   {			       /* checkes for "--" and " -" */
   if ( *ptr != '-' )
      return (0);    /* that was easy! */

   --ptr;

   return ( *ptr!=' ' && *ptr!='-' );
   }


int _find_token_length(register char far *curr, unsigned len, int *size, int *width)
   {
   register int _size  = 0;
   register int _width = 0;
   int tok;

   if (len == 0)
      tok = TOK_DONE;

   else
      {
      switch ( *curr )
	 {
	 case ' ':    /* it's a run of spaces */
	    tok = TOK_SPACE;
	    while ( *curr == ' ' && _size < (int)len )
	       {
	       ++curr;
	       ++_size;
	       ++_width;
	       }
	    break;

	 case CMD_SPACE:
	    tok = TOK_SPACE;
	    ++curr;
	    ++_size;
	    _width = *curr;
	    ++curr;
	    ++_size;
	    break;

	 case CMD_LINK:
	    tok = TOK_LINK;
            _size += 1+3*sizeof(int); /* skip CMD_LINK + topic_num + topic_off + page_num */
            curr += 1+3*sizeof(int);

	    while ( *curr != CMD_LINK )
	       {
	       if ( *curr == CMD_LITERAL )
		  {
		  ++curr;
		  ++_size;
		  }
	       ++curr;
	       ++_size;
	       ++_width;
	       assert(_size < len);
	       }

	    ++_size;   /* skip ending CMD_LINK */
	    break;

	 case CMD_PARA:
	    tok = TOK_PARA;
	    _size += 3;     /* skip CMD_PARA + indent + margin */
	    break;

	 case CMD_XONLINE:
	    tok = TOK_XONLINE;
	    ++_size;
	    break;

	 case CMD_XDOC:
	    tok = TOK_XDOC;
	    ++_size;
	    break;

	 case CMD_CENTER:
	    tok = TOK_CENTER;
	    ++_size;
	    break;

	 case '\n':
	    tok = TOK_NL;
	    ++_size;
	    break;

	 case CMD_FF:
	    tok = TOK_FF;
	    ++_size;
	    break;

	 default:   /* it must be a word */
	    tok = TOK_WORD;
	    for(;;)
	       {
	       if ( _size >= (int)len )
		  break;

	       else if ( *curr == CMD_LITERAL )
		  {
		  curr += 2;
		  _size += 2;
		  _width += 1;
		  }

	       else if ( *curr == '\0' )
		  {
		  assert(0);
		  }

	       else if ((unsigned)*curr <= MAX_CMD || *curr == ' ' ||
			*curr == '\n')
		  break;

	       else if ( *curr == '-' )
		  {
		  ++curr;
		  ++_size;
		  ++_width;
		  if ( is_hyphen(curr-1) )
		     break;
		  }

	       else
		  {
		  ++curr;
		  ++_size;
		  ++_width;
		  }
	       }
	    break;
	 } /* switch */
      }

   if (size  != NULL)	*size  = _size;
   if (width != NULL)	*width = _width;

   return (tok);
   }


int find_token_length(int mode, char far *curr, unsigned len, int *size, int *width)
   {
   int tok;
   int t;
   int _size;

   tok = _find_token_length(curr, len, &t, width);

   if ( (tok == TOK_XONLINE && mode == ONLINE) ||
	(tok == TOK_XDOC    && mode == DOC)	 )
      {
      _size = 0;

      for(;;)
	 {
	 curr  += t;
	 len   -= t;
	 _size += t;

	 tok = _find_token_length(curr, len, &t, NULL);

	 if ( (tok == TOK_XONLINE && mode == ONLINE) ||
	      (tok == TOK_XDOC	  && mode == DOC)    ||
	      (tok == TOK_DONE) 		       )
	    break;
	 }

      _size += t;
      }
   else
      _size = t;

   if (size != NULL )
      *size = _size;

   return (tok);
   }


int find_line_width(int mode, char far *curr, unsigned len)
   {
   int size   = 0,
       width  = 0,
       lwidth = 0,
       done   = 0,
       tok;

   do
      {
      tok = find_token_length(mode, curr, len, &size, &width);

      switch(tok)
	 {
	 case TOK_DONE:
	 case TOK_PARA:
	 case TOK_NL:
	 case TOK_FF:
	    done = 1;
	    break;

	 case TOK_XONLINE:
	 case TOK_XDOC:
	 case TOK_CENTER:
	    curr += size;
	    len -= size;
	    break;

	 default:   /* TOK_SPACE, TOK_LINK or TOK_WORD */
	    lwidth += width;
	    curr += size;
	    len -= size;
	    break;
	 }
      }
   while ( !done );

   return (lwidth);
   }


#define DO_PRINTN(ch,n)  ( pd.s = &(ch), pd.i = (n), output(PD_PRINTN, &pd, info) )
#define DO_PRINT(str,n)  ( pd.s = (str), pd.i = (n), output(PD_PRINT, &pd, info) )


int process_document(PD_FUNC get_info, PD_FUNC output, VOIDPTR info)
   {
   int	     skip_blanks;
   int	     tok;
   int	     size,
	     width;
   int	     col;
   char      page_text[10];
   PD_INFO   pd;
   char      nl = '\n',
	     sp = ' ';
   int	     first_section,
	     first_topic;

   pd.pnum = 1;
   pd.lnum = 0;

   col = 0;

   output(PD_HEADING, &pd, info);

   first_section = 1;

   while ( get_info(PD_GET_CONTENT, &pd, info) )
      {
      if ( !output(PD_START_SECTION, &pd, info) )
	 return (0);

      if ( pd.new_page && pd.lnum != 0 )
	 {
	 if ( !output(PD_FOOTING, &pd, info) )
	    return (0);
	 ++pd.pnum;
	 pd.lnum = 0;
	 if ( !output(PD_HEADING, &pd, info) )
	    return (0);
	 }

      else
	 {
	 if ( pd.lnum+2 > PAGE_DEPTH-CONTENT_BREAK )
	    {
	    if ( !output(PD_FOOTING, &pd, info) )
	       return (0);
	    ++pd.pnum;
	    pd.lnum = 0;
	    if ( !output(PD_HEADING, &pd, info) )
	       return (0);
	    }
	 else if (pd.lnum > 0)
	    {
	    if ( !DO_PRINTN(nl, 2) )
	       return (0);
	    pd.lnum += 2;
	    }
	 }

      if ( !output(PD_SET_SECTION_PAGE, &pd, info) )
	 return (0);

      if ( !first_section )
	 {
	 if ( !output(PD_PRINT_SEC, &pd, info) )
	    return (0);
	 ++pd.lnum;
	 }

      col = 0;

      first_topic = 1;

      while ( get_info(PD_GET_TOPIC, &pd, info) )
	 {
	 if ( !output(PD_START_TOPIC, &pd, info) )
	    return (0);

	 skip_blanks = 0;
	 col = 0;

	 if ( !first_section )	 /* do not skip blanks for DocContents */
	    {
	    while (pd.len > 0)
	       {
	       tok = find_token_length(DOC, pd.curr, pd.len, &size, NULL);
	       if (tok != TOK_XDOC && tok != TOK_XONLINE &&
		   tok != TOK_NL   && tok != TOK_DONE )
		  break;
	       pd.curr += size;
	       pd.len  -= size;
	       }
	    if ( first_topic && pd.len != 0 )
	       {
	       if ( !DO_PRINTN(nl, 1) )
		  return (0);
	       ++pd.lnum;
	       }
	    }

	 if ( pd.lnum > PAGE_DEPTH-TOPIC_BREAK )
	    {
	    if ( !output(PD_FOOTING, &pd, info) )
	       return (0);
	    ++pd.pnum;
	    pd.lnum = 0;
	    if ( !output(PD_HEADING, &pd, info) )
	       return (0);
	    }
	 else if ( !first_topic )
	    {
	    if ( !DO_PRINTN(nl, 1) )
	       return (0);
	    pd.lnum++;
	    }

	 if ( !output(PD_SET_TOPIC_PAGE, &pd, info) )
	    return (0);

	 do
	    {
	    if ( !output(PD_PERIODIC, &pd, info) )
	       return (0);

	    tok = find_token_length(DOC, pd.curr, pd.len, &size, &width);

	    switch ( tok )
	       {
	       case TOK_PARA:
		  {
		  int	    indent,
			    margin;
		  unsigned  holdlen = 0;
		  char far *holdcurr = 0;
		  int	    in_link = 0;

		  ++pd.curr;

		  indent = *pd.curr++;
		  margin = *pd.curr++;

		  pd.len -= 3;

		  if ( !DO_PRINTN(sp, indent) )
		     return (0);

		  col = indent;

		  for(;;)
		     {
		     if ( !output(PD_PERIODIC, &pd, info) )
			return (0);

		     tok = find_token_length(DOC, pd.curr, pd.len, &size, &width);

		     if ( tok == TOK_NL || tok == TOK_FF )
			break;

		     if ( tok == TOK_DONE )
			{
			if (in_link == 0)
			   {
			   col = 0;
			   ++pd.lnum;
			   if ( !DO_PRINTN(nl, 1) )
			      return (0);
			   break;
			   }

			else if (in_link == 1)
			   {
			   tok = TOK_SPACE;
			   width = 1;
			   size = 0;
			   ++in_link;
			   }

			else if (in_link == 2)
			   {
			   tok = TOK_WORD;
			   width = strlen(page_text);
			   col += 8 - width;
			   size = 0;
			   pd.curr = page_text;
			   ++in_link;
			   }

			else if (in_link == 3)
			   {
			   pd.curr = holdcurr;
			   pd.len = holdlen;
			   in_link = 0;
			   continue;
			   }
			}

		     if ( tok == TOK_PARA )
			{
			col = 0;   /* fake a nl */
			++pd.lnum;
			if ( !DO_PRINTN(nl, 1) )
			   return (0);
			break;
			}

		     if (tok == TOK_XONLINE || tok == TOK_XDOC )
			{
			pd.curr += size;
			pd.len -= size;
			continue;
			}

		     if ( tok == TOK_LINK )
			{
			pd.s = pd.curr+1;
			if ( get_info(PD_GET_LINK_PAGE, &pd, info) )
			   {
			   in_link = 1;
			   sprintf(page_text, "(p. %d)", pd.i);
			   }
			else
			   in_link = 3;
			holdcurr = pd.curr + size;
			holdlen = pd.len - size;
                        pd.len = size - 2 - 3*sizeof(int);
                        pd.curr += 1 + 3*sizeof(int);
			continue;
			}

		     /* now tok is TOK_SPACE or TOK_WORD */

		     if (col+width > PAGE_WIDTH)
			{	   /* go to next line... */
			if ( !DO_PRINTN(nl, 1) )
			   return (0);
			if ( ++pd.lnum >= PAGE_DEPTH )
			   {
			   if ( !output(PD_FOOTING, &pd, info) )
			      return (0);
			   ++pd.pnum;
			   pd.lnum = 0;
			   if ( !output(PD_HEADING, &pd, info) )
			      return (0);
			   }

			if ( tok == TOK_SPACE )
			   width = 0;	/* skip spaces at start of a line */

			if ( !DO_PRINTN(sp, margin) )
			   return (0);
			col = margin;
			}

		     if (width > 0)
			{
			if (tok == TOK_SPACE)
			   {
			   if ( !DO_PRINTN(sp, width) )
			      return (0);
			   }
			else
			   {
			   if ( !DO_PRINT(pd.curr, (size==0) ? width : size) )
			      return (0);
			   }
			}

		     col += width;
		     pd.curr += size;
		     pd.len -= size;
		     }

		  skip_blanks = 0;
		  width = size = 0;
		  break;
		  }

	       case TOK_NL:
		  if (skip_blanks && col == 0)
		     break;

		  ++pd.lnum;

		  if ( pd.lnum >= PAGE_DEPTH || (col == 0 && pd.lnum >= PAGE_DEPTH-BLANK_BREAK) )
		     {
		     if ( col != 0 )	/* if last wasn't a blank line... */
			{
			if ( !DO_PRINTN(nl, 1) )
			   return (0);
			}
		     if ( !output(PD_FOOTING, &pd, info) )
			return (0);
		     ++pd.pnum;
		     pd.lnum = 0;
		     skip_blanks = 1;
		     if ( !output(PD_HEADING, &pd, info) )
			return (0);
		     }
		  else
		     {
		     if ( !DO_PRINTN(nl, 1) )
			return (0);
		     }

		  col = 0;
		  break;

	       case TOK_FF:
		  if (skip_blanks)
		     break;
		  if ( !output(PD_FOOTING, &pd, info) )
		     return (0);
		  col = 0;
		  pd.lnum = 0;
		  ++pd.pnum;
		  if ( !output(PD_HEADING, &pd, info) )
		     return (0);
		  break;

	       case TOK_CENTER:
		  width = (PAGE_WIDTH - find_line_width(DOC,pd.curr,pd.len)) / 2;
		  if ( !DO_PRINTN(sp, width) )
		     return (0);
		  break;

	       case TOK_LINK:
		  skip_blanks = 0;
		  if ( !DO_PRINT(pd.curr+1+3*sizeof(int),
			  size-3*sizeof(int)-2) )
		     return (0);
		  pd.s = pd.curr+1;
		  if ( get_info(PD_GET_LINK_PAGE, &pd, info) )
		     {
		     width += 9;
		     sprintf(page_text, " (p. %d)", pd.i);
		     if ( !DO_PRINT(page_text, strlen(page_text)) )
			return (0);
		     }
		  break;

	       case TOK_WORD:
		  skip_blanks = 0;
		  if ( !DO_PRINT(pd.curr, size) )
		     return (0);
		  break;

	       case TOK_SPACE:
		  skip_blanks = 0;
		  if ( !DO_PRINTN(sp, width) )
		     return (0);
		  break;

	       case TOK_DONE:
	       case TOK_XONLINE:   /* skip */
	       case TOK_XDOC:	   /* ignore */
		  break;

	       } /* switch */

	    pd.curr += size;
	    pd.len  -= size;
	    col     += width;
	    }
	 while (pd.len > 0);

	 get_info(PD_RELEASE_TOPIC, &pd, info);

	 first_topic = 0;
	 } /* while */

      first_section = 0;
      } /* while */

   if ( !output(PD_FOOTING, &pd, info) )
      return (0);

   return (1);
   }

#undef DO_PRINT
#undef DO_PRINTN


#undef INCLUDE_COMMON
#endif	 /* #ifdef INCLUDE_COMMON */
