view help2html/mhh6.syn @ 8:ec2b657edf13

Add explicit lint-comment-style fallthrough annotations. GCC now assumes that if you don't have these you're making a mistake, which is annoying. XXX: This changeset updates the AG output files only (by hand) and is XXX: abusive - rebuilding them will erase the change. However, I need XXX: to get things to build before I can try to get AG to issue the XXX: annotations itself, so this seems like a reasonable expedient.
author David A. Holland
date Mon, 30 May 2022 23:51:43 -0400
parents 13d2b8934445
children
line wrap: on
line source

/*
 * October 30, 2007. mhh6: use C, not C++.
 *
 * Sept 5, 2006. mhh5.syn modified for XML_OUTPUT option.
 *
 * June 20, 2001. mhh5.syn
 *   Modifying mhh4 to create HTML output
 *   (mhh4 sorts a help.src file case-insensitively by title line while 
 *   cleaning it up a bit.)
 *
 * June 16 2001 
 *   Unlike mhh3b, mhh4 stores and sorts title *lines*, not titles, 
 *   from help.src. Another AgStringDirectory has been added to hold
 *   topic bodies.
 *
 * Change mhh3b.syn to store title lines in string dicts.
 * Change mhh3a.syn to use AgStringDictionary for titles.
 * Jun 8/01 Change mhh3.syn to add main and some reduc. procedures
 *
 * June 8, 2001 - mhh3.syn now parses Jerry's current help.src (b8)
 * with no conflicts or keyword anomalies but spaces (and tabs) are
 * not allowed in blank lines. Why the eof line tolerates spaces is
 * something of a mystery.
 */

// -- CONFIGURATION SECTION ----------------------------
[
  pointer input
  no cr           // no carr. ret. in output parser
  parser file name = "#.c"
  line numbers
]
//------------------------------------------------------


eof = 0
char = ~eof
lead title char = char - blank - tab - ',' - bullet - '\n' - '\r'
title char = char - ',' - bullet - '\n' - '\r'
lead topic char = char - blank - tab - bullet - '\n' - '\r'
text char = char - bullet - '\n' - '\r'
table char = char  - tab - '\n' - '\r'

blank = 0x20
tab = 9
bullet = 7

//blank line
//      -> blank?..., '\n'
blank line
      -> '\n'   /* Don't allow spaces in line */
      -> '\r','\n'

eof line
      -> blank?..., eof

eol
      -> '\n'
      -> '\r','\n'

(void) help sourcefile $                 // ** Grammar token **
      -> topic..., blank line?, eof line


topic
      -> blank line?..., title line, eol, blank line?..., topic lines, 
	 end topic

title line
      -> title line too              = {
		title_line_count++; 
                //  buffer_append(&titleLine, "</h3>\n<p>"); 
                saveTitleLine();
         }

title line too
      -> title                                 = title_count++, putTitle();
      -> title line too, ',', blank?..., title = title_count++, appendTitle();

title
      -> lead title char:c   = 
		buffer_start(&title, c), buffer_start(&title1, toupper(c));
      -> title, title char:c = 
		buffer_add(&title, c), buffer_add(&title1, toupper(c));

end topic
      -> "##", blank?..., eol
      -> "\n##", blank?..., eol   
      -> "\r\n##", blank?..., eol   

topic lines
      -> topic lines too                               = { saveTopicBody(); }

topic lines too
      -> text parag                           =buffer_append(&topicBody,"\n"); 
      -> topic lines too, blank line..., parag=buffer_append(&topicBody,"\n"); 

parag
      -> text parag
      -> table parag
      -> list1 parag           = finishList(Tlist1);
      -> list2 parag           = finishList(Tlist2);
      -> listtab parag         = finishList(Tlisttab);
      -> code parag


text parag
      -> partial text parag
      -> partial text parag, table parag
      -> text block, list1 parag                = finishList(Tlist1);
      -> text block, listtab parag              = finishList(Tlisttab);
      -> text block, code parag
      -> text block, code parag, text block


partial text parag
      -> text block
      -> partial text parag, table parag, text block



text block
      -> !buffer_append(&topicBody, "<p>");, 
	 first text line, other text line?... = 
		buffer_append(&topicBody, "</p>");


table parag
      -> table parag too         = buffer_append(&topicBody, "\n</table>\n\n");

table parag too
      -> first table line
      -> table parag too,  other table line


