netscape-revival
lib/libnet/mkhelp.c
/* -*- Mode: C; tab-width: 4 -*- */
/***************************************************
*
* Routines to parse HTML help mapping file
*/
#include "mkutils.h"
#include "xp.h"
#include "mkparse.h"
extern int MK_OUT_OF_MEMORY;
#define DEFAULT_HELP_ID "DEFAULT"
#define DEFAULT_HELP_WINDOW_NAME "_HelpWindow"
#define DEFAULT_HELP_WINDOW_HEIGHT 500
#define DEFAULT_HELP_WINDOW_WIDTH 400
/* @@@ Global hack. See below */
PRIVATE URL_Struct * frame_content_for_pre_exit_routine=NULL;
typedef struct _HTMLHelpParseObj {
XP_Bool in_id_mapping;
int32 window_width;
int32 window_height;
char *window_name;
char *id_value;
char *default_id_value;
char *url_to_map_file;
char *id;
char *line_buffer;
int32 line_buffer_size;
char *content_target;
XP_List *frame_group_stack;
} HTMLHelpParseObj;
typedef struct {
char *address;
char *target;
} frame_set_struct;
PRIVATE void
simple_exit(URL_Struct *URL_s, int status, MWContext *window_id)
{
if(status != MK_CHANGING_CONTEXT)
NET_FreeURLStruct(URL_s);
}
PRIVATE void
net_help_free_frame_group_struct(frame_set_struct *obj)
{
XP_ASSERT(obj);
if(!obj)
return;
FREEIF(obj->address);
FREEIF(obj->target);
}
/* load the HTML help mapping file and search for
* the id or text to load a specific document
*/
PUBLIC void
NET_GetHTMLHelpFileFromMapFile(MWContext *context,
char *map_file_url,
char *id,
char *search_text)
{
URL_Struct *URL_s;
XP_ASSERT(map_file_url && id);
if(!map_file_url || !id)
return;
URL_s = NET_CreateURLStruct(map_file_url, NET_DONT_RELOAD);
if(!URL_s)
return;
URL_s->fe_data = XP_STRDUP(id);
NET_GetURL(URL_s, FO_CACHE_AND_LOAD_HTML_HELP_MAP_FILE, context, simple_exit);
}
PUBLIC HTMLHelpParseObj *
NET_ParseHTMLHelpInit(char *url_to_map_file, char *id)
{
HTMLHelpParseObj *rv = XP_NEW(HTMLHelpParseObj);
if(!rv)
return(NULL);
XP_ASSERT(url_to_map_file && id);
if(!url_to_map_file || !id)
return(NULL);
XP_MEMSET(rv, 0, sizeof(HTMLHelpParseObj));
rv->url_to_map_file = XP_STRDUP(url_to_map_file);
rv->id = XP_STRDUP(id);
rv->window_height = DEFAULT_HELP_WINDOW_HEIGHT;
rv->window_width = DEFAULT_HELP_WINDOW_WIDTH;
rv->frame_group_stack = XP_ListNew();
return(rv);
}
PUBLIC void
NET_ParseHTMLHelpFree(HTMLHelpParseObj * obj)
{
XP_ASSERT(obj);
if(!obj)
return;
FREEIF(obj->window_name);
FREEIF(obj->id_value);
FREEIF(obj->default_id_value);
FREEIF(obj->url_to_map_file);
FREEIF(obj->line_buffer);
FREEIF(obj->id);
if(obj->frame_group_stack)
{
frame_set_struct * frame_group_ptr;
while((frame_group_ptr = XP_ListRemoveTopObject(obj->frame_group_stack)))
net_help_free_frame_group_struct(frame_group_ptr);
FREE(obj->frame_group_stack);
}
FREE(obj);
}
/* parse a line that looks like
* [whitespace]=[whitespace]"token" other stuff...
*
* return token or NULL.
*/
PRIVATE char *
net_get_html_help_token(char *line_data, char**next_word)
{
char * line = line_data;
char * cp;
if(next_word)
*next_word = NULL;
while(isspace(*line)) line++;
if(*line != '=')
return(NULL);
line++; /* go past '=' */
while(isspace(*line)) line++;
if(*line != '"')
return(NULL);
line++; /* go past '"' */
for(cp=line; *cp; cp++)
if(*cp == '"' && *(cp-1) != '\\')
{
*cp = '\0';
if(next_word)
{
*next_word = cp+1;
while(isspace(*(*next_word))) (*next_word)++;
}
break;
}
return(line);
}
/* parse lines in an HTML help mapping file.
* get window_size and name, etc...
*
* when the id is found function returns HTML_HELP_ID_FOUND
* on error function returns negative error code.
*/
PRIVATE int
net_ParseHTMLHelpLine(HTMLHelpParseObj *obj, char *line_data)
{
char *line = XP_StripLine(line_data);
char *token;
char *next_word;
if(*line == '<')
{
/* find and terminate the end '>' */
XP_STRTOK(line, ">");
token = XP_StripLine(line+1);
#define ID_MAP_TOKEN "ID_MAP"
if(!strncasecomp(token,
ID_MAP_TOKEN,
sizeof(ID_MAP_TOKEN)-1))
{
obj->in_id_mapping = TRUE;
}
#define END_ID_MAP_TOKEN "/ID_MAP"
else if(!strncasecomp(token,
END_ID_MAP_TOKEN,
sizeof(END_ID_MAP_TOKEN)-1))
{
obj->in_id_mapping = FALSE;
}
#define FRAME_GROUP_TOKEN "FRAME_GROUP"
else if(!strncasecomp(token,
FRAME_GROUP_TOKEN,
sizeof(FRAME_GROUP_TOKEN)-1))
{
char *cp = token + sizeof(FRAME_GROUP_TOKEN)-1;
frame_set_struct * fgs = XP_NEW(frame_set_struct);
while(isspace(*cp)) cp++;
if(fgs)
{
XP_MEMSET(fgs, 0, sizeof(frame_set_struct));
next_word=NULL; /* init */
do {
#define SRC_TOKEN "SRC"
if(!strncasecomp(cp, SRC_TOKEN, sizeof(SRC_TOKEN)-1))
{
char *address = net_get_html_help_token(
cp+sizeof(SRC_TOKEN)-1,
&next_word);
cp = next_word;
fgs->address = XP_STRDUP(address);
}
#define WINDOW_TOKEN "DEFAULT_WINDOW"
else if(!strncasecomp(cp,
WINDOW_TOKEN,
sizeof(WINDOW_TOKEN)-1))
{
char *window = net_get_html_help_token(
cp+sizeof(WINDOW_TOKEN)-1,
&next_word);
cp = next_word;
fgs->target = XP_STRDUP(window);
}
else
{
/* unknown attribute. Skip to next whitespace
*/
while(*cp && !isspace(*cp))
cp++;
if(*cp)
{
while(isspace(*cp)) cp++;
next_word = cp;
}
else
{
next_word = NULL;
}
}
} while(next_word);
XP_ListAddObject(obj->frame_group_stack, fgs);
}
}
#define END_FRAME_GROUP_TOKEN "/FRAME_GROUP"
else if(!strncasecomp(token,
END_FRAME_GROUP_TOKEN,
sizeof(END_FRAME_GROUP_TOKEN)-1))
{
frame_set_struct *fgs;
fgs = XP_ListRemoveTopObject(obj->frame_group_stack);
if(fgs)
net_help_free_frame_group_struct(fgs);
}
}
else if(!obj->in_id_mapping)
{
#define WINDOW_SIZE_TOKEN "WINDOW-SIZE"
if(!strncasecomp(line,
WINDOW_SIZE_TOKEN,
sizeof(WINDOW_SIZE_TOKEN)-1))
{
/* get window size */
char *comma=0;
char *window_size = net_get_html_help_token(line+
sizeof(WINDOW_SIZE_TOKEN)-1,
NULL);
if(window_size)
comma = XP_STRCHR(window_size, ',');
if(comma)
{
*comma = '\0';
obj->window_width = XP_ATOI(window_size);
obj->window_height = XP_ATOI(comma+1);
}
}
#define WINDOW_NAME_TOKEN "WINDOW-NAME"
else if(!strncasecomp(line,
WINDOW_NAME_TOKEN,
sizeof(WINDOW_NAME_TOKEN)-1))
{
char *window_name = net_get_html_help_token(line+
sizeof(WINDOW_NAME_TOKEN)-1,
NULL);
if(window_name)
{
FREEIF(obj->window_name);
obj->window_name = XP_STRDUP(window_name);
}
}
}
else
{
/* id mapping pair */
if(!strncasecomp(line, obj->id, XP_STRLEN(obj->id)))
{
char *id_value = net_get_html_help_token(line+XP_STRLEN(obj->id),
&next_word);
if(id_value)
{
obj->id_value = XP_STRDUP(id_value);
while(next_word)
{
char *cp = next_word;
#define TARGET_TOKEN "TARGET"
if(!strncasecomp(cp,
TARGET_TOKEN,
sizeof(TARGET_TOKEN)-1))
{
char *target = net_get_html_help_token(
cp+sizeof(TARGET_TOKEN)-1,
&next_word);
cp = next_word;
obj->content_target = XP_STRDUP(target);
}
else
{
/* unknown attribute. Skip to next whitespace
*/
while(*cp && !isspace(*cp))
cp++;
if(*cp)
{
while(isspace(*cp)) cp++;
next_word = cp;
}
else
{
next_word = NULL;
}
}
}
}
return(HTML_HELP_ID_FOUND);
}
if(!strncasecomp(line, DEFAULT_HELP_ID, sizeof(DEFAULT_HELP_ID)-1))
{
char *default_id_value = net_get_html_help_token(
line+sizeof(DEFAULT_HELP_ID)-1,
NULL);
if(default_id_value)
obj->default_id_value = XP_STRDUP(default_id_value);
}
}
return(0);
}
int
NET_ParseHTMLHelpPut(HTMLHelpParseObj *obj, char *str, int32 len)
{
int32 string_len;
char *new_line;
int32 status;
if(!obj)
return(MK_OUT_OF_MEMORY);
/* buffer until we have a line */
BlockAllocCat(obj->line_buffer, obj->line_buffer_size, str, len);
obj->line_buffer_size += len;
/* see if we have a line */
while((new_line = strchr_in_buf(obj->line_buffer,
obj->line_buffer_size,
LF))
|| (new_line = strchr_in_buf(obj->line_buffer,
obj->line_buffer_size,
CR)) )
{
/* terminate the line */
*new_line = '\0';
status = net_ParseHTMLHelpLine(obj, obj->line_buffer);
/* remove the parsed line from obj->line_buffer */
string_len = (new_line - obj->line_buffer) + 1;
XP_MEMCPY(obj->line_buffer,
new_line+1,
obj->line_buffer_size-string_len);
obj->line_buffer_size -= string_len;
if(status == HTML_HELP_ID_FOUND)
return(HTML_HELP_ID_FOUND);
}
return(0);
}
PUBLIC void
NET_HelpPreExitRoutine(URL_Struct *URL_s, int status, MWContext *context)
{
/* @@@@. I wanted to use the fe_data to pass the URL struct
* of the frame content URL. But the fe_data gets cleared
* by the front ends. Therefore I'm using a private global
* store the information. This will work fine as long
* as two help requests don't come in simulatainiously.
*/
#if 0
/* load the content url */
if(URL_s->fe_data)
FE_GetURL(context, (URL_Struct *)URL_s->fe_data);
#else
if(frame_content_for_pre_exit_routine)
{
/* FE_GetURL(context, frame_content_for_pre_exit_routine); */
/* FE_GetURL interrupts the context */
NET_GetURL(frame_content_for_pre_exit_routine,
FO_CACHE_AND_PRESENT,
context,
simple_exit);
frame_content_for_pre_exit_routine=NULL;
}
#endif
}
PRIVATE void
net_help_init_chrome(Chrome *window_chrome, int32 w, int32 h)
{
window_chrome->type = MWContextBrowser;
window_chrome->w_hint = w;
window_chrome->h_hint = h;
window_chrome->allow_resize = TRUE;
window_chrome->show_scrollbar = TRUE;
window_chrome->allow_close = TRUE;
}
PUBLIC void
NET_ParseHTMLHelpLoadHelpDoc(HTMLHelpParseObj *obj, MWContext *context)
{
URL_Struct *URL_s;
char *frame_address = NULL;
char *content_address = NULL;
MWContext *new_context;
frame_set_struct *fgs;
if(obj->id_value || obj->default_id_value)
content_address = NET_MakeAbsoluteURL(obj->url_to_map_file,
obj->id_value ?
obj->id_value :
obj->default_id_value);
if(!content_address)
{
FE_Alert(context, "Unable to load the requested help topic");
return;
}
fgs = XP_ListPeekTopObject(obj->frame_group_stack);
if(fgs)
{
if(fgs->address)
{
frame_address = NET_MakeAbsoluteURL(obj->url_to_map_file,
fgs->address);
}
}
if(frame_address)
URL_s = NET_CreateURLStruct(frame_address, NET_DONT_RELOAD);
else
URL_s = NET_CreateURLStruct(content_address, NET_DONT_RELOAD);
if(!URL_s)
goto cleanup;
URL_s->window_chrome = XP_NEW(Chrome);
if(!URL_s->window_chrome)
goto cleanup;
XP_MEMSET(URL_s->window_chrome, 0, sizeof(Chrome));
if(obj->window_name)
URL_s->window_target = XP_STRDUP(obj->window_name);
else
URL_s->window_target = XP_STRDUP(DEFAULT_HELP_WINDOW_NAME);
net_help_init_chrome(URL_s->window_chrome,
obj->window_width,
obj->window_height);
new_context = XP_FindNamedContextInList(NULL, URL_s->window_target);
if(frame_address)
{
URL_Struct *content_URL_s;
/* if there is a frame_address then we load the
* frame first and then load the contents
* in the frame exit function.
*/
content_URL_s = NET_CreateURLStruct(content_address, NET_DONT_RELOAD);
if(obj->content_target)
content_URL_s->window_target = XP_STRDUP(obj->content_target);
else if(fgs->target)
content_URL_s->window_target = XP_STRDUP(fgs->target);
/* doesn't work: URL_s->fe_data = (void *) content_URL_s; */
/* hack, see pre_exit_routine_above */
frame_content_for_pre_exit_routine = content_URL_s;
URL_s->pre_exit_fn = NET_HelpPreExitRoutine;
}
if(!new_context)
{
/* this will cause the load too */
FE_MakeNewWindow(context,
URL_s,
URL_s->window_target,
URL_s->window_chrome);
}
else
{
FE_RaiseWindow(new_context);
FE_GetURL(new_context, URL_s);
}
cleanup:
FREEIF(frame_address);
FREE(content_address);
return;
}
typedef struct {
HTMLHelpParseObj *parse_obj;
MWContext *context;
XP_Bool file_is_local;
} html_help_map_stream;
PRIVATE int
net_HMFConvPut(html_help_map_stream *obj, char *s, int32 l)
{
int status = NET_ParseHTMLHelpPut(obj->parse_obj, s, l);
if(obj->file_is_local && status == HTML_HELP_ID_FOUND)
{
/* abort since we don't need any more of the file */
return(MK_UNABLE_TO_CONVERT);
}
return(status);
}
PRIVATE int
net_HMFConvWriteReady(html_help_map_stream *obj)
{
return(MAX_WRITE_READY);
}
PRIVATE void
net_HMFConvComplete(html_help_map_stream *obj)
{
NET_ParseHTMLHelpLoadHelpDoc(obj->parse_obj, obj->context);
}
PRIVATE void
net_HMFConvAbort(html_help_map_stream *obj, int32 status)
{
if(status == MK_UNABLE_TO_CONVERT)
NET_ParseHTMLHelpLoadHelpDoc(obj->parse_obj, obj->context);
}
PUBLIC NET_StreamClass *
NET_HTMLHelpMapToURL(int format_out,
void *data_object,
URL_Struct *URL_s,
MWContext *window_id)
{
html_help_map_stream* obj;
NET_StreamClass* stream;
TRACEMSG(("Setting up display stream. Have URL: %s\n", URL_s->address));
stream = XP_NEW(NET_StreamClass);
if(stream == NULL)
return(NULL);
obj = XP_NEW(html_help_map_stream);
if (obj == NULL)
{
FREE(stream);
return(NULL);
}
XP_MEMSET(obj, 0, sizeof(html_help_map_stream));
if(URL_s->cache_file || URL_s->memory_copy)
obj->file_is_local = TRUE;
else
obj->file_is_local = NET_IsLocalFileURL(URL_s->address);
obj->parse_obj = NET_ParseHTMLHelpInit(URL_s->address, URL_s->fe_data);
if(!obj->parse_obj)
{
FREE(stream);
FREE(obj);
}
obj->context = window_id;
stream->name = "HTML Help Map File converter";
stream->complete = (MKStreamCompleteFunc) net_HMFConvComplete;
stream->abort = (MKStreamAbortFunc) net_HMFConvAbort;
stream->put_block = (MKStreamWriteFunc) net_HMFConvPut;
stream->is_write_ready = (MKStreamWriteReadyFunc) net_HMFConvWriteReady;
stream->data_object = obj; /* document info object */
stream->window_id = window_id;
return(stream);
}