#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], "<" ); i += 4;
} else if ( *p == '>' ) {
strcpy ( &fixup[i], ">" ); i += 4;
} else if ( *p == '&' ) {
strcpy ( &fixup[i], "&" ); 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" );
cgi_printf("%s\n", entify(root->first.title));
cgi_printf("\n",
root->first.partcount, root->first.sectioncount );
if ( root->first.author[0] ) cgi_printf("\n",
root->first.author );
cgi_printf("\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 ( "Available tables:
\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("- %s (%d entries)
\n",
href_fname, ndx_name, href_type,
ndx_name, ndx_count );
}
bki_find_index_end ( bki );
cgi_printf("
\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("
%s
(%d entries)\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("
%s\n", entify(desc) );
} else {
status = bkf_lookup_first_section
( bkf, ndx_value, &part_num, &first );
cgi_printf("- %s
\n",
href_fname, part_num, href_type, ndx_value,
entify(desc) );
}
}
cgi_printf("
\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("\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(""); }
} else {
if ( fnt->weight[0] == 'B' ) { cvt->bold_on = 1; cgi_printf(""); }
}
if ( cvt->italic_on ) {
if ( fnt->style[0] != 'I' ) { cvt->italic_on = 0; cgi_printf(""); }
} else {
if ( fnt->style[0] == 'I' ) { cvt->italic_on = 1; cgi_printf(""); }
}
if ( cvt->monospace_on ) {
if ( fnt->spacing[0] != 'P' ) { cvt->monospace_on = 1; cgi_printf(""); }
} else {
/* test for proportional spacing */
if ( fnt->style[0] == 'P' ) { cvt->monospace_on = 0; cgi_printf(""); }
}
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("- ");
cvt->dl_depth++;
}
} else if ( target_dl < cvt->dl_depth ) {
while ( target_dl < cvt->dl_depth ) {
cgi_printf("
");
cvt->dl_depth--;
}
cgi_printf("\n");
} else {
cgi_printf( (new_y - cvt->in_y) > 100 ? "\n" : "
\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 ? "
" : "
", 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 ( "", 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 ? "" :"" );
if ( (status&1) == 0 ) break;
#ifdef VERBOSE
cgi_printf("\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 ( "", hot_link->hdr[7] );
else {
int part_num;
bkf_lookup_section ( cvt->bkf,
hot_link->hdr[7], &part_num );
cgi_printf ( "", 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("", glue);
#endif
}
if ( hot_link ) cgi_printf ( "" );
}else if ( t_type == 1 ) {
cgi_printf("
\n");
cvt->in_x = 0; cvt->in_y += 40;
}
}
cgi_printf("
\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("[next] ",
href_fname, part_info[2], href_type );
else cgi_printf ( "[next] " );
if ( part_info[1] > 0 ) cgi_printf("[previous] ",
href_fname, part_info[1], href_type );
else cgi_printf ( "[previous] " );
cgi_printf("[contents]
\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\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(
"\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("",
href_fname, hdr[2], href_type );
}
}
if ( cvt.monospace_on ) cgi_printf("");
if ( cvt.italic_on ) cgi_printf("");
if ( cvt.bold_on ) cgi_printf("");
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;
}