view help2html/mhh6.syn @ 21:1c9dac05d040

Add lint-style FALLTHROUGH annotations to fallthrough cases. (in the parse engine and thus the output code) Document this, because the old output causes warnings with gcc10.
author David A. Holland
date Mon, 13 Jun 2022 00:04:38 -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 ----------------------------------------- */