/*
 * This program is a CGI script for presenting a bookreader bookshelf
 * heirarchy as HTML.
 *
 * The first element path_info string is assumed to be the name of
 * the bookshelf file to read (default decw$book:library.decw$bookshelf).  
 * Succeeding elements are used to hold the history of which shelves were 
 * travered to get to the current shelf.
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "cgilib.h"

#define SHELF_ICON "<IMG SRC=\"/demo/shelf.xbm\">"
#define BOOK_ICON "<IMG SRC=\"/demo/book.xbm\">"
#define BOOK_SCRIPT "/htbin/webbook/"

struct shelf_rec {
    struct shelf_rec *next;
    char *type;		/* first element. */
    char *fname;
    char *desc;
};
typedef struct shelf_rec *shelf_recp;

static shelf_recp get_shelf_rec ( FILE *sf, shelf_recp prev );
static char *escape_string ( char * );
static char *entify_string ( char * );
static char *apply_default ( char *fname, char *defname, char *out );
static int send_http_header ( char *stsline, char *content );

int main ( int argc, char **argv )
{
   char *path, *title, *elem[20];
   int status, i, j, lvl;
   FILE *sf;
   struct shelf_rec *rec, first_rec;
   char shelf_spec[256];
   char shelf_history[2048];

   /*
    * Establish communication with http server and set up CGI environment
    */
   status = cgi_init ( argc, argv );
   if ( (status&1) == 0 ) exit ( status );

   path = cgi_info ( "PATH_INFO" );
   if ( !path ) {
	send_http_header ( "500 bad path info", "text/plain" );
	return 20;
   }
   /*
    * Parse the path elements.
    */
   strncpy ( shelf_history, path, sizeof(shelf_history)-1 );
   path = shelf_history;
   path[sizeof(shelf_history)-1] = '\0';
   if ( *path == '/' ) path++;
   elem[0] = path;
   for ( j = i = 0; path[i]; i++ ) if ( path[i] == '/' ) {
	path[i] = '\0';
	j++;
	elem[j] = &path[i+1];
	if ( j > 19 ) break;
   }
   /*
    * Attempt to open the shelf file and parse.
    */
   first_rec.next = (shelf_recp) 0;
   first_rec.type = "";
   rec = &first_rec;
   title = path;
   sf = fopen ( elem[j], "r", "dna=decw$book:.decw$bookshelf", "mbc=64" );
   if ( sf ) {
	while ( rec = get_shelf_rec ( sf, rec ) ) {
	    if ( 0 == strcmp ( rec->type, "TITLE" ) ) {
		title = rec->desc;
	    }
	}
	fgetname(sf, shelf_spec, 1);	/* full file name of bookshelf */
	fclose ( sf );
   }
   /*
    * Generate HTML header.
    */
   send_http_header ( "200 Sending generated HTML\n", "Text/html" );
   cgi_printf("<HTML>\n<HEAD><TITLE>%s</TITLE></HEAD>\n", 
	entify_string(title) );
   cgi_printf("<BODY>\n<H2>%s</H2><P>\n", path);
   /*
    *  Generate history path.
    */
   lvl = 2;
   for ( i = 0; i < j; i++ ) {
	lvl++;
	if ( lvl > 6 ) lvl = 6;
	cgi_printf ( "<H%d>%s</H%d>\n", lvl, entify_string(elem[i]), lvl );
   }
   cgi_printf("<BR><HR>\n");
   /*
    * Generate directory with links to each.
    */
   if ( ! first_rec.next ) {
	cgi_printf ( "Error opening bookshelf file %s\n", path);
   }
   for ( rec = first_rec.next; rec; rec = rec->next ) {
	char *escaped, *entified;
	char target[256];
	apply_default ( rec->fname, shelf_spec, target );
	entified = entify_string(rec->desc);
	if ( 0 == strcmp ( rec->type, "SHELF" ) ) {
	    escaped = escape_string(target);
	    cgi_printf("%s<A HREF=\"%s/%s\">%s</A><BR>\n", SHELF_ICON, 
		escape_string(rec->desc), escaped, entified );
	} else if ( 0 == strcmp ( rec->type, "BOOK" ) ) {
	    escaped = escape_string(target);
	    cgi_printf("%s<A HREF=\"%s%s\">%s</A><BR>\n", BOOK_ICON, 
			BOOK_SCRIPT, escaped, entified );
	}
   }
   cgi_printf ( "</BODY></HTML>\n" );
}
/****************************************************************************/
/* 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;
}
/****************************************************************************/
/* Convert strings containing punctuation characters into escaped strings.
 */
