/*
 * Bookreader to HTML comverter usage:
 *
 *   path-info:
 *	/dir/file.[Pnnn.|Itable.|Gnnn.]type	(type = decw$book)
 *	/hist1[/histn...]/disk:<dir>file.type/ 	(type = decw$bookshelf)
 *
 *   The middle element ([Pnn.|Itable...\]) speficies a sub-section of
 *   the file to format:
 *
 *       Pnnn	Returns part indicated by nnn, which is a decimal number.
 *              The part number is an internal addressing scheme used by
 *		the bookreader to reference a chunk to data to be read at
 *		one time.
 *
 *	 Itable Format index named by 'table', name is case sensitive and
 *		the list of available tables is defined by the document.
 *
 *	Gnnn	Format part nnn as graphic image (GIF).
 *
 *    When the path-info ends in a slash, webbook assumes the preceding
 *    path element is a VMS file specification of a shelf file to
 *    format and display.  Path element preceding the file name are
 *    formatted as header lines in the resulting HTML.
 *
 *   Conditional compilation symbols:
 *	NOCGILIB	When defined, build script for DCL-based CGI
 *			environment: argv[1] is path, CGI variables are
 *			DCL symbols/logicals of form WWW_var_name.
 *
 *	NOCGIPREFIX	If defined, inhibits WWW_ prefix on env. vars.
 *
 *	VERBOSE		When defined, include bookreader internal codes
 *			as HTML comments and 'glue' bytes as <gxx>.
 *    
 * Author:	David Jones
 * Date:	5-MAY-1996
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unixlib.h>
#ifdef __DECC
#ifndef shell$translate_vms		/* pre 5.0 on VAX */
int decc$to_vms();
#endif
#define SHELL_TO_VMS(a,b,c) decc$to_vms(a,b,c,0)	/* set no-directory*/
#else
#define SHELL_TO_VMS shell$to_vms
int shell$to_vms();
#endif

#ifdef NOCGILIB
#ifdef NOCGIPREFIX
#define PLEN 0
#else
#define PLEN 4
#endif
#define file_arg argv[1]
#define cgi_init(a,b) 1
#define cgi_printf printf
#define cgi_info(a)  getenv((char *)( strcpy(&cgi_info_buf[PLEN],a))-PLEN)
static char cgi_info_buf[64] = { 'W', 'W', 'W', '_' };
#else
#include "scriptlib.h"
#include "cgilib.h"
#define printf cgi_printfxxx
#define file_arg cgi_info("PATH_INFO")
#endif

#include "bookreader_recdef.h"
#include "bookfile_io.h"
#include "bookfile_index.h"
#include "bookfile_section.h"
#include "bookfile_text.h"
#include "bookfile_figure.h"

static char *webbook_version = "WEBBOOK 0.90, 11-JUL-1999";

static int show_part ( void * bkf, int part_num, bktxt_fntptr fontdef, int );
static int show_image ( void *bkf, int sect_num );

static char *href_fname;
static char *href_type;			/* for genrating HREF="name.xxx.type"*/
static char *escape_string ( char *source );
static char vms_bookfile[256];
static int save_bookfile_name ( char *name, int flags )
{ strncpy ( vms_bookfile, name, 255 ); vms_bookfile[255] = '\0'; return 1; }
static char nbsp = 160;		/* non-breaking space */

static void error_abort ( char *sts_line, char *message )
{
    cgi_printf ( "Content-type: text/plain\n%s\n\n%s\n", sts_line, message );
    exit ( 1 );
}

static char *entify ( char *source )
{
    int i; char *p, *d;
    static char fixup[8192];
    for ( p = source; *p; p++ ) if ( *p == '<' || *p == '>' || *p == '&' ) {
	for ( i = 0, p = source; *p; p++ ) {
	    if ( *p == '<' ) {
		strcpy ( &fixup[i], "&lt;" ); i += 4;
	    } else if ( *p == '>' ) {
		strcpy ( &fixup[i], "&gt;" ); i += 4;
	    } else if ( *p == '&' ) {
		strcpy ( &fixup[i], "&amp;" ); i += 5;
	    } else fixup[i++] = *p;
	}
	if ( i > 8184 ) return source;	/* give up */
	fixup[i] = '\0';
	return fixup;
    }
    return source;	/* string OK as is. */
}

int webbook_shelf ( char *, char * );

