/*
 *	fmtr -		formatter for web pages...
 *
 *	Created by:	Jack C Lipton   (see http://www.asstr.org/~CupaSoup/
 *
 *	Some notes:
 *
 *		Input paragraphs are NOT indented;  tab-based indented
 *		lines are discarded.
 *
 *		Paragraphs are separated by a blank line.
 *
 *		*word* emboldens the "word" (or words).
 *
 *		_word_ emphasises the "word" (or words) via bold+italics.
 *
 *		|line will set the line as "centered.
 *
 *		_	alone on a line will emit '<HR SIZE=5>'.
 *
 *
 * ===========================================================================
 *
 *	Portability:
 *		This program should be relatively easy to port to any
 *		system that handles the file namespace in a reasonably
 *		Unix-like way.
 *
 * ===========================================================================
 * 
 *	$Id: fmtr.c,v 1.20 2007/12/24 19:11:20 jcl Exp jcl $
 *
 *	$Author: jcl $
 *
 *	$Log: fmtr.c,v $
 *	Revision 1.20  2007/12/24 19:11:20  jcl
 *	fixed the problem of not recognizing escapes for '_' emphasis
 *
 *	Revision 1.19  2006/07/20 21:19:48  jcl
 *	pass along name of the input file to allow
 *	the inclusion of name.prefix and name.suffix
 *	to be recognized an (pre/post)-pended.
 *
 *	Revision 1.18  2006/04/04 19:10:16  jcl
 *	first cut at parametrizing the text to HTML transcoder.
 *
 *	Revision 1.17  2005/02/06 17:38:57  jcl
 *	another chunk of logic intended to parse the "parts" of a story
 *	so that a "fini" doesn't appear too soon.  It still doesn't work
 *	but at least the logic is present.
 *
 *	Revision 1.16  2005/01/08 19:38:44  jcl
 *	updated the copyright information.
 *
 *	Revision 1.15  2005/01/03 18:56:54  jcl
 *	added logic (and switches) to handle different feedback forms...
 *
 *	Revision 1.14  2004/06/27 14:57:08  jcl
 *	fully parameterized the "author specifics" and corrected egregious syntax errors.
 *
 *	Revision 1.13  2004/06/27 14:48:49  jcl
 *	added:
 *	   1)  logic to allow author overrides to be done globally
 *	       (and to be compiled in, too)
 *	   2)  ability to add preface/suffix to a transcoded page;
 *	       this allows adding the ASSTR sponsorship or any
 *	       disclaimers by including them from one place.
 *	   3)  Changed addresses from softhome.net (and fixed the
 *	       bad "pele.net" address which probably dropped a bunch
 *	       of rate/reviews off the planet and into a bit bucket).
 *
 *	I really need to tack on a GNU Public License to this code
 *	so that people aren't afraid to use it.
 *
 *	Revision 1.12  2004/05/14 02:15:55  jcl
 *	added logic to enhance ability to find "(incomplete)" in a heading.
 *	This allows the actual transcoding logic to behave differently when
 *	closing the end of a file.
 *
 *	Revision 1.11  2004/03/09 02:16:24  jcl
 *	handle insertion info for the Universe name
 *
 *	Revision 1.10  2004/02/08 14:03:49  jcl
 *	added logic to handle EWPUB as well as ASSTR... and allows other formats as well.
 *
 *	Revision 1.9  2003/08/11 22:58:54  jcl
 *	make the "Fini" dependant upon whether a "next chapter" link exists.
 *
 *	Revision 1.8  2003/08/10 15:26:53  jcl
 *	added ability to use -P to provide file for "next" linkage as well
 *	as -N for "prev" linkages, handled implicitly.
 *	new logic to allow >file:label alone on a line to add links inside
 *	a text file.
 *
 *	Revision 1.7  2003/08/10 14:37:36  jcl
 *	corrected issues w/ double // in URL
 *
 *	Revision 1.6  2003/07/26 02:32:30  jcl
 *	corrected closure of TT section.
 *
 *	Revision 1.5  2003/07/21 17:26:26  jcl
 *	corrected some stuff
 *
 *	Revision 1.4  2003/07/14 11:48:41  jcl
 *	formatting logic
 *
 *	Revision 1.3  2003/07/12 14:31:52  jcl
 *	corrected issue in emphasis/bolding logic.
 *
 *	Revision 1.2  2003/07/12 06:03:28  jcl
 *	default to a zero exit code.
 *
 *	Revision 1.1  2003/07/12 05:57:31  jcl
 *	Initial revision
 *
 * ===========================================================================
 *
 */

#include	<stdio.h>

/*
 *	Provide early location for compiled-in global overrides
 */

/* #define	OVERRIDE_AUTHOR		"Jack C Lipton"*/

#define	WEB_AUTHORNAME		"Jack C Lipton"
#define	WEB_AUTHOREMAIL		"cupasoup@pele.cx"
#define	WEB_ROOTDIR		"http://www.asstr.org/~CupaSoup/"
#define	WEB_SERIALNO		"CXNRITNZQLQQBDITGAVD"
#define	COPYRIGHT_HEADING	"2002, 2003, 2004, 2005 Jack C Lipton"

/*
 *	Manifest constants  (stupid, yes.)
 */

#define	INBUFSIZE	4096


/*
 *	These are used in generating the "snippet" for the
 *	Reader's Rate/Review system and the copyright notice.
 *	(Note that the "Copyright (c)" with the circle around
 *	the "c" is embedded in the putFini() logic.)
 *
 *	Each author needs to tune this for themselves.
 */

#if	1
char		 web_authorname[512];
char		 web_authoremail[512];
char		 web_rootdir[512];
char		 web_serial_number[256];
#else
char		*web_authorname =	WEB_AUTHORNAME;
char		*web_authoremail =	WEB_AUTHOREMAIL;
char		*web_rootdir =		WEB_ROOTDIR;
char		*web_serial_number =	WEB_SERIALNO;
#endif

char		 author_override[512];

#if	defined(LIX_ENABLED)
char		*acrrs_url =
			"http://cgi.tripod.com/lixdw1192/cgi-bin/loadeval.pl";