list1 parag
      -> list1 block, list1 block?...

list1 block
      -> first list1 line, other list1 line?... =
		buffer_append(&topicBody, "</li>\n");
      -> first list1 line, other list1 line?..., table parag =
		buffer_append(&topicBody, "</li>\n");
      -> first list1 line, other list1 line?..., code parag =
		buffer_append(&topicBody, "</li>\n");


list2 parag
      -> first list2 line, other list2 line?... =
		buffer_append(&topicBody, "</li>\n");
 
listtab parag
      -> listtab block, listtab block?...

listtab block
      -> first listtab line, other listtab line?... =
		buffer_append(&topicBody, "</li>\n");
      -> first listtab line, other listtab line?..., table parag =
		buffer_append(&topicBody, "</li>\n");

code parag
      -> code parag too                         = 
		buffer_append(&topicBody, "\n</pre>");

code parag too
      -> first code line
      -> code parag too,  other code line


first text line
      -> lead topic char:c, text frag?, eol         = {
		total1sttextline++;
		appendEnd(c);
	 } 
      -> blank, lead topic char:c, text frag?,eol   = {
		total1sttextlineb++;
                buffer_add(&topicBody, 0x20);
		appendEnd(c);
	 }
other text line
      -> lead topic char:c, text frag?, eol              = appendEnd(c);


//################# OLD ####################################################
//first table line
//      -> tab seq, blank seq?, lead topic char:c, text frag?, '\n'  = {
//		total1sttableline++;
//              buffer_append(&topicBody, "\n<pre>  ");
//              appendEnd(c);
//		buffer_clear(&tabFrag);
//		buffer_clear(&blankFrag);
//	 }
//
//other table line
//      -> tab seq, blank seq?, lead topic char:c, text frag?, '\n'  = {
//		buffer_append(&topicBody, "  ");
//		appendEnd(c);
//		buffer_clear(&tabFrag);
//		buffer_clear(&blankFrag);
//	 }
//############################################################################




first table line
      -> first table line body, eol         =  
		buffer_append(&topicBody, "</td></tr>"); 

first table line body
      -> tab seq, blank seq?, lead topic char:c, table frag?     = { 
		total1sttableline++;
                buffer_append(&topicBody,
  "\n\n<table cellpadding=\"7\" cellspacing=\"2\" >\n<tr><td>  ");
                appendTableCell(c);
		buffer_clear(&tabFrag);
		buffer_clear(&blankFrag);
	}
 
      -> first table line body, tab seq, blank seq?, lead topic char:c, 
	 table frag?    = {
		buffer_append(&topicBody, "</td>\n<td>  ");
		appendTableCell(c); 
		buffer_clear(&tabFrag);
		buffer_clear(&blankFrag);
	 }

other table line
      -> other table line body, eol         =  
		buffer_append(&topicBody, "</td></tr>");

other table line body
      -> tab seq, blank seq?, lead topic char:c, table frag?     = {
		buffer_append(&topicBody, "\n\n<tr><td>  ");
		appendTableCell(c);
		buffer_clear(&tabFrag);
		buffer_clear(&blankFrag);
	 }
 
      -> other table line body, tab seq, blank seq?, lead topic char:c, 
	 table frag?    = {
		buffer_append(&topicBody, "</td>\n<td>  ");
		appendTableCell(c);
		buffer_clear(&tabFrag);
		buffer_clear(&blankFrag);
	 }

  

first list1 line
      -> bullet, blank, lead topic char:c, text frag?, eol   = {
		total1stlist1line++;
	        if (intstack_top(&paragType) != Tlist1) {
		  intstack_push(&paragType, Tlist1);
		  buffer_append(&topicBody, "\n<ul>");
		}
        	buffer_append(&topicBody, "\n<li>");
		buffer_add(&topicBody, 0x20);
		appendEnd(c);
	 }

other list1 line
      -> lead topic char:c, text frag?, eol                   = appendEnd(c);


first list2 line
      -> bullet, blank, blank, lead topic char:c, text frag?, eol = {
		total1stlist2line++;
		if (intstack_top(&paragType) != Tlist2) {
		  intstack_push(&paragType, Tlist2);
		  buffer_append(&topicBody, "\n<ul>");
		}
 		buffer_append(&topicBody, "\n<li>");
		buffer_append(&topicBody, "  ");
		appendEnd(c);
	 }           

