[0001]
[0002]
[0003]
[0004]
[0005]
[0006]
[0007]
[0008]
[0009]
[0010]
[0011]
[0012]
[0013]
[0014]
[0015]
[0016]
[0017]
[0018]
[0019]
[0020]
[0021]
[0022]
[0023]
[0024]
[0025]
[0026]
[0027]
[0028]
[0029]
[0030]
[0031]
[0032]
[0033]
[0034]
[0035]
[0036]
[0037]
[0038]
[0039]
[0040]
[0041]
[0042]
[0043]
[0044]
[0045]
[0046]
[0047]
[0048]
[0049]
[0050]
[0051]
[0052]
[0053]
[0054]
[0055]
[0056]
[0057]
[0058]
[0059]
[0060]
[0061]
[0062]
[0063]
[0064]
[0065]
[0066]
[0067]
[0068]
[0069]
[0070]
[0071]
[0072]
[0073]
[0074]
[0075]
[0076]
[0077]
[0078]
[0079]
[0080]
[0081]
[0082]
[0083]
[0084]
[0085]
[0086]
[0087]
[0088]
[0089]
[0090]
[0091]
[0092]
[0093]
[0094]
[0095]
[0096]
[0097]
[0098]
[0099]
[0100]
[0101]
[0102]
[0103]
[0104]
[0105]
[0106]
[0107]
[0108]
[0109]
[0110]
[0111]
[0112]
[0113]
[0114]
[0115]
[0116]
[0117]
[0118]
[0119]
[0120]
[0121]
[0122]
[0123]
[0124]
[0125]
[0126]
[0127]
[0128]
[0129]
[0130]
[0131]
[0132]
[0133]
[0134]
[0135]
[0136]
[0137]
[0138]
[0139]
[0140]
[0141]
[0142]
[0143]
[0144]
[0145]
[0146]
[0147]
[0148]
[0149]
[0150]
[0151]
[0152]
[0153]
[0154]
[0155]
[0156]
[0157]
[0158]
[0159]
[0160]
[0161]
[0162]
[0163]
[0164]
[0165]
[0166]
[0167]
[0168]
[0169]
[0170]
[0171]
[0172]
[0173]
[0174]
[0175]
[0176]
[0177]
[0178]
[0179]
[0180]
[0181]
[0182]
[0183]
[0184]
[0185]
[0186]
[0187]
[0188]
[0189]
[0190]
[0191]
[0192]
[0193]
[0194]
[0195]
[0196]
[0197]
[0198]
[0199]
[0200]
[0201]
[0202]
[0203]
[0204]
[0205]
[0206]
[0207]
[0208]
[0209]
[0210]
[0211]
[0212]
[0213]
[0214]
[0215]
[0216]
[0217]
[0218]
[0219]
[0220]
[0221]
[0222]
[0223]
[0224]
[0225]
[0226]
[0227]
[0228]
[0229]
[0230]
[0231]
[0232]
[0233]
[0234]
[0235]
[0236]
[0237]
[0238]
[0239]
[0240]
[0241]
[0242]
[0243]
[0244]
[0245]
[0246]
[0247]
[0248]
[0249]
[0250]
[0251]
[0252]
[0253]
[0254]
[0255]
[0256]
[0257]
[0258]
[0259]
[0260]
[0261]
[0262]
[0263]
[0264]
[0265]
[0266]
[0267]
[0268]
[0269]
[0270]
[0271]
[0272]
[0273]
[0274]
[0275]
[0276]
[0277]
[0278]
[0279]
[0280]
[0281]
[0282]
[0283]
[0284]
[0285]
[0286]
[0287]
[0288]
[0289]
[0290]
[0291]
[0292]
[0293]
[0294]
[0295]
[0296]
[0297]
[0298]
[0299]
[0300]
[0301]
[0302]
[0303]
[0304]
[0305]
[0306]
[0307]
[0308]
[0309]
[0310]
[0311]
[0312]
[0313]
[0314]
[0315]
[0316]
[0317]
[0318]
[0319]
[0320]
[0321]
[0322]
[0323]
[0324]
[0325]
[0326]
[0327]
[0328]
[0329]
[0330]
[0331]
[0332]
[0333]
[0334]
[0335]
[0336]
[0337]
[0338]
[0339]
[0340]
[0341]
[0342]
[0343]
[0344]
[0345]
[0346]
[0347]
[0348]
[0349]
[0350]
[0351]
[0352]
[0353]
[0354]
[0355]
[0356]
[0357]
[0358]
[0359]
[0360]
[0361]
[0362]
[0363]
[0364]
[0365]
[0366]
[0367]
[0368]
[0369]
[0370]
[0371]
[0372]
[0373]
[0374]
[0375]
[0376]
[0377]
[0378]
[0379]
[0380]
[0381]
[0382]
[0383]
[0384]
[0385]
[0386]
[0387]
[0388]
[0389]
[0390]
[0391]
[0392]
[0393]
[0394]
[0395]
[0396]
[0397]
[0398]
[0399]
[0400]
[0401]
[0402]
[0403]
[0404]
[0405]
[0406]
[0407]
[0408]
[0409]
[0410]
[0411]
[0412]
[0413]
[0414]
[0415]
[0416]
[0417]
[0418]
[0419]
[0420]
[0421]
[0422]
[0423]
[0424]
[0425]
[0426]
[0427]
[0428]
[0429]
[0430]
[0431]
[0432]
[0433]
[0434]
[0435]
[0436]
[0437]
[0438]
[0439]
[0440]
[0441]
[0442]
[0443]
[0444]
[0445]
[0446]
[0447]
[0448]
[0449]
[0450]
[0451]
[0452]
[0453]
[0454]
[0455]
[0456]
[0457]
[0458]
[0459]
[0460]
[0461]
[0462]
[0463]
[0464]
[0465]
[0466]
[0467]
[0468]
[0469]
[0470]
[0471]
[0472]
[0473]
[0474]
[0475]
[0476]
[0477]
[0478]
[0479]
[0480]
[0481]
[0482]
[0483]
[0484]
[0485]
[0486]
[0487]
[0488]
[0489]
[0490]
[0491]
[0492]
[0493]
[0494]
[0495]
[0496]
[0497]
[0498]
[0499]
[0500]
[0501]
[0502]
[0503]
[0504]
[0505]
[0506]
[0507]
[0508]
[0509]
[0510]
[0511]
[0512]
[0513]
[0514]
[0515]
[0516]
[0517]
[0518]
[0519]
[0520]
[0521]
[0522]
[0523]
[0524]
[0525]
[0526]
[0527]
[0528]
[0529]
[0530]
[0531]
[0532]
[0533]
[0534]
[0535]
[0536]
[0537]
[0538]
[0539]
[0540]
[0541]
[0542]
[0543]
[0544]
[0545]
[0546]
[0547]
[0548]
[0549]
[0550]
[0551]
[0552]
[0553]
[0554]
[0555]
[0556]
[0557]
[0558]
[0559]
[0560]
[0561]
[0562]
[0563]
[0564]
[0565]
[0566]
[0567]
[0568]
[0569]
[0570]
[0571]
[0572]
[0573]
[0574]
[0575]
[0576]
[0577]
[0578]
[0579]
[0580]
[0581]
[0582]
[0583]
[0584]
[0585]
[0586]
[0587]
[0588]
[0589]
[0590]
[0591]
[0592]
[0593]
[0594]
[0595]
[0596]
[0597]
[0598]
[0599]
[0600]
[0601]
[0602]
[0603]
[0604]
[0605]
[0606]
[0607]
[0608]
[0609]
[0610]
[0611]
[0612]
[0613]
[0614]
[0615]
[0616]
[0617]
[0618]
[0619]
[0620]
[0621]
[0622]
[0623]
[0624]
[0625]
[0626]
[0627]
[0628]
[0629]
[0630]
[0631]
[0632]
[0633]
[0634]
[0635]
[0636]
[0637]
[0638]
[0639]
[0640]
[0641]
[0642]
[0643]
[0644]
[0645]
[0646]
[0647]
[0648]
[0649]
[0650]
[0651]
[0652]
[0653]
[0654]
[0655]
[0656]
[0657]
[0658]
[0659]
[0660]
[0661]
[0662]
[0663]
[0664]
[0665]
[0666]
[0667]
[0668]
[0669]
[0670]
[0671]
[0672]
[0673]
[0674]
[0675]
[0676]
[0677]
[0678]
[0679]
[0680]
[0681]
[0682]
[0683]
[0684]
[0685]
[0686]
[0687]
[0688]
[0689]
[0690]
[0691]
[0692]
[0693]
[0694]
[0695]
[0696]
[0697]
[0698]
[0699]
[0700]
[0701]
[0702]
[0703]
[0704]
[0705]
[0706]
[0707]
[0708]
[0709]
[0710]
[0711]
[0712]
[0713]
[0714]
[0715]
[0716]
[0717]
[0718]
[0719]
[0720]
[0721]
[0722]
[0723]
[0724]
[0725]
[0726]
[0727]
[0728]
[0729]
[0730]
[0731]
[0732]
[0733]
[0734]
[0735]
[0736]
[0737]
[0738]
[0739]
[0740]
[0741]
[0742]
[0743]
[0744]
[0745]
[0746]
[0747]
[0748]
[0749]
[0750]
[0751]
[0752]
[0753]
[0754]
[0755]
[0756]
[0757]
[0758]
[0759]
[0760]
[0761]
[0762]
[0763]
[0764]
[0765]
[0766]
[0767]
[0768]
[0769]
[0770]
[0771]
[0772]
[0773]
[0774]
[0775]
[0776]
/*
 * 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;
/* 12-MAY-2000  MGD  (char)! */
    if ( (char)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;
}