#endif

char		*copyright_heading =	COPYRIGHT_HEADING;
char		*copyright_body =	"All rights reserved.  Please contact author for other use.";

char		 web_page_name[512];
char		 corename[512];

char		 next_page_name[512];
char		 prev_page_name[512];

/*
 *	SHS Formats for publication
 *
 *	|Author: 
 *	|Title: 
 *	|Part: 
 *	|Universe:
 *	|Summary: 
 *	|Keywords: 
 *	|Revision: $Revision: 1.20 $
 *	|Archive: 
 *	|Mailing List: 
 *	|FAQ: 
 *	|RCS: $Id: fmtr.c,v 1.20 2007/12/24 19:11:20 jcl Exp jcl $
 *
 */

char		 shs_authorname[1024];
char		 shs_title[1024];
char		 shs_part[1024];
char		 shs_universe[1024];
char		 shs_summary[4096];
char		 shs_keywords[1024];
char		 shs_revision[1024];
char		 shs_archive[1024];
char		 shs_mailinglist[1024];
char		 shs_faq[1024];
char		 shs_rcs[1024];



/*
 *	Here's the structure I use to map the tag names and point to
 *	the buffers, above;  This makes the logic to fill the buffers
 *	that much easier.
 *
 *	Yes, this is awkward and I might have been able to do better.
 */

struct MapItems	{
	char			*label;		/*    label part of SHS line */
	int			 lablen,	/*  length of label to check */
				 buflen;	/*      length of the buffer */
	char			*buffer;	/*         pointer to buffer */
		};

struct MapItems		shsInfo[] =	{
	{	"Author:",		7,	1024,	shs_authorname	},
	{	"Title:",		6,	1024,	shs_title	},
	{	"Part:",		5,	1024,	shs_part	},
	{	"Universe:",		9,	1024,	shs_universe	},
	{	"Summary:",		8,	4096,	shs_summary	},
	{	"Keywords:",		9,	1024,	shs_keywords	},
	{	"Revision:",		9,	1024,	shs_revision	},
	{	"Archive:",		8,	1024,	shs_archive	},
	{	"Mailing List:",	13,	1024,	shs_mailinglist	},
	{	"FAQ:",			4,	1024,	shs_faq		},
	{	"RCS:",			4,	1024,	shs_rcs		},
	{	NULL,			0,	0,	NULL		}
					};

/*
 *	Handle baseline emphasis logic
 */

char		*emphasizers[2] =	{	"<I><B>", "</B></I>"	};
char		*bolders[2] =		{	"<B>", "</B>"	};

static	int	 bolding =		0,
		 emphasized =		0;

static	int	 leadin_skip =		1;

static	int	 consec_blank =		0;	/*   consecutive blank lines */

static	int	 force_fb_form =	0;	/* force creation of fb file */


/*
 *	A fill_mode of zero indicates normal delivery of the file;  a 1
 *	will cause each input line to get a line break (which is really
 *	needed when delivering poetry).
 *	What's interesting is that someday I'll want to be able to flip
 *	this on-and-off on the fly via embedded operations, but that can
 *	wait...
 */

int		 fill_mode =		0;	/*    assume normal wrapping */
int		 adultflag =		1;	/*     assume ADULTSONLY tag */
int		 asstrflag =		1;	/*   generate HTML for ASSTR */
int		 ewpubflag =		0;	/* generate HTML for EWP.ORG */
int		 fini_flag =		1;	/*  assume one complete part */

char		 SecName[256];			/*   section name (Universe) */



/*
 *	Some things really want to have function prototypes to shut
 *	up all of the warning messages I keep getting...
 */

extern	char		*basename( char *);
extern	char		*dirname( char *);
extern	char		*strchr( char *, char);
extern	char		*strrchr( char *, char);
extern	char		*malloc( unsigned int);
extern	char		*strstr( char *, char *);



/*
 *	Here are the function prototypes so that all forward references
 *	won't bitch and moan about function typing...
 */

int cvtFile( char *, FILE *, FILE *);	/*       convert text part of a file */
int cvtLine( FILE *, char *);		/*   convert a text ling from a file */
int	 procSHS( char *, FILE *, FILE *, char *);	/*        digest SHS */
void	 put_acrrs_snippet( FILE *);	/*         insert rate/review button */
void	 putFini( FILE *);		/* put appropriate ending on webfile */
int	 FindInBuffer( char *, char *);	/* scan buffer for matching contents */
int	 mkFBform( char *);		/*       generate HTML feedback form */
int	 appendFBform( FILE *, char *);	/*            append a feedback form */