other list2 line
      -> lead topic char:c, text frag?, eol                  = appendEnd(c);


first listtab line
      -> bullet, tab:t, lead topic char:c, text frag?, eol   = {
		total1stlisttabline++;
  		if (intstack_top(&paragType) != Tlisttab) {
		  intstack_push(&paragType, Tlisttab);
		  buffer_append(&topicBody, "\n<ul>");
		}
		buffer_append(&topicBody, "\n<li>");
		buffer_add(&topicBody, t);
		appendEnd(c);
	 }

other listtab line
      -> lead topic char:c, text frag?, eol                    = appendEnd(c);



first code line
      -> "  ", lead topic char:c, text frag?, eol           = {
		total1stcodeline++;
		buffer_append(&topicBody, "\n<pre>  ");
		appendEnd(c);
	 }
      -> "   ", lead topic char:c, text frag?, eol          = {
		total1stcodeline++;
		buffer_append(&topicBody, "\n<pre>   ");
		appendEnd(c);
	 }
      -> "    ", lead topic char:c, text frag?, eol         = {
		total1stcodeline++;
		buffer_append(&topicBody, "\n<pre>    ");
		appendEnd(c); 
	 }
      -> "     ", lead topic char:c, text frag?, eol        = {
		total1stcodeline++;
		buffer_append(&topicBody, "\n<pre>     ");
		appendEnd(c); 
	 } 
      -> "      ", lead topic char:c, text frag?, eol       = { 
		total1stcodeline++;
		buffer_append(&topicBody, "\n<pre>      ");
		appendEnd(c);
	 }
      -> "        ", lead topic char:c, text frag?, eol     = {
		total1stcodeline++;
		buffer_append(&topicBody, "\n<pre>        ");
		appendEnd(c);
	 }
      -> "          ", lead topic char:c, text frag?, eol   = { 
		total1stcodeline++;
		buffer_append(&topicBody, "\n<pre>          ");
		appendEnd(c);
	 }


other code line
      -> "  ", lead topic char:c, text frag?, eol         = {
		buffer_append(&topicBody, "  ");
		appendEnd(c);
	 }
      -> "   ", lead topic char:c, text frag?, eol        = {
		buffer_append(&topicBody, "   ");
		appendEnd(c);
	 }
      -> "    ", lead topic char:c, text frag?, eol       = {
		buffer_append(&topicBody, "    ");
		appendEnd(c);
	 }
      -> "     ", lead topic char:c, text frag?, eol      = {
		buffer_append(&topicBody, "     ");
		appendEnd(c);
	 }
      -> "      ", lead topic char:c, text frag?, eol     = {
		buffer_append(&topicBody, "      ");
		appendEnd(c);
	 }
      -> "        ", lead topic char:c, text frag?, eol   = {
		buffer_append(&topicBody, "        ");
		appendEnd(c);
	 }
      -> "          ", lead topic char:c, text frag?, eol = {
		buffer_append(&topicBody, "          ");
		appendEnd(c);
	 }

text frag
      -> text char:c                   = buffer_start(&topicFrag, c);
      -> text frag, text char:c        = buffer_add(&topicFrag, c);

table frag
      -> table char:c                  = buffer_start(&topicFrag, c);
      -> table frag, table char:c      = buffer_add(&topicFrag, c);


tab seq
      -> tab:t                         = buffer_start(&tabFrag, t);
      -> tab seq, tab:t                = buffer_add(&tabFrag, t);

blank seq
      -> blank                         = buffer_start(&blankFrag, 0x20);
      -> blank seq, blank              = buffer_add(&blankFrag, 0x20);