static char *entify_string ( char *source )
{
   int i, j,brack_count;
   char *dest;
   dest = source;
   brack_count = 0;
   for ( i = 0; source[i]; i++ ) {
	if ( (source[i] == '<') || (source[i] == '>') ||
	    (source[i] == '&') ) brack_count++;
   }
   if ( brack_count > 0 ) {
	dest = malloc ( i + brack_count*4 + 1 );
	if ( !dest ) return source;

	for ( i = j = 0; source[i]; i++ ) {
	    if ( source[i] == '<' ) {
		dest[j++] = '&';
		dest[j++] = 'l';
		dest[j++] = 't';
		dest[j++] = ';';
	    } else if ( source[i] == '>' ) {
		dest[j++] = '&';
		dest[j++] = 'g';
		dest[j++] = 't';
		dest[j++] = ';';
	    } else if ( source[i] == '&' ) {
		dest[j++] = '&';
		dest[j++] = 'a';
		dest[j++] = 'm';
		dest[j++] = 'p';
		dest[j++] = ';';
	    } else
		dest[j++] = source[i];
	}
	dest[j] = '\0';
   }
   return dest;
}
/****************************************************************************/
/* Read next record from bookshelf file and copy to structure.
 */
static shelf_recp get_shelf_rec ( FILE *sf, shelf_recp previous )
{
    int i,j;
    shelf_recp cur;
    char *line, tline[4096];
    /*
     * Be optimistice and allocate new structure.
     */
    cur = malloc ( sizeof(struct shelf_rec) );
    if ( !cur ) return cur;
    cur->next = (shelf_recp) 0;
    cur->type = cur->fname = cur->desc = "";

    if ( fgets ( tline, sizeof(tline)-1, sf ) ) {
	/*
	 * Copy and Parse the record on backslashes.
	 */
	for ( i = 0; i < sizeof(tline) && tline[i] && tline[i] != '\n'; i++ );
	line = malloc ( i + 1 );
	strncpy ( line, tline, i );
	if ( !line ) return (shelf_recp) 0;
	line[i] = '\0';

	cur->type = line;
	for ( j = i = 0; line[i]; i++ ) if ( line[i] == '\\' ) {
	    line[i] = '\0';
	    if ( j == 0 ) {
		cur->fname = &line[i+1];
		j++;
	    } else {
		cur->desc = &line[i+1];		/* rest of line */
		break;
	    }
	}
	/*
	 * Unescape characters and upcase type.
	 */
	for ( i = 0; line[i]; i++ ) 
		if ( islower(line[i]) ) line[i] = _toupper(line[i]);
	
    } else {
	/*
	 * Read error, return null.
	 */
	free ( cur );
	cur = (shelf_recp) 0;
    }
    /*
     * append new record to list.
     */
    previous->next = cur;
    return cur;
}
/**************************************************************************/
/* Prepare to send back response.  Build standard response header.
 */
static int send_http_header ( char *stsline, char *content )
{
    int status;
    /*
     */
    cgi_printf ( "Content-type: %s\n", content );
    cgi_printf ( "status: %s\n\n", stsline );
    return 1;
}
/**************************************************************************/
/* Take default directory from defname and apply to fname.
 */
static char *apply_default ( char *fname, char *defname, char *out )
{
    int i, d_limit;

    for ( d_limit = i = 0; defname[i]; i++ ) {
	if ( defname[i] == ':' || defname[i] == ']' || defname[i] == '>' )
	    d_limit = i+1;
    }
    if ( d_limit == 0 ) {
	defname = "decw$book:";
	d_limit = 10;
    }
    for ( i = 0; fname[i]; i++ ) {
	if ( fname[i] == ':' || fname[i] == ']' || fname[i] == '>' )
	    d_limit = 0;
    }
    if ( d_limit ) strncpy ( out, defname, d_limit );
    if ( i+d_limit > 255 ) i = 255-d_limit;
    strncpy ( &out[d_limit], fname, i );
    out[i+d_limit] = '\0';
    return out;
}