main( argc, argv, envp)			/*                 formatter utility */
int	 argc;
char	*argv[];
char	*envp[];
{
	int		 a;			/*             arglist index */

	char		 textpath[1024],	/*   path name of input file */
			 webpath[1024],		/*  path of output file w/ + */
			 oldwebpage[1024];	/* assumed name of base page */

	FILE		*textfile,		/* input file handle/pointer */
			*webfile;		/*    resultant file pointer */



	/*
	 *	Set default values for all global variables...
	 */

	fill_mode =	0;			/*  ensure default fill mode */
	strcpy( next_page_name, "");
	strcpy( prev_page_name, "");

#if	defined(WEB_AUTHORNAME)
	strcpy( web_authorname, WEB_AUTHORNAME);
	strcpy( web_authoremail, WEB_AUTHOREMAIL);
	strcpy( web_rootdir, 	WEB_ROOTDIR);
	strcpy( web_serial_number, WEB_SERIALNO);
#else
	strcpy( web_authorname, "");
	strcpy( web_authoremail, "");
	strcpy( web_rootdir, 	"");
	strcpy( web_serial_number, "");
#endif


#if	defined(OVERRIDE_AUTHOR)
	strcpy( author_override, OVERRIDE_AUTHOR);/* allow for global change */
#else
	strcpy( author_override, "");
#endif



	/*
	 *	Work through the arglist, handling options that change
	 *	the behavior of this text file filter.
	 */

	for	( a = 1 ; a < argc ; a++ )
	    {
		if ( argv[a][0] == '-' )
		    {
			switch( argv[a][1])
			    {
			    case 'I':		/*       override who "I" am */
				if ( strlen( argv[a]) > 2 )
					strcpy( author_override, &argv[a][2]);
				else
					strcpy( author_override, argv[++a]);
				break;

			    case 'w':		/*                 word fill */
				fill_mode =	0;
				break;

			    case 'l':		/*    line mode (for poetry) */
				fill_mode =	1;
				break;

			    case 'A':		/*   turn off ADULTSONLY tag */
				adultflag =	0;
				break;

			    case 'a':		/*   turn on ADULTSONLY tag */
				adultflag =	1;
				break;

			    case 'P':		/*      previous file's name */
				if ( strlen(argv[a]) > 2 )
					strcpy( prev_page_name, &argv[a][2]);
				else
					strcpy( prev_page_name, argv[++a]);
				break;

			    case 'N':		/*          next file's name */
				if ( strlen(argv[a]) > 2 )
					strcpy( next_page_name, &argv[a][2]);
				else
					strcpy( next_page_name, argv[++a]);
				break;

			    case 'e':		/*    target EWPUB not ASSTR */
				asstrflag =	0;
				ewpubflag =	1;
				adultflag =	0;
				break;

			    case 'E':		/*    target ASSTR not EWPUB */
				asstrflag =	1;
				ewpubflag =	0;
				adultflag =	1;
				break;

			    }
		    }
		else			/*         process the file as named */
		    {
			int		 rc;

			char		*cp;
			char		 cmdline[512];



			/*
			 *	We need to synthesize the target named from
			 *	the input name.
			 */

			strcpy( textpath, argv[a]);	/*  copy in the name */

			textfile =	fopen( textpath, "r");


			/*
			 *	If we can't open the input file we can
			 *	give up trying to write something...
			 */

			if ( !textfile )
			    {
				fprintf( stderr,
					 "Could not open [%s] for reading.\n",
					 textpath);

				fflush( stderr);

				exit( 1);
			    }


			/*
			 *	capture the name of the file w/o extensions
			 *	(ie. no .txt, .x, or whatnot) and without
			 *	the directory part (should it be present).
			 */

			strcpy( corename, basename(textpath));
			cp =		strrchr( corename, '.');

			if ( cp && *cp )	{ *cp =	0x00; }


			/*
			 *	Synthesize the name we'll be outputting to;
			 *	it'll be a dir/corename.html+ so that we
			 *	can choose whether to do a cutover (or not).
			 */

			strcpy( web_page_name, corename);

			strcpy( webpath, dirname( textpath));
			strcat( webpath, "/");
			strcat( webpath, corename);
			strcat( webpath, ".html");
			strcpy( oldwebpage, webpath);	/*  name for compare */

			strcat( webpath, "+");		/*          new name */


			/*
			 *	Report on the start of the conversion process
			 */

#if	defined(BE_VERBOSE)
			fprintf( stderr, "%s ... ", corename);
			fflush( stderr);
#endif

			webfile =	fopen( webpath, "w");
			fini_flag =	1;		/* assume completed */

			if ( !webfile )
			    {
				/*
				 *	if we fail to open output, puke it up
				 */

				fclose( textfile);

				fprintf( stderr,
					 "Could not open [%s] for writing.\n",
					 webpath);

				fflush( stderr);

				exit( 2);
			    }


			/*
			 *	Now we can convert the file.  The cvtFile()
			 *	function will digest the SHS headers and
			 *	then process the actual text.  When the
			 *	input file has been finished the putFini()
			 *	call closes it out.
			 */

			cvtFile( corename, textfile, webfile);
			putFini( webfile);

#if	defined(BE_VERBOSE)
			fprintf( stderr, "\n");	/*       indicate completion */
			fflush( stderr);
#endif


			/*
			 *	Done with the files, close 'em
			 */

			fclose( textfile);
			fclose( webfile);


			/*
			 *	We want to make the "cutover" process a
			 *	conditional one;  if no changes have been
			 *	made, we want to skip the cutover.
			 */

			sprintf( cmdline, "diff %s %s >/dev/null",
				 oldwebpage, webpath);

			rc =	system( cmdline);


			/*
			 *	If the diff command exited w/ a zer0, there's
			 *	no differences, so we'll ditch the new file
			 *	since there's no point to cutting over a file
			 *	that has no differences.
			 */

			if ( rc == 0 )		/*       files are identical */
			    {
				unlink( webpath);	/* discard redundant */
			    }
			else
			    {
				sprintf( cmdline, "cutover %s", oldwebpage);
				system( cmdline);
			    }
		    }
	    }

	exit( 0);

} /* fmtr main */

/*
 *	procSHS	-	This function reads in the source file's SHS
 *			(Standard Header Sequence) for ASSTR which is
 *			intended to keep the story meta-information in
 *			proximity to the actual story content.  It is
 *			hoped this will not be clipped since it keeps
 *			the most useful information in a recognizable
 *			form.
 */

int
procSHS( corename, si, so, inbuf)  /* process SHS headers and populate table */
char	*corename;			/* used to check for importable item */
FILE	*si,				/*    flat ascii w/ SHS-based prefix */
	*so;				/*  resultant HTML file to be filled */