int main ( int argc, char **argv )
{
    long ndx_value;
    int i, j, bad, status, part_length, length, single_sect, part_num;
    int first, ndx_type, ndx_count, iter_count, font_count, select, type;
    int dir_delim;
    short ndx_hdr[9];
    char *desc, *bookfile, *defdir, *table, *sec_str, *tmp;
    char bookpath[300], ndx_name[256];
    unsigned char attr[4];
    bkrdr_recptr root;
    bktxt_fntptr fontdef;
    void *bkf, *bki;
    /*
     * setup CGI environment.
     */
    status = cgi_init ( argc, argv );
    if ( (status&1) == 0 ) fprintf(stderr,"Status of cgi_init: %d\n", status );
    if ( (status&1) == 0 ) exit ( status );
    if ( argc < 2 ) {
	error_abort ( "500 missing argument", 
		"usage: webbook /path/file[.xnnn].type[/]" );
    }
    bookfile = file_arg;
    i = strlen ( bookfile );
    if ( bookfile[i-1] == '/' ) {
	bookfile[i-1] = '\0';
	return webbook_shelf ( bookfile, webbook_version );
    } else if ( (bookfile[i-1] == ']') || (bookfile[i-1] == '>') ) {
	char *port;
	/*
	 * Idiot forgot the trailing slash, issure redirect.
         */
	port = cgi_info("SERVER_PORT");
	if ( !port ) port = "80";
	if ( strcmp ( port, "80" ) == 0 ) port = "";
	cgi_printf("Location: http://%s%s%s%s%s/\n\n", cgi_info("SERVER_NAME"),
		*port ? ":" : "", port, cgi_info("SCRIPT_NAME"), bookfile );
	return 1;
    }
    /*
     * Interpret command line arguments.
     */
    i = strlen ( bookfile );
    if ( i+1 >= sizeof(bookpath) ) {
	error_abort ( "400 bad argument", "Argument too long" );
    }
    strcpy ( bookpath, bookfile );
    /*
     * Scan from back for filename portion and track the periods.
     */
    select = type = dir_delim = i;
    for (j=i-1; (j >= 0) && (bookpath[j] != '/'); --j) {
	if ( bookpath[j] == '.' ) {
	    if ( dir_delim != i ) ;
	    else if ( type == i ) type = j;
	    else if ( select == i ) select = j;
	    else {
		error_abort ( "404 bad filename", bookpath );
	    }
	} 
	else if ( (bookpath[j] == ']') || (bookpath[j] == '>') ) {
	    dir_delim = j;
	}
    }
    href_fname = &bookpath[j+1];	/* just filename without path */

    if ( select != i ) {
	/* Reorder things, we stil have original in argv[1] */
	strcpy ( &bookpath[select], bookfile + type );
	strncpy ( &bookpath[select-type+i], bookfile+select, type - select );
	bookpath[select-type+i] = '\0';
	j = i + select - type;
	type = select;
	select = j+1;
    }
fprintf(stdout,"argv[1] = '%s' -> '%s' (%d)\n", bookfile, bookpath,
	strcspn (":[<", bookpath) );
    /*
     * convert filename to vms format for bkf_open.
     */
    if ( strcspn ( ":[<", bookpath ) > 3 ) {
        vms_bookfile[0] = '\0';
        status = SHELL_TO_VMS ( bookpath, save_bookfile_name, 1 );
        if ( (status&1) == 0 ) {
	    cgi_printf("Content-type: text/plain\nStatus: 404 bad filename\n\n");
	    cgi_printf("Failure to convert filename format:'%s'\n", vms_bookfile );
	    exit(status);
        }
     } else {
	/* Already in VMS format */
	strncpy ( vms_bookfile, bookpath[0] == '/' ? &bookpath[1] : bookpath, 
		255 );
	vms_bookfile[255] = '\0';
     }
    i = strlen(vms_bookfile);
    if ( i > 0 ) if ( vms_bookfile[i-1] == '.' ) 
	strcpy ( &vms_bookfile[i], "decw$book" );
fprintf(stdout,"VMS bookfile spec: '%s' (%d)\n", vms_bookfile, i );
    bookfile = vms_bookfile;
    /*
     * Make printf control strings for generating HREF targets for
     * different tables.
     */
    href_type = (bookpath[type] == '.') ? &bookpath[type+1] : &bookpath[type];
    bookpath[type] = '\0';
    href_fname = escape_string(href_fname);   /* encode punctuation */
    /*
     * Select arguments
     */
    defdir = "sys$disk:[].decw$book";
    table = sec_str = (char *) 0;
    if ( bookpath[select] == 'n' ) sec_str = &bookpath[select+1];
    else if ( bookpath[select] == 't' ) table = &bookpath[select+1];
    /*
     * Open file, read root page, and display some of it's fields.
     */
    status = bkf_open ( bookfile, defdir, &bkf );
    if ( (status&1) == 0 ) {
	cgi_printf("Content-type: text/plain\n\n");
	cgi_printf("error opening bookfile '%s'\n", bookfile ); 
	exit ( status );
    }
    status = bkf_read_page ( bkf, 0, &part_length, &root, &length );
    if ( (status&1) == 0 ) {
	cgi_printf("Content-type: text/plain\n\n");
	cgi_printf("error reading root part: %d\n", status );
	exit ( status );
    }
    if ( bookpath[select] != 'g' && bookpath[select] != 'G' ) {
	cgi_printf ( "Content-type: text/html\n\n<HTML>" );
        cgi_printf("<HEAD><TITLE>%s</TITLE></HEAD>\n", entify(root->first.title));
        cgi_printf("<!-- partcount: %d, sectioncount %d -->\n", 
           	root->first.partcount, root->first.sectioncount );
        if ( root->first.author[0] )  cgi_printf("<!-- Author: %s.-->\n",
	    root->first.author );
	cgi_printf("<!-- Software: %s --><HEAD>\n", webbook_version );
    }
    /*
     * Read font table.
     */
    status = bkt_read_font_map ( bkf, &fontdef, &font_count );
    /*
     * Take action depending upon the first char of selector.
     */
    part_num = 0;
    switch ( bookpath[select] ) {
		/* Convert section argument to part number and fall throuh */
	case 's':
	case 'S':
		single_sect = atoi ( &bookpath[select+1] );
	        status = bkf_lookup_section ( bkf, single_sect, &part_num );
		
	case 'p':
	case 'P':
		if ( part_num == 0 ) part_num = atoi ( &bookpath[select+1] );
		status = show_part ( bkf, part_num, fontdef, font_count );

	     break;
	case '\0':
	case 't':
	case 'T':
	    /*
	     * Create context for index operations.
	     */
	    status = bki_create_context ( bkf, &bki );
	    if ( (status&1) == 0 ) {
		cgi_printf("error creating index context\n" ); 
		exit ( status );
	    }
	    /*
	     * Make directory of indexes.
	     */
	    table = bookpath[select] ? &bookpath[select+1] : "";
	    cgi_printf ( "<H2>Available tables:</H2><UL>\n" );

	    for ( status = bki_find_index ( bki,"*",-1, ndx_name,
		    &ndx_type, &ndx_count ); (status&1) == 1;
	    	    status = bki_find_index ( bki, "*", -1, ndx_name,
	   		&ndx_type, &ndx_count ) ) {
	            cgi_printf("<LI><A HREF=\"%s.t%s.%s\">%s</A> (%d entries)</LI><BR>\n",
	                href_fname, ndx_name, href_type, 
			ndx_name, ndx_count );
	    }
	    bki_find_index_end ( bki );
	    cgi_printf("</UL>\n");
	    if (strcmp(table,"*") == 0) break;
	    /*
	     * List named table.
	     */
	    if ( *table == '\0' ) {
		/* Lookup first table */
		status = bki_find_index ( bki, "*", 5, ndx_name, &ndx_type,
			&ndx_count );
fprintf(stderr, "Find status for default index: %d, name: %s\n", 
status, ndx_name );
		table = ndx_name;
	    }

	    status = bki_open_index ( bki, table );
		if ( (status&1) == 0 ) {
		    cgi_printf("Unable to open index '%s'\n", ndx_name );
		    break;
		}
	    cgi_printf("<HR><H2>%s</H2>(%d entries)<UL>\n", entify(table), 
			ndx_count );
	    for ( ; ; ) {
		int first;
		status = bki_read_index( bki, ndx_hdr, attr, ndx_name, 
			&desc, &ndx_value);

		if ( (status&1) == 0 ) break;
		if ( ndx_value <= 0 || ndx_value > root->first.sectioncount) {
		    cgi_printf("</UL>%s<UL>\n", entify(desc) );
	  	} else {
	            status = bkf_lookup_first_section 
			( bkf, ndx_value, &part_num, &first );
		    cgi_printf("<LI><A HREF=\"%s.p%d.%s#%d\">%s</A></LI><BR>\n", 
			href_fname, part_num, href_type, ndx_value, 
			entify(desc) );
		}
	     }
	     cgi_printf("</UL>\n");

	break;
	case 'g':
	case 'G':
	    /*
	     * Return figure as graphic image, argument is section number.
	     */
	   	cgi_printf ( "Content-type: image/gif\n\n" );
		single_sect = atoi ( &bookpath[select+1] );
	        status = bkf_lookup_section ( bkf, single_sect, &part_num );
		if ( (status&1) == 1 )	status = show_image(bkf, single_sect);
		else {
		}
	default:
	break;
    }
    if ( bookpath[select] != 'g' && bookpath[select] != 'G' )
	cgi_printf("</BODY>\n");

    /*
     * Cleanup.
     */
    status = bkf_close ( bkf );
    return status;
}
/* Sub-record summary information */
struct sb_summary {
	int type;
	int length;
	struct sb_summary *hot;		/* link to hotspot def */
	long hdr[9];
};