{ /* ----- Embedded C --------------------------------------------*/


#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <assert.h>
#include <ctype.h>
#include <assert.h>
#include <err.h>
#include "uintarray.h"
#include "support.h"
#include "buffer.h"
#include "stringdict.h"
#include "must.h"

  int verbose = 0;

  int total1sttextline = 0;
  int total1sttextlineb = 0;
  int total1sttableline = 0;
  int total1stlist1line = 0;
  int total1stlist2line = 0;
  int total1stlisttabline = 0;
  int total1stcodeline = 0;

  int title_count =0;
  int title_line_count = 0;

  char *helpentStr;
  struct buffer title;
  struct buffer title1;
  struct buffer titleLine;
  struct buffer titleLine1;
  struct stringdict *titleDict;
  struct stringdict *titleDict1;             // upper case
  struct stringdict *titleLineDict;
  struct stringdict *titleLineDict1;         // upper case
  struct buffer topicFrag;
  struct buffer tabFrag;
  struct buffer blankFrag;
  struct buffer topicBody;
  struct stringdict *topicBodyDict;
  struct uintarray titleToTitleLine;
  struct intstack paragType;

  enum paragTypes {Tnone=0, Ttext=1, Ttable=2, Tlist1=3, Tlist2=4, 
        Tlisttab=5, Tcode=6};

  char *charToEntity(const char *instring);
  void putTitle(void);
  void appendTitle(void);
  void saveTitleLine(void);
  void appendEnd(int c);
  void removeFinalNewline(void);
  void saveTopicBody(void);
  void printDict(const struct stringdict *dictionary);
  void writeSortedHtml(FILE *output);
  void writeFullTopics(FILE *output, const struct stringdict *dictionary,
		       const struct stringdict *dictionary1,
		       const struct stringdict *dictionaryb);
  void writeTitles(FILE *output, const struct stringdict *dictionary,
                   const struct stringdict *dictionary1); 
  int processLinkString(FILE *filein, FILE *fileout);


  // replace  &, <, > in S with entities 
  char *charToEntity(const char *s) {
    char *ret;
    int i, j, len=0;

    for (i=0; s[i]; i++) {
      if (s[i] == '&') len += 5; /* &amp; */
      else if (s[i] == '<') len += 4; /* &lt; */
      else if (s[i] == '>') len += 4; /* &gt; */
      else len++;
    }

    ret = malloc(len+1);
    if (!ret) {
      errx(1, "Out of memory");
    }

    for (i=j=0; s[i]; i++) {
      if (s[i] == '&')       { strcpy(ret+j, "&amp;"); j += 5; }
      else if (s[i] == '<' ) { strcpy(ret+j, "&lt;"); j += 4; }
      else if (s[i] == '>' ) { strcpy(ret+j, "&gt;"); j += 4; }
      else ret[j++] = s[i];
    }
    ret[j] = 0;

    return ret;
  }

  // Save title both ways, make map entry for title<--->title line
  void saveTitle(void) {
    if (stringdict_exists(titleDict, title.text)) {
      fprintf( stderr, "Warning: Repeated title %s\n", title.text );
    }
#if 0
    else if (!strcmp(title.text, "Secret of Life")) {
      // Do not save "Secret of Life" title
      return;
    }
#endif
    else {
      // Save title in dictionary
      unsigned titleIndex = stringdict_intern(titleDict, title.text);

      // Save title in upper case dictionary
      stringdict_intern(titleDict1, title1.text);

      // count() should give next index
      unsigned titleLineIndex = stringdict_count(titleLineDict);

      // use an array for this (the keys are array indexes anyway)
      if (titleIndex >= uintarray_num(&titleToTitleLine)) {
        unsigned x, old = uintarray_num(&titleToTitleLine);
        uintarray_setsize(&titleToTitleLine, titleIndex+1);
        for (x=old; x<titleIndex; x++) {
          uintarray_set(&titleToTitleLine, x, (unsigned) -1);
        }
      }
      // Store indices in map
      uintarray_set(&titleToTitleLine, titleIndex, titleLineIndex);
    } 
  }


  void putTitle(void) {
    // Save title itself in both reg. and upper case title dicts
    saveTitle();         
    buffer_append(&titleLine, title.text);
    buffer_append(&titleLine1, title1.text);
  }


  void appendTitle() {
    // Save title itself in both reg. and upper case title dicts
    saveTitle();
    buffer_append(&titleLine, ", ");
    buffer_append(&titleLine1, ", ");
    buffer_append(&titleLine, title.text);
    buffer_append(&titleLine1, title1.text);
  }


  void saveTitleLine() {
#if 0
    static int foundSoL=0;
    if (stringdict_count(titleLineDict)==0 && 
	strcmp(titleLine.text, "Secret of Life")!=0 && foundSoL==0) {
      fprintf(stderr, "Warning: Secret of Life does not lead file!\n");
    }
#endif

#if 0
    if (!strcmp(titleLine.text, "Secret of Life")) {
      //  Don't save title line in dicts.
      // Should be at beginning of help file
      assert(stringdict_count(titleLineDict)==0);
      foundSoL = 1;
      if (verbose) {
        printf( "\n  Found Secret of Life!\n\n" );
      }
    }
    else
#endif
    if ( stringdict_exists(titleLineDict, titleLine.text) ) {
      fprintf( stderr, "Warning: Repeated title line %s\n",
	       titleLine.text );
    }
    else {
      // Save title line in dictionary
      stringdict_intern(titleLineDict, titleLine.text);
      // Save title line in upper case dictionary
      stringdict_intern(titleLineDict1, titleLine1.text);
    }
    buffer_clear(&titleLine);
    buffer_clear(&titleLine1);
  }


  // Append the latter part of the line
  void appendEnd(int c) {
    buffer_add(&topicBody, c);
    buffer_append(&topicBody, topicFrag.text);
    buffer_append(&topicBody,  "\n" );
    buffer_clear(&topicFrag);
  }

  // Append a cell to the table row
  void appendTableCell(int c) {
    // Could insert <pre> </pre>  or <code> </code> tags here for cell
    buffer_append(&topicBody, "<code> " );
    buffer_add(&topicBody, c);
    buffer_append(&topicBody, topicFrag.text);
    buffer_append(&topicBody, "</code>" );
    //buffer_append(&topicBody, "\n" );
    buffer_clear(&topicFrag);
  }

   void removeFinalNewline(void) {
     int x = topicBody.len;
     assert(topicBody.text[x-2]=='\n' && topicBody.text[x-1]=='\n');
     topicBody.text[x-1] = 0; 
   }

   void saveTopicBody(void) {
#if 0
     // do not save Secret of Life topic body
     if (stringdict_count(titleLineDict) != 0) {
#endif
       // save topic body
       stringdict_intern(topicBodyDict, topicBody.text);
#if 0
     }
#endif
     buffer_clear(&topicBody);
   }

/*
  void startTable(void) {
    // If we don't currently have table, start a new one
    if (paragType.top() != Ttable) {
      // Start table
      paragType.push( Ttable );
    }
  }
*/

  void finishList(int listtype){
    //printf( "\nfinishList() - top type = %d, listtype = %d, "
    //        "stack size = %d\n"
    //        "  titleLineDict size = %d\n",
    //        paragType.top(), listtype, paragType.size(), 
    //        titleLineDict.size() );
    assert(intstack_top(&paragType) == listtype);
    intstack_pop(&paragType);
    buffer_append(&topicBody, "\n</ul>");
  }

  void printDict(const struct stringdict *dictionary) {
    unsigned i;
    for (i=0; i<stringdict_count(dictionary); i++) {
      printf("%4d: %s\n", i, stringdict_getbynum(dictionary, i));
    }
  }

  char *SqueezeWS(const char *Input) {
    /* return a (strdup()-like) copy of Input, with whitespace squeezed out */
    char *copy;
    int cnt, outcnt;

    copy = must_malloc(strlen(Input)+1);

    for (cnt=0, outcnt=0; Input[cnt]; cnt++) {
      unsigned char ch = Input[cnt];
      if (!isspace(ch)) {
	copy[outcnt]=ch;
	outcnt++;
      }
    }
    copy[outcnt]=0;

    return copy;
  }

  void writeFullTopics(FILE *output,
		       const struct stringdict *dictionary, /* title lines */
		       const struct stringdict *dictionary1, /* UC titlelines*/
		       const struct stringdict *dictionaryb) /* topic bodies */
  {
    unsigned i;

#if 0
    /* Write "Secret of Life" topic at beginning of topics */
    //fprintf(output, "Secret of Life>\n\n");
    //fprintf(output, "No help message for this topic.\n##\n");
#endif

    assert( stringdict_count(dictionary) == stringdict_count(dictionary1) );
    struct permutation *perm = mySort(dictionary1); // Sort  dictionary1


    /* Write out topics in a definition list <dl> */
    fprintf( output, "\n\n<dl>\n\n" );

    // write dictionary, sorted according to dict1
    for (i = 0; i < stringdict_count(dictionary); i++) {  
      //fprintf(output, "<dt><b><a name=\"%04d\">%s</a></b></dt>\n"
      //                "<dd>%s\n</dd><br/>\n\n",
      //  perm->v[i], 
      //  stringdict_getbynum(dictionary, perm->v[i]),
      //  stringdict_getbynum(dictionaryb, perm->v[i])  );

      char *anchorname=SqueezeWS(stringdict_getbynum(dictionary, perm->v[i]));
      fprintf(output, "<dt><b><a name=\"%s\">%s</a></b></dt>\n"
	              "<dd>%s\n</dd>\n\n",
	      anchorname, 
	      stringdict_getbynum(dictionary, perm->v[i]),
	      stringdict_getbynum(dictionaryb, perm->v[i])  );
      free(anchorname);
    }
    fprintf( output, "\n\n</dl>\n\n" );
    permutation_destroy(perm);
  }

  void writeTitles(FILE *output,
		   const struct stringdict *dictionary, /* titles */
		   const struct stringdict *dictionary1) /* uppercase titles */
  {
    assert( stringdict_count(dictionary) == stringdict_count(dictionary1) );
    struct permutation *perm = mySort(dictionary1);   // Sort  dictionary1

    /* Write 2-column table of titles */
    /*
      // n_t is true title count w/o Secret of Life
      int n_t = stringdict_count(dictionary);
      // we better have some titles
      assert( n_t >= 2 );
      // n_t1 is #titles in 1st column
      int n_t1 = n_t%2 ? n_t/2 +1 : n_t/2;
      // n_t2 is #titles in 2nd column
      int n_t2 = n_t%2;
      int i;         	
      fprintf( output, "\n\n<table width=\"100%%\" "
      		       "style=\"margin-left: auto ; margin-right: auto\" \n"
                       "  cellpadding=\"15\" cellspacing=\"5\" >\n"
		       "<tr align=\"left\">\n"
		       "<td valign=\"top\" style=\"white-space: nowrap\">"
		       "\n\n\n");

     // Write out dictionary sorted acc. to dictionary1 
     // write out the first half, sorted
     for (i = 0; i < n_t1; i++) {
       fprintf(output, " \xA9%s\xAA\n<br/>",
               stringdict_getbynum(dictionary, perm->v[i]));
     }

     fprintf(output, "</td>\n\n");
     fprintf(output, "<td valign=\"top\" style=\"white-space: nowrap\">\n\n");

     // write out the last half, sorted
     for ( i = n_t1; i < n_t; i++) {
       fprintf(output, " \xA9%s\xAA\n<br/>",
               stringdict_getbynum(dictionary, perm->v[i]));
     }

     fprintf(output, "</td>\n</tr>\n</table>\n\n<hr><br/><br/>\n\n" );
    */

    /* Write 1-column list of titles */
     
    // n_t is true title count w/o Secret of Life
    unsigned n_t = stringdict_count(dictionary);
    // we better have some titles
    assert( n_t >= 2 );                        
    unsigned i;         	

    fprintf(output, "<h2>Help Topic Index</h2>\n\n" );
    // Write out the index, sorted acc. to dictionary1 
    for (i = 0; i < n_t; i++) {
      fprintf(output, "\xA9%s\xAA\n<br/>",
	      stringdict_getbynum(dictionary, perm->v[i]));
    }
  }

  void writeSortedHtml( FILE *output ) {
    /* Leading HTML */

#ifdef XML_OUTPUT
    fprintf(output, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
	    "<?xml-stylesheet type=\"text/xsl\" href=\"../ag_web.xml\"?>\n"
	    "\n"
	    "<body rootdir=\"..\" navname=\"Documentation: AnaGram Help\">\n");
 
#else
    fprintf(output, "<html>\n<head>\n");
    fprintf(output, "<title>AnaGram Help Topics - HTML version</title>\n");
    fprintf(output, "</head>\n\n\n");
    fprintf(output, "<body bgcolor=\"#ffffff\" text=\"#000000\"");
    fprintf(output, " link=\"#0033CC\" vlink=\"#CC0033\" alink=\"#CC0099\">");
    fprintf(output, "\n\n\n");
#endif

    /* Write page title */
#ifdef XML_OUTPUT
    fprintf(output, "<h1>AnaGram Help</h1>\n\n");
#else
    fprintf(output, "<hr><h2>AnaGram Help Topics - HTML Version</h2><hr>\n\n");
#endif
    /* Write 2-column table of titles */
    writeTitles( output, titleDict, titleDict1 );

    /* Write full topics in single- column table */
    //fprintf( output, "\n\n<table width=\"100%%\">\n<tr><td>\n<p>\n\n");
    //writeFullTopics( output, titleLineDict, titleLineDict1, topicBodyDict );
    //fprintf( output, "</td></tr>\n</table>\n" );

    /* Write full topics directly to the output page */
    writeFullTopics( output, titleLineDict, titleLineDict1, topicBodyDict );

#ifdef XML_OUTPUT
    fprintf(output,"\n</body>\n");
#else 
    /* Ending HTML */
    fprintf( output, "\n<p><br/><address><a "
	     "NAME=\"copyright\">AnaGram Help Topics, HTML version.</a>\n"
	     "<br> Copyright &copy; Parsifal Software, 2001.<br>\n"
	     "All Rights Reserved.</address>" 
	     "\n\n</body>\n</html>\n\n" );
#endif
  }

  int processLinkString( FILE *filein, FILE *fileout ) {
    struct buffer linkString;                   // lower case
    struct buffer linkString1;                  // upper case version
    unsigned index;
    int c;
    int wspaceFlag =0;

    buffer_init(&linkString);
    buffer_init(&linkString1);

    while ( (c=fgetc(filein)) != EOF ) {
    
      if ( c != 0xAA) {                            // test for end of link char
	if ( c == 0x20 || c == 0x0D || c == 0x0A ) {
	  // don't append these chars
	  wspaceFlag = 1;  
	}
	else {
	  if (wspaceFlag) {
            // Replace space, cr, lf with single space
	    buffer_add(&linkString, 0x20);
	    buffer_add(&linkString1, toupper(0x20));         
	  }
	  buffer_add(&linkString, c);
	  buffer_add(&linkString1, toupper(c));
	  wspaceFlag = 0;
	}
      }
      else {
	// end of link - look up using upper case string
	if (!stringdict_exists(titleDict1, linkString1.text)) {
	  // try match w/o final S
	  if ( linkString1.text[linkString1.len - 1] == 'S' ) {
	    linkString1.text[linkString1.len - 1] = 0;
	    if (stringdict_exists(titleDict1, linkString1.text)) {
	      goto matched;      // Eccch - a goto!
	    }
	  }

	  fprintf(stderr, "Can't find this link in titleDict1: %s\n",
		 linkString1.text);
	  return 21; 
	}

        /* find corresp. index in title line direc. */      
    matched:

	index = stringdict_findbyname(titleDict1, linkString1.text);

	unsigned ilink = uintarray_get(&titleToTitleLine, index);
	assert(ilink != (unsigned) -1);

	// Write out string, linked to title line
	char *linkname=SqueezeWS(stringdict_getbynum(titleLineDict, ilink));
	//fprintf(fileout, "<a href=\"#%04d\">%s</a>", ilink, linkString.text);
	fprintf( fileout, "<a href=\"#%s\">%s</a>", linkname, linkString.text);
	free(linkname);

	buffer_cleanup(&linkString);
	buffer_cleanup(&linkString1);

	return 0;            // normal return - have found  and written link 
      }
    }
    // Error - unexpected end of file
    fprintf(stderr, "Error: EOF detected while searching for end of link.\n");
    fprintf(stderr, "  Current link string is: %s\n", linkString.text );
    return 23;
  }

  static void init(void) {
    buffer_init(&title);
    buffer_init(&title1);
    buffer_init(&titleLine);
    buffer_init(&titleLine1);
    titleDict = stringdict_create();
    titleDict1 = stringdict_create();
    titleLineDict = stringdict_create();
    titleLineDict1 = stringdict_create();
    topicBodyDict = stringdict_create();
    buffer_init(&topicFrag);
    buffer_init(&tabFrag);
    buffer_init(&blankFrag);
    buffer_init(&topicBody);
    intstack_init(&paragType);
    uintarray_init(&titleToTitleLine);
  }

/* -- Main Program -- */

int main(int argc, char *argv[]) {

  FILE *input;

  long fileLength;
  size_t stringLength;
  char *helpsrcString;

  init();

  if (verbose) {
    printf( "\n  This program reads a help.src-type file, "
	    "replaces &, <, > with entities,\n"
	    "sorts in a case-insensitive manner and writes "
	    "to output file as HTML\n"
	    "with a preceding list of the help topics. \n\n" );
  }

  /* Check for enough arguments */
  if (argc != 3) {
    fprintf(stderr, "Usage: mhh6 helpdata.src help.html\n");
    return 1;
  }

  /* Open input file */
  input = fopen(argv[1],"r");
  if (input == NULL) {
    fprintf(stderr, "Cannot open %s\n", argv[1]);
    return 2;
  }

  /* find out how big the input file is */
  if (fseek(input, SEEK_SET, SEEK_END)) {
    fprintf(stderr, "Strange problems with %s\n", argv[1]);
    return 3;
  }
  fileLength = ftell(input);
  if (fileLength < 0 ) {    // -1L is error return
    fprintf(stderr, "Error getting file length (%ld) of %s\n",
	    fileLength, argv[1]);
    return 4;
  }

  /* fseek to beginning of file */
  if (fseek(input, 0, SEEK_SET)) {
    fprintf(stderr, "Strange problems with %s\n", argv[1]);
    return 5;
  }

  /* Allocate storage for input string */
  helpsrcString = must_malloc(fileLength + 1);

  /* read file */
  stringLength = fread(helpsrcString, 1, (unsigned)fileLength, input);
  if (stringLength == 0) {
    fprintf(stderr, "Unable to read %s\n", argv[1]);
    free(helpsrcString);
    fclose(input);
    return 7;
  }
  // Terminate string with null
  helpsrcString[stringLength] = 0;

  /* first, replace < > & with entities */
  helpentStr = charToEntity( helpsrcString ); 

  /* no more need for input string or file */
  free(helpsrcString);
  fclose(input);

  /* initialize stack of parag types */
  intstack_push(&paragType, Tnone);
 
  /* call parser */
  PCB.pointer = (unsigned char *)(const char *)helpentStr;
  mhh6();

  /* Print file statistics */
  if (verbose) {
    printf("No. of title lines in line dict.= %d\n",
	   stringdict_count(titleLineDict) );
    printDict(titleLineDict);              // print title lines
    printf( "\n\n" );

    printf( "title count = %d, includes Secret of Life \n\n", title_count );
    printf( "title line count = %d, includes Secret of Life \n\n", 
	    title_line_count );
    printf( "total1sttextline = %d, \n", total1sttextline );
    printf( "total1sttextlineb = %d \n", total1sttextlineb  );
    printf( "total1sttableline = %d \n", total1sttableline  );
    printf( "total1stlist1line = %d \n", total1stlist1line  );
    printf( "total1stlist2line = %d \n", total1stlist2line  );
    printf( "total1stlisttabline = %d \n", total1stlisttabline  );
    printf( "total1stcodeline = %d \n", total1stcodeline  );
  }

  /* check for error */
  if (verbose) {
    printf( "PCB.exit_flag = %d (%d for success)\n", PCB.exit_flag,
	    AG_SUCCESS_CODE);
  }
  if (PCB.exit_flag != AG_SUCCESS_CODE) {
    fprintf(stderr, "File %s: error at line %d, column %d\n",
	    argv[1],
	    PCB.line,
	    PCB.column);
    return 8;
  }

  // Write sorted title lines & topics as HTML to intermediate file
  FILE *intermed;
  const char *filename = "intermed.html";
  /* Open intermediate file */
  intermed = fopen(filename ,"w+");  // create intermediate text file 
  if (intermed == NULL) {
    fprintf(stderr, "Cannot open %s\n", filename);
    return 9;
  }
  if (verbose) {
    printf( "Writing sorted title lines & topic bodies to "
	    "intermediate file in HTML format...\n");
  }
  writeSortedHtml(intermed); 
  rewind(intermed);

  /* Create output HTML file, inserting links */
  FILE *output;
  /* Open output file */
  output = fopen(argv[2] ,"w");   
  if (output == NULL) {
    fprintf(stderr, "Cannot open %s\n", argv[2]);
    return 10;
  }

  if (verbose) {
    printf( "Writing output file with HTML links...\n");
  }

  int c = 0;
  int ctest = 0;
  
  while ( (c=fgetc(intermed)) != EOF ){

    if ( c == 0xA9 ) {            // begins link string
      //  printf( "\n Found beginning of link" );
      int itest = processLinkString(intermed, output);
      if (itest !=0) return itest;    // error return
    }
    else {
      ctest = fputc( c, output );    // write out current character
      if (ctest == EOF) return 11;
    }
  }  

  fclose(intermed);
  fclose(output);


  /* done */
  if (verbose) {
    printf( "All done.\n" );
  }
  return 0;                          // normal return 

}  /* -- End of main() function -- */

} /* ---- End of embedded C ----------------------------------------- */