/*----------------------------------------------------------------------*\
 | Code to write HTML version of formal metadata.						|
 |																		|
 | Peter N. Schweitzer (U.S. Geological Survey, Reston, VA 20192)		|
\*----------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

#include "keyword.h"
#include "item.h"
#include "config.h"
#include "revision.h"
#include "stricmp.h"

extern char *iso8859_1[];
extern void unescape_string (char *s);
extern char *related_file (char *type);
extern char *read_text (char *input_file);

extern int valid_utf8 (unsigned char *s);		/* in encoding.c */
extern int unicode_of_utf8 (char **s);			/* in encoding.c */
extern unsigned char *utf8_of (unsigned char *dst, int c); /* in encoding.c */
extern char *get_character_encoding (void);		/* in xml.c */

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static struct item *parse_html (char *name) {
	return (NULL);
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static char html_doctype[] = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">";

static char preformat_indicator = '>';
static int do_preformatting = 1;
static int do_meta = 1;
static int utf8_in  = 0;
static int utf8_out = 0;

static char *empty = "";
static char *begin_tag = "<em>";
static char *end_tag   = "</em>";

static void set_keyword_formatting (char *prefix, char *suffix) {
	if (prefix) begin_tag = prefix;
	if (suffix) end_tag   = suffix;
	}

/*----------------------------------------------------------------------*/

struct format {
	struct format *next;
	enum fgdc_keyword key;
	struct {
		char *begin_tag;
		char *end_tag;
		int obeylines;
		} keyword, value;
	int omit;
	};

static struct format *format = NULL;

static struct format *format_of (enum fgdc_keyword key) {
	struct format *g;

	if (format == NULL) return (NULL);
	else
		for (g=format; g; g=g->next)
			if (g->key == key) break;
	return (g);
	}

static void add_format (enum fgdc_keyword key, char *key_prefix, char *key_suffix, char *value_prefix, char *value_suffix, int obeylines, int omit) {
	struct format *g;

	if (key == Wnull) return;

	if (!key_prefix) key_prefix = begin_tag;
	if (!key_suffix) key_suffix = end_tag;
	if (!value_prefix) value_prefix = empty;
	if (!value_suffix) value_suffix = empty;

	if (format == NULL)
		if (format = (struct format *) malloc (sizeof (struct format))) {
			format->next = NULL;
			format->key = key;
			format->keyword.begin_tag = key_prefix;
			format->keyword.end_tag   = key_suffix;
			format->value.begin_tag   = value_prefix;
			format->value.end_tag     = value_suffix;
			format->value.obeylines   = obeylines;
			format->omit              = omit;
			}
		else {
			fprintf (stderr,"Error: could not allocate space for HTML format info\n");
			exit (1);
			}
	else
		if (g = format_of (key)) {
			g->keyword.begin_tag = key_prefix;
			g->keyword.end_tag   = key_suffix;
			g->value.begin_tag   = value_prefix;
			g->value.end_tag     = value_suffix;
			g->value.obeylines   = obeylines;
			g->omit              = omit;
			}
		else {
			for (g=format; g->next; g=g->next);
			if (g->next = (struct format *) malloc (sizeof (struct format))) {
				g = g->next;
				g->next = NULL;
				g->key = key;
				g->keyword.begin_tag = key_prefix;
				g->keyword.end_tag   = key_suffix;
				g->value.begin_tag   = value_prefix;
				g->value.end_tag     = value_suffix;
				g->value.obeylines   = obeylines;
				g->omit              = omit;
				}
			else {
				fprintf (stderr,"Error: could not allocate space for HTML child format\n");
				exit (1);
				}
			}
	}

static void dump_formats (void) {
	struct format *g;

	printf ("Formats:\n");
	for (g=format; g; g=g->next) {
		printf ("  Element: %s\n",text_of(g->key));
		printf ("    Name\n");
		printf ("      begin_tag (0x%08lX): %s\n",g->keyword.begin_tag,g->keyword.begin_tag);
		printf ("      end_tag   (0x%08lX): %s\n",g->keyword.end_tag,g->keyword.end_tag);
		printf ("    Value\n");
		printf ("      begin_tag (0x%08lX): %s\n",g->value.begin_tag,g->value.begin_tag);
		printf ("      end_tag   (0x%08lX): %s\n",g->value.end_tag,g->value.end_tag);
		printf ("      obeylines             : %d\n",g->value.obeylines);
		}
	}

static char *key_prefix_of (enum fgdc_keyword key) {
	struct format *g;

	if (g = format_of (key))
		return (g->keyword.begin_tag);
	else
		return (begin_tag);
	}

static char *key_suffix_of (enum fgdc_keyword key) {
	struct format *g;

	if (g = format_of (key))
		return (g->keyword.end_tag);
	else
		return (end_tag);
	}

static char *value_prefix_of (enum fgdc_keyword key) {
	struct format *g;

	if (g = format_of (key))
		return (g->value.begin_tag);
	else
		return (empty);
	}

static char *value_suffix_of (enum fgdc_keyword key) {
	struct format *g;

	if (g = format_of (key))
		return (g->value.end_tag);
	else
		return (empty);
	}

static int obeylines (enum fgdc_keyword key) {
	struct format *g;

	if (g = format_of (key))
		return (g->value.obeylines);
	else
		return (0);
	}

static int omit (enum fgdc_keyword key) {
	struct format *g;

	if (g = format_of (key))
		return (g->omit);
	else
		return (0);
	}


/*----------------------------------------------------------------------*/

static int do_text_translation = 1;
static char *(*munge)(char *string);
static char *buffer = NULL;
static int buffer_length = 0;

static char *as_is (char *string) {
	return (string);
	}

static void clear_buffer (void) {
	if (buffer) *buffer = 0;
	else
		if (buffer = (char *) malloc (4096)) {
			memset (buffer,0,4096);
			buffer_length = 4096;
			}
		else {
			fprintf (stderr,"Error: failed to allocate 4096 bytes for html translation buffer\n");
			exit (1);
			}
	}

static char *add_to_buffer (char *string, int j) {
	int k,n;
	char *t;

	if (!string || j == 0) {
		t = buffer + strlen (buffer);
		return (t);
		}

	if (!buffer)
		if (buffer = (char *) malloc (4096)) {
			memset (buffer,0,4096);
			buffer_length = 4096;
			}
		else {
			fprintf (stderr,"Error: failed to allocate 4096 bytes for html translation buffer\n");
			exit (1);
			}

	k = strlen (buffer);
	n = ((j < 4096) ? 4096 : j+1);

	if (k + j + 1 >= buffer_length)
		if (buffer = (char *) realloc (buffer,buffer_length+n)) {
			buffer_length += n;
			}
		else {
			fprintf (stderr,"Error: failed to reallocate %d bytes for html translation buffer\n",buffer_length + n);
			exit (1);
			}

	t = buffer + k;
	memcpy (t,string,j);
	t += j;
	*t = 0;
	return (t);
	}

static void add_character (char **s) {
	int n;
	char *t = *s;
	char entity [16];	/* for entity references */
	char bytes[8];		/* for UTF-8 encoded characters */

	if (utf8_in) {
		n = unicode_of_utf8 (s);

		if (n < 128) add_to_buffer (t,1);
		else
			if (n < 256 && iso8859_1[n] != NULL) {
				sprintf (entity,"&%s;",iso8859_1[n]);
				add_to_buffer (entity,strlen (entity));
				}
			else
				if (utf8_out) {
					utf8_of (bytes,n);
					add_to_buffer (bytes,strlen(bytes));
					}
				else {} /* if no encoding, write no output */
		}
	else {
		if (utf8_out) {
			n = *t;

			if (n < 256 && iso8859_1[n] != NULL) {
				sprintf (entity,"&%s;",iso8859_1[n]);
				add_to_buffer (entity,strlen (entity));
				}
			else {
				utf8_of (bytes,n);
				add_to_buffer (bytes,strlen(bytes));
				}
			}
		else {
			add_to_buffer (t,1);
			}
		t++;	/* one byte per character, so increment the pointer */
		*s = t;	/* store t in address pointed to by s */
		}
	}

static char *htmlentities (char *string) {
	char *s,*t;

	clear_buffer();
	if (!string) return (buffer);
	s = string;
	while (*s) {
		switch (*s) {
			case '<':
				add_to_buffer ("&lt;",4);
				s++;
				break;
			case '>':
				add_to_buffer ("&gt;",4);
				s++;
				break;
			case '&':
				add_to_buffer ("&amp;",5);
				s++;
				break;
			case '"':
				add_to_buffer ("&quot;",6);
				s++;
				break;
			default:
				add_character (&s);
				break;
			}
		}
	return (buffer);
	}

static char *translated (char *string) {
	int n;
	char *b,*e,*s,*t;
	char entity [16];
	char bytes[8];		/* for UTF-8 encodings */

	clear_buffer();
	if (!string) return (buffer);
	s = string;
	while (*s) {
		switch (*s) {
			case '<':
				if (e = strchr (s,'>')) {
					if ((b = strrchr (s,'<')) == s) {
						char *d = b+1;
						while (*d && isspace (*d)) d++;
						if (memicmp (d,"URL",3) == 0) {

							/* Start A tag with HREF attribute */

							add_to_buffer ("<a href=\"",9);

							/* Skip to beginning of the actual URL */

							b = d + 3;
							if (*b == ':') b++;
							while (*b && isspace (*b)) b++;

							d = e;
							if (isspace(*(d-1)))
								while (d > b && isspace (*(d-1))) d--;

							/* Copy the URL into the tag */

							add_to_buffer (b,d-b);

							/* Close the A tag, add an entity lt*/

							add_to_buffer ("\">&lt;URL:",10);

							/* Copy the URL into the text */

							add_to_buffer (b,d-b);

							/* Close the URL and the anchor */

							add_to_buffer ("&gt;</a>",8);

							/* Point s to the end of the URL */

							s = e + 1;

							break;
							}
						}
					}
				add_to_buffer ("&lt;",4);
				s++;
				break;
			case '>':
				add_to_buffer ("&gt;",4);
				s++;
				break;
			case '&':
				add_to_buffer ("&amp;",5);
				s++;
				break;
			case '"':
				add_to_buffer ("&quot;",6);
				s++;
				break;

			/* Intercept ftp://url and make it a live link */

			case 'f':
				if (memcmp (s,"ftp://",6) == 0) {
					b = s;
					e = b;
					while (*e && !isspace(*e) && *e != '>' && *e != ')') e++;

					/*--------------------------------------------------*\
					 | If the last character is punctuation, assume		|
					 | that the punctuation is part of the context, not	|
					 | part of the URL.  This works for most, but not	|
					 | all punctuation.  For example, / is a reasonable	|
					 | character with which to end a URL, as is ?.		|
					\*--------------------------------------------------*/

					if (ispunct (*(e-1)))
						if (*(e-1) != '/' && *(e-1) != '?') e--;

					/* Start A tag with HREF attribute */

					add_to_buffer ("<a href=\"",9);

					/* Copy the URL into the tag */

					add_to_buffer (b,e-b);

					/* Close the A tag*/

					add_to_buffer ("\">",2);

					/* Add entity &lt; if one is not already present */

					if (s > string) {
						if (*(s-1) != '<') add_to_buffer ("&lt;",4);
						}
					else add_to_buffer ("&lt;",4);

					/* Copy the URL into the text */

					add_to_buffer (b,e-b);

					/* Add entity &gt; if one is not already present */

					if (*e != '>') add_to_buffer ("&gt;",4);

					/* Close the URL and the anchor */

					add_to_buffer ("</a>",4);

					/* Point s to the end of the URL */

					s = e;
					}
				else
					add_character (&s);
				break;

			/* Intercept http://url and make it a live link */

			case 'h':
				if (memcmp (s,"http://",7) == 0) {
					b = s;
					e = b;
					while (*e && !isspace(*e) && *e != '>' && *e != ')') e++;

					/*--------------------------------------------------*\
					 | If the last character is punctuation, assume		|
					 | that the punctuation is part of the context, not	|
					 | part of the URL.  This works for most, but not	|
					 | all punctuation.  For example, / is a reasonable	|
					 | character with which to end a URL, as is ?.		|
					\*--------------------------------------------------*/

					if (ispunct (*(e-1)))
						if (*(e-1) != '/' && *(e-1) != '?') e--;

					/* Start A tag with HREF attribute */

					add_to_buffer ("<a href=\"",9);

					/* Copy the URL into the tag */

					add_to_buffer (b,e-b);

					/* Close the A tag*/

					add_to_buffer ("\">",2);

					/* Add entity &lt; if one is not already present */

					if (s > string) {
						if (*(s-1) != '<') add_to_buffer ("&lt;",4);
						}
					else add_to_buffer ("&lt;",4);

					/* Copy the URL into the text */

					add_to_buffer (b,e-b);

					/* Add entity &gt; if one is not already present */

					if (*e != '>') add_to_buffer ("&gt;",4);

					/* Close the URL and the anchor */

					add_to_buffer ("</a>",4);

					/* Point s to the end of the URL */

					s = e;
					}
				else
					add_character (&s);
				break;

			/* Intercept mailto:address and make it a live link */

			case 'm':
				if (memcmp (s,"mailto:",7) == 0) {
					b = s;
					e = b;
					while (*e && !isspace(*e) && *e != '>' && *e != ')') e++;

					/*--------------------------------------------------*\
					 | If the last character is punctuation, assume		|
					 | that the punctuation is part of the context, not	|
					 | part of the URL.  This works for most, but not	|
					 | all punctuation.  For example, / is a reasonable	|
					 | character with which to end a URL, as is ?.		|
					\*--------------------------------------------------*/

					if (ispunct (*(e-1)))
						if (*(e-1) != '/' && *(e-1) != '?') e--;

					/* Start A tag with HREF attribute */

					add_to_buffer ("<a href=\"",9);

					/* Copy the URL into the tag */

					add_to_buffer (b,e-b);

					/* Close the A tag*/

					add_to_buffer ("\">",2);

					/* Add entity &lt; if one is not already present */

					if (s > string) {
						if (*(s-1) != '<') add_to_buffer ("&lt;",4);
						}
					else add_to_buffer ("&lt;",4);

					/* Copy the URL into the text */

					add_to_buffer (b,e-b);

					/* Add entity &gt; if one is not already present */

					if (*e != '>') add_to_buffer ("&gt;",4);

					/* Close the URL and the anchor */

					add_to_buffer ("</a>",4);

					/* Point s to the end of the URL */

					s = e;
					}
				else
					add_character (&s);
				break;

			default:
				add_character (&s);
				break;
			}
		}
	return (buffer);
	}

static char *what_to_print_for_Wblank = "<br/>\n";
static FILE *fp = NULL;

static void write_html_value (struct item *p) {
	struct item *q;
	char *s;
	int preformatting = 0;

	if (!p) return;

	for (q=p->child; q; q=q->next)
		if (q->key == Wblank) {
			if (do_preformatting)
				if (preformatting) {/* do nothing */}
				else
					fprintf (fp,"%s",what_to_print_for_Wblank);
			else
				fprintf (fp,"%s",what_to_print_for_Wblank);
			}
		else {
			if (s = q->d) {
				if (do_preformatting)
					if (preformatting)
						if (*q->d == preformat_indicator) s++;
						else {
							fprintf (fp,"</pre>\n");
							preformatting = 0;
							}
					else
						if (*q->d == preformat_indicator) {
							fprintf (fp,"<pre>\n");
							preformatting = 1;
							s++;
							}
				fprintf (fp,"%s",munge(s));
				}
			if (!preformatting)
				if (obeylines(p->key)) fprintf (fp,"<br/>");
			fprintf (fp,"\n");
			}
	if (preformatting) fprintf (fp,"</pre>\n");
	}

static void write_html_item (struct item *p) {
	char *s;
	struct item *q;

	if (!p) return;

	if (omit (p->key)) return;

	if (p->key != Wblank && p->key != Wunknown)
		fprintf (fp,"<dt>%s%s:%s  ",key_prefix_of(p->key),munge(text_of(p->key)),key_suffix_of(p->key));

	if (q = p->child) {
		while (q)
			if (q->key != Wblank) break;
			else q = q->next;

		if (!q) return;

		if (q->key == Wunknown) {

			/*----------------------------------------------------------*\
			 | If the first child of p is Wunknown, assume that all		|
			 | other children are as well.								|
			\*----------------------------------------------------------*/

			if (p->child->next == NULL && strlen (p->child->d) < 64) {
				if (s = value_prefix_of(p->key)) fprintf (fp,"%s",s);
				fprintf (fp,"%s",munge(p->child->d));
				if (s = value_suffix_of(p->key)) fprintf (fp,"%s",s);
				fprintf (fp,"</dt>\n");
				}
			else {

				fprintf (fp,"\n<dd>\n");
				if (s = value_prefix_of(p->key)) fprintf (fp,"%s\n",s);
				write_html_value (p);
				if (s = value_suffix_of(p->key)) fprintf (fp,"%s\n",s);
				fprintf (fp,"</dd>\n");
				}
			}
		else {

			/*----------------------------------------------------------*\
			 | The first child of p is a known element.  Assume that	|
			 | all other children are as well.							|
			\*----------------------------------------------------------*/

			fprintf (fp,"\n<dd>\n");
			fprintf (fp,"<dl>\n");
			for (q=p->child; q; q=q->next)
				if (q->key == Wblank) fprintf (fp,"%s",what_to_print_for_Wblank);
				else
					if (q->key == Wunknown) {
						if (q->d) fprintf (fp,"%s<br/>\n",q->d);
						}
					else
						write_html_item (q);
			fprintf (fp,"</dl>\n");
			fprintf (fp,"</dd>\n");
			}
		}
	else {
		/* There are no children of p */
		fprintf (fp,"</dt>\n");
		}
	}

static void write_links (char *which);
static void do_meta_tags (struct item *first);

void write_html (char *output_file, struct item *first) {
	int i;
	struct item *p,*q;
	char *b,*s,*t,*title;
	int title_allocated = 0;
	void *opt,*v,*vv;
	time_t T;
	unsigned char bytes[8];		/* for UTF-8 translation */

	if (first == NULL) return;

	/* If encoding is UTF-8, remember this for later */

	if (stricmp (get_character_encoding(),"UTF-8") == 0) {
		utf8_in  = 1;
		utf8_out = 1;
		}

	/*------------------------------------------------------------------*\
	 | Get options from config tree										|
	\*------------------------------------------------------------------*/

	if (opt = find_option (NULL,"output"))
		if (opt = find_option (opt,"html")) {

			/*----------------------------------------------------------*\
			 | "translate off" activates html code in text values.  By	|
			 | Default such html code is rendered literally.			|
			\*----------------------------------------------------------*/

			if (v = find_option (opt,"translate")) {
				do_text_translation = 1;
				if (s = text_of_option (v))
					if (stricmp (s,"off") == 0) do_text_translation = 0;
				}

			/*----------------------------------------------------------*\
			 | "preformat" encloses groups of lines that begin with > 	|
			 | in <pre></pre> tags.  Optional value is the character	|
			 | used to indicate sections that should be preformatted.	|
			 | Default is >.  Turn off with preformat off.				|
			\*----------------------------------------------------------*/

			if (v = find_option (opt,"preformat")) {
				do_preformatting = 1;
				if (s = text_of_option (v))
					if (stricmp (s,"off") == 0) do_preformatting = 0;
					else
						if (*s) preformat_indicator = *s;
				}

			/*----------------------------------------------------------*\
			 | "meta off" disables dublin-core meta elements generation |
			\*----------------------------------------------------------*/

			if (v = find_option (opt,"meta")) {
				do_meta = 1;
				if (s = text_of_option (v))
					if (stricmp (s,"off") == 0) do_meta = 0;
				}

			/*----------------------------------------------------------*\
			 | "encoding" specifies output encoding, typically used for	|
			 | UTF-8.  If not specified and UTF-8 is input, make UTF-8	|
			 | also the output encoding.								|
			\*----------------------------------------------------------*/

			if (v = find_option (opt,"encoding")) {
				if (s = text_of_option (v))
					if (stricmp (s,"utf-8") == 0) utf8_out = 1;
					else utf8_out = 0;
				}
			}

	/* Generally you want to translate characters */

	if (do_text_translation) munge = translated;
	else munge = as_is;

	if (!format) {
		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html")) {

				if (v = find_option (opt,"keyword")) {
					char *prefix = NULL;
					char *suffix = NULL;

					if (vv = find_option (v,"prefix"))
						if (s = text_of_option (vv)) prefix = s; else prefix = empty;
					if (vv = find_option (v,"suffix"))
						if (s = text_of_option (vv)) suffix = s; else suffix = empty;
					set_keyword_formatting (prefix,suffix);
					}

				if (v = find_option (opt,"blanks")) {
					char *s = text_of_option (v);
					if (*s) unescape_string (s);
					what_to_print_for_Wblank = s;
					}

				if (opt = find_option (opt,"element"))
					do {
						if (v = find_option (opt,"key")) {
							char *name = NULL;
							if (name = text_of_option (v)) {
								char *key_prefix   = NULL;
								char *key_suffix   = NULL;
								char *value_prefix = NULL;
								char *value_suffix = NULL;
								int obeylines      = 0;
								int omit           = 0;
								enum fgdc_keyword key;
								key = key_of (name);

								if (v = find_option (opt,"name")) {
									if (vv = find_option (v,"prefix"))
										if (!(key_prefix = text_of_option (vv)))
											key_prefix = empty;
									if (vv = find_option (v,"suffix"))
										if (!(key_suffix = text_of_option (vv)))
											key_suffix = empty;
									}

								if (v = find_option (opt,"value")) {
									if (vv = find_option (v,"prefix"))
										if (!(value_prefix = text_of_option (vv)))
											value_prefix = empty;
									if (vv = find_option (v,"suffix"))
										if (!(value_suffix = text_of_option (vv)))
											value_prefix = empty;
									if (vv = find_option (v,"obeylines")) obeylines = 1;
									}

								if (v = find_option (opt,"omit")) omit = 1;

								add_format (key,key_prefix,key_suffix,value_prefix,value_suffix,obeylines,omit);
								}
							}
						} while (opt = find_next_option (opt,"element"));
				}
		}

	title = output_file;
	if (p = find_key (first,WCitation))
		if (p = find_key (p,WTitle))
			if (p->child)
				if (p->child->key == Wunknown) {
					int n = 0;
					for (q=p->child; q; q=q->next)
						if (q->d) n += 1 + strlen (q->d);
					if (title = (char *) malloc (1 + n)) {
						char *s = title;
						for (q=p->child; q; q=q->next) {
							if (q->d) {
								strcpy (s,q->d);
								s += strlen (q->d);
								}
							if (q->next) *s++ = ' ';
							*s = 0;
							}
						title_allocated = 1;
						}
					else
						title = p->child->d;
					}

	if (fp = fopen (output_file,"w")) {
		fprintf (fp,"%s\n",html_doctype);
		fprintf (fp,"<html>\n");
		fprintf (fp,"<head>\n");

		if (utf8_out)
			fprintf (fp,"  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n");
		else
			fprintf (fp,"  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\"/>\n");

		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html"))
				if (opt = find_option (opt,"base"))
					if (s = text_of_option (opt)) {
						fprintf (fp,"  <base href=\"%s",s);
						if (*(s + strlen(s) - 1) != '/') fprintf (fp,"/");
						fprintf (fp,"%s\"/>\n",output_file);
						}

		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html"))
				if (opt = find_option (opt,"style_file"))
					if (s = text_of_option (opt)) {
						if (t = read_text (s)) {
							char *tt = t + strlen (t) - 1;
							fprintf (fp,"<!-- begin style from %s -->\n",s);
							for (b=t; *b; b++)
								if (*b == 0x0D) *b = ' ';
							fprintf (fp,"%s",t);
							if (*tt != '\n') fprintf (fp,"\n");
							fprintf (fp,"<!-- end style from %s -->\n",s);
							free (t);
							}
						}

		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html"))
				if (opt = find_option (opt,"stylesheet"))
					do {
						char *style_type = "text/css";
						if (v = find_option (opt,"type"))
							if (s = text_of_option (v)) style_type = s;
						if (v = find_option (opt,"href"))
							if (s = text_of_option (v))
								fprintf (fp,"  <link rel=\"stylesheet\" type=\"%s\" href=\"%s\"/>\n",style_type,s);
						} while (opt = find_next_option (opt,"stylesheet"));

		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html"))
				if (opt = find_option (opt,"script_file"))
					if (s = text_of_option (opt)) {
						if (t = read_text (s)) {
							char *tt = t + strlen (t) - 1;
							fprintf (fp,"<!-- begin script from %s -->\n",s);
							for (b=t; *b; b++)
								if (*b == 0x0D) *b = ' ';
							fprintf (fp,"%s",t);
							if (*tt != '\n') fprintf (fp,"\n");
							fprintf (fp,"<!-- end script from %s -->\n",s);
							free (t);
							}
						}

		/* Print the title */

		fprintf (fp,"  <title>");
		fprintf (fp,htmlentities(title));
		fprintf (fp,"</title>\n");

		if (do_meta) do_meta_tags (first);

		if (s = related_file ("original"))
			fprintf (fp,"  <meta name=\"generated-from\" content=\"%s\"/>\n",s);

		fprintf (fp,"</head>\n");
		fprintf (fp,"<body");
		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html"))
				if (opt = find_option (opt,"body"))
					if (s = text_of_option (opt))
						fprintf (fp," %s",s);
		fprintf (fp,">\n");

		/*--------------------------------------------------------------*\
		 | Output user-specified header if there is one.				|
		\*--------------------------------------------------------------*/

		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html"))
				if (v = find_option (opt,"header")) {
					if (s = text_of_option (v))
						fprintf (fp,"%s\n",s);
					}
				else
					if (v = find_option (opt,"header_file")) {
						if (s = text_of_option (v)) {
							if (t = read_text (s)) {
								fprintf (fp,"<!-- begin header from %s -->\n",s);
								for (b=t; *b; b++)
									if (*b == 0x0D) *b = ' ';
								fprintf (fp,"%s",t);
								b = t + strlen (t) - 1;
								if (*b != '\n') fprintf (fp,"\n");
								fprintf (fp,"<!-- end header from %s -->\n",s);
								free (t);
								}
							}
						}

		/*--------------------------------------------------------------*\
		\*--------------------------------------------------------------*/

		fprintf (fp,"<h1>");
		fprintf (fp,htmlentities(title));
		fprintf (fp,"</h1>\n");

		if (title_allocated) {
			free (title);
			title = NULL;
			title_allocated = 0;
			}

		/*--------------------------------------------------------------*\
		 | Links to other formats of this metadata record?				|
		\*--------------------------------------------------------------*/

		write_links ("html");

		/*--------------------------------------------------------------*\
		 | Build a simple table of contents, using the major sections	|
		 | of the standard.  Start with HREF anchors.					|
		\*--------------------------------------------------------------*/

		p = first;
		if (p->key == WMetadata)
			if (p->next) p = p->next;
			else
				if (p->child) p = p->child;
				else
					p = NULL;

		fprintf (fp,"<h1>%s:</h1>\n",htmlentities(text_of(WMetadata)));
		fprintf (fp,"<ul>\n");
		i = 1;
		while (p) {
			char *s = text_of(p->key);
			if (!omit(p->key)) {
				if (p->key != Wunknown && p->key != Wblank)
					fprintf (fp,"<li><a href=\"#%d\">%s</a></li>\n",i,htmlentities(s));
				}
			p = p->next;
			i++;
			}
		fprintf (fp,"</ul>\n");

		/*--------------------------------------------------------------*\
		 | Output the metadata, emplacing NAME anchors at the tops of	|
		 | major sections to which the HREF anchors have referred.		|
		\*--------------------------------------------------------------*/

		p = first;
		if (p->key == WMetadata)
			if (p->next) p = p->next;
			else
				if (p->child) p = p->child;
				else
					p = NULL;

		i = 1;
		while (p) {
			if (p->key != Wblank && !omit(p->key)) {
				fprintf (fp,"<a name=\"%d\">\n",i);
				fprintf (fp,"</a>\n");
				fprintf (fp,"<hr/>\n");

				fprintf (fp,"<dl>\n");
				write_html_item (p);
				fprintf (fp,"</dl>\n");

				fprintf (fp,"\n");
				}
			p = p->next;
			i++;
			}

		/*--------------------------------------------------------------*\
		 | Bottom of page.  If BASE was specified, write the page URL.	|
		\*--------------------------------------------------------------*/

		fprintf (fp,"<hr/>\n");

		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html"))
				if (!find_option (opt,"omit_url"))
					if (opt = find_option (opt,"base"))
						if (s = text_of_option (opt)) {
							fprintf (fp,"This page is &lt;%s",s);
							if (*(s + strlen(s) - 1) != '/') fprintf (fp,"/");
							fprintf (fp,"%s&gt;<br/>\n",output_file);
							}

		/*--------------------------------------------------------------*\
		 | Output user-specified footer if there is one.				|
		\*--------------------------------------------------------------*/

		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html"))
				if (v = find_option (opt,"footer")) {
					if (s = text_of_option (v))
						fprintf (fp,"%s\n",s);
					}
				else
					if (v = find_option (opt,"footer_file")) {
						if (s = text_of_option (v)) {
							if (t = read_text (s)) {
								fprintf (fp,"<!-- begin header from %s -->\n",s);
								for (b=t; *b; b++)
									if (*b == 0x0D) *b = ' ';
								fprintf (fp,"%s",t);
								b = t + strlen (t) - 1;
								if (*b != '\n') fprintf (fp,"\n");
								fprintf (fp,"<!-- end footer from %s -->\n",s);
								free (t);
								}
							}
						}

		/*--------------------------------------------------------------*\
		 | Put a link and a time-stamp into the output file.			|
		\*--------------------------------------------------------------*/

		T = time (&T);
		t = ctime (&T);
		s = t + strlen (t) - 1;
		if (*s == '\n') *s-- = 0;
		if (*s == '\r') *s-- = 0;
		fprintf (fp,"Generated by <a href=\"http://geology.usgs.gov/tools/metadata/tools/doc/mp.html\"><tt>mp</tt></a> version %s on %s<br/>\n",revision.mp,t);

		/*--------------------------------------------------------------*\
		\*--------------------------------------------------------------*/

		fprintf (fp,"\n");
		fprintf (fp,"</body>\n");
		fprintf (fp,"</html>\n");
		fclose (fp);
		}
	else {
		fprintf (stderr,"Error: could not create output file %s\n",output_file);
		return;
		}
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static void do_meta_tags (struct item *first) {
	struct item *p, *q, *r, *s;

	char *t;
	void *opt;
	char *language;
	int WLanguage;

	/*------------------------------------------------------------------*\
	 | Dublin Core SCHEMA link
	\*------------------------------------------------------------------*/

	fprintf (fp,"  <link rel=\"schema.dc\" href=\"http://purl.org/metadata/dublin_core\"/>\n");

	/*------------------------------------------------------------------*\
	 | Dublin Core TITLE element
	\*------------------------------------------------------------------*/

	if (p = find_key (first,WCitation))
		if (p = find_key (p,WTitle))
			if (p = p->child)
				if (p->key == Wunknown) {
					fprintf (fp,"  <meta name=\"dc.title\" content=\"");
					while (p) {
						if (p->d) fprintf (fp,"%s ",htmlentities(p->d));
						p = p->next;
						}
					fprintf (fp,"\"/>\n");
					}

	/*------------------------------------------------------------------*\
	 | Dublin Core CREATOR element
	\*------------------------------------------------------------------*/

	if (p = find_key (first,WCitation))
		if (p = find_key (p,WOriginator)) {
			fprintf (fp,"  <meta name=\"dc.creator\" content=\"");
			while (p) {
				if (p->key == WOriginator)
					if (q = p->child)
						if (q->d) fprintf (fp,"%s ",htmlentities(q->d));
				p = p->next;
				}
			fprintf (fp,"\"/>\n");
			}

	/*------------------------------------------------------------------*\
	 | Dublin Core SUBJECT element
	\*------------------------------------------------------------------*/

	if (p = find_key (first,WTheme))
		if (p = p->child) {
			fprintf (fp,"  <meta name=\"dc.subject\" content=\"");
			while (p) {
				if (p->key == WTheme_Keyword)
					if (q = p->child)
						if (q->d) fprintf (fp,"%s ",htmlentities(q->d));
				p = p->next;
				}
			fprintf (fp,"\"/>\n");
			}

	/*------------------------------------------------------------------*\
	 | Dublin Core DESCRIPTION element
	\*------------------------------------------------------------------*/

	if (p = find_key (first,WDescription))
		if (p = find_key (p,WAbstract))
			if (p = p->child)
				if (p->key == Wunknown) {
					fprintf (fp,"  <meta name=\"dc.description\" content=\"");
					while (p) {
						if (p->d) fprintf (fp,"%s ",htmlentities(p->d));
						p = p->next;
						}
					fprintf (fp,"\"/>\n");
					}

	/*------------------------------------------------------------------*\
	 | Dublin Core PUBLISHER element
	\*------------------------------------------------------------------*/

	if (p = find_key (first,WMetadata_Contact)) {
		int found = 0;
		if (q = find_key (p,WContact_Organization))
			if (r = q->child)
				if (r->d) {
					fprintf (fp,"  <meta name=\"dc.publisher\" content=\"%s\"/>\n",htmlentities(r->d));
					found = 1;
					}
		if (!found)
			if (q = find_key (p,WContact_Person))
				if (r = q->child)
					if (r->d) {
						fprintf (fp,"  <meta name=\"dc.publisher\" content=\"%s\"/>\n",htmlentities(r->d));
						found = 1;
						}
		}

	/*------------------------------------------------------------------*\
	 | Dublin Core CONTRIBUTOR element
	\*------------------------------------------------------------------*/

	if (p = find_key (first,WIdentification_Information))
		if (p = find_key (p,WData_Set_Credit))
			if (p = p->child)
				if (p->key == Wunknown) {
					fprintf (fp,"  <meta name=\"dc.contributor\" content=\"");
					while (p) {
						if (p->d) fprintf (fp,"%s ",htmlentities(p->d));
						p = p->next;
						}
					fprintf (fp,"\"/>\n");
					}

	/*------------------------------------------------------------------*\
	 | Dublin Core DATE element
	\*------------------------------------------------------------------*/

	if (p = find_key (first,WCitation))
		if (p = find_key (p,WPublication_Date))
			if (q = p->child)
				if (q->d)
					fprintf (fp,"  <meta name=\"dc.date\" content=\"%s\"/>\n",htmlentities(q->d));

	/*------------------------------------------------------------------*\
	 | Dublin Core TYPE element
	\*------------------------------------------------------------------*/

	if (p = find_key (first,WCitation)) {
		int found = 0;
		if (p = find_key (p,WGeospatial_Data_Presentation_Form))
			if (q = p->child)
				if (q->d) {
					fprintf (fp,"  <meta name=\"dc.type\" content=\"data.%s\"/>\n",htmlentities(q->d));
					found = 1;
					}
		if (!found)
			fprintf (fp,"  <meta name=\"dc.type\" content=\"data.structured-text\"/>\n");
		}

	/*------------------------------------------------------------------*\
	 | Dublin Core FORMAT element
	 | Search each Standard_Order_Process, and for each, search each Digital_Form for Format_Name
	\*------------------------------------------------------------------*/

	if (p = find_key (first,WDistribution_Information))
		if (p = find_key (p,WStandard_Order_Process))
			do {
				if (q = find_key (p,WDigital_Form))
					do {
						if (r = find_key (q,WFormat_Name))
							if (s = r->child)
								if (s->d)
									fprintf (fp,"  <meta name=\"dc.format\" content=\"%s\"/>\n",htmlentities(s->d));
						} while (q = find_next_key (q,WDigital_Form));
				} while (p = find_next_key (p,WStandard_Order_Process));

	/*------------------------------------------------------------------*\
	 | Dublin Core IDENTIFIER element
	 | Hmmm.  I don't think this will be reliably unique.
	\*------------------------------------------------------------------*/

	if (p = find_key (first,WCitation))
		if (p = find_key (p,WOnline_Linkage))
			if (q = p->child)
				if (q->d)
					fprintf (fp,"  <meta name=\"dc.identifier\" content=\"%s\"/>\n",htmlentities(q->d));

	/*------------------------------------------------------------------*\
	 | Dublin Core SOURCE element
	 | This also will not be reliably unique; depends on Distributor.
	\*------------------------------------------------------------------*/

	if (p = find_key (first,WDistribution_Information))
		if (p = find_key (p,WResource_Description))
			if (q = p->child)
				if (q->d)
					fprintf (fp,"  <meta name=\"dc.source\" content=\"%s\"/>\n",htmlentities(q->d));

	/*------------------------------------------------------------------*\
	 | Dublin Core LANGUAGE element
	 |
	 | This isn't a standard element, but we could use clues.  First,
	 | if there's an extended element with this content, and it has a
	 | value, we should use that.  Second, if the language of the
	 | metadata was specified in the config file, use that.  Otherwise
	 | assume that the language is English.
	\*------------------------------------------------------------------*/

	language = NULL;

	WLanguage = key_of ("Language");

	if (p = find_key (first,WLanguage))
		if (q = p->child)
			if (q->d) language = q->d;

	if (!language)
		if (p = find_key (first,WMetadata_Language))
			if (q = p->child)
				if (q->d) language = q->d;

	if (!language)
		if (opt = find_option (NULL,"input"))
			if (opt = find_option (opt,"language"))
				if (t = text_of_option (opt)) language = t;

	if (!language) language = "en";

	fprintf (fp,"  <meta name=\"dc.lang\" content=\"%s\"/>\n",language);

	/*------------------------------------------------------------------*\
	 | Dublin Core RELATION element
	 | Use if there is a Larger_Work_Citation
	\*------------------------------------------------------------------*/

	if (p = find_key (first,WCitation))
		if (p = find_key (p,WLarger_Work_Citation))
			if (p = find_key (p,WTitle))
				if (q = p->child)
					if (q->d)
						fprintf (fp,"  <meta name=\"dc.relation\" content=\"Part of %s\"/>\n",htmlentities(q->d));

	/*------------------------------------------------------------------*\
	 | Dublin Core COVERAGE element
	\*------------------------------------------------------------------*/

	if (p = find_key (first,WWest_Bounding_Coordinate))
		if (q = p->child)
			if (q->d)
				fprintf (fp,"  <meta name=\"dc.coverage.x.min\" scheme=\"DD\" content=\"%s\"/>\n",htmlentities(q->d));
	if (p = find_key (first,WEast_Bounding_Coordinate))
		if (q = p->child)
			if (q->d)
				fprintf (fp,"  <meta name=\"dc.coverage.x.max\" scheme=\"DD\" content=\"%s\"/>\n",htmlentities(q->d));
	if (p = find_key (first,WSouth_Bounding_Coordinate))
		if (q = p->child)
			if (q->d)
				fprintf (fp,"  <meta name=\"dc.coverage.y.min\" scheme=\"DD\" content=\"%s\"/>\n",htmlentities(q->d));
	if (p = find_key (first,WNorth_Bounding_Coordinate))
		if (q = p->child)
			if (q->d)
				fprintf (fp,"  <meta name=\"dc.coverage.y.max\" scheme=\"DD\" content=\"%s\"/>\n",htmlentities(q->d));

	if (p = find_key (first,WPlace))
		if (q = find_key (p,WPlace_Keyword))
			do {
				if (r = q->child)
					if (r->d)
						fprintf (fp,"  <meta name=\"dc.coverage.placeName\" content=\"%s\"/>\n",htmlentities(r->d));
				} while (q = find_next_key (q,WPlace_Keyword));

	if (p = find_key (first,WTime_Period_of_Content))
		if (p = find_key (p,WRange_of_Dates_Times)) {
			fprintf (fp,"  <meta name=\"dc.coverage.t.min\" content=\"");
			if (q = find_key (p,WBeginning_Date))
				if (r = q->child)
					if (r->d) fprintf (fp,"%s",htmlentities(r->d));
			if (q = find_key (p,WBeginning_Time))
				if (r = q->child)
					if (r->d) fprintf (fp,"T%s",htmlentities(r->d));
			fprintf (fp,"\"/>\n");

			fprintf (fp,"  <meta name=\"dc.coverage.t.max\" content=\"");
			if (q = find_key (p,WEnding_Date))
				if (r = q->child)
					if (r->d) fprintf (fp,"%s",htmlentities(r->d));
			if (q = find_key (p,WEnding_Time))
				if (r = q->child)
					if (r->d) fprintf (fp,"T%s",htmlentities(r->d));
			fprintf (fp,"\"/>\n");
			}

	if (p = find_key (first,WTemporal))
		if (q = find_key (p,WTemporal_Keyword))
			do {
				if (r = q->child)
					if (r->d)
						fprintf (fp,"  <meta name=\"dc.coverage.periodName\" content=\"%s\"/>\n",htmlentities(r->d));
				} while (q = find_next_key (q,WTemporal_Keyword));

	/*------------------------------------------------------------------*\
	 | Dublin Core RIGHTS element
	 | Combine Access_Constraints and Use_Constraints
	\*------------------------------------------------------------------*/

	fprintf (fp,"  <meta name=\"dc.rights\" content=\"");
	if (p = find_key (first,WAccess_Constraints))
		if (q = p->child)
			if (q->d)
				fprintf (fp,"Access constraints: %s; ",htmlentities(q->d));
	if (p = find_key (first,WUse_Constraints))
		if (q = p->child)
			if (q->d)
				fprintf (fp,"Use constraints: %s",htmlentities(q->d));
	fprintf (fp,"\"/>\n");

	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static char *child_text (struct item *p) {
	struct item *q;
	if (p)
		for (q=p->child; q; q=q->next)
			if (q->key == Wunknown) return (q->d);
	return (NULL);
	}

static char *full_text_buffer = NULL;
static int full_text_buffer_length = 0;

static char *full_text (struct item *p) {
	char *s;
	struct item *q;

	if (!p       ) return (NULL);
	if (!p->child) return (NULL);

	/* Find the first non-blank child of p */

	for (q=p->child; q; q=q->next)
		if (q->key != Wblank) break;

	/* If the first nonblank child is Wunknown, you can get a value */

	if (q->key == Wunknown) {
		int n = 0;
		for (q=p->child; q; q=q->next)
			if (q->d) n += 1 + strlen (q->d);

		if (1+n > full_text_buffer_length) {
			int k = 1 + (1+n)/4096;
			if (full_text_buffer = (char *) realloc (full_text_buffer,k*4096))
				full_text_buffer_length = k*4096;
			else
				return (NULL);
			}

		s = full_text_buffer;
		for (q=p->child; q; q=q->next)
			if (q->d) {
				strcpy (s,q->d);
				s += strlen (q->d);
				if (q->next) *s++ = ' ';
				}
		*s = 0;
		return (full_text_buffer);
		}
	else
		return (NULL);
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

#define MAX_NAME 128

static void parse_name (char *name, char *first, char *middle, char *last) {
	char *b,*e;
	int i,n;

	if (*name == 0 || *name == '<') {
		*first = *middle = *last = 0;
		return;
		}

	/*------------------------------------------------------------------*\
	 | How many names are there?  If we see more than one comma, or if	|
	 | we see the word "and", then we might safely assume that there	|
	 | are multiple names in this element, and we should leave it alone.|
	\*------------------------------------------------------------------*/

	n = 0;
	for (b=name; *b; b++)
		if (*b == ',') n++;

	if (n > 1 || strstr (name," and ")) {
		*first = *middle = *last = 0;
		return;
		}

	/* One or zero commas, no occurrence of the word "and" */

	b = name;
	if (e = strchr (name,',')) {	/* Last, First Middle */
		if (e - b < MAX_NAME) {
			memcpy (last,b,e-b);
			last[e-b] = 0;
			}
		else {
			memcpy (last,b,MAX_NAME-1);
			last[MAX_NAME-1] = 0;
			}
		e++;
		b = e; while (*b &&  isspace(*b)) b++;
		e = b; while (*e && !isspace(*e)) e++;
		if (e - b < MAX_NAME) {
			memcpy (first,b,e-b);
			first[e-b] = 0;
			}
		else {
			memcpy (first,b,MAX_NAME-1);
			first[MAX_NAME-1] = 0;
			}
		b = e; while (*b && isspace(*b)) b++;
		if (strlen (b) < MAX_NAME) {
			strcpy (middle,b);
			}
		else {
			memcpy (middle,b,MAX_NAME-1);
			middle[MAX_NAME-1] = 0;
			}
		}
	else {							/* First Middle Last */
		e = b; while (*e && !isspace(*e)) e++;
		if (e - b < MAX_NAME) {
			memcpy (first,b,e-b);
			first[e-b] = 0;
			}
		else {
			memcpy (first,b,MAX_NAME-1);
			first[MAX_NAME-1] = 0;
			}
		b = e; while (*b &&  isspace(*b)) b++;
		e = b; while (*e && !isspace(*e)) e++;
		if (e - b < MAX_NAME) {
			memcpy (middle,b,e-b);
			middle[e-b] = 0;
			}
		else {
			memcpy (middle,b,MAX_NAME-1);
			middle[MAX_NAME-1] = 0;
			}
		b = e; while (*b &&  isspace(*b)) b++;
		e = b; while (*e && !isspace(*e)) e++;

		/*--------------------------------------------------------------*\
		 | If no more words, what we put in middle is really the last	|
		 | name, otherwise store in the last name what remains.			|
		 | This won't properly parse names like "John Smith Jr."		|
		\*--------------------------------------------------------------*/

		if (e - b == 0) {
			strcpy (last,middle);
			*middle = 0;
			}
		else {
			if (e - b < MAX_NAME) {
				memcpy (last,b,e-b);
				last[e-b] = 0;
				}
			else {
				memcpy (last,b,MAX_NAME-1);
				last[MAX_NAME-1] = 0;
				}

			/*----------------------------------------------------------*\
			 | If what's left is too large for an ancestral suffix like	|
			 | Sr., Jr., or III, cancel the whole parse operation.		|
			 | Otherwise, append what's left to the middle name.		|
			\*----------------------------------------------------------*/

			b = e; while (*b &&  isspace(*b)) b++;
			if (strlen (b) > 4)
				*first = *middle = *last = 0;
			else
				if (strlen (middle) < 28) {
					strcat (middle," ");
					strcat (middle,b);
					}

			/*----------------------------------------------------------*\
			 | Check the first name; if it's U.S., then cancel parse.	|
			\*----------------------------------------------------------*/

			if (strcmp (first,"U.S.") == 0)
				*first = *middle = *last = 0;
			}
		}
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static void write_date (char *string) {
	int y,m,d;
	char *s;
	struct tm tm;
	char date_string [64];

	if (!fp)      return;
	if (! string) return;

	for (s=string; *s && isspace(*s); s++);
	if (!*s) return;

	if (!isdigit (*s)) {
		fprintf (fp,"%s",munge(string));
		return;
		}

	y = m = d = 0;
	if (*s && isdigit(*s)) y = 10 * y + (*s++ - '0');
	if (*s && isdigit(*s)) y = 10 * y + (*s++ - '0');
	if (*s && isdigit(*s)) y = 10 * y + (*s++ - '0');
	if (*s && isdigit(*s)) y = 10 * y + (*s++ - '0');
	if (y < 100) y += 1900;
	if (y > 1900) tm.tm_year = y - 1900;
	else return;

	/*------------------------------------------------------------------*\
	 | If only a year was specified, just output the year.				|
	\*------------------------------------------------------------------*/

	while (*s && isspace(*s)) s++;
	if (!isdigit(*s)) {
		fprintf (fp,"%d",y);
		return;
		}

	/*------------------------------------------------------------------*\
	 | If YYYYMM or YYYYMMDD, decode the whole date.					|
	\*------------------------------------------------------------------*/

	if (*s && isdigit(*s)) m = 10 * m + (*s++ - '0');
	if (*s && isdigit(*s)) m = 10 * m + (*s++ - '0');
	tm.tm_mon = m - 1;

	if (*s && isdigit(*s)) d = 10 * d + (*s++ - '0');
	if (*s && isdigit(*s)) d = 10 * d + (*s++ - '0');
	if (d > 0) tm.tm_mday = d;
	else tm.tm_mday = 1;
	tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
	tm.tm_isdst = 0;

	if (mktime (&tm) == (time_t) -1) {
		fprintf (fp,"%s",munge(string));
		return;
		}

	*date_string = 0;

	if (d > 0)
		strftime (date_string,64,"%d-%b-%Y",&tm);
	else
		if (m > 0)
			strftime (date_string,64,"%b-%Y",&tm);
		else
			if (y > 0)
				strftime (date_string,64,"%Y",&tm);

	fprintf (fp,"%s",date_string);
	if (*s) fprintf (fp,"%s",munge(s));
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static void write_citation (struct item *p) {
	char *s;
	struct item *q,*r;
	int wrote_series_information = 0;

	if (!fp || !p) return;
	if (p->key != WCitation_Information) return;

	fprintf (fp,"<blockquote>\n");

	/* Authors and other originators */

	if (q = find_key (p,WOriginator))
		if (q->parent == p) {
			if (s = full_text (q)) {
				char FirstName[MAX_NAME],MiddleName[MAX_NAME],LastName[MAX_NAME];

				parse_name (s,FirstName,MiddleName,LastName);
				if (*LastName) {
					fprintf (fp,"%s",munge(LastName));
					if (*FirstName)
						fprintf (fp,", %s",munge(FirstName));
					if (*MiddleName)
						fprintf (fp," %s",munge(MiddleName));
					}
				else
					fprintf (fp,"%s",munge(s));
				}
			while (q = find_next_key (q,WOriginator))
				if (s = full_text (q)) {
					char FirstName[64],MiddleName[64],LastName[64];

					if (r = find_next_key (q,WOriginator))
						fprintf (fp,", ");
					else
						fprintf (fp,", and ");

					parse_name (s,FirstName,MiddleName,LastName);
					if (*LastName) {
						fprintf (fp,"%s",munge(LastName));
						if (*FirstName)
							fprintf (fp,", %s",munge(FirstName));
						if (*MiddleName)
							fprintf (fp," %s",munge(MiddleName));
						}
					else
						fprintf (fp,"%s",munge(s));
					}
			}

	/* Publication date */

	if (q = find_key (p,WPublication_Date))
		if (s = full_text (q))
			fprintf (fp,", %s",munge(s));

	/* Title */

	if (q = find_key (p,WTitle))
		if (q->parent == p)
			if (s = full_text (q)) {
				fprintf (fp,", %s",munge(s));
				if (find_key (p,WSeries_Information) ||
					find_key (p,WPublication_Information)) fprintf (fp,":");
				}

	/* Series Information */

	if (q = find_key (p,WSeries_Information))
		if (q->parent == p) {
			if (r = find_key (q,WSeries_Name))
				if (s = full_text (r))
					fprintf (fp," %s",munge(s));
			if (r = find_key (q,WIssue_Identification))
				if (s = full_text (r))
					fprintf (fp," %s",munge(s));
			wrote_series_information = 1;
			}

	/* Publication Information */

	if (q = find_key (p,WPublication_Information))
		if (q->parent == p) {
			if (wrote_series_information) fprintf (fp,",");
			if (r = find_key (q,WPublisher))
				if (s = full_text (r))
					fprintf (fp," %s",munge(s));
			if (r = find_key (q,WPublication_Place))
				if (s = full_text (r))
					fprintf (fp,", %s",munge(s));
			}

	fprintf (fp,".<p>\n");

	/* Online Linkage */

	if (q = find_key (p,WOnline_Linkage))
		if (q->parent == p) {
			fprintf (fp,"Online Links:\n");
			fprintf (fp,"<ul>\n");
			do {
				if (s = full_text (q))
					fprintf (fp,"<li>%s\n",munge(s));
				} while (q = find_next_key (q,WOnline_Linkage));
			fprintf (fp,"</ul>\n");
			fprintf (fp,"<p>\n");
			}

	fprintf (fp,"</blockquote>\n");

	/* Other Citation Details */

	if (q = find_key (p,WOther_Citation_Details))
		if (q->parent == p) {
			fprintf (fp,"<dl>\n");
			write_html_item (q);
			fprintf (fp,"</dl>\n");
			}

	if (q = find_key (p,WLarger_Work_Citation)) {
		if (q->child)
			if (q->child->key == WCitation_Information) {
				fprintf (fp,"This is part of the following larger work.<p>\n");
				write_citation (q->child);
				}
		}

	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static void write_contact (struct item *p) {
	struct item *q,*r,*u;
	char *s;

	if (!fp || !p) return;

	fprintf (fp,"<div style=\"margin-left: 2em\">\n");

	/*-- Contact_Person, Contact_Organization --*/

	if (q = find_key (p,WContact_Person_Primary)) {
		if (r = find_key (q,WContact_Person))
			if (s = full_text(r)) fprintf (fp,"%s<br/>\n",munge(s));
		if (r = find_key (q,WContact_Organization))
			if (s = full_text(r)) fprintf (fp,"%s<br/>\n",munge(s));
		}
	else
		if (q = find_key (p,WContact_Organization_Primary)) {
			if (r = find_key (q,WContact_Organization))
				if (s = full_text(r)) fprintf (fp,"%s<br/>\n",munge(s));
			if (r = find_key (q,WContact_Person))
				if (s = full_text(r)) fprintf (fp,"c/o %s<br/>\n",munge(s));
			}

	/*-- Contact_Position --*/

	if (q = find_key (p,WContact_Position))
		if (s = full_text (q)) fprintf (fp,"%s<br/>\n",munge(s));

	/*-- Contact_Address --*/

	if (q = find_key (p,WContact_Address)) {
		if (r = find_key (q,WAddress))
			for (u=r->child; u; u=u->next)
				if (u->d) fprintf (fp,"%s<br/>\n",munge(u->d));
		if (r = find_key (q,WCity))
			if (s = full_text(r)) fprintf (fp,"%s, ",munge(s));
		if (r = find_key (q,WState_or_Province))
			if (s = full_text(r)) fprintf (fp,"%s ",munge(s));
		if (r = find_key (q,WPostal_Code))
			if (s = full_text(r)) fprintf (fp,"%s<br/>\n",munge(s));
		if (r = find_key (q,WCountry))
			if (s = full_text(r)) fprintf (fp,"%s<br/>\n",munge(s));
		}
	fprintf (fp,"<p>\n");

	/*-- phone, FAX, email --*/

	if (q = find_key (p,WContact_Voice_Telephone))
			if (s = full_text(q)) fprintf (fp,"%s (voice)<br/>\n",munge(s));
	if (q = find_key (p,WContact_Facsimile_Telephone))
			if (s = full_text(q)) fprintf (fp,"%s (FAX)<br/>\n",munge(s));
	if (q = find_key (p,WContact_Electronic_Mail_Address))
			if (s = full_text(q)) fprintf (fp,"%s<br/>\n",munge(s));

	/*-- hours and instructions --*/

	if (find_key (p,WHours_of_Service) || find_key (p,WContact_Instructions)) {
		fprintf (fp,"<dl>\n");
		if (q = find_key (p,WHours_of_Service)) write_html_item (q);
		if (q = find_key (p,WContact_Instructions)) write_html_item (q);
		fprintf (fp,"</dl>\n");
		}

	fprintf (fp,"</div>\n");
	}

/*----------------------------------------------------------------------*\
 | Link to related files in HTML output.								|
\*----------------------------------------------------------------------*/

static void write_link_for (char *kind, char *label, char *which, int base_href) {
	char *f,*g,*t;

	if (f = related_file (kind)) {
		if (base_href)
			if (g = related_file (which))
				while (t = strchr (f,'/'))
					if (memcmp (f,g,t-f+1) == 0) {
						g += t - f + 1;
						f += t - f + 1;
						}
					else
						break;
		fprintf (fp," - [<a href=\"%s\">%s</a>]",f,label);
		}
	}

static void write_links (char *which) {
	int done = 0;
	void *opt,*v;
	char *s,*t,*f,*g,*b;
	int base_href = 0;
	char *lead_text = NULL;

	/*------------------------------------------------------------------*\
	 | If there is a "link" element in the config file, create links	|
	 | based on the directives given there.								|
	\*------------------------------------------------------------------*/

	if (opt = find_option (NULL,"output"))
		if (opt = find_option (opt,"html")) {
			if (v = find_option (opt,"base"))
				if (text_of_option (v)) base_href = 1;
			if (opt = find_option (opt,"link")) {
				if (find_option (opt,"link_faq" ) ||
					find_option (opt,"link_html") ||
					find_option (opt,"link_text") ||
					find_option (opt,"link_sgml") ||
					find_option (opt,"link_xml" ) ||
					find_option (opt,"link_dif" )) {

					if (!(b = related_file ("input"))) b = "error_no_input_file";

					if (v = find_option (opt,"label"))
						if (s = text_of_option (v))
							lead_text = s;

					if (lead_text)
						fprintf (fp,"%s",lead_text);
					else
						fprintf (fp,"Metadata also available as");

					/*--------------------------------------------------*\
					 | Link to FAQ-style HTML
					\*--------------------------------------------------*/

					if (strcmp (which,"faq") != 0)
						if (v = find_option (opt,"link_faq"))
							if (s = text_of_option (v)) {
								fprintf (fp," [<a href=\"");
								fprintf (fp,s,b);
								fprintf (fp,"\">Questions &amp; Answers</a>]");
								}
							else
								write_link_for ("faq","Questions &amp; Answers",which,base_href);

					/*--------------------------------------------------*\
					 | Link to outline-style HTML
					\*--------------------------------------------------*/

					if (strcmp (which,"html") != 0)
						if (v = find_option (opt,"link_html"))
							if (s = text_of_option (v)) {
								fprintf (fp," - [<a href=\"");
								fprintf (fp,s,b);
								fprintf (fp,"\">Outline</a>]");
								}
							else
								write_link_for ("html","Outline",which,base_href);

					/*--------------------------------------------------*\
					 | Link to parseable text
					\*--------------------------------------------------*/

					if (v = find_option (opt,"link_text"))
						if (s = text_of_option (v)) {
							fprintf (fp," - [<a href=\"");
							fprintf (fp,s,b);
							fprintf (fp,"\">Parseable text</a>]");
							}
						else
							write_link_for ("text","Parseable text",which,base_href);

					/*--------------------------------------------------*\
					 | Link to SGML
					\*--------------------------------------------------*/

					if (v = find_option (opt,"link_sgml"))
						if (s = text_of_option (v)) {
							fprintf (fp," - [<a href=\"");
							fprintf (fp,s,b);
							fprintf (fp,"\">SGML</a>]");
							}
						else
							write_link_for ("sgml","SGML",which,base_href);

					/*--------------------------------------------------*\
					 | Link to XML
					\*--------------------------------------------------*/

					if (v = find_option (opt,"link_xml"))
						if (s = text_of_option (v)) {
							fprintf (fp," - [<a href=\"");
							fprintf (fp,s,b);
							fprintf (fp,"\">XML</a>]");
							}
						else
							write_link_for ("xml","XML",which,base_href);

					/*--------------------------------------------------*\
					 | Link to DIF
					\*--------------------------------------------------*/

					if (v = find_option (opt,"link_dif"))
						if (s = text_of_option (v)) {
							fprintf (fp," - [<a href=\"");
							fprintf (fp,s,b);
							fprintf (fp,"\">DIF</a>]");
							}
						else
							write_link_for ("dif","DIF",which,base_href);

					fprintf (fp,"<p>\n");
					}
				done = 1;
				}
			}

	/*------------------------------------------------------------------*\
	 | If there was no "link" element in the config file, create links	|
	 | based on the expected names of the output files.					|
	\*------------------------------------------------------------------*/

	if (!done) {
		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html"))
				if (opt = find_option (opt,"link"))
					if (v = find_option (opt,"label"))
						if (s = text_of_option (v))
							lead_text = s;
		if (lead_text)
			fprintf (fp,"%s",lead_text);
		else
			fprintf (fp,"Metadata also available as");

		if (strcmp (which,"faq") != 0)
			write_link_for ("faq","Questions &amp; Answers",which,base_href);
		if (strcmp (which,"html") != 0)
			write_link_for ("html","Outline",which,base_href);
		write_link_for ("text","Parseable text",which,base_href);
		write_link_for ("sgml","SGML",which,base_href);
		write_link_for ("xml","XML",which,base_href);
		write_link_for ("dif","DIF",which,base_href);
		fprintf (fp,"<p>\n");
		}
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static void write_data_set_part (struct item *q) {
	struct item *r;
	char *s;

	int WData_Set_Part    = key_of ("Data_Set_Part");
	int WPart_Name        = key_of ("Part_Name");
	int WPart_Type        = key_of ("Part_Type");
	int WPart_Description = key_of ("Part_Description");

	if (q->key == WData_Set_Part) {
		fprintf (fp,"<dt>");
		if (r = find_key (q,WPart_Name))
			if (r->parent == q)
				if (s = full_text (r))
					fprintf (fp,"<b>%s</b>\n",munge(s));
		if (r = find_key (q,WPart_Type))
			if (r->parent == q)
				if (s = full_text (r))
					fprintf (fp," (%s)",munge(s));
		fprintf (fp,"\n");
		fprintf (fp,"<dd>\n");
		if (r = find_key (q,WPart_Description))
			if (r->parent == q)
				write_html_value (r);

		for (r=q->child; r; r=r->next)
			if (r->key == WData_Set_Part) break;
		if (r) {
			fprintf (fp,"<p>\n");
			fprintf (fp,"<dl>\n");
			while (r) {
				if (r->key == WData_Set_Part)
					write_data_set_part (r);
				r = r->next;
				}
			fprintf (fp,"</dl>\n");
			fprintf (fp,"</p>\n");
			}
		}
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static int projection[] = {
	WAlbers_Conical_Equal_Area,
	WAzimuthal_Equidistant,
	WEquidistant_Conic,
	WEquirectangular,
	WGeneral_Vertical_Near_sided_Perspective,
	WGnomonic,
	WLambert_Azimuthal_Equal_Area,
	WLambert_Conformal_Conic,
	WMercator,
	WModified_Stereographic_for_Alaska,
	WMiller_Cylindrical,
	WOblique_Mercator,
	WOrthographic,
	WPolar_Stereographic,
	WPolyconic,
	WRobinson,
	WSinusoidal,
	WSpace_Oblique_Mercator_Landsat,
	WStereographic,
	WTransverse_Mercator,
	Wvan_der_Grinten,
	Wnull
	};

void write_html_faq (char *output_file, struct item *first) {
	struct item *p,*q,*r,*u,*w,*h,*hh;
	char *b,*s,*t,*title;
	int title_allocated = 0;
	void *opt,*v;
	time_t T;
	unsigned char bytes[8];		/* for UTF-8 translation */

	int WDescription_of_Geographic_Extent;
	int WData_Set_Structure;
	int WTaxonomy;
	int WAnalytical_Tool;
	int WASCII_File_Structure;
	int WMethodology;

	int distributor,distributor_count,counter;

	if (first == NULL) return;

	/* If encoding is UTF-8, remember this for later */

	if (stricmp (get_character_encoding(),"UTF-8") == 0) {
		utf8_in  = 1;
		utf8_out = 1;
		}

	/*------------------------------------------------------------------*\
	 | Get options from config tree										|
	\*------------------------------------------------------------------*/

	if (opt = find_option (NULL,"output"))
		if (opt = find_option (opt,"html")) {

			/*----------------------------------------------------------*\
			 | "translate off" activates html code in text values.  By	|
			 | Default such html code is rendered literally.			|
			\*----------------------------------------------------------*/

			if (v = find_option (opt,"translate")) {
				do_text_translation = 1;
				if (s = text_of_option (v))
					if (stricmp (s,"off") == 0) do_text_translation = 0;
				}

			/*----------------------------------------------------------*\
			 | "preformat" encloses groups of lines that begin with > 	|
			 | in <pre></pre> tags.  Optional value is the character	|
			 | used to indicate sections that should be preformatted.	|
			 | Default is >.  Turn off with preformat off.				|
			\*----------------------------------------------------------*/

			if (v = find_option (opt,"preformat")) {
				do_preformatting = 1;
				if (s = text_of_option (v))
					if (stricmp (s,"off") == 0) do_preformatting = 0;
					else
						if (*s) preformat_indicator = *s;
				}

			/*----------------------------------------------------------*\
			 | "meta off" disables dublin-core meta elements generation |
			\*----------------------------------------------------------*/

			if (v = find_option (opt,"meta")) {
				do_meta = 1;
				if (s = text_of_option (v))
					if (stricmp (s,"off") == 0) do_meta = 0;
				}

			/*----------------------------------------------------------*\
			 | "encoding" specifies output encoding, typically used for	|
			 | UTF-8.  If not specified and UTF-8 is input, make UTF-8	|
			 | also the output encoding.								|
			\*----------------------------------------------------------*/

			if (v = find_option (opt,"encoding")) {
				if (s = text_of_option (v))
					if (stricmp (s,"utf-8") == 0) utf8_out = 1;
					else utf8_out = 0;
				}
			}

	/* Generally you want to translate characters */

	if (do_text_translation) munge = translated;
	else munge = as_is;

	title = output_file;
	if (p = find_key (first,WCitation))
		if (p = find_key (p,WTitle))
			if (p->child)
				if (p->child->key == Wunknown) {
					int n = 0;
					for (q=p->child; q; q=q->next)
						if (q->d) n += 1 + strlen (q->d);
					if (title = (char *) malloc (1 + n)) {
						char *s = title;
						for (q=p->child; q; q=q->next) {
							if (q->d) {
								strcpy (s,q->d);
								s += strlen (q->d);
								}
							if (q->next) *s++ = ' ';
							*s = 0;
							}
						title_allocated = 1;
						}
					else
						if (p->child->d) title = p->child->d;
						else title = "(untitled)";
					}

	if (fp = fopen (output_file,"w")) {
		fprintf (fp,"%s\n",html_doctype);
		fprintf (fp,"<html>\n");
		fprintf (fp,"<head>\n");

		if (utf8_out)
			fprintf (fp,"  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n");
		else
			fprintf (fp,"  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\"/>\n");

		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html"))
				if (opt = find_option (opt,"base"))
					if (s = text_of_option (opt)) {
						fprintf (fp,"  <base href=\"%s",s);
						if (*(s + strlen(s) - 1) != '/') fprintf (fp,"/");
						fprintf (fp,"%s\">\n",output_file);
						}

		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html"))
				if (opt = find_option (opt,"style_file"))
					if (s = text_of_option (opt)) {
						if (t = read_text (s)) {
							char *tt = t + strlen (t) - 1;
							fprintf (fp,"<!-- begin style from %s -->\n",s);
							for (b=t; *b; b++)
								if (*b == 0x0D) *b = ' ';
							fprintf (fp,"%s",t);
							if (*tt != '\n') fprintf (fp,"\n");
							fprintf (fp,"<!-- end style from %s -->\n",s);
							free (t);
							}
						}

		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html"))
				if (opt = find_option (opt,"stylesheet"))
					do {
						char *style_type = "text/css";
						if (v = find_option (opt,"type"))
							if (s = text_of_option (v)) style_type = s;
						if (v = find_option (opt,"href"))
							if (s = text_of_option (v))
								fprintf (fp,"  <link rel=\"stylesheet\" type=\"%s\" href=\"%s\"/>\n",style_type,s);
						} while (opt = find_next_option (opt,"stylesheet"));

		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html"))
				if (opt = find_option (opt,"script_file"))
					if (s = text_of_option (opt)) {
						if (t = read_text (s)) {
							char *tt = t + strlen (t) - 1;
							fprintf (fp,"<!-- begin script from %s -->\n",s);
							for (b=t; *b; b++)
								if (*b == 0x0D) *b = ' ';
							fprintf (fp,"%s",t);
							if (*tt != '\n') fprintf (fp,"\n");
							fprintf (fp,"<!-- end script from %s -->\n",s);
							free (t);
							}
						}

		fprintf (fp,"  <title>");
		fprintf (fp,htmlentities(title));
		fprintf (fp,"</title>\n");
		if (do_meta) do_meta_tags (first);

		if (s = related_file ("original"))
			fprintf (fp,"  <meta name=\"generated-from\" content=\"%s\"/>\n",s);

		fprintf (fp,"</head>\n");
		fprintf (fp,"<body");
		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html"))
				if (opt = find_option (opt,"body"))
					if (s = text_of_option (opt))
						fprintf (fp," %s",s);
		fprintf (fp,">\n");

		/*--------------------------------------------------------------*\
		 | Output user-specified header if there is one.				|
		\*--------------------------------------------------------------*/

		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html"))
				if (v = find_option (opt,"header")) {
					if (s = text_of_option (v))
						fprintf (fp,"%s\n",s);
					}
				else
					if (v = find_option (opt,"header_file")) {
						if (s = text_of_option (v)) {
							if (t = read_text (s)) {
								char *tt = t + strlen (t) - 1;
								fprintf (fp,"<!-- begin header from %s -->\n",s);
								for (b=t; *b; b++)
									if (*b == 0x0D) *b = ' ';
								fprintf (fp,"%s",t);
								if (*tt != '\n') fprintf (fp,"\n");
								fprintf (fp,"<!-- end header from %s -->\n",s);
								free (t);
								}
							}
						}

		/*--------------------------------------------------------------*\
		\*--------------------------------------------------------------*/

		fprintf (fp,"<h1>");
		fprintf (fp,htmlentities(title));
		fprintf (fp,"</h1>\n");

		if (title_allocated) {
			free (title);
			title = NULL;
			title_allocated = 0;
			}

		/*--------------------------------------------------------------*\
		 | Links to other formats of this metadata record?				|
		\*--------------------------------------------------------------*/

		write_links ("faq");

		/*--------------------------------------------------------------*\
		 | See whether some common extensions are in use
		\*--------------------------------------------------------------*/

		WDescription_of_Geographic_Extent = key_of ("Description_of_Geographic_Extent");
		WData_Set_Structure               = key_of ("Data_Set_Structure");
		WTaxonomy                         = key_of ("Taxonomy");
		WAnalytical_Tool                  = key_of ("Analytical_Tool");
		WASCII_File_Structure             = key_of ("ASCII_File_Structure");
		WMethodology                      = key_of ("Methodology");

		/*--------------------------------------------------------------*\
		 | Build a simple table of contents, using the major questions	|
		 | of the plain-language document.  Start with HREF anchors.	|
		\*--------------------------------------------------------------*/

		fprintf (fp,"<h4>Frequently anticipated questions:</h4>\n");
		fprintf (fp,"<ul>\n");
		fprintf (fp,"  <li><a href=\"#what\">What does this data set describe?</a>\n");
		fprintf (fp,"    <ol>\n");
		fprintf (fp,"      <li><a href=\"#what.1\">How should this data set be cited?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#what.2\">What geographic area does the data set cover?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#what.3\">What does it look like?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#what.4\">Does the data set describe conditions during a particular time period?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#what.5\">What is the general form of this data set?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#what.6\">How does the data set represent geographic features?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#what.7\">How does the data set describe geographic features?</a></li>\n");
		if ((WData_Set_Structure != Wunknown) && find_key (first,WData_Set_Structure))
			fprintf (fp,"      <li><a href=\"#what.8\">What are the components of this data set?</a></li>\n");
		if ((WTaxonomy != Wunknown) && find_key (first,WTaxonomy))
			fprintf (fp,"      <li><a href=\"#what.9\">What biological taxa does this data set concern?</a></li>\n");
		if ((WAnalytical_Tool != Wunknown) && find_key (first,WAnalytical_Tool))
			fprintf (fp,"      <li><a href=\"#what.10\">What special analytical tools are available to help me understand this data set?</a></li>\n");
		fprintf (fp,"    </ol>\n");
		fprintf (fp,"  </li>\n");
		fprintf (fp,"  <li><a href=\"#who\">Who produced the data set?</a>\n");
		fprintf (fp,"    <ol>\n");
		fprintf (fp,"      <li><a href=\"#who.1\">Who are the originators of the data set?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#who.2\">Who also contributed to the data set?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#who.3\">To whom should users address questions about the data?</a></li>\n");
		fprintf (fp,"    </ol>\n");
		fprintf (fp,"  </li>\n");
		fprintf (fp,"  <li><a href=\"#why\">Why was the data set created?</a></li>\n");
		fprintf (fp,"  <li><a href=\"#how\">How was the data set created?</a>\n");
		fprintf (fp,"    <ol>\n");
		if ((WMethodology != Wunknown) && find_key (first,WMethodology))
			fprintf (fp,"      <li><a href=\"#how.0\">What methods were used to collect the data?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#how.1\">From what previous works were the data drawn?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#how.2\">How were the data generated, processed, and modified?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#how.3\">What similar or related data should the user be aware of?</a></li>\n");
		fprintf (fp,"    </ol>\n");
		fprintf (fp,"  </li>\n");
		fprintf (fp,"  <li><a href=\"#quality\">How reliable are the data; what problems remain in the data set?</a>\n");
		fprintf (fp,"    <ol>\n");
		fprintf (fp,"      <li><a href=\"#quality.1\">How well have the observations been checked?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#quality.2\">How accurate are the geographic locations?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#quality.3\">How accurate are the heights or depths?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#quality.4\">Where are the gaps in the data?  What is missing?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#quality.5\">How consistent are the relationships among the data, including topology?</a></li>\n");
		fprintf (fp,"    </ol>\n");
		fprintf (fp,"  </li>\n");
		fprintf (fp,"  <li><a href=\"#getacopy\">How can someone get a copy of the data set?</a>\n");
		fprintf (fp,"    <ol>\n");
		fprintf (fp,"      <li><a href=\"#getacopy.0\">Are there legal restrictions on access or use of the data?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#getacopy.1\">Who distributes the data?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#getacopy.2\">What's the catalog number I need to order this data set?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#getacopy.3\">What legal disclaimers am I supposed to read?</a></li>\n");
		fprintf (fp,"      <li><a href=\"#getacopy.4\">How can I download or order the data?</a></li>\n");
		fprintf (fp,"    </ol>\n");
		fprintf (fp,"  </li>\n");
		fprintf (fp,"  <li><a href=\"#metaref\">Who wrote the metadata?</a></li>\n");
		fprintf (fp,"</ul>\n");

		/*--------------------------------------------------------------*\
		 | Output the metadata, emplacing NAME anchors at the tops of	|
		 | major sections to which the HREF anchors have referred.		|
		\*--------------------------------------------------------------*/

		fprintf (fp,"<hr/>\n");
		fprintf (fp,"<h3><a name=\"what\">What does this data set describe?</a></h3>\n");

		fprintf (fp,"<div style=\"margin-left: 2em\">\n");
		fprintf (fp,"<dl>\n");
		if (p = find_key (first,WCitation))
			if (p = find_key (p,WTitle))
				write_html_item (p);

		if (p = find_key (first,WDescription)) {
			if (q = find_key (p,WAbstract))
				write_html_item (q);
			if (q = find_key (p,WSupplemental_Information))
				write_html_item (q);
				}
		fprintf (fp,"</dl>\n");
		fprintf (fp,"</div>\n");

		fprintf (fp,"<ol>\n");

		/*--------------------------------------------------------------*/

		fprintf (fp,"<li><a name=\"what.1\"><b>How should this data set be cited?</b></a><p>\n");

		if (p = find_key (first,WCitation))
			if (p = find_key (p,WCitation_Information))
				write_citation (p);
		fprintf (fp,"<p>\n");
		fprintf (fp,"</li>\n");	/* what.1 */

		/*--------------------------------------------------------------*/

		fprintf (fp,"<li><a name=\"what.2\"><b>What geographic area does the data set cover?</b></a><p>\n");

		if (p = find_key (first,WSpatial_Domain)) {
			fprintf (fp,"<dl>\n");
			if (q = find_key (p,WBounding_Coordinates))
				for (r=q->child; r; r=r->next)
					write_html_item (r);

			/* Nice extension from NBII; use if present */

			if (WDescription_of_Geographic_Extent != Wunknown)
				if (q = find_key (p,WDescription_of_Geographic_Extent))
					write_html_item (q);
			fprintf (fp,"</dl>\n");
			}
		fprintf (fp,"<p>\n");
		fprintf (fp,"</li>\n");	/* what.2 */

		/*--------------------------------------------------------------*/

		fprintf (fp,"<li><a name=\"what.3\"><b>What does it look like?</b></a><p>\n");

		if (p = find_key (first,WIdentification_Information)) {
			if (q = find_key (p,WBrowse_Graphic)) {
				fprintf (fp,"<dl>\n");
				do {
					fprintf (fp,"<dt>");
					if (r = find_key (q,WBrowse_Graphic_File_Name))
						if (s = full_text (r))
							fprintf (fp,"%s",munge(s));
					if (r = find_key (q,WBrowse_Graphic_File_Type))
						if (s = full_text (r))
							fprintf (fp," (%s)",munge(s));
					fprintf (fp,"\n<dd>\n");
					if (r = find_key (q,WBrowse_Graphic_File_Description))
						if (s = full_text (r))
							write_html_value (r);
					} while (q = find_next_key (q,WBrowse_Graphic));
				fprintf (fp,"</dl>\n");
				}
			}
		fprintf (fp,"<p>\n");
		fprintf (fp,"</li>\n");	/* what.3 */

		/*--------------------------------------------------------------*/

		fprintf (fp,"<li><a name=\"what.4\"><b>Does the data set describe conditions during a particular time period?</b></a><p>\n");

		fprintf (fp,"<dl>\n");
		if (p = find_key (first,WTime_Period_of_Content)) {
			if (q = find_key (p,WSingle_Date_Time)) {
				if (r = find_key (q,WCalendar_Date))
					if (s = full_text (r)) {
						fprintf (fp,"<dt>Calendar_Date: ");
						write_date (s);
						}
				if (r = find_key (q,WTime_of_Day))
					write_html_item (r);
				}
			else
				if (q = find_key (p,WMultiple_Dates_Times)) {
					for (q=q->child; q; q=q->next)
						if (s = full_text (q)) {
							fprintf (fp,"<dt>Calendar_Date: ");
							write_date (s);
							}
					}
				else
					if (q = find_key (p,WRange_of_Dates_Times))
						for (r=q->child; r; r=r->next)
							if (r->key == WBeginning_Date) {
								if (s = full_text (r)) {
									fprintf (fp,"<dt>Beginning_Date: ");
									write_date (s);
									}
								}
							else
								if (r->key == WEnding_Date) {
									if (s = full_text (r)) {
										fprintf (fp,"<dt>Ending_Date: ");
										write_date (s);
										}
									}
								else
									write_html_item (r);
			if (q = find_key (p,WCurrentness_Reference))
				write_html_item (q);
			}
		fprintf (fp,"</dl>\n");
		fprintf (fp,"<p>\n");
		fprintf (fp,"</li>\n");	/* what.4 */

		/*--------------------------------------------------------------*/

		fprintf (fp,"<li><a name=\"what.5\"><b>What is the general form of this data set?</b></a><p>\n");

		fprintf (fp,"<dl>\n");
		if (p = find_key (first,WCitation))
			if (p = find_key (p,WGeospatial_Data_Presentation_Form))
				write_html_item (p);
		fprintf (fp,"</dl>\n");
		fprintf (fp,"<p>\n");
		fprintf (fp,"</li>\n");	/* what.5 */

		/*--------------------------------------------------------------*/

		fprintf (fp,"<li><a name=\"what.6\"><b>How does the data set represent geographic features?</b></a><p>\n");
		fprintf (fp,"<ol type=\"a\">\n");

		fprintf (fp,"<li><a name=\"what.6.a\"><b>How are geographic features stored in the data set?</b></a><p>\n");

		if (p = find_key (first,WSpatial_Data_Organization_Information)) {

			/*----------------------------------------------------------*\
			 | If there is an Indirect_Spatial_Reference, output it.	|
			\*----------------------------------------------------------*/

			if (q = find_key (p,WIndirect_Spatial_Reference)) {
				fprintf (fp,"<dl>\n");
				write_html_item (q);
				fprintf (fp,"</dl>\n");
				}

			/*----------------------------------------------------------*\
			 | If there is a Direct_Spatial_Reference_Method, show it.	|
			\*----------------------------------------------------------*/

			if (q = find_key (p,WDirect_Spatial_Reference_Method))
				if (s = full_text (q))
					fprintf (fp,"This is a %s data set.\n",munge(s));

			/*----------------------------------------------------------*\
			 | Point_and_Vector_Object_Information						|
			\*----------------------------------------------------------*/

			/*----------------------------------------------------------*\
			 | For each SDTS_Terms_Description, list the types, and if	|
			 | a count follows the type, show the count in parentheses	|
			 | after the type.  Put the types in an unordered list.		|
			\*----------------------------------------------------------*/

			if (q = find_key (p,WPoint_and_Vector_Object_Information))
				if (q = find_key (q,WSDTS_Terms_Description)) {
					fprintf (fp,"It contains the following vector data types (SDTS terminology):\n");
					fprintf (fp,"<ul>\n");
					do {
						for (r=q->child; r; r=r->next)
							if (r->key == WSDTS_Point_and_Vector_Object_Type) {
								if (r->child)
									fprintf (fp,"<li>%s",munge(full_text(r)));
									if (r->next)
										if (r->next->key == WPoint_and_Vector_Object_Count)
											if (s = full_text (r->next))
												fprintf (fp," (%s)",munge(s));
									fprintf (fp,"\n");
								}
						} while (q = find_next_key (q,WSDTS_Terms_Description));
					fprintf (fp,"</ul>\n");
					}

			/*----------------------------------------------------------*\
			 | For each VPF_Point_and_Vector_Object_Information list	|
			 | the types, and if a count follows the type, show the		|
			 | count in parentheses after the type.  Put the types in	|
			 | an unordered list.										|
			\*----------------------------------------------------------*/

			if (q = find_key (p,WPoint_and_Vector_Object_Information))
				if (q = find_key (q,WVPF_Terms_Description)) {
					if (r = find_key (q,WVPF_Topology_Level))
						if (s = full_text(r)) fprintf (fp,"VPF Topology level %s<p>\n",munge(s));
					fprintf (fp,"It contains the following vector data types (VPF terminology):\n");
					fprintf (fp,"<ul>\n");
					for (r=q->child; r; r=r->next)
						if (r->key == WVPF_Point_and_Vector_Object_Information) {
							if (u = find_key (r,WVPF_Point_and_Vector_Object_Type))
								if (s = full_text(u)) fprintf (fp,"<li>%s",munge(s));
							if (u = find_key (r,WPoint_and_Vector_Object_Count))
								if (s = full_text(u)) fprintf (fp," (%s)",munge(s));
							fprintf (fp,"\n");
							}
					fprintf (fp,"</ul>\n");
					}

			/*----------------------------------------------------------*\
			 | Raster_Object_Information								|
			\*----------------------------------------------------------*/

			if (q = find_key (p,WRaster_Object_Information)) {
				fprintf (fp,"It contains the following raster data types:\n");
				fprintf (fp,"<ul>\n");
				do {
					fprintf (fp,"<li> Dimensions");
					if (r = find_key (q,WRow_Count))
						if (s = full_text(r)) fprintf (fp," %s",munge(s));
					if (r = find_key (q,WColumn_Count))
						if (s = full_text(r)) fprintf (fp," x %s",munge(s));
					if (r = find_key (q,WVertical_Count))
						if (s = full_text(r)) fprintf (fp," x %s",munge(s));
					if (r = find_key (q,WRaster_Object_Type))
						if (s = full_text(r)) fprintf (fp,", type %s",munge(s));
					fprintf (fp,"\n");
					} while (q = find_next_key (q,WRaster_Object_Information));
				fprintf (fp,"</ul>\n");
				}

			}
		fprintf (fp,"<p>\n");
		fprintf (fp,"</li>\n");	/* what.6a */

		/*--------------------------------------------------------------*/

		fprintf (fp,"<li><a name=\"what.6.b\"><b>What coordinate system is used to represent geographic features?</b></a><p>\n");

		if (p = find_key (first,WSpatial_Reference_Information)) {

			/*----------------------------------------------------------*\
			 | Horizontal
			\*----------------------------------------------------------*/

			if (q = find_key (p,WHorizontal_Coordinate_System_Definition)) {
				if (r = find_key (q,WGeographic)) {
					fprintf (fp,"Horizontal positions are specified in geographic coordinates, that is, latitude and longitude.\n");
					if (u = find_key (r,WLatitude_Resolution))
						if (s = full_text (u))
							fprintf (fp,"Latitudes are given to the nearest %s.\n",munge(s));
					if (u = find_key (r,WLongitude_Resolution))
						if (s = full_text (u))
							fprintf (fp,"Longitudes are given to the nearest %s.\n",munge(s));
					if (u = find_key (r,WGeographic_Coordinate_Units))
						if (s = full_text (u))
							fprintf (fp,"Latitude and longitude values are specified in %s.\n",munge(s));
					fprintf (fp,"<p>\n");
					}
				else
					if (r = find_key (q,WPlanar)) {

						/*----------------------------------------------*\
						 | Map_Projection
						\*----------------------------------------------*/

						if (u = find_key (r,WMap_Projection)) {
							if (w = find_key (r,WMap_Projection_Name)) {
								if (s = full_text (w))
									fprintf (fp,"The map projection used is %s.<p>\n",munge(s));

								if (w->next) w=w->next;
								else if (w->prev) w=w->prev;
								}
							else {
								int i;
								s = NULL;
								for (i=0; projection[i] != Wnull; i++)
									if (w = find_key (u,projection[i])) {
										s = text_of (w->key);
										break;
										}
								if (s)
									fprintf (fp,"The map projection used is %s.<p>\n",munge(s));
								}

							if (!w) w = find_key (r,WMap_Projection_Parameters);

							if (w) {
								fprintf (fp,"<dl>\n");
								fprintf (fp,"<dt>Projection parameters:\n");
								fprintf (fp,"<dd>\n");
								fprintf (fp,"<dl>\n");
								for (h=w->child; h; h=h->next)
									write_html_item (h);
								fprintf (fp,"</dl>\n");
								fprintf (fp,"</dl>\n");
								}
							fprintf (fp,"<p>\n");
							}

						/*----------------------------------------------*\
						 | Grid_Coordinate_System
						\*----------------------------------------------*/

						if (u = find_key (r,WGrid_Coordinate_System)) {
							fprintf (fp,"<dl>\n");
							for (w=u->child; w; w=w->next)
								write_html_item (w);
							fprintf (fp,"</dl>\n");
							fprintf (fp,"<p>\n");
							}

						/*----------------------------------------------*\
						 | Local_Planar
						\*----------------------------------------------*/

						if (u = find_key (r,WLocal_Planar)) {
							fprintf (fp,"Horizontal coordinates are specified using a local planar system.<br/>\n");
							if (w = find_key (u,WLocal_Planar_Description))
								if (s = full_text (w))
									fprintf (fp,"%s<p>\n",munge(s));
							if (w = find_key (u,WLocal_Planar_Georeference_Information))
								if (s = full_text (w))
									fprintf (fp,"%s<p>\n",munge(s));
							}

						/*----------------------------------------------*\
						 | Planar_Coordinate_Information
						\*----------------------------------------------*/

						if (u = find_key (r,WPlanar_Coordinate_Information)) {
							if (v = find_key (u,WPlanar_Coordinate_Encoding_Method))
								if (s = full_text (v))
									fprintf (fp,"Planar coordinates are encoded using %s<br/>\n",munge(s));
							if (v = find_key (u,WCoordinate_Representation)) {
								if (w = find_key (v,WAbscissa_Resolution))
									if (s = full_text (w))
										fprintf (fp,"Abscissae (x-coordinates) are specified to the nearest %s<br/>\n",munge(s));
								if (w = find_key (v,WOrdinate_Resolution))
									if (s = full_text (w))
										fprintf (fp,"Ordinates (y-coordinates) are specified to the nearest %s<br/>\n",munge(s));
								}
							else
								if (v = find_key (u,WDistance_and_Bearing_Representation)) {
									fprintf (fp,"Planar coordinates are specified using distance and bearing values.\n");
									if (w = find_key (v,WDistance_Resolution))
										if (s = full_text (w))
											fprintf (fp,"Resolution of distance values: %s<br/>\n",munge(s));
									if (w = find_key (v,WBearing_Resolution))
										if (s = full_text (w))
											fprintf (fp,"Resolution of bearing values: %s<br/>\n",munge(s));
									if (w = find_key (v,WBearing_Units))
										if (s = full_text (w))
											fprintf (fp,"Bearing is specified in units of %s<br/>\n",munge(s));
									if (w = find_key (v,WBearing_Reference_Direction))
										if (s = full_text (w))
											fprintf (fp,"Bearing is measured %s<br/>\n",munge(s));
									if (w = find_key (v,WBearing_Reference_Meridian))
										if (s = full_text (w))
											fprintf (fp,"Bearing is measured from the %s meridian<br/>\n",munge(s));
									}
							if (v = find_key (u,WPlanar_Distance_Units))
								if (s = full_text (v))
									fprintf (fp,"Planar coordinates are specified in %s<p>\n",munge(s));
							}
						fprintf (fp,"<p>\n");
						}
					else
						if (r = find_key (q,WLocal)) {
							if (u = find_key (r,WLocal_Description))
								if (s = full_text (u))
									fprintf (fp,"This local coordinate system was used: %s<p>\n",munge(s));
							if (u = find_key (r,WLocal_Georeference_Information))
								if (s = full_text (u))
									fprintf (fp,"%s",munge(s));
							fprintf (fp,"<p>\n");
							}
						else {
							}
				if (r = find_key (q,WGeodetic_Model)) {
					if (u = find_key (r,WHorizontal_Datum_Name))
						if (s = full_text (u))
							fprintf (fp,"The horizontal datum used is %s.<br/>\n",munge(s));
					if (u = find_key (r,WEllipsoid_Name))
						if (s = full_text (u))
							fprintf (fp,"The ellipsoid used is %s.<br/>\n",munge(s));
					if (u = find_key (r,WSemi_major_Axis))
						if (s = full_text (u))
							fprintf (fp,"The semi-major axis of the ellipsoid used is %s.<br/>\n",munge(s));
					if (u = find_key (r,WDenominator_of_Flattening_Ratio))
						if (s = full_text (u))
							fprintf (fp,"The flattening of the ellipsoid used is 1/%s.<br/>\n",munge(s));
					fprintf (fp,"<p>\n");
					}
				}

			/*----------------------------------------------------------*\
			 | Vertical
			\*----------------------------------------------------------*/

			if (q = find_key (p,WVertical_Coordinate_System_Definition)) {
				fprintf (fp,"<dl>\n");
				write_html_item (q);
				fprintf (fp,"</dl>\n");
				}
			}

		fprintf (fp,"</li>\n");	/* what.6b */
		fprintf (fp,"</ol>\n");
		fprintf (fp,"<p>\n");
		fprintf (fp,"</li>\n");	/* what.6 */

		/*--------------------------------------------------------------*/

		fprintf (fp,"<li><a name=\"what.7\"><b>How does the data set describe geographic features?</b></a><p>\n");

		if (p = find_key (first,WEntity_and_Attribute_Information)) {
			fprintf (fp,"<dl>\n");
			for (q=p->child; q; q=q->next)
				switch (q->key) {
					case WDetailed_Description:
						if (r = find_key (q,WEntity_Type)) {
							fprintf (fp,"<dt>");
							if (u = find_key (r,WEntity_Type_Label))
								if (s = full_text (u))
									fprintf (fp,"<b><tt>%s</tt></b>",munge(s));
							fprintf (fp,"<dd>");
							if (u = find_key (r,WEntity_Type_Definition))
								write_html_value (u);
							if (u = find_key (r,WEntity_Type_Definition_Source))
								if (s = full_text (u))
									fprintf (fp,"  (Source: %s)\n",munge(s));
							fprintf (fp,"<p>\n");

							fprintf (fp,"<dl>\n");
							for (u=q->child; u; u=u->next)
								if (u->key == WAttribute) {
									fprintf (fp,"<dt>");
									if (w = find_key (u,WAttribute_Label))
										if (s = full_text (w))
											fprintf (fp,"<b><tt>%s</tt></b>",munge(s));

									fprintf (fp,"<dd>");
									if (w = find_key (u,WAttribute_Definition))
										write_html_value (w);
									if (w = find_key (u,WAttribute_Definition_Source))
										if (s = full_text (w))
											fprintf (fp,"  (Source: %s)",munge(s));
									fprintf (fp,"<p></p>\n");

									if (w = find_key (u,WAttribute_Measurement_Frequency))
										if (s = full_text (w))
											fprintf (fp,"<em>Frequency of measurement:</em> %s\n",munge(s));
									fprintf (fp,"<p>\n");

									/*----------------------------------*\
									 | Tricky issues ahead!  Find all	|
									 | the Enumerated_Domain elements	|
									 | and put them in a single table.	|
									\*----------------------------------*/

									if (w = find_key (u,WAttribute_Domain_Values)) {
										int count = 0;
										do {
											if (h = find_key (w,WEnumerated_Domain))
												do {
													count++;
													} while (h = find_next_key (h,WEnumerated_Domain));
											} while (w = find_next_key (w,WAttribute_Domain_Values));
										if (count > 0) {
											fprintf (fp,"<table border=\"1\" cellpadding=\"4\" cellspacing=\"0\">\n");
											fprintf (fp,"<tr><th>Value</th><th>Definition</th></tr>\n");
											w = find_key (u,WAttribute_Domain_Values);
											do {
												if (h = find_key (w,WEnumerated_Domain))
													do {
														fprintf (fp,"<tr>");
														fprintf (fp,"<td valign=\"top\">");
														if (hh = find_key (h,WEnumerated_Domain_Value))
															if (s = full_text (hh))
																fprintf (fp,"%s",munge(s));
														fprintf (fp,"</td>");
														fprintf (fp,"<td>");
														if (hh = find_key (h,WEnumerated_Domain_Value_Definition))
															if (s = full_text (hh))
																if (*s) write_html_value (hh);
																else fprintf (fp,"&nbsp;");
															else fprintf (fp,"&nbsp;");
														else fprintf (fp,"&nbsp;");
														fprintf (fp,"</td>");
														fprintf (fp,"</tr>\n");
														} while (h = find_next_key (h,WEnumerated_Domain));
												} while (w = find_next_key (w,WAttribute_Domain_Values));
											fprintf (fp,"</table>\n");
											fprintf (fp,"<p>\n");
											}
										}

									/*----------------------------------*\
									 | Now find and show in tables all	|
									 | of the Range_, Codeset_, and		|
									 | Unrepresentable_ Domains.		|
									\*----------------------------------*/


									if (w = find_key (u,WAttribute_Domain_Values)) {
										do {
											if (h = find_key (w,WRange_Domain)) {
												fprintf (fp,"<table border=\"1\" cellpadding=\"4\" cellspacing=\"0\">\n");
												fprintf (fp,"<tr><th colspan=\"2\">Range of values</th></tr>\n");
												if (hh = find_key (h,WRange_Domain_Minimum))
													if (s = full_text (hh))
														fprintf (fp,"<tr><th>Minimum:</th><td>%s</td></tr>\n",munge(s));
												if (hh = find_key (h,WRange_Domain_Maximum))
													if (s = full_text (hh))
														fprintf (fp,"<tr><th>Maximum:</th><td>%s</td></tr>\n",munge(s));
												if (hh = find_key (h,WAttribute_Units_of_Measure))
													if (s = full_text (hh))
														fprintf (fp,"<tr><th>Units:</th><td>%s</td></tr>\n",munge(s));
												if (hh = find_key (h,WAttribute_Measurement_Resolution))
													if (s = full_text (hh))
														fprintf (fp,"<tr><th>Resolution:</th><td>%s</td></tr>\n",munge(s));
												fprintf (fp,"</table>\n");
												fprintf (fp,"<p>\n");
												}

											if (h = find_key (w,WCodeset_Domain)) {
												fprintf (fp,"<table border=\"1\" cellpadding=\"4\" cellspacing=\"0\">\n");
												fprintf (fp,"<tr><th colspan=\"2\">Formal codeset</th></tr>\n");
												if (hh = find_key (h,WCodeset_Name))
													if (s = full_text (hh))
														fprintf (fp,"<tr><th>Codeset Name:</th><td>%s</td></tr>\n",munge(s));
												if (hh = find_key (h,WCodeset_Source))
													if (s = full_text (hh))
														fprintf (fp,"<tr><th>Codeset Source:</th><td>%s</td></tr>\n",munge(s));
												fprintf (fp,"</table>\n");
												fprintf (fp,"<p>\n");
												}

											if (h = find_key (w,WUnrepresentable_Domain)) {
												if (s = full_text (h))
													if (*s) {
														fprintf (fp,"<em>\n");
														write_html_value (h);
														fprintf (fp,"</em>\n");
														}
												fprintf (fp,"<p>\n");
												}
											} while (w = find_next_key (w,WAttribute_Domain_Values));
										}
									}

							fprintf (fp,"</dl>\n");
							fprintf (fp,"<p>\n");
							}
						break;

					case WOverview_Description:
						for (r=q->child; r; r=r->next)
							write_html_item (r);
						break;

					default:
						break;
					}
			fprintf (fp,"</dl>\n");
			fprintf (fp,"<p>\n");
			}
		fprintf (fp,"</li>\n");	/* what.7 */

		/*--------------------------------------------------------------*/

		if (WData_Set_Structure != Wunknown)
			if (p = find_key (first,WData_Set_Structure)) {
				int WData_Set_Part = key_of ("Data_Set_Part");

				fprintf (fp,"<li><a name=\"what.8\"><b>What are the components of this data set?</b></a><p>\n");
				fprintf (fp,"<dl>\n");
				for (q=p->child; q; q=q->next)
					if (q->key == WData_Set_Part)
						write_data_set_part (q);
				fprintf (fp,"</dl>\n");
				fprintf (fp,"</li>\n");	/* what.8 */
				}

		/*--------------------------------------------------------------*/

		if (WTaxonomy != Wunknown)
			if (p = find_key (first,WTaxonomy)) {
				fprintf (fp,"<li><a name=\"what.9\"><b>What biological taxa does this data set concern?</b></a><p>\n");
				fprintf (fp,"<dl>\n");
				write_html_item (p);
				fprintf (fp,"</dl>\n");
				fprintf (fp,"</li>\n");	/* what.9 */
				}

		/*--------------------------------------------------------------*/

		if (WAnalytical_Tool != Wunknown)
			if (p = find_key (first,WAnalytical_Tool)) {
				int WAnalytical_Tool_Description        = key_of ("Analytical_Tool_Description");
				int WTool_Access_Information            = key_of ("Tool_Access_Information");
				int WTool_Network_Resource_Name         = key_of ("Tool_Network_Resource_Name");
				int WTool_Access_Instructions           = key_of ("Tool_Access_Instructions");
				int WTool_Computer_and_Operating_System = key_of ("Tool_Computer_and_Operating_System");
				int WTool_Contact                       = key_of ("Tool_Contact");
				int WTool_Citation                      = key_of ("Tool_Citation");
				int i,count,j;

				fprintf (fp,"<li><a name=\"what.10\"><b>What special analytical tools are available to help me understand this data set?</b></a><p>\n");

				/* How many Analytical_Tool elements are here? */

				count = 0;
				for (q=p; q; q = find_next_key (q,WAnalytical_Tool)) count++;

				/* Write about each one in turn */

				i = 0;
				fprintf (fp,"<dl>\n");
				do {
					fprintf (fp,"<dt>Tool %d of %d\n",i,count);
					fprintf (fp,"<dd>\n");

					if (q = find_key (p,WAnalytical_Tool_Description))
						write_html_value (q);
					fprintf (fp,"<p>\n");

					if (q = find_key (p,WTool_Access_Information)) {
						fprintf (fp,"How to obtain this tool:<p>\n");
						if (r = find_key (q,WTool_Network_Resource_Name)) {
							j = 0;
							fprintf (fp,"<ul>\n>");
							do {
								if (s = full_text (r)) {
									fprintf (fp,"<li>%s\n",munge(s));
									j++;
									}
								} while (r = find_next_key (r,WTool_Network_Resource_Name));
							fprintf (fp,"</ul>\n>");
							if (j > 0) fprintf (fp,"<p>\n");
							}

						if (r = find_key (q,WTool_Access_Instructions)) {
							fprintf (fp,"<dl>\n");
							fprintf (fp,"<dt>How to access the tool:<p>\n");
							fprintf (fp,"<dd>\n");
							write_html_item (r);
							fprintf (fp,"</dl>\n");
							fprintf (fp,"<p>\n");
							}

						if (r = find_key (q,WTool_Computer_and_Operating_System)) {
							fprintf (fp,"<dl>\n");
							fprintf (fp,"<dt>Computer and operating system needed:<p>\n");
							fprintf (fp,"<dd>\n");
							write_html_item (r);
							fprintf (fp,"</dl>\n");
							}
						}

					if (q = find_key (p,WTool_Contact)) {
						fprintf (fp,"Whom to contact for more information:<p>\n");
						write_contact (q);
						}

					if (q = find_key (p,WTool_Citation)) {
						fprintf (fp,"References:<p>\n");
						write_citation (q);
						}

					} while (p = find_next_key (p,WAnalytical_Tool));
				fprintf (fp,"<dd>\n");
				fprintf (fp,"</li>\n");	/* what.10 */
				}

		fprintf (fp,"</ol>\n");
		fprintf (fp,"<p>\n");

		/*--------------------------------------------------------------*\
		\*--------------------------------------------------------------*/

		fprintf (fp,"<hr/>\n");
		fprintf (fp,"<h3><a name=\"who\">Who produced the data set?</a></h3>\n");

		fprintf (fp,"<ol>\n");	/* who */

		/*--------------------------------------------------------------*/

		fprintf (fp,"<li><a name=\"who.1\"><b>Who are the originators of the data set?</b></a> (may include formal authors, digital compilers, and editors)<p>\n");

		if (p = find_key (first,WCitation))
			if (q = find_key (p,WOriginator)) {
				fprintf (fp,"<ul>\n");
				do {
					if (s = full_text (q)) fprintf (fp,"<li>%s\n",munge(s));
					} while (q = find_next_key (q,WOriginator));
				fprintf (fp,"</ul>\n");
				}
		fprintf (fp,"<p>\n");
		fprintf (fp,"</li>\n");	/* who.1 */

		/*--------------------------------------------------------------*/

		fprintf (fp,"<li><a name=\"who.2\"><b>Who also contributed to the data set?</b></a><p>\n");

		if (p = find_key (first,WData_Set_Credit)) {
			fprintf (fp,"<blockquote>\n");
			write_html_value (p);
			fprintf (fp,"</blockquote>\n");
			}
		fprintf (fp,"<p>\n");
		fprintf (fp,"</li>\n");	/* who.2 */

		/*--------------------------------------------------------------*/

		fprintf (fp,"<li><a name=\"who.3\"><b>To whom should users address questions about the data?</b></a><p>\n");

		if (p = find_key (first,WPoint_of_Contact))
			write_contact (p);

		fprintf (fp,"<p>\n");
		fprintf (fp,"</li>\n");	/* who.3 */

		/*--------------------------------------------------------------*/

		fprintf (fp,"</ol>\n");	/* who */

		/*--------------------------------------------------------------*\
		\*--------------------------------------------------------------*/

		fprintf (fp,"<hr/>\n");
		fprintf (fp,"<h3><a name=\"why\">Why was the data set created?</a></h3>\n");


		if (p = find_key (first,WPurpose)) {
			fprintf (fp,"<blockquote>\n");
			write_html_value (p);
			fprintf (fp,"</blockquote>\n");
			}

		fprintf (fp,"<p>\n");

		/*--------------------------------------------------------------*\
		\*--------------------------------------------------------------*/

		fprintf (fp,"<hr/>\n");
		fprintf (fp,"<h3><a name=\"how\">How was the data set created?</a></h3>\n");

		fprintf (fp,"<ol>\n");	/* how */

		if ((WMethodology != Wunknown) && find_key (first,WMethodology)) {
			int WMethodology_Type              = key_of ("Methodology_Type");
			int WMethodology_Identifier        = key_of ("Methodology_Identifier");
			int WMethodology_Description       = key_of ("Methodology_Description");
			int WMethodology_Citation          = key_of ("Methodology_Citation");
			int WMethodology_Keyword           = key_of ("Methodology_Keyword");
			int WMethodology_Keyword_Thesaurus = key_of ("Methodology_Keyword_Thesaurus");
			int i,count;

			fprintf (fp,"<li><a name=\"how.0\"><b>What methods were used to collect the data?</b></a><p>\n");

			if (p = find_key (first,WData_Quality_Information))
				if (p = find_key (p,WLineage))
					if (p = find_key (p,WMethodology)) {
						count = 0;
						for (q=p; q; q = find_next_key (q,WMethodology)) count++;

						i = 0;
						fprintf (fp,"<dl>\n");
						do {
							fprintf (fp,"<dt>Method %d of %d\n",i,count);
							fprintf (fp,"<dd>\n");
							if (q = find_key (p,WMethodology_Type))
								if (s = full_text (q))
									fprintf (fp,"Type: %s<p>\n",munge(s));

							if (q = find_key (p,WMethodology_Description)) {
								write_html_value (q);
								fprintf (fp,"<p>\n");
								}

							if (q = find_key (p,WMethodology_Citation)) {
								fprintf (fp,"Reference:<p>\n");
								write_citation (q);
								fprintf (fp,"<p>\n");
								}
							} while (p = find_next_key (p,WMethodology));
						}

			}

		fprintf (fp,"<li><a name=\"how.1\"><b>From what previous works were the data drawn?</b></a><p>\n");

		if (p = find_key (first,WLineage)) {
			int count = 0;
			if (q = find_key (p,WSource_Information)) {
				count++;
				while (q = find_next_key (q,WSource_Information)) count++;
				}

			if (q = find_key (p,WSource_Information)) {
				int k = 0;
				fprintf (fp,"<dl>\n");
				do {
					k++;
					fprintf (fp,"<dt>");
					if (r = find_key (q,WSource_Citation_Abbreviation))
						if (s = full_text (r))
							fprintf (fp,"<b>%s</b>",munge(s));
					fprintf (fp," (source %d of %d)\n",k,count);
					fprintf (fp,"<dd>\n");
					if (r = find_key (q,WSource_Citation))
						if (r = find_key (r,WCitation_Information))
							write_citation (r);
					fprintf (fp,"<dl>\n");
					if (r = find_key (q,WType_of_Source_Media))
						write_html_item (r);
					if (r = find_key (q,WSource_Scale_Denominator))
						write_html_item (r);
					if (r = find_key (q,WSource_Contribution))
						write_html_item (r);
					fprintf (fp,"</dl>\n");
					fprintf (fp,"<p>\n");
					} while (q = find_next_key (q,WSource_Information));
				fprintf (fp,"</dl>\n");
				}
			}
		fprintf (fp,"</li>\n");	/* how.1 */

		fprintf (fp,"<li><a name=\"how.2\"><b>How were the data generated, processed, and modified?</b></a><p>\n");

		if (p = find_key (first,WLineage)) {
			int count = 0;
			if (q = find_key (p,WProcess_Step)) {
				count++;
				while (q = find_next_key (q,WProcess_Step)) count++;
				}

			if (q = find_key (p,WProcess_Step)) {
				int k = 0;
				fprintf (fp,"<dl>\n");
				do {
					k++;
					fprintf (fp,"<dt>");
					if (r = find_key (q,WProcess_Date))
						if (s = full_text (r)) {
							fprintf (fp,"Date: ");
							write_date (s);
							}
					fprintf (fp," (process %d of %d)\n",k,count);
					fprintf (fp,"<dd>\n");

					if (r = find_key (q,WProcess_Description))
						write_html_value (r);
					fprintf (fp,"<p>\n");

					if (r = find_key (q,WProcess_Contact)) {
						fprintf (fp,"Person who carried out this activity:<br/>\n");
						write_contact (r);
						}

					if (r = find_key (q,WSource_Used_Citation_Abbreviation)) {
						fprintf (fp,"Data sources used in this process:\n");
						fprintf (fp,"<ul>\n");
						do {
							if (s = full_text (r))
								fprintf (fp,"<li>%s\n",munge(s));
							} while (r = find_next_key (r,WSource_Used_Citation_Abbreviation));
						fprintf (fp,"</ul>\n");
						fprintf (fp,"<p>\n");
						}

					if (r = find_key (q,WSource_Produced_Citation_Abbreviation)) {
						fprintf (fp,"Data sources produced in this process:\n");
						fprintf (fp,"<ul>\n");
						do {
							if (s = full_text (r))
								fprintf (fp,"<li>%s\n",munge(s));
							} while (r = find_next_key (r,WSource_Produced_Citation_Abbreviation));
						fprintf (fp,"</ul>\n");
						fprintf (fp,"<p>\n");
						}

					} while (q = find_next_key (q,WProcess_Step));
				fprintf (fp,"</dl>\n");
				}
			}
		fprintf (fp,"</li>\n");	/* how.2 */

		fprintf (fp,"<li><a name=\"how.3\"><b>What similar or related data should the user be aware of?</b></a><p>\n");

		if (p = find_key (first,WIdentification_Information))
			if (p = find_key (p,WCross_Reference))
				do {
					if (q = find_key (p,WCitation_Information))
						write_citation (q);
					} while (p = find_next_key (p,WCross_Reference));

		fprintf (fp,"</li>\n");	/* how.3 */


		fprintf (fp,"</ol>\n");	/* how */
		fprintf (fp,"<p>\n");

		/*--------------------------------------------------------------*\
		\*--------------------------------------------------------------*/

		fprintf (fp,"<hr/>\n");
		fprintf (fp,"<h3><a name=\"quality\">How reliable are the data; what problems remain in the data set?</a></h3>\n");

		if (p = find_key (first,WData_Quality_Information)) {

			fprintf (fp,"<ol>\n");	/* quality */

			fprintf (fp,"<li><a name=\"quality.1\"><b>How well have the observations been checked?</b></a><p>\n");
			if (q = find_key (p,WAttribute_Accuracy_Report))
				write_html_value (q);
			fprintf (fp,"<p>\n");
			fprintf (fp,"</li>\n");	/* quality.1 */

			fprintf (fp,"<li><a name=\"quality.2\"><b>How accurate are the geographic locations?</b></a><p>\n");
			if (q = find_key (p,WHorizontal_Positional_Accuracy_Report))
				write_html_value (q);
			fprintf (fp,"<p>\n");
			fprintf (fp,"</li>\n");	/* quality.2 */

			fprintf (fp,"<li><a name=\"quality.3\"><b>How accurate are the heights or depths?</b></a><p>\n");
			if (q = find_key (p,WVertical_Positional_Accuracy_Report))
				write_html_value (q);
			fprintf (fp,"<p>\n");
			fprintf (fp,"</li>\n");	/* quality.3 */

			fprintf (fp,"<li><a name=\"quality.4\"><b>Where are the gaps in the data?  What is missing?</b></a><p>\n");
			if (q = find_key (p,WCompleteness_Report))
				write_html_value (q);
			fprintf (fp,"<p>\n");
			fprintf (fp,"</li>\n");	/* quality.4 */

			fprintf (fp,"<li><a name=\"quality.5\"><b>How consistent are the relationships among the observations, including topology?</b></a><p>\n");
			if (q = find_key (p,WLogical_Consistency_Report))
				write_html_value (q);
			fprintf (fp,"</li>\n");	/* quality.5 */

			fprintf (fp,"</ol>\n");	/* quality */
			}

		fprintf (fp,"<p>\n");

		/*--------------------------------------------------------------*\
		\*--------------------------------------------------------------*/

		fprintf (fp,"<hr/>\n");
		fprintf (fp,"<h3><a name=\"getacopy\">How can someone get a copy of the data set?</a></h3>\n");

		fprintf (fp,"<blockquote>\n");
		fprintf (fp,"<a name=\"getacopy.0\"><b>Are there legal restrictions on access or use of the data?</b></a><p>\n");

		fprintf (fp,"<blockquote>\n");
		if (p = find_key (first,WIdentification_Information)) {
			fprintf (fp,"<dl>\n");
			if (q = find_key (p,WAccess_Constraints))
				write_html_item (q);
			if (q = find_key (p,WUse_Constraints))
				write_html_item (q);
			fprintf (fp,"</dl>\n");
			}
		fprintf (fp,"</blockquote>\n");
		fprintf (fp,"</blockquote>\n");
		fprintf (fp,"<p>\n");

		/* How many distributors are there? */

		distributor = 0;
		distributor_count = 0;
		if (p = find_key (first,WDistribution_Information))
			do {
				if (q = find_key (p,WDistributor)) distributor_count++;
				} while (p = find_next_key (p,WDistribution_Information));

		counter = 0;
		if (p = find_key (first,WDistribution_Information))
			do {
				fprintf (fp,"<ol>\n");	/* getacopy */

				if (counter == 0)
					fprintf (fp,"<li><a name=\"getacopy.1\"><b>Who distributes the data set?</b></a>");
				else
					fprintf (fp,"<li><a name=\"getacopy.1.%d\"><b>Who distributes the data set?</b></a>",counter);
				if (q = find_key (p,WDistributor)) {
					distributor++;
					fprintf (fp," (Distributor %d of %d)<p>\n",distributor,distributor_count);
					write_contact (q);
					}
				else
					fprintf (fp,"[Distributor contact information not provided.]<p>\n");
				fprintf (fp,"</li>\n");	/* getacopy.1 */

				if (counter == 0)
					fprintf (fp,"<li><a name=\"getacopy.2\"><b>What's the catalog number I need to order this data set?</b></a><p>\n");
				else
					fprintf (fp,"<li><a name=\"getacopy.2.%d\"><b>What's the catalog number I need to order this data set?</b></a><p>\n",counter);

				if (q = find_key (p,WResource_Description))
					if (s = full_text (q)) {
						fprintf (fp,"<tt>%s</tt>\n",munge(s));
						fprintf (fp,"<p>\n");
						}
				fprintf (fp,"</li>\n");	/* getacopy.2 */

				if (counter == 0)
					fprintf (fp,"<li><a name=\"getacopy.3\"><b>What legal disclaimers am I supposed to read?</b></a><p>\n");
				else
					fprintf (fp,"<li><a name=\"getacopy.3.%d\"><b>What legal disclaimers am I supposed to read?</b></a><p>\n",counter);

				if (q = find_key (p,WDistribution_Liability)) {
					fprintf (fp,"<blockquote>\n");
					write_html_value (q);
					fprintf (fp,"</blockquote>\n");
					fprintf (fp,"<p>\n");
					}
				fprintf (fp,"</li>\n");	/* getacopy.3 */

				if (counter == 0)
					fprintf (fp,"<li><a name=\"getacopy.4\"><b>How can I download or order the data?</b></a><p>\n");
				else
					fprintf (fp,"<li><a name=\"getacopy.4.%d\"><b>How can I download or order the data?</b></a><p>\n",counter);

				/*------------------------------------------------------*\
				 | for each Standard_Order_Process
				 |   for each Digital_Form
				 |     Format_Information_Content
				 |     Format_Name
				 |     Format_Version or Format_Date
				 |     Format_Specification
				 |     Transfer_Size
				 |     for each Digital_Transfer_Option
				 |       if Online_Option
				 |         label "Online access"
				 |         for each Online_Option
				 |           for each Computer_Contact_Information
				 |             find Network_Address
				 |               for each Network_Resource_Name
				 |                 list the value
				 |       if Offline_Option
				 |         label "Offline ordering"
				 |         for each Offline_Option
				 |           Offline_Media
				 |           Recording_Density
				 |           Recording_Density_Units
				 |           Recording_Format
				 |           Compatibility_Information
				\*------------------------------------------------------*/

				if (q = find_key (p,WStandard_Order_Process)) {
					do {
						fprintf (fp,"<ul>\n");
						if (r = find_key (q,WDigital_Form)) {
							fprintf (fp,"<li><b>Availability in digital form:</b><p>\n");
							do {
								fprintf (fp,"<table border=\"0\" cellpadding=\"2\">\n");
								fprintf (fp,"<tr><th align=\"left\" valign=\"top\">Data&nbsp;format:</th>\n");
								fprintf (fp,"<td>\n");
								if (u = find_key (r,WFormat_Information_Content)) {
									write_html_value (u);
									if (u = find_key (r,WFormat_Name))
										if (s = full_text (u))
											fprintf (fp," in format %s",munge(s));
									}
								else
									if (u = find_key (r,WFormat_Name))
										if (s = full_text (u))
											fprintf (fp,"%s",munge(s));
								fprintf (fp,"\n");

								if (u = find_key (r,WFormat_Version_Number))
									if (s = full_text (u))
										fprintf (fp," (version %s)",munge(s));
								else
									if (u = find_key (r,WFormat_Version_Date))
										if (s = full_text (u)) {
											fprintf (fp," (version of ");
											write_date (s);
											fprintf (fp,")");
											}
								fprintf (fp,"\n");
								if (u = find_key (r,WFormat_Specification))
									write_html_value (u);

								/* NBII extension describing ASCII file structure */

								if (WASCII_File_Structure != Wunknown)
									if (u = find_key (r,WASCII_File_Structure)) {
										fprintf (fp,"<dl>\n");
										write_html_item (u);
										fprintf (fp,"</dl>\n");
										}

								if (u = find_key (r,WTransfer_Size))
									if (s = full_text (u))
										fprintf (fp,"Size: %s\n",munge(s));
								fprintf (fp,"</td>\n");
								fprintf (fp,"</tr>\n");

								if (u = find_key (r,WDigital_Transfer_Option)) {
									if (w = find_key (u,WOnline_Option)) {
										fprintf (fp,"<tr><th align=\"left\" valign=\"top\">Network&nbsp;links:</th>\n");
										fprintf (fp,"<td>\n");
										do {
											struct item *w1,*w2,*w3;
											if (w1 = find_key (w,WComputer_Contact_Information))
												do {
													if (w2 = find_key (w1,WNetwork_Address))
														if (w3 = find_key (w2,WNetwork_Resource_Name))
															do {
																if (s = full_text (w3))
																	fprintf (fp,"%s<br/>\n",munge(s));
																} while (w3 = find_next_key (w3,WNetwork_Resource_Name));
													} while (w1 = find_next_key (w1,WComputer_Contact_Information));
											} while (w = find_next_key (w,WOnline_Option));
										fprintf (fp,"</td>\n");
										fprintf (fp,"</tr>\n");
										}

									if (w = find_key (u,WOffline_Option)) {
										fprintf (fp,"<tr><th align=\"left\" valign=\"top\" style=\"white-space: nowrap\">Media you can order:</th>\n");
										fprintf (fp,"<td>\n");
										do {
											if (h = find_key (w,WOffline_Media))
												if (s = full_text (h))
													fprintf (fp,"%s\n",munge(s));
											if (h = find_key (w,WRecording_Density))
												if (s = full_text (h))
													fprintf (fp,"(Density %s\n",munge(s));
											if (h = find_key (w,WRecording_Density_Units))
												if (s = full_text (h))
													fprintf (fp,"%s)\n",munge(s));
											if (h = find_key (w,WRecording_Format))
												if (s = full_text (h))
													fprintf (fp,"(format %s)\n",munge(s));
											if (h = find_key (w,WCompatibility_Information))
												if (s = full_text (h))
													fprintf (fp,"<p>Note: %s\n",munge(s));
											fprintf (fp,"<p>\n");
											} while (w = find_next_key (w,WOffline_Option));
										fprintf (fp,"</td>\n");
										fprintf (fp,"</tr>\n");
										}
									}
								fprintf (fp,"</table>\n");
								fprintf (fp,"<p>\n");
								} while (r = find_next_key (r,WDigital_Form));
							}

						if (r = find_key (q,WNon_digital_Form)) {
							fprintf (fp,"<li><b>Availability in non-digital form:</b><p>\n");
							write_html_value (r);
							fprintf (fp,"<p>\n");
							}

						fprintf (fp,"<li><b>Cost to order the data:</b> ");
						if (r = find_key (q,WFees)) {
							if (s = full_text (r)) {
								if (strlen (s) > 64) fprintf (fp,"<p>\n");
								fprintf (fp,"%s<p>\n",munge(s));
								}
							}

						if (r = find_key (q,WOrdering_Instructions)) {
							fprintf (fp,"<li><b>Special instructions:</b><p>\n");
							fprintf (fp,"<blockquote>\n");
							write_html_value (r);
							fprintf (fp,"</blockquote>\n");
							fprintf (fp,"<p>\n");
							}

						if (r = find_key (q,WTurnaround)) {
							fprintf (fp,"<li><b>How long will it take to get the data?</b><p>\n");
							fprintf (fp,"<blockquote>\n");
							write_html_value (r);
							fprintf (fp,"</blockquote>\n");
							fprintf (fp,"<p>\n");
							}

						fprintf (fp,"</ul>\n");
						} while (q = find_next_key (q,WStandard_Order_Process));

					fprintf (fp,"<p>\n");
					}
				fprintf (fp,"</li>\n");	/* getacopy.4 */

				if (q = find_key (p,WCustom_Order_Process)) {
					if (counter == 0)
						fprintf (fp,"<li><a name=\"getacopy.5\"><b>Is there some other way to get the data?</b></a><p>\n");
					else
						fprintf (fp,"<li><a name=\"getacopy.5.%d\"><b>Is there some other way to get the data?</b></a><p>\n",counter);
					fprintf (fp,"<blockquote>\n");
					write_html_value (q);
					fprintf (fp,"</blockquote>\n");
					fprintf (fp,"<p>\n");
					fprintf (fp,"</li>\n");	/* getacopy.5 */
					}

				if (q = find_key (p,WTechnical_Prerequisites)) {
					if (counter == 0)
						fprintf (fp,"<li><a name=\"getacopy.6\"><b>What hardware or software do I need in order to use the data set?</b></a><p>\n");
					else
						fprintf (fp,"<li><a name=\"getacopy.6.%d\"><b>What hardware or software do I need in order to use the data set?</b></a><p>\n",counter);
					fprintf (fp,"<blockquote>\n");
					write_html_value (q);
					fprintf (fp,"</blockquote>\n");
					fprintf (fp,"<p>\n");
					fprintf (fp,"</li>\n");	/* getacopy.6 */
					}

				if (find_next_key (p,WDistribution_Information)) fprintf (fp,"<hr/>\n");

				fprintf (fp,"</ol>\n");	/* getacopy */
				counter++;
				} while (p = find_next_key (p,WDistribution_Information));

		fprintf (fp,"<p>\n");

		/*--------------------------------------------------------------*\
		\*--------------------------------------------------------------*/

		fprintf (fp,"<hr/>\n");
		fprintf (fp,"<h3><a name=\"metaref\">Who wrote the metadata?</a></h3>\n");

		if (p = find_key (first,WMetadata_Reference_Information)) {

			fprintf (fp,"<dl>\n");

			/* Metadata_Date and friends */

			fprintf (fp,"<dt>Dates:\n");
			fprintf (fp,"<dd>");
			if (q = find_key (p,WMetadata_Date))
				if (s = full_text (q)) {
					fprintf (fp,"Last modified: ");
					write_date (s);
					fprintf (fp,"<br/>\n");
					}
			if (q = find_key (p,WMetadata_Review_Date))
				if (s = full_text (q)) {
					fprintf (fp,"Last Reviewed: ");
					write_date (s);
					fprintf (fp,"<br/>\n");
					}
			if (q = find_key (p,WMetadata_Future_Review_Date))
				if (s = full_text (q)) {
					fprintf (fp,"To be reviewed: ");
					write_date (s);
					fprintf (fp,"<br/>\n");
					}
			fprintf (fp,"</dd>\n");

			/* Metadata_Contact */

			fprintf (fp,"<dt>Metadata author:\n");
			fprintf (fp,"<dd>\n");
			if (q = find_key (p,WMetadata_Contact))
				write_contact (q);
			fprintf (fp,"</dd>\n");

			/* Standard name and version */

			if (q = find_key (p,WMetadata_Standard_Name))
				if (s = full_text (q))
					fprintf (fp,"<dt>Metadata standard: <dd>%s",munge(s));
			if (q = find_key (p,WMetadata_Standard_Version))
				if (s = full_text (q))
					fprintf (fp," (%s)",munge(s));

			if (q = find_key (p,WMetadata_Extensions)) {
				fprintf (fp,"<dt>Metadata extensions used:\n");
				fprintf (fp,"<dd>\n");
				fprintf (fp,"<ul>\n");
				do {
					if (r = find_key (q,WProfile_Name))
						if (s = full_text (q))
							fprintf (fp,"<li>%s</li>\n",munge(s));
					if (r = find_key (q,WOnline_Linkage))
						do {
							if (s = full_text (r))
								fprintf (fp,"<li>%s</li>\n",munge(s));
							} while (r = find_next_key (r,WOnline_Linkage));
					} while (q = find_next_key (q,WMetadata_Extensions));
				fprintf (fp,"</ul>\n");
				fprintf (fp,"</dd>\n");
				}

			fprintf (fp,"</dl>\n");
			fprintf (fp,"<p></p>\n");
			}

		/*--------------------------------------------------------------*\
		 | Bottom of page.  If BASE was specified, write the page URL.	|
		\*--------------------------------------------------------------*/

		fprintf (fp,"<hr/>\n");

		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html"))
				if (!find_option (opt,"omit_url"))
					if (opt = find_option (opt,"base"))
						if (s = text_of_option (opt)) {
							fprintf (fp,"<span class=\"mp-fileid\">This page is &lt;%s",s);
							if (*(s + strlen(s) - 1) != '/') fprintf (fp,"/");
							fprintf (fp,"%s&gt;<br/></span>\n",output_file);
							}

		/*--------------------------------------------------------------*\
		 | Output user-specified footer if there is one.				|
		\*--------------------------------------------------------------*/

		if (opt = find_option (NULL,"output"))
			if (opt = find_option (opt,"html"))
				if (v = find_option (opt,"footer")) {
					if (s = text_of_option (v))
						fprintf (fp,"%s\n",s);
					}
				else
					if (v = find_option (opt,"footer_file")) {
						if (s = text_of_option (v)) {
							if (t = read_text (s)) {
								char *tt = t + strlen (t) - 1;
								fprintf (fp,"<!-- begin footer from %s -->\n",s);
								fprintf (fp,"%s",t);
								if (*tt != '\n') fprintf (fp,"\n");
								fprintf (fp,"<!-- end footer from %s -->\n",s);
								free (t);
								}
							}
						}

		/*--------------------------------------------------------------*\
		 | Put a link and a time-stamp into the output file.			|
		\*--------------------------------------------------------------*/

		T = time (&T);
		t = ctime (&T);
		s = t + strlen (t) - 1;
		if (*s == '\n') *s-- = 0;
		if (*s == '\r') *s-- = 0;
		fprintf (fp,"<span class=\"mp-generated\">Generated by <a href=\"http://geology.usgs.gov/tools/metadata/tools/doc/mp.html\"><tt>mp</tt></a> version %s on %s<br/></span>\n",revision.mp,t);

		/*--------------------------------------------------------------*\
		\*--------------------------------------------------------------*/

		fprintf (fp,"\n");
		fprintf (fp,"</body>\n");
		fprintf (fp,"</html>\n");
		fclose (fp);
		}
	else {
		fprintf (stderr,"Error: could not create output file %s\n",output_file);
		return;
		}
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/