struct convert_ctx {
    /* input state (bookreader) */
    void *bkf, *cursor;
    bktxt_fntptr fontdef;
    int font_count, cur_font;
    int in_x, in_y;

    /* output state (HTML) */
    int bold_on;
    int italic_on;
    int monospace_on;
    int font_nonprintable;
    int hdr_level;
    int dl_depth;		/* indentation level ((x-100)/50)) */
    int out_x, out_y;		/* virtual */
};


static struct sb_summary *check_hotspot 
	( short x, short y, struct sb_summary *hot )
{
    for ( ; hot; hot = hot->hot ) {
	/* cgi_printf("\ncheckhot x: %d y: %d, spot: %d %d lw: %d %d\n", x, y,
	  hot->hdr[3],hot->hdr[4],hot->hdr[5],hot->hdr[6]); */
	if ( hot->hdr[4] <= y && hot->hdr[4]+hot->hdr[6] >= y &&
	   hot->hdr[3] <= x && hot->hdr[3] + hot->hdr[5] >= x ) return hot;
    }
    return hot;
}
/*
 * Generate necessary HTML to change from current font to desired font and
 * update state.
 */
static int change_font ( unsigned char new_font, struct convert_ctx *cvt )
{
    bktxt_fntptr fnt;
    if ( new_font < 0 || new_font >= cvt->font_count ) return 0;

    cvt->cur_font = new_font;
    fnt = &cvt->fontdef[new_font];
    if ( cvt->bold_on ) {
	if ( fnt->weight[0] != 'B' ) { cvt->bold_on = 0; cgi_printf("</B>"); }
    } else {
	if ( fnt->weight[0] == 'B' ) { cvt->bold_on = 1; cgi_printf("<B>"); }
    }

    if ( cvt->italic_on ) {
	if ( fnt->style[0] != 'I' ) { cvt->italic_on = 0; cgi_printf("</I>"); }
    } else {
	if ( fnt->style[0] == 'I' ) { cvt->italic_on = 1; cgi_printf("<I>"); }
    }
    if ( cvt->monospace_on ) {
	if ( fnt->spacing[0] != 'P' ) { cvt->monospace_on = 1; cgi_printf("<TT>"); }
    } else {
	/* test for proportional spacing */
	if ( fnt->style[0] == 'P' ) { cvt->monospace_on = 0; cgi_printf("</TT>"); }
    }
    if ( cvt->font_nonprintable ) {
	if ( fnt->encoding[0] != '*' ) cvt->font_nonprintable = 0;
    } else {
	if ( fnt->encoding[0] == '*' ) cvt->font_nonprintable = 1;
    }
    return 1;
}