char	*inbuf;				/*  line that broke the SHS scanning */
{
	int		 i,			/*  index into SHS map table */
			 llen;			/*       line length as seen */

	char		*cp;			/*           utility pointer */

	char		 importname[4096];	/* name for imported objects */



	/*
	 *	Before processing the open file we need to clear the
	 *	target buffers
	 */

	for ( i = 0 ; shsInfo[i].buflen > 0 ; i++ )	/*    clear previous */
	    {
		strcpy( shsInfo[i].buffer, "");
	    }


	/*
	 *	Read lines from the file and scan them for leadin
	 *	SHS lines...
	 */

	while ( fgets( inbuf, 4095, si) )
	    {
		llen =		strlen( inbuf);
		inbuf[--llen] =	0x00;			/*  drop the newline */


		/*
		 *	Detect changes that will indicate the end of
		 *	the heading section...
		 */

		if ( llen == 0 )				break;
		if ( inbuf[0] == ' ' )				break;
		if ( inbuf[0] == '\t' )				break;


		/*
		 *	OK, scan the input buffer against the list of
		 *	labels we're looking to deal with.
		 */

		for ( i = 0 ; shsInfo[i].buflen > 0 ; i++ )
		    {
			struct MapItems		*map;



			map =		&shsInfo[i];


			/*
			 *	If the label matches, we'll process it
			 *	properly by finding the data part...
			 */

			if ( strncmp( inbuf, map->label, map->lablen ) == 0 )
			    {
				char		*cp;


				cp =		strchr( inbuf, ':');

				if ( cp && *cp )	cp++;
				if ( cp && *cp )	cp++;

				strncpy( map->buffer, cp, map->buflen);

				break;
			    }

		    }

	    }



	/*
	 *	Provide opportunity to process non-RCS updates
	 *	(Any overrides that are either compiled in or have been
	 *	added to the command line in the make file.)
	 */

	if	( *author_override )	/*       is this a non-empty string? */
	    {
		strcpy( shs_authorname, author_override);
	    }


	/*
	 *	Note:	I need this code to scratch a bogus part of
	 *		the Author: line without me having to go
	 *		back in to each file just to remove an email
	 *		address from the SHS headers.
	 */

	cp =		shs_authorname;

	while	(	cp
		   &&	*cp
		   &&	( *cp != '<' )    )
	    {
		cp++;
	    }

	if	( *cp == '<' )			/*   e-mail address present? */
	    {
		*cp-- =		'\0';

		if	( ( *cp == ' ' ) || ( *cp == '\t' ) )
		    {
			*cp =		'\0';
		    }
	    }


	/*
	 *	All the SHS information has been accumulated at this point
	 *	so we can now start generating the web-formatted file.
	 *
	 *	Note that this is ancient HTML, capiche?
	 */

	fprintf( so, "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
	fprintf( so, "<HTML>\n<HEAD>\n\n");
	fprintf( so, "<TITLE> %s </TITLE>\n\n", shs_title);


	/*
	 *	The following tag is assumed for content on ASSTR
	 *	(I'm not really sure how this _should_ be done, BTW)
	 *	I've implementest this as a conditional so that it
	 *	can be turned off for other content.
	 */

	if ( adultflag )
	    {
		fprintf( so, "<!--ADULTSONLY-->\n");
	    }



	/*
	 *	Here we fill in the Meta information we've accumulated
	 *	from the SHS headers, as recommended by others.
	 */

	if ( asstrflag != 0 )
	    {
		fprintf( so, "<META NAME=\"shs-title\" CONTENT=\"%s\" />\n", shs_title);
		fprintf( so, "<META NAME=\"shs-part\" CONTENT=\"%s\" />\n", shs_part);
		fprintf( so, "<META NAME=\"shs-author\" CONTENT=\"%s\" />\n", shs_authorname);
		fprintf( so, "<META NAME=\"shs-keywords\" CONTENT=\"%s\" />\n", shs_keywords);
#if	0
		fprintf( so, "<META NAME=\"date\" CONTENT=\"%s\" />\n", "`date`");
#endif
		fprintf( so, "<META NAME=\"shs-universe\" CONTENT=\"%s\" />\n", shs_universe);
		fprintf( so, "<META NAME=\"shs-summary\" CONTENT=\"%s\" />\n", shs_summary);
		fprintf( so, "<META NAME=\"shs-revision\" CONTENT=\"%s\" />\n", shs_revision);
	    }


	fprintf( so, "</HEAD>\n");


	if ( ewpubflag == 1 )
	    {
		fprintf( so, "<BODY>\n\n");
	    }
	else
	    {
		FILE		*insertable;
		char		 insertbuf[4096];



		fprintf( so, "<BODY TEXT=\"#000000\" BGCOLOR=\"#FAF0E6\" LINK=\"#7F0000\" VLINK=\"#007F00\" ALINK=\"#00007F\">\n\n");
		

		/*
		 *	Insert the contents of "web.prefix" at this point
		 */

		insertable =	fopen( "web.prefix", "r");

		while	( insertable && fgets( insertbuf, 4095, insertable) )
		    {
			fputs( insertbuf, so);		/* copy line out */
		    }

		if ( insertable )
			fclose( insertable);

	    }


	/*
	 *	Now we can start populating the actual content of the
	 *	web page by plugging in the initial H1 header.
	 */

	fprintf( so, "<H1 ALIGN=\"CENTER\">\n");
	fprintf( so, "%s\n", shs_title);

	if ( strcmp( shs_part, "") != 0 )
	    {
		fprintf( so, "(part %s)\n", shs_part);
	    }

	fprintf( so, "</H1>\n");
	fprintf( so, "<CENTER>\n");


	/*
	 *	With the title out of the way we can now present the
	 *	remainder:
	 *		Keywords;
	 *		Author's MAILTO and name;
	 *		Main Page back-link;
	 *		Rate/Review button (if enabled).
	 */

	if ( strcmp( shs_keywords, "" ) != 0 )
	    {
		fprintf( so, "<B>codes:</B> %s\t<BR>\n", shs_keywords);
	    }

#if	0
	fprintf( so, "by <A HREF=\"mailto:%s\">%s</A><BR>\n",
		web_authoremail, web_authorname);
#else
	fprintf( so, "by <B><BIG>%s</BIG></B> <BR>\n", shs_authorname);
#endif

	if ( asstrflag != 0 )
	    {
		fprintf( so, "(<A HREF=\"index.html\">Main Page</A>)<BR>\n");
	    }

	if ( strlen( prev_page_name) != 0 )
	    {
		fprintf( so, " <A HREF=\"%s\"> Previous Chapter </A> <BR>\n",
			 prev_page_name);
	    }

	fflush( so);

	fprintf( so, "</CENTER>\n");
	fprintf( so, "<BR>\n");

#if	0
	if ( asstrflag != 0 )
		put_acrrs_snippet( so);
#endif

	fprintf( so, "<BR>\n");

	fflush( so);

	/*
	 *	If we find a "(SecName).preface" file present we want to
	 *	merge it into the resultant stream at this point.  This
	 *	allows acknowledgements to be assembled dynamically.
	 */

	strcpy( SecName, "");

	if ( shs_universe[0] )
	    {
		FILE	*pf;
		char	*cp =		shs_universe;
		char	*dp =		SecName;
		char	 preface_path[512];


		while	( cp && *cp && (*cp != ' ') )
			*dp++ =		*cp++;

		*dp =		0x00;

		sprintf( preface_path, "%s.preface", SecName);

		pf =	fopen( preface_path, "r");

		if ( pf )
		    {
			char		 iobuf[4096];

			while	( fgets( iobuf, 4095, pf) )
				fputs( iobuf, so);

			fclose( pf);
		    }
	    }


	/*
	 *	Cheat - check for a specific prefix
	 */

	{
		FILE	*pf;

		char	 importName[4096];



		sprintf( importname, "%s.prefix", corename);

		pf =		fopen( importname, "r");
	
		if ( pf )
		    {
			char		 iobuf[4096];


			while	( fgets( iobuf, 4095, pf) )
				fputs( iobuf, so);

			fclose( pf);
		    }
	}


	/*
	 *	We make a pass through the shs_title to check for an
	 *	incomplete work (where the word "incomplete" will be
	 *	in the 'Title:' line):
	 */

	fini_flag =	( FindInBuffer(shs_title, "(incomplete)") == 0 );


	/*
	 *	Now we're ready for the game
	 *	PLEASE NOTE:  the first non-header line is left inside the
	 *	"inbuf" buffer zone where the cvtLine() function can then
	 *	digest it.
	 */

	return( 0);

} /* procSHS */

/*
 *	This function inserts the Rate/Review button into the web page
 *	being generated.
 */

void
put_acrrs_snippet( so)			/*       insert a rate/review button */
FILE	*so;
{
#if	defined(LIX_ENABLED)
	fprintf( so,
		 "<form method=POST action=\"%s\"> ", acrrs_url);

	fprintf( so,
		 "<input type=\"hidden\" name=\"hr\" value=\"%s/%s.html\"> ",
		 web_rootdir, web_page_name);

	fprintf( so, "<input type=\"hidden\" name=\"cn\" value=\"1\">");

	fprintf( so, "<input type=\"hidden\" name=\"em\" value=\"%s\"> ",
		 web_authoremail);

	fprintf( so, "<input type=\"hidden\" name=\"title\" value=\"%s\"> ",
		 shs_title);

	fprintf( so, "<input type=\"hidden\" name=\"author\" value=\"%s\"> ",
		 web_authorname);

	fprintf( so, "<input type=\"submit\" value=\"Rate / Review\"> </form>");
#else
# if	1
#  if	1
	appendFBform( so, shs_title);	/*       generate feedback form file */
#  else
	mkFBform( shs_title);		/*       generate feedback form file */

	fprintf( so,
		 "<A HREF=\"fb-%s.html\"> Comment on this story </A>\n",
		 corename);
#  endif
# else
	fprintf( so,
		 "<A HREF=\"feedback-form.html\">Comment on this story</A>\n");
# endif
#endif

} /* put_acrrs_snippet */

/*
 *	cvtLine	-	(Convert Line) tags a buffer and parses it out
 *			into HTML for reading.
 */

int
cvtLine( so, inbuf)	/*        process content of the targetted text line */
FILE	*so;
char	*inbuf;
{
	int		 llen,			/*     chars in input buffer */
			 centered;		/*   centering tag was found */

	char		 lastchar;		/*  last character processed */

	char		*cp;			/*    copy character pointer */




	cp =		inbuf;			/*  point to start of buffer */


	/*
	 *	Detect a line we need to completely ignore.
	 */

	if ( strncmp( cp, "//", 2) == 0 )	/*      no action, meta info */
		return( 0);		/* this won't even break a paragraph */


	/*
	 *	Sneak in a link button to next/previous page
	 */

	if ( *cp == '>' )			/* button configuration line */
	    {
		/*
		 *	>file:label
		 */


		char		 nextfile[512];
		char		 label[512];
		char		*dp;



		/*
		 *	grab the name of the file to be linked
		 */

		cp++;
		dp =		nextfile;

		while ( cp && *cp && (*cp != ':') )
			*dp++ =		*cp++;

		*dp =		0x00;
		cp++;				/*    skip the ':' delimiter */


		/*
		 *	now grab the name to be placed as the link
		 */

		dp =		label;

		while ( cp && *cp )
			*dp++ =		*cp++;

		*dp =		0x00;


		/*
		 *	Next we'll sneak it into the output
		 */

		fprintf( so, " <A HREF=\"%s\"> %s </A> ", nextfile, label);
	    }


	/*
	 *	Recognize the left-margin marker that tells us
	 *	to center the rest of the line.
	 */

	if ( *cp == '|' )	/*       tells formatter to center this line */
	    {
		cp++;				/*           skip the marker */

		fputs( "<BR><CENTER>", so);	/*  start centering for line */

		centered =	1;
	    }
	else
		centered =	0;


	/*
	 *	Recognize when a '_' is alone at the left margin;  that
	 *	tells us to generate a horizontal line as a break point.
	 */

	if ( strcmp( inbuf, "_") == 0 )
	    {
		fputs( "<HR SIZE=5>\n", so);
		leadin_skip =	0;
		consec_blank =	0;

		return( 0);
	    }


	/*
	 *	Some more special defects...
	 *		a { alone on a line will turn on the fill-mode
	 *		a } alone on a line will turn off the <BR> per line
	 *		a [ alone on a line will place the following
	 *			text into a table.
	 *		a ] alone on a line will close the table "box".
	 */

	if ( strcmp( inbuf, "{") == 0 )
	    {
		fputs( "<TT>\n", so);
		fill_mode =	1;

		return( 0);
	    }

	if ( strcmp( inbuf, "}") == 0 )
	    {
		fputs( "</TT>\n", so);
		fill_mode =	0;

		return( 0);
	    }

	if ( strcmp( inbuf, "[") == 0 )
	    {
		fputs( "<TABLE ALIGN=CENTER BORDER=2 WIDTH=\"50%\">\n", so);
		fputs( "<TR><TD>\n", so);
		fill_mode =	0;
		return( 0);
	    }

	if ( strcmp( inbuf, "]") == 0 )
	    {
		fputs( "</TD></TR>\n</TABLE>\n", so);
		fill_mode =	0;
		return( 0);
	    }





	/*
	 *	I will not have even one tab character in front of a line
	 *	containing significant text to be processed, so we'll
	 *	detect such lines, treat them as paragraph breaks, and
	 *	return having processed the line.
	 */

	if (    ( *cp == '\0' )			/*          zero-length line */
#if	1
	     || ( *cp == '\t' )			/*     detect first tab char */
#else
	     || ( strncmp( inbuf, "\t\t", 2) == 0 )	/*    multiple tabs? */
#endif
	   )
	    {
		fprintf( so, "\n");

		if ( bolding != 0 )
		    {
			fprintf( so, "%s\n", bolders[bolding]);
			bolding =	0;
		    }

		if ( emphasized != 0 )
		    {
			fprintf( so, "%s\n", emphasizers[emphasized--]);
			emphasized =	0;
		    }

#if	0
		if (    ( leadin_skip == 0 )
		     || ( consec_blank++ < 2 )    )
#else
		if ( consec_blank++ < 1 )
#endif
		    {
			if ( fill_mode != 0 )	{ fprintf( so, "<BR>\n"); }
			else 			{ fprintf( so, "<P>\n"); }
		    }

		return( 0);
	    }

	leadin_skip =	0;		/*       turn off leadin suppression */
	consec_blank =	0;


	/*
	 *	Since the line has significant text, we'll process it
	 *	looking for the "emphasis" indicators...
	 */

	lastchar =		'\0';	/*    a NUL to indicate left margin */

	while	( cp && *cp )
	    {
		char		 gotc;


		gotc =		*cp++;

		if	(    ( gotc == '\\' )	/*  skip over an escape char */
			  && ( lastchar != '\\' )    ) /* that wasn't escape */
		    {
			lastchar =		gotc;

			continue;
		    }


		/*
		 *	Note:	The characters used for various special
		 *		defects should really be done using a
		 *		table but this cut is primitive enough
		 *		that I'm not desperate to use.
		 */

		/*
		 *	Recognize a bold enter/leave tag
		 */

#if	0
		if ( gotc == '*' )	/*   detect emphasis */
#else
		if (    ( gotc == '*' )		/*        unescaped emphasis */
		     && ( lastchar != '\\' )    )
#endif
		    {
			fprintf( so, "%s", bolders[bolding]);
			bolding ^=	1;

			continue;
		    }


		/*
		 *	Recognize an emphasize enter/leave tag
		 */

#if	0
		if ( gotc == '_' )
#else
		if (    ( gotc == '_' )		/*        unescaped emphasis */
		     && ( lastchar != '\\' )    )
#endif
		    {
			fprintf( so, "%s",emphasizers[emphasized]);
			emphasized ^=	1;

			continue;
		    }


		/*
		 *	If we didn't have to remap the character, we'll
		 *	just pass it through unmolested.
		 */

		fputc( gotc, so);		/*  put out the literal char */

		lastchar =		gotc;
	    }



	/*
	 *	Based on whether fill_mode has been set, we choose to
	 *	append a line BReak for each input line
	 */

	if	( fill_mode != 0 )		{ fprintf( so, " <BR>\n"); }
	else					{ fprintf( so, " \n"); }


	/*
	 *	If we got centered we want to turn it off here.
	 */

	if ( centered )				fputs( "</CENTER>\n", so);

	return( 1);

} /* cvtLile */

/*
 *	cvtFile -	Given an input (text) file handle and an output
 *			(web format) file handle, this will manage the
 *			processing of the SHS headings and generates the
 *			necessary HTML content.
 */

int
cvtFile( corename, si, so)	/*   process content of the target text file */
char	*corename;
FILE	*si,
	*so;
{
	int		 lines,
			 llen;

	char		 importname[4096];	/* name for imported objects */

	char		 inbuf[INBUFSIZE];	/*     buffer for input text */

	char		 outbuf[8192];		/*    buffer for html output */

	char		*cp,			/*    copy character pointer */
			*dp,			/*    --> destination buffer */
			*twp,
			*nwp;



	bolding =	0;
	emphasized =	0;
	consec_blank =	0;

	lines =		0;			/*  reset the count of lines */

	procSHS( corename, si, so, inbuf);  /* leaves first non-hdr in inbuf */

	leadin_skip =	1;			/* we want to suppress stuff */

	cvtLine( so, inbuf);			/*        pass captured line */
	lines++;


	/*
	 *	While we still have contents, pass the line for conversion
	 */

	while ( fgets( inbuf, 4095, si ) )
	    {
		llen =		strlen( inbuf);
		inbuf[--llen] =	0x00;		/*       remove the linefeed */

		lines++;

		cvtLine( so, inbuf);		/*    do the line conversion */
	    }

	return( 0);

} /* cvtFile */

/*
 *	putFini -	Handles closure of the generate Web Page which
 *			includes the Rate/Review button, a copyright
 *			notice, and a flat copy of the actual SHS headers.
 */

void
putFini( so)
FILE	*so;
{
	int		 i;



	fprintf( so, "<CENTER> <BR> \n");

	if ( strlen( next_page_name) != 0 )
	    {
		fprintf( so, " <A HREF=\"%s\"> Next Chapter </A> <BR>\n",
			 next_page_name);
	    }
	else
	    {
		int		 this_part =	-1,
				 last_part =	-2;



		/*
		 *	We'll re-check the part information for "Fini" flag...
		 *	Part information is formated part/total, so...
		 *		12/12
		 *	will justify a "Fini" whilst
		 *		10/12
		 *	won't (but, really, this should have been covered
		 *	by the prev/next link info, above).
		 */

		if ( strlen( shs_part) > 0 )
		    {
			char		*cp;


			cp =		shs_part;

			while ( cp && *cp && (*cp == ' ') )
				cp++;		/*   skip any leading blanks */


			/*
			 *	Too lazy to fix (xx/xx) damnit...
			 */

			if ( cp && *cp && (*cp == '(') )
				cp++;

			if ( cp && *cp )
			    {
				this_part =	atoi(cp);

				cp =		strchr( cp, '/');

				if ( cp && *cp )
				    {
					cp++;		/*    skip the slash */
	
					while ( cp && *cp && (*cp == ' ')  )
						cp++;
	
					if ( cp && *cp )
						last_part =	atoi(cp);
				    }
			    }


			/*
			 *	check the collected info
			 */

			if ( this_part == last_part )
				fini_flag =	1;
		    }
	

		/*
		 *	generate the "default" closing
		 */

		if ( fini_flag != 0 )
		    {
			fprintf( so, " <BR> * Fini * <BR> \n");
		    }
		else
		    {
			fprintf( so, " <BR> * (To Be Continued) * <BR> \n");
		    }
	    }


	fprintf( so, "<BR>\n");
	fprintf( so, "\n</CENTER>\n");
	fprintf( so, "<BR>\n");

	if ( asstrflag != 0 )
		put_acrrs_snippet( so);		/*        Rate/Review button */

	fprintf( so, "<BR> <BR>\n<HR SIZE=5> <BR>\n");


	/*
	 *	Insert the Copyright notice
	 */

	fprintf( so, "<B>Copyright © %s</B>\n", copyright_heading);
	fprintf( so, "%s\n<BR> <BR>\n<HR SIZE=5>\n", copyright_body);
	fprintf( so, "<BR> <BR>\n");


	/*
	 *	Insert the contents of "web.suffix" at this point
	 */

	{
		FILE	*insertable;
		char	 insertbuf[4096];


		insertable =	fopen( "web.suffix", "r");

		while	( insertable && fgets( insertbuf, 4095, insertable) )
		    {
			fputs( insertbuf, so);		/* copy line out */
		    }

		if ( insertable )
			fclose( insertable);
	}


	/*
	 *	Now we deliver the original SHS contents to the tail
	 *	end of the web page.  We pull the contents back from
	 *	the shsInfo table.
	 */

	if ( asstrflag != 0 )
	    {

		fprintf( so, "<PRE>\n");

		for ( i = 0 ; shsInfo[i].buflen > 0 ; i++ )
		    {
			struct MapItems		*map;
			char			*cp,
					 ch;


			map =		&shsInfo[i];

			fprintf( so, "%s ", map->label);

			cp =		map->buffer;

			while	( cp && *cp )	
			    {
				if ( *cp == '<' )
				    {
					fputs( "<", so);
				    }
				else if ( *cp == '>' )
				    {
					fputs( ">", so);
				    }
				else
					fputc( *cp, so);
	
				cp++;
			    }

			fputs( "\n", so);
		    }

		fprintf( so, "</PRE>\n");
	    }


	fprintf( so, "\n\n</BODY>\n</HTML>\n");		/* emit HTML closure */

} /* putFini */

int
FindInBuffer( buffer, look4)		/* look for a pattern */
char	*buffer;
char	*look4;
{
	unsigned int	 isize;

	char		*tbuf;			/* temp buffer space */

	char		*sp,			/* source buffer */
			*dp,			/* copy to buffer */
			*tp,			/* test pointer */
			 ch;



	/*
	 *	make a case-limited version
	 */

	isize =		strlen( buffer);	/* get size of existing */
	isize +=	4;			/* pad this up */

	tbuf =		malloc( isize);		/* build up a buffer */

	dp =		tbuf;			/* point to local copy */
	sp =		buffer;			/* point to original */

	while ( sp && *sp )		/* does a copy as lower text */
	    {
		ch =		*sp++;

		if ( isupper( ch) )
			*dp++ =		tolower( ch);
		else
			*dp++ =		ch;
	    }

	*dp =		0x00;		/* terminate temp buffer */

	sp =		strstr( tbuf, look4);	/* look for a match */

	free( tbuf);			/* release temp space */

	if ( sp && *sp )		/* recognize successful copy */
		return( 1);

	return( 0);

} /* FindInBuffer */

int
mkFBform( title)			/*    generate an HTML feedback form */
char	*title;					/*    title to pre-fill form */
{
	FILE		*so;			/*   file for generated form */

	char		 fbpath[512];		/*    name of generated form */



	sprintf( fbpath, "fb-%s.html", corename);


	/*
	 *	If the feedback form file already exists don't step on it
	 */

	if ( ( force_fb_form == 0 ) && ( access( fbpath, 04) == 0 ) )
	    {
		return( 0);
	    }


	/*
	 *	Open the file where we'll place the feedback form
	 */

	so =		fopen( fbpath, "w");


	/*
	 *	Populate the file with the appropriate HTML
	 */

	fprintf( so, "\
<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n\
<html lang=en>\n\
<head>\n\
<!--ADULTSONLY-->\n\
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">\n\
<meta name=\"shs-Author\" content=\"Jack C Lipton\">\n");

	
	fprintf( so, "\
<title>Jack Lipton's Feedback Form for %s</title>\n\
</head>\n\n",
		 title);


	fprintf( so, "\
<H1 ALIGN=CENTER>\n\
Jack C Lipton's Feedback Form for %s\n\
</H1>\n\
<br>\n",
		 title);


	fprintf( so, "\
<H2> A Message to my Readers </H2>\n\
<p>\n\
Thank you for reading my stories.\n\
<p>\n\
This page is how the ASSTR.ORG site\n\
provides the capability to send your comments\n\
to me anonymously.\n\
However, if you would like me to respond, you may\n\
provide a return e-mail address.\n\
If you ask me questions I can only answer if a\n\
working e-mail address is provided.\n\
<p>\n");


	fprintf( so, "\
<H2> Why provide feedback? </H2>\n\
<p>\n\
Feedback is the lifeblood of authors here on ASSTR.ORG;\n\
we don't get paid in any currency beyond hits on the\n\
pages and <B>feedback</B> from our readership.\n\
Encouragement, questions, critiques, typos... as a\n\
reader you may be surprised how much your words mean to us.\n\
<p>\n\
This page gives you the opportunity to tell me what\n\
you thought of the words I've written...\n\
<p>\n\
It is a <U>good</U> feeling to have my words read.\n\
<p>\n");


	fprintf( so, "\
<H2> Here's your turn to talk to <b>me</b> </H2>\n\
<FORM method=\"post\" ACTION=\"http://acctcgi.asstr.org/formmail.cgi\">\n\
	<INPUT TYPE=HIDDEN NAME=\"emailrequired\" VALUE=\"no\">\n\
	<INPUT TYPE=HIDDEN NAME=\"successpage\" VALUE=\"AllDone.html\">\n\
<p>\n");


	fprintf( so, "\
Subject (change if you wish):\n\
	<INPUT NAME=\"subject\" VALUE=\"{ASSTR} Feedback - %s\" size=60>\n",
		 title);


	fprintf( so, "\
<p>\n\
I allow for anonymous feedback, but,\n\
If you provide your e-mail address here,\n\
you <B>allow</B> me the <U>privilege</U>\n\
of replying or conversing with you:\n\
<p>\n\
E-mail:\n\
	<INPUT TYPE=TEXT NAME=\"useremail\" size=60>			<BR>\n\
<p>\n\
Name:\n\
	<INPUT TYPE=TEXT NAME=\"username\" size=60>			<BR>\n\
<p>\n\
Please enter the name of the story and\n\
your comments about it to me in the box below:\n\
<p>\n\
	<TEXTAREA rows=10 cols=80 name=\"story\"></textarea>		<BR>\n\
Note:									<BR>\n\
Returns are only necessary between paragraphs.			<BR><BR><BR>\n\
<CENTER>\n\
	<INPUT type=\"submit\" value=\"Press to send message\">		<BR>\n\
</CENTER>\n\
\n\
</form>\n");

	fprintf( so, "\
<hr>\n\
<p>\n\
If the above does not work for you, try:\n\
<A HREF=\"http://www1.asstr.org/cgi-bin/ah_feedback.cgi?id=CXNRITNZQLQQBDITGAVD\" TARGET=\"_new\" onClick=\"remote=window.open('http://www1.asstr.org/cgi-bin/ah_feedback.cgi?id=CXNRITNZQLQQBDITGAVD','_new', 'toolbar=no,width=570,height=700,status=no,scrollbars=yes,resizable=1');remote.opener=self;return false\">ASSTR Msg Form</A>\n\
<hr>\n\
</body>\n\
</html>\n</body>");

	fclose( so);

	return( 0);

} /* mkFBform */

int
appendFBform( so, title)		/*          append the feedback form */
FILE	*so;					/*                HTML image */
char	*title;					/*    title to pre-fill form */
{


	/*
	 *	Populate the file with the appropriate HTML
	 */

	fprintf( so, "<hr>\n");

	fprintf( so, "<H2 ALIGN=CENTER> Feedback Form </H2> <br>\n");

	fprintf( so, "Thank you for reading my stories.\n<p>\n");
	fprintf( so, "If you would like me to respond to your comments, ");
	fprintf( so, "you need to provide a return e-mail address.\n<p>\n");

	fprintf( so, "<b> Why provide feedback? </b>\n\
Feedback is the lifeblood of authors here on ASSTR.ORG;\n\
we don't get paid in any currency beyond hits on the\n\
pages and <B>feedback</B> from our readership.\n\
Encouragement, questions, critiques, typos... as a\n\
reader you may be surprised how much your words mean to us.\n\
<p>\n\
It is a <U>good</U> feeling to know my words have been read ... and enjoyed.\n\
<p>\n");

	fprintf( so, "\
<FORM method=\"post\" ACTION=\"http://acctcgi.asstr.org/formmail.cgi\">\n\
	<INPUT TYPE=HIDDEN NAME=\"emailrequired\" VALUE=\"no\">\n\
	<INPUT TYPE=HIDDEN NAME=\"successpage\" VALUE=\"AllDone.html\">\n\
	<INPUT TYPE=HIDDEN NAME=\"serial_number\" VALUE=\"%s\">\n<p>\n",
	web_serial_number);

	fprintf( so, "\
Subject (change if you wish):\n\
	<INPUT NAME=\"subject\" VALUE=\"{ASSTR} Feedback - %s\" size=60>\n",
		 title);

	fprintf( so, "<p>\nI allow for anonymous feedback, but,\n\
If you provide your e-mail address here,\n\
you <B>allow</B> me the <U>privilege</U>\n\
of replying or conversing with you:\n\
<p>\n\
E-mail:\n\
	<INPUT TYPE=TEXT NAME=\"useremail\" size=60>			<BR>\n\
<p>\n\
Name:\n\
	<INPUT TYPE=TEXT NAME=\"username\" size=60>			<BR>\n\
<p>\n\
Please enter your comments about it to me in the box below:\n\
<p>\n\
	<TEXTAREA rows=10 cols=80 name=\"story\"></textarea>		<BR>\n\
Note:									<BR>\n\
Returns are only necessary between paragraphs.			<BR><BR><BR>\n\
<CENTER>\n\
	<INPUT type=\"submit\" value=\"Press to send message\">		<BR>\n\
</CENTER>\n\
\n\
</form>\n");

	fprintf( so, "\
<hr>\n\
<p>\n\
If the above does not work for you, try:\n\
<A HREF=\"http://www1.asstr.org/cgi-bin/ah_feedback.cgi?id=CXNRITNZQLQQBDITGAVD\" TARGET=\"_new\" onClick=\"remote=window.open('http://www1.asstr.org/cgi-bin/ah_feedback.cgi?id=CXNRITNZQLQQBDITGAVD','_new', 'toolbar=no,width=570,height=700,status=no,scrollbars=yes,resizable=1');remote.opener=self;return false\">ASSTR Msg Form</A>\n\
<hr>\n");

} /* appendFBform */