/*
 * Generate HTML to perform line breaks.
 */
#ifdef OLD_WAY
static void change_line ( int new_x, int new_y, struct convert_ctx *cvt )
{
    int target_dl;

    target_dl = (new_x-100) / 50;
    if ( target_dl > cvt->dl_depth ) {
	/* Change indentation level */
	while ( target_dl > cvt->dl_depth ) {
	    cgi_printf("<DL><DD>");
	    cvt->dl_depth++;
	}
    } else if ( target_dl < cvt->dl_depth ) {
	while ( target_dl < cvt->dl_depth ) {
	    cgi_printf("</DD></DL>");
	    cvt->dl_depth--;
	}
	cgi_printf("\n");
    } else {

        cgi_printf( (new_y - cvt->in_y) > 100 ? "<P>\n" : "<BR>\n");
    }
    cvt->in_x = cvt->dl_depth * 50;
}
#endif
static void change_line ( int new_x, int new_y, struct convert_ctx *cvt, int para )
{
    int target_dl, i;
    char indent[200];

    target_dl = ((new_x) / 50) * 2;
    if ( target_dl > 120 ) target_dl = 60;
    if ( target_dl < 0 ) target_dl = 0;
    for ( i = 0; i < target_dl; i++ ) indent[i] = nbsp;
    indent[target_dl] = '\0';
    cgi_printf ( "%s\n%s", para ? "<p>" : "<br>", indent );
}

static int format_bodytext ( struct convert_ctx *cvt, struct sb_summary *sb )
{
    int status, is_last, t_len, t_type, i, offset, glue, slen, is_link;
    short h_v[2]; unsigned char attr[4]; char *data; char buffer[256];
    /*
     * Scan sub-sections.
     */
    cgi_printf ( "<A NAME=\"%d\">", sb->hdr[1] );
    for ( is_last = 0; !is_last; ) {
	status = bks_read_section 
		( cvt->cursor, &t_type, h_v, attr, &t_len, &data, &is_last );
	if ( (status&1) == 0 ) 
		    cgi_printf("%sread error, status: %d, is_last: %d%s\n",
			is_last ? "<!-- ":"", status, is_last, 
			is_last ? " -->" :"" );
	if ( (status&1) == 0 ) break;
#ifdef VERBOSE
	cgi_printf("<!-- subtxt: t=%d h=%d v=%d attr: %d %d %d %d -->\n",
	    t_type, h_v[0], h_v[1], attr[0], attr[1], attr[2], attr[3] );
#endif

	if ( t_type == 3 || t_type == 2 ) {
	    struct sb_summary *hot_link;

	    if ( attr[0] != cvt->cur_font ) change_font ( attr[0], cvt );
	    if ( cvt->in_y > h_v[1] ) {
		change_line ( h_v[0], h_v[1], cvt, 1 );
	    } else if ( (cvt->in_x > h_v[0]) || (cvt->in_y < h_v[1]) ) {
		change_line ( h_v[0], h_v[1], cvt, 0 );
	    } else cgi_printf ( " " ); /* separate words */

	    hot_link = sb->hot;
	    if (hot_link) hot_link = check_hotspot ( h_v[0], h_v[1], sb->hot );
	    if ( hot_link ) {
		if ( hot_link->hdr[7] == sb->hdr[1] )
		    cgi_printf ( "<A HREF=\"#7%d\">", hot_link->hdr[7] );
		else {
		    int part_num;
		    bkf_lookup_section ( cvt->bkf, 
			hot_link->hdr[7], &part_num );
		    cgi_printf ( "<A HREF=\"%s.p%d.%s#%d\">", href_fname,
			part_num, href_type, hot_link->hdr[7] );
		}
	    }

	    cvt->in_x = h_v[0]; cvt->in_y = h_v[1];
	    for ( offset = 0; offset < t_len; ) {
		bkt_text3_scan ( t_len, data, &offset, buffer, &slen, &glue );
		if ( offset < t_len ) buffer[slen++] = ' ';
		buffer[slen] = '\0';
		if ( cvt->font_nonprintable ) { char jj;
		    for ( jj = 0; jj < slen; jj++ ) buffer[jj] = '.';
		    cvt->in_x = 1000;
		}
		cgi_printf("%s", entify(buffer));
#ifdef VERBOSE
		cgi_printf("<g%x>", glue);
#endif
	    }

	    if ( hot_link ) cgi_printf ( "</A>" );
	}else if ( t_type == 1 ) {
	    cgi_printf("<HR> <!-- type 1 subrec -->\n");
	    cvt->in_x = 0; cvt->in_y += 40;
	}
    }
    cgi_printf("</A><BR>\n");
    cvt->in_x = 0;
    return status;
}


static int show_part ( void * bkf, int part_num, bktxt_fntptr fontdef, 
	int font_count )
{
    struct convert_ctx cvt;
    char *desc;
    long ndx_value, hdr[9];
    int j, i, part_info[4], sect_info[4];
    int status, count, match, iter, iter_count, type, length, is_last;
    short ndx_hdr[9];
    char name[256];
    struct sb_summary *sb;
    /*
     * Create data structures used by bookfile_section.c
     */
    cvt.bkf = bkf;
    cvt.fontdef = fontdef;
    cvt.font_count = font_count;
    cvt.hdr_level = 0;
    cvt.dl_depth = 0;
    status = bks_create_section_cursor ( bkf, &cvt.cursor );
    if ( (status&1) == 0 ) {
        cgi_printf("Error creating cursor: %d\n", status );
	exit ( status );
    }
    /*
     * Position to begining of part, seek function returns number of
     * sub-records (sections) in part.
     */
    status = bks_seek_part ( cvt.cursor, part_num, 0, &iter_count );
    if ( (status&1) == 0 ) {
         cgi_printf("Error seeking part: %d\n", status );
	exit ( status );
    }
    /*
     * Make links to previous and next.
     */
    status = bks_get_cursor_info ( cvt.cursor, part_info, sect_info );
    if ( (status&1) == 1 ) {
	if ( part_info[2] > 0 )	cgi_printf("[<A HREF=\"%s.p%d.%s\">next</A>] ", 
		href_fname, part_info[2], href_type );
	else cgi_printf ( "[next] " );
	if ( part_info[1] > 0 ) cgi_printf("[<A HREF=\"%s.p%d.%s\">previous</A>] ", 
		href_fname, part_info[1], href_type );
	else cgi_printf ( "[previous] " );
	cgi_printf("[<A HREF=\"%s.%s\">contents</A>]<BR><HR>\n", href_fname,
		href_type );
    }
    /*
     * Allocate array to hold summary information.
     */
    sb = (struct sb_summary *) malloc(iter_count*sizeof(struct sb_summary));
    if ( !sb ) return 0;
    for ( iter = 0; iter < iter_count; iter++ ) 
		sb[iter].hot = (struct sb_summary *) 0;
    /*
     * Make first pass over part to get header info and hotspot info.
     */
    for ( iter = 0; iter < iter_count; iter++ ) {
	status = bks_seek_section ( cvt.cursor, iter? 1 : 0, 1, &sb[iter].type, 
		&sb[iter].length, sb[iter].hdr );
	if ( (status&1) == 0 ) cgi_printf("Error in seek: %d\n", status);
	if ( (status&1) == 0 ) break;
	if ( sb[iter].type == BKSBREC_FIGHOT ) {
	    i = sb[iter].hdr[2];
	    for ( j = iter-1; j >=0; --j ) if ( sb[j].type == BKSBREC_BODYTEXT
			&& (sb[j].hdr[1] == i) ) {
	        sb[iter].hot = sb[j].hot;
	        sb[j].hot = &sb[iter];
	    }
	}
    }
    /*
     * Make second pass over data to output it.
     */
    cvt.cur_font = -1;
    cvt.in_x = 0;
    cvt.in_y = 10000;
    cvt.bold_on = cvt.italic_on = cvt.monospace_on = 0;
    cvt.font_nonprintable = 0;
    status = bks_seek_part ( cvt.cursor, part_num, 0, &iter_count );
    if ( (status&1) == 0 ) {
         cgi_printf("Error seeking part: %d\n", status );
	exit ( status );
    }
    for ( iter = 0; iter < iter_count; iter++ ) {
	status = bks_seek_section ( cvt.cursor, iter? 1 : 0, 1, &type, 
		&length, hdr );
	if ( (status&1) == 0 ) break;
	/* cgi_printf("section type: %d, length: %d nxthost: %d\n", 
		type, length, sb[iter].hot ); */

	if ( type == BKSBREC_BODYTEXT ) {
#ifdef VERBOSE
	    int k; 
	    static char *hnm[9] = { "[0]", "sec", "[2]", "[3]", "[4]",
			"[5]", "hgt", "[7]", "[8]" };
	    cgi_printf("\n<!-- bodytext hdr:" );
	    for ( k = 0; k < 9; k++ ) if ( hdr[k] ) cgi_printf ( " %s=%d",
			hnm[k], hdr[k] );
	    cgi_printf("-->\n");
#endif
	    status = format_bodytext ( &cvt, &sb[iter] );
	} else if ( type == BKSBREC_FIGURE ) {
	    int t_len, t_type, is_last;
	    short h_v[2]; unsigned char attr[4]; char *data; char buffer[256];
	    cgi_printf(
		"<!-- figure sec %d pos: (%d,%d) size: (%d %d)=%d type: %d -->\n",
		hdr[2], hdr[3], hdr[4], hdr[5], hdr[6], length, hdr[7]&255 );
	    /* */
	    status = bks_read_section 
		( cvt.cursor, &t_type, h_v, attr, &t_len, &data, &is_last );

	    cgi_printf("<IMG SRC=\"%s.g%d.%s\">", 
		href_fname, hdr[2], href_type );
	}
    }
    if ( cvt.monospace_on ) cgi_printf("</TT>");
    if ( cvt.italic_on ) cgi_printf("</I>");
    if ( cvt.bold_on ) cgi_printf("</B>");
    bks_delete_section_cursor ( cvt.cursor );
    return status; 
}

static int out_to_netlink ( void *fptr, int size, char *buffer )
{
    int status;
#ifndef NOCGILIB
    status = net_link_write ( buffer, size );
    return status;
#else
    status = fwrite ( buffer, size, 1, (FILE *) fptr );
    return 1;
#endif
}
static int show_image ( void *bkf, int sect_num )
{
    void *cursor;
    long hdr[9];
    int j, i, k, width, height, mask, value, t_type, t_len;
    int status, count, match, type, length, is_last;
    short *cols, *rows;
    short h_v[2]; unsigned char attr[4];
    char *data;
    unsigned char *cur_row, *cur_col;
    unsigned char *pixel_image, *pixel_row;
    unsigned char color_table[6] = { 0, 0, 0, 255, 255, 255 };
    /*
     * Seek to section containing image.
     */
    status = bks_create_section_cursor ( bkf, &cursor );
    if ( (status&1) == 1 ) status = bks_seek_section 
	( cursor, sect_num, 0, &type, &length, hdr );
    while ( (type != BKSBREC_FIGURE) && ((status&1) == 1)  ) {
	status = bks_seek_section ( cursor, 1, 1, &type, &length, hdr );
    }
    if ( (status&1) == 1 ) status = bks_read_section 
		( cursor, &t_type, h_v, attr, &t_len, &data, &is_last );
    if ( (status&1) == 0 ) {
        cgi_printf("Error retrieving figure data: %d\n", status );
	exit ( status );
    }
    if ( type == BKSBREC_FIGURE ) {
	/*
	 * Create gif file, with data going back to net link.
	 */
#ifdef NOCGILIB
	char fname[256];
	FILE *out;
	sprintf ( fname, "%s-g%d.gif", href_fname, sect_num );
	out = fopen ( fname, "wb" );
	if ( !out ) { 
	    fprintf(stderr, "Error openning '%s'\n", fname );
	} else {
#else
	    void *out;
	    out = (void *) 0;
#endif
	    status = bkg_convert_figure_to_gif 
		( hdr, (unsigned char *) data, t_len, out_to_netlink, out );
#ifdef NOCGILIB
	    fprintf(stderr,"status of gif generate (%s): %d\n", fname, status );
	    fclose ( out );
	}
#endif
    }
    /*
     * Cleanup up.
     */
    bks_delete_section_cursor ( cursor );
    return status;
}
/****************************************************************************/
/* Convert strings containing punctuation characters into escaped strings.
 */
static char *escape_string ( char *source )
{
   int i, j, punct_count;
   char *dest;
   dest = source;
   punct_count = 0;
   for ( i = 0; source[i]; i++ ) if ( ispunct ( source[i] ) ) {
	if ( (source[i] != '.') && (source[i] != '$') )punct_count++;
   } else if ( isspace ( source[i] ) ) punct_count++;

   if ( punct_count > 0 ) {
	dest = malloc ( i + punct_count*3 + 1 );
	if ( !dest ) return source;

	for ( i = j = 0; source[i]; i++ ) {
	    if ( isspace(source[i]) || (ispunct ( source[i] ) &&
			(source[i] != '.') && (source[i] != '$')) ) {
		sprintf ( &dest[j], "%%%02x", source[i] );
		if ( dest[j+1] == ' ' ) dest[j+1] = '0';
		j += 3;
	    } else
		dest[j++] = source[i];
	}
	dest[j] = '\0';
   }
   return dest;
}
