mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 22:25:30 +00:00
2021 lines
49 KiB
C
2021 lines
49 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
|
|
#include "pa_parse.h"
|
|
#include <stdio.h>
|
|
#include "merrors.h"
|
|
#include "net.h"
|
|
#include "hk_funcs.h"
|
|
#include "libevent.h"
|
|
#include "intl_csi.h"
|
|
|
|
extern int MK_OUT_OF_MEMORY;
|
|
|
|
|
|
#ifdef PROFILE
|
|
#pragma profile on
|
|
#endif
|
|
|
|
#ifdef XP_WIN16
|
|
#define HOLD_BUF_UNIT 32000
|
|
#define SIZE_LIMIT 32000
|
|
#else
|
|
#define HOLD_BUF_UNIT 16384
|
|
#endif /* XP_WIN16 */
|
|
|
|
#define WRITE_READY_SIZE (unsigned int) 8192
|
|
|
|
/*
|
|
* Function to call with parsed tag elements.
|
|
* It should be initialized by a call to PA_ParserInit*().
|
|
*/
|
|
static intn (*PA_ParsedTag)(void *data_object, PA_Tag *tags, intn status) = NULL;
|
|
|
|
typedef struct pa_DocDataList_struct {
|
|
MWContext* window_id;
|
|
pa_DocData *doc_data;
|
|
struct pa_DocDataList_struct *next;
|
|
} pa_DocDataList;
|
|
|
|
static pa_DocDataList *DocDataList = NULL;
|
|
|
|
static pa_DocData *pa_FetchDocData(MWContext *window_id);
|
|
static Bool pa_RemoveDocData(pa_DocData *target_doc_data);
|
|
static Bool pa_StoreDocData(MWContext *window_id, pa_DocData *new_doc_data);
|
|
|
|
/*************************
|
|
* The following is to speed up case conversion
|
|
* to allow faster checking of caseless equal among strings.
|
|
*************************/
|
|
#ifndef NON_ASCII_STRINGS
|
|
unsigned char lower_lookup[256]={
|
|
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,
|
|
27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,
|
|
51,52,53,54,55,56,57,58,59,60,61,62,63,64,
|
|
97,98,99,100,101,102,103,104,105,106,107,108,109,
|
|
110,111,112,113,114,115,116,117,118,119,120,121,122,
|
|
91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,
|
|
111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,
|
|
129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,
|
|
147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,
|
|
165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,
|
|
183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,
|
|
201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,
|
|
219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,
|
|
237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,
|
|
255};
|
|
#endif /* not NON_ASCII_STRINGS */
|
|
|
|
|
|
|
|
/*************************************
|
|
* Function: pa_caseless_equal
|
|
*
|
|
* Description: This function will compare two
|
|
* strings, similar to strcmp(), but ignoring the
|
|
* case of the letters A-Z.
|
|
*
|
|
* Params: Takes two \0 terminated strings.
|
|
*
|
|
* Returns: 1 if strings are equal, 0 if not.
|
|
*************************************/
|
|
#if 0
|
|
static intn
|
|
pa_caseless_equal(char *string_1, char *string_2)
|
|
{
|
|
/*
|
|
* If either is NULL, they are not equal, even if both are NULL
|
|
*/
|
|
if ((string_1 == NULL)||(string_2 == NULL))
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* While not at the end of the string, if they ever differ
|
|
* they are not equal.
|
|
*/
|
|
while ((*string_1 != '\0')&&(*string_2 != '\0'))
|
|
{
|
|
if (TOLOWER((unsigned char) *string_1) != TOLOWER((unsigned char) *string_2))
|
|
{
|
|
return(0);
|
|
}
|
|
string_1++;
|
|
string_2++;
|
|
}
|
|
|
|
/*
|
|
* One of the strings has ended, if they are both ended, then they
|
|
* are equal, otherwise not.
|
|
*/
|
|
if ((*string_1 == '\0')&&(*string_2 == '\0'))
|
|
{
|
|
return(1);
|
|
}
|
|
else
|
|
{
|
|
return(0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*************************************
|
|
* Function: pa_TagEqual
|
|
*
|
|
* Description: This function is a special purpose caseless compare
|
|
* to save me a few cycles of performance.
|
|
* Since we know the first string is a predefined TAG
|
|
* we are guaranteeing it will always be in lower case,
|
|
* thus we don't need to TOLOWER its characters as we
|
|
* compare them.
|
|
*
|
|
* Params: Takes two \0 terminated strings. The first, being a predefined TAG
|
|
* is guaranteed to be all in lower case.
|
|
*
|
|
* Returns: 1 if strings are equal, 0 if not.
|
|
*************************************/
|
|
intn
|
|
pa_TagEqual(char *tag, char *str)
|
|
{
|
|
/*
|
|
* If str is NULL, they are not equal, tag cannot be NULL.
|
|
*/
|
|
if (str == NULL)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* While not at the end of the string, if they ever differ
|
|
* they are not equal.
|
|
*/
|
|
while ((*tag != '\0')&&(*str != '\0'))
|
|
{
|
|
if ((int)(*tag) != TOLOWER((unsigned char) *str))
|
|
{
|
|
return(0);
|
|
}
|
|
tag++;
|
|
str++;
|
|
}
|
|
|
|
/*
|
|
* One of the strings has ended, if they are both ended, then they
|
|
* are equal, otherwise not.
|
|
*/
|
|
if ((*tag == '\0')&&(*str == '\0'))
|
|
{
|
|
return(1);
|
|
}
|
|
else
|
|
{
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************
|
|
* Function: PA_FreeTag
|
|
*
|
|
* Description: This function frees up all memory associated
|
|
* with a PA_Tag structure, including the structure
|
|
* itself.
|
|
*
|
|
* Params: Takes pointer to a PA_Tag structure.
|
|
*
|
|
* Returns: none.
|
|
*************************************/
|
|
void
|
|
PA_FreeTag(PA_Tag *tag)
|
|
{
|
|
/*
|
|
* Nothing to do for already freed tags.
|
|
*/
|
|
if (tag == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If we have data, free it.
|
|
*/
|
|
if (tag->data != NULL)
|
|
{
|
|
PA_FREE(tag->data);
|
|
}
|
|
|
|
/*
|
|
* Free the tag structure.
|
|
*/
|
|
XP_DELETE(tag);
|
|
}
|
|
|
|
|
|
static int32 doc_id_gen = 0; /* generator for document identifiers */
|
|
|
|
|
|
/*************************************
|
|
* Function: pa_new_document
|
|
*
|
|
* Description: register a new document, create and initialize
|
|
* the pa_DocData structure for it.
|
|
*
|
|
* Params: Takes a unique document id, and the URL_Struct for this doc.
|
|
*
|
|
* Returns: a pointer to the new pa_DocData structure, already
|
|
* initialized, and with the doc_id filled in.
|
|
* Return NULL on failure.
|
|
*************************************/
|
|
static pa_DocData *
|
|
pa_new_document(FO_Present_Types format_out,
|
|
MWContext *window_id,
|
|
PA_OutputFunction *output_func,
|
|
URL_Struct *url_struct)
|
|
{
|
|
pa_DocData *doc_data, *old_doc_data;
|
|
Bool is_inline_stream;
|
|
|
|
if (format_out == FO_PRESENT_INLINE) {
|
|
is_inline_stream = TRUE;
|
|
old_doc_data = pa_FetchDocData(window_id);
|
|
}
|
|
else {
|
|
/* Added by Lou:
|
|
* This will interrupt anything else trying to go into
|
|
* this same window, so that we have a clear path
|
|
* to load this new document
|
|
*/
|
|
NET_SilentInterruptWindow(window_id);
|
|
is_inline_stream = FALSE;
|
|
}
|
|
|
|
|
|
doc_data = XP_NEW(pa_DocData);
|
|
if (doc_data == NULL)
|
|
{
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Allocate a static hold buffer. This will
|
|
* save on malloc calls in the long run.
|
|
*/
|
|
doc_data->hold_buf = XP_ALLOC_BLOCK(HOLD_BUF_UNIT * sizeof(char));
|
|
if (doc_data->hold_buf == NULL)
|
|
{
|
|
XP_DELETE(doc_data);
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Now that we can't fail, create the unique document ID.
|
|
*/
|
|
if (is_inline_stream) {
|
|
/*
|
|
* If we're still parsing the original HTML stream, then
|
|
* just get some of the required information from the
|
|
* existing doc_data. Otherwise, we need to get it from
|
|
* MWContext - is there a better way to do this?
|
|
*/
|
|
if (old_doc_data) {
|
|
doc_data->doc_id = old_doc_data->doc_id;
|
|
doc_data->layout_state = old_doc_data->layout_state;
|
|
}
|
|
else {
|
|
doc_data->doc_id = window_id->doc_id;
|
|
doc_data->layout_state = NULL;
|
|
}
|
|
}
|
|
else {
|
|
doc_data->doc_id = ++doc_id_gen;
|
|
doc_data->layout_state = NULL;
|
|
}
|
|
doc_data->window_id = window_id;
|
|
doc_data->output_tag = output_func;
|
|
doc_data->hold = 0;
|
|
doc_data->hold_size = HOLD_BUF_UNIT;
|
|
doc_data->hold_len = 0;
|
|
doc_data->brute_tag = P_UNKNOWN;
|
|
doc_data->comment_bytes = 0;
|
|
doc_data->lose_newline = FALSE;
|
|
if (url_struct->address == NULL)
|
|
{
|
|
doc_data->url = NULL;
|
|
}
|
|
else
|
|
{
|
|
doc_data->url = XP_STRDUP(url_struct->address);
|
|
}
|
|
#ifdef NU_CACHE
|
|
/* TODO -Gagan*/
|
|
doc_data->from_net = (url_struct->cache_file != NULL) ? FALSE : TRUE;
|
|
#else
|
|
if ((url_struct->cache_file != NULL)||(url_struct->memory_copy != NULL))
|
|
{
|
|
doc_data->from_net = FALSE;
|
|
}
|
|
else
|
|
{
|
|
doc_data->from_net = TRUE;
|
|
}
|
|
#endif /* NU_CACHE */
|
|
|
|
/*
|
|
* A NET_SUPER_RELOAD should always make everything reload, so no
|
|
* matter what, act as if it all came new from the net.
|
|
*/
|
|
if (url_struct->force_reload == NET_SUPER_RELOAD)
|
|
{
|
|
doc_data->from_net = TRUE;
|
|
}
|
|
|
|
/* save the url struct and make sure it doesn't disappear on us */
|
|
doc_data->url_struct = url_struct;
|
|
NET_HoldURLStruct(doc_data->url_struct);
|
|
|
|
doc_data->edit_buffer = NULL;
|
|
doc_data->is_inline_stream = is_inline_stream;
|
|
|
|
if (!doc_data->is_inline_stream || !old_doc_data)
|
|
pa_StoreDocData(window_id, doc_data);
|
|
|
|
doc_data->overflow_stack = 0;
|
|
doc_data->overflow_depth = 0;
|
|
doc_data->stream_status = 0;
|
|
doc_data->stream_count = 0;
|
|
return(doc_data);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
unsigned int
|
|
pa_ParseWriteReady (NET_StreamClass *stream)
|
|
{
|
|
|
|
pa_DocData *doc_data = (pa_DocData *) stream->data_object;
|
|
#if !defined(XP_UNIX) && !defined(XP_WIN32)
|
|
if (doc_data->overflow_depth)
|
|
return 0;
|
|
#endif
|
|
|
|
return WRITE_READY_SIZE;
|
|
}
|
|
|
|
static pa_DocData *
|
|
pa_check_doc_data_count(NET_StreamClass *stream)
|
|
{
|
|
pa_DocData *doc_data=stream->data_object;
|
|
/* netlib will no longer send us data */
|
|
if (doc_data->stream_count + doc_data->overflow_depth <= 0) {
|
|
|
|
if (doc_data->stream_status != 0) {
|
|
PA_MDLAbort(stream, doc_data->stream_status);
|
|
}
|
|
else {
|
|
PA_MDLComplete(stream);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return doc_data;
|
|
}
|
|
|
|
/*
|
|
* Flush the data out of the current overflow buffer and send it up through
|
|
* the parser. After we push the data through PA_ParseBlock()
|
|
* the overflow buffer is empty, any unparsed data will be left
|
|
* in the hold buffer
|
|
*/
|
|
void
|
|
pa_FlushOverflow(NET_StreamClass *stream)
|
|
{
|
|
pa_DocData *doc_data=stream->data_object;
|
|
int len = 0;
|
|
pa_Overflow *overflow;
|
|
char *buf = NULL;
|
|
|
|
if (!doc_data || doc_data->overflow_depth)
|
|
return;
|
|
|
|
/*
|
|
* Make sure the doc_data doesn't get removed out from under us.
|
|
*/
|
|
PA_HoldDocData(doc_data);
|
|
|
|
/*
|
|
** We need to coalesce the overflow buffers into a single one, since
|
|
** we don't want to be calling PA_ParseBlock multiple times -- as this could
|
|
** possibly get us back into the overflow code.
|
|
*/
|
|
for (overflow = doc_data->overflow_stack;
|
|
overflow != NULL;
|
|
overflow = overflow->next)
|
|
len += overflow->len;
|
|
|
|
if (len > 0)
|
|
{
|
|
buf = (char *)XP_ALLOC_BLOCK((len + 1) * sizeof(char));
|
|
buf[0] = 0;
|
|
}
|
|
|
|
while ((overflow = PA_PopOverflow(doc_data)))
|
|
{
|
|
if (buf != NULL)
|
|
XP_STRNCAT(buf, (char*)overflow->buf, overflow->len);
|
|
|
|
PA_FreeOverflow(overflow);
|
|
}
|
|
|
|
doc_data->overflow_stack = NULL;
|
|
|
|
if (buf != NULL)
|
|
{
|
|
PA_ParseBlock(stream, (const char *) buf, len);
|
|
XP_FREE(buf);
|
|
}
|
|
|
|
PA_DropDocData(stream);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
pa_unload_complete(NET_StreamClass *stream)
|
|
{
|
|
pa_DocData *doc_data = (pa_DocData *) stream->data_object;
|
|
|
|
/* The overflow value was set to one just before sending the JS
|
|
onUnload event and the only thing that can change it is
|
|
either a document.write or encountering a <SCRIPT> tag, neither
|
|
of which should have happened. */
|
|
XP_ASSERT(doc_data->overflow_depth == 1);
|
|
|
|
/* flush any old data that has built up */
|
|
pa_FlushOverflow(stream);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* If there is a current document see if its OK to unload it now
|
|
*/
|
|
static void
|
|
pa_check_for_new_doc(MWContext *window_id, pa_DocData *doc_data)
|
|
{
|
|
/* Don't want to do this for layer src changes */
|
|
if (doc_data->is_inline_stream)
|
|
return;
|
|
|
|
/* call to see if top_state has unload or we're resizing */
|
|
if (LO_CheckForUnload(window_id) ||
|
|
doc_data->url_struct->resize_reload)
|
|
{
|
|
NET_StreamClass *s=NET_NewStream("Place holder",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
doc_data,
|
|
window_id);
|
|
/* don't let netlib pass any data up to us yet */
|
|
/* doc_data->overflow = 1;*/
|
|
PA_PushOverflow(doc_data);
|
|
doc_data->overflow_depth ++;
|
|
/* send the event to mocha so we get called back */
|
|
ET_SendLoadEvent(window_id, EVENT_UNLOAD, (ETVoidPtrFunc) pa_unload_complete,
|
|
s, LO_DOCUMENT_LAYER_ID,
|
|
doc_data->url_struct->resize_reload);
|
|
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Send the event to mocha but we don't need to be
|
|
* called back
|
|
*/
|
|
ET_SendLoadEvent(window_id, EVENT_UNLOAD, NULL, NULL,
|
|
LO_DOCUMENT_LAYER_ID,
|
|
doc_data->url_struct->resize_reload);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Someone has decided they are no longer interested in sending
|
|
* data through us. If they were the last ones we were waiting
|
|
* on clean everything up now.
|
|
*/
|
|
pa_DocData *
|
|
PA_DropDocData(NET_StreamClass *stream)
|
|
{
|
|
pa_DocData *doc_data=(pa_DocData *)stream->data_object;
|
|
XP_ASSERT(doc_data &&
|
|
(doc_data->stream_count >= 1));
|
|
|
|
if (!doc_data)
|
|
return NULL;
|
|
|
|
doc_data->stream_count--;
|
|
|
|
return pa_check_doc_data_count(stream);
|
|
|
|
}
|
|
|
|
/*
|
|
* netlib will no longer send us data
|
|
*/
|
|
static void
|
|
pa_cleanup_after_netlib(NET_StreamClass *stream)
|
|
{
|
|
pa_DocData *doc_data=stream->data_object;
|
|
XP_ASSERT(doc_data);
|
|
|
|
if (doc_data->window_id)
|
|
LO_NetlibComplete(doc_data->window_id);
|
|
PA_DropDocData(stream);
|
|
}
|
|
|
|
static void
|
|
pa_netlib_stream_complete(NET_StreamClass *stream)
|
|
{
|
|
pa_cleanup_after_netlib(stream);
|
|
}
|
|
|
|
static void
|
|
pa_netlib_stream_abort(NET_StreamClass *stream, int status)
|
|
{
|
|
pa_DocData *doc_data = (pa_DocData *)stream->data_object;
|
|
doc_data->stream_status = status;
|
|
pa_cleanup_after_netlib(stream);
|
|
}
|
|
|
|
pa_DocData *
|
|
PA_HoldDocData(pa_DocData * doc_data)
|
|
{
|
|
XP_ASSERT(doc_data);
|
|
XP_ASSERT(doc_data->stream_count >= 0);
|
|
|
|
doc_data->stream_count++;
|
|
|
|
return doc_data;
|
|
}
|
|
|
|
|
|
/*************************************
|
|
* Function: PA_BeginParseMDL
|
|
*
|
|
* Description: The outside world's main access to the parser.
|
|
* call this when you are going to start parsing
|
|
* a new document to set up the parsing stream.
|
|
* This function cannot be called successfully
|
|
* until PA_ParserInit() has been called.
|
|
*
|
|
* Params: Takes lots of document information that is all
|
|
* ignored right now, just used the window_id to create
|
|
* a unique document id.
|
|
*
|
|
* Returns: a pointer to a new NET_StreamClass structure, set up to
|
|
* give the caller a parsing stream into the parser.
|
|
* Returns NULL on error.
|
|
*************************************/
|
|
NET_StreamClass *
|
|
PA_BeginParseMDL(FO_Present_Types format_out,
|
|
void *init_data, URL_Struct *anchor, MWContext *window_id)
|
|
{
|
|
NET_StreamClass *new_stream = NULL;
|
|
NET_StreamClass *netlib_stream = NULL;
|
|
PA_InitData *new_data;
|
|
pa_DocData *doc_data;
|
|
|
|
new_data = (PA_InitData *)init_data;
|
|
|
|
/*
|
|
* If there was a Window-Target http header from the server,
|
|
* we probably need to switch where this document goes.
|
|
*/
|
|
if ((anchor->window_target != NULL)&&
|
|
(*anchor->window_target != '\0')&&
|
|
((format_out == FO_PRESENT)||(format_out == FO_CACHE_AND_PRESENT)))
|
|
{
|
|
if (NET_IsSafeForNewContext(anchor) != FALSE)
|
|
{
|
|
MWContext *new_context;
|
|
|
|
/*
|
|
* Find the named window if it already exists.
|
|
*/
|
|
new_context = XP_FindNamedContextInList(window_id,
|
|
anchor->window_target);
|
|
/*
|
|
* If the named window didn't exist, create it.
|
|
*/
|
|
if (new_context == NULL)
|
|
{
|
|
/*
|
|
* Don't pass in the URL_Struct if we're going to manually
|
|
* get it to load ourselves, as MakeNewWindow will load
|
|
* a URL passed in!
|
|
*/
|
|
new_context = FE_MakeNewWindow(window_id, NULL /* anchor */,
|
|
anchor->window_target, anchor->window_chrome);
|
|
}
|
|
/*
|
|
* Else is the named window did exist, and we are it, we
|
|
* don't need to do anything.
|
|
*/
|
|
else if (new_context == window_id)
|
|
{
|
|
new_context = NULL;
|
|
}
|
|
|
|
/*
|
|
* Switch to loading this URL in the named window.
|
|
*/
|
|
if (new_context != NULL)
|
|
{
|
|
Net_GetUrlExitFunc *exit_func;
|
|
|
|
exit_func = NULL;
|
|
FE_SetWindowLoading(new_context, anchor, &exit_func);
|
|
if (NET_SetNewContext(anchor, new_context, exit_func)
|
|
== 0)
|
|
{
|
|
window_id = new_context;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((anchor != NULL)&&(anchor->address != NULL))
|
|
{
|
|
char *ret_str;
|
|
|
|
ret_str = NULL;
|
|
(void)HK_CallHook(HK_DOC_START, NULL, XP_CONTEXTID(window_id),
|
|
anchor->address, &ret_str);
|
|
if (ret_str != NULL)
|
|
{
|
|
XP_FREE(ret_str);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Allocate the crucial data object that contains all the
|
|
* per document parsing state information.
|
|
*/
|
|
doc_data = pa_new_document(format_out, window_id,
|
|
new_data->output_func, anchor);
|
|
|
|
if (doc_data == NULL)
|
|
{
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Create the new stream that will stay around even after
|
|
* netlib has finished
|
|
*/
|
|
new_stream = NET_NewStream("netlib stream",
|
|
(MKStreamWriteFunc) PA_ParseBlock,
|
|
(MKStreamCompleteFunc) PA_MDLComplete,
|
|
(MKStreamAbortFunc) PA_MDLAbort,
|
|
(MKStreamWriteReadyFunc) pa_ParseWriteReady,
|
|
NULL,
|
|
window_id);
|
|
|
|
if (new_stream == NULL)
|
|
{
|
|
pa_RemoveDocData(doc_data);
|
|
if (doc_data->hold_buf)
|
|
XP_FREE_BLOCK(doc_data->hold_buf);
|
|
if (doc_data->url)
|
|
XP_FREE(doc_data->url);
|
|
XP_DELETE(doc_data);
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Don't create the separate netlib stream for a stream request
|
|
* that's an inline stream that's piggy-backing on some other
|
|
* stream (the case if layout_state is non-NULL).
|
|
*/
|
|
if (!doc_data->is_inline_stream || !doc_data->layout_state)
|
|
{
|
|
/*
|
|
* Create the new stream that netlib is going to free
|
|
* out from under us before we are done with it.
|
|
*/
|
|
netlib_stream = NET_NewStream("netlib stream",
|
|
(MKStreamWriteFunc) PA_ParseBlock,
|
|
(MKStreamCompleteFunc) pa_netlib_stream_complete,
|
|
(MKStreamAbortFunc) pa_netlib_stream_abort,
|
|
(MKStreamWriteReadyFunc) pa_ParseWriteReady,
|
|
NULL,
|
|
window_id);
|
|
|
|
if (netlib_stream == NULL)
|
|
{
|
|
pa_RemoveDocData(doc_data);
|
|
if (doc_data->hold_buf)
|
|
XP_FREE_BLOCK(doc_data->hold_buf);
|
|
if (doc_data->url)
|
|
XP_FREE(doc_data->url);
|
|
XP_DELETE(doc_data);
|
|
XP_DELETE(new_stream);
|
|
return(NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
netlib_stream = new_stream;
|
|
}
|
|
|
|
new_stream->data_object = (void *)PA_HoldDocData(doc_data);
|
|
netlib_stream->data_object = (void *)doc_data;
|
|
|
|
/*
|
|
* Check to see if its OK to unload the current document (if there
|
|
* is one).
|
|
*/
|
|
pa_check_for_new_doc(window_id, doc_data);
|
|
|
|
doc_data->format_out = format_out;
|
|
doc_data->parser_stream = new_stream;
|
|
doc_data->no_newline_count = 0;
|
|
doc_data->newline_count = 0;
|
|
|
|
return(netlib_stream);
|
|
}
|
|
|
|
|
|
/*************************************
|
|
* Function: PA_ParserInit
|
|
*
|
|
* Description: Very main interface to the parser library.
|
|
* It must be called before the parser can be used.
|
|
* Right now it just sets the one static global we use.
|
|
* This function can only be called once, all
|
|
* subsequent calls will fail.
|
|
*
|
|
* Params: Pass in a pointer to a PA_Functions struct, which tells the parser
|
|
* what functions to use for certain important functionality.
|
|
*
|
|
* Returns: A status code. 1 on success, -1 on failure.
|
|
*************************************/
|
|
intn
|
|
PA_ParserInit(PA_Functions *funcs)
|
|
{
|
|
/*
|
|
* If this is not the first call, fail them.
|
|
*/
|
|
if (PA_ParsedTag != NULL)
|
|
{
|
|
return(-1);
|
|
}
|
|
|
|
PA_ParsedTag = funcs->PA_ParsedTag;
|
|
|
|
if (PA_ParsedTag == NULL)
|
|
{
|
|
return(-1);
|
|
}
|
|
else
|
|
{
|
|
return(1);
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************
|
|
* Function: PA_ParseBlock
|
|
*
|
|
* Description: This is a very important entry point to the parser,
|
|
* but it will never be called directly. It will be
|
|
* placed into the stream returned by PA_BeginParseMDL()
|
|
* and be called from there.
|
|
*
|
|
* Params: The data_object created and placed in the stream class
|
|
* in PA_BeginParseMDL(). A buffer of characters to be
|
|
* parsed, and the length of that buffer. The buffer is NOT
|
|
* a \0 terminated string.
|
|
*
|
|
* Returns: a status code. 1 = success, -1 = failure.
|
|
*************************************/
|
|
intn
|
|
PA_ParseBlock(NET_StreamClass *stream, const char *block, int block_len)
|
|
{
|
|
void *data_object=stream->data_object;
|
|
pa_DocData *doc_data=data_object;
|
|
PA_Tag *tag;
|
|
intn ret;
|
|
int32 len;
|
|
char *buf;
|
|
char *hold_buf;
|
|
XP_Block buff;
|
|
#ifdef XP_WIN16
|
|
int32 extra;
|
|
char *extra_ptr;
|
|
|
|
extra_ptr = NULL;
|
|
extra = 0;
|
|
#endif /* XP_WIN16 */
|
|
buf = (char *)block;
|
|
len = (int32)block_len;
|
|
|
|
/*
|
|
* Parse this unique MDL document. Get per-document state info.
|
|
*/
|
|
|
|
/*
|
|
* If we are overflowing we shouldn't be getting any data
|
|
* just glomb it into the overflow buffer
|
|
*/
|
|
if (doc_data->overflow_depth && len > 0)
|
|
{
|
|
pa_Overflow *overflow = doc_data->overflow_stack; /* check for NULL? */
|
|
char * overflow_buf;
|
|
int32 over_size = overflow->size;
|
|
int32 over_len = overflow->len;
|
|
int32 new_size = over_len + len;
|
|
|
|
if (over_size < new_size)
|
|
{
|
|
buff = XP_REALLOC_BLOCK(overflow->buf,
|
|
new_size * sizeof(char));
|
|
if (buff == NULL)
|
|
{
|
|
return(MK_OUT_OF_MEMORY);
|
|
}
|
|
overflow->buf = buff;
|
|
overflow->size = new_size;
|
|
}
|
|
|
|
/* lock the buffer so our pointer wizardry works correctly */
|
|
XP_LOCK_BLOCK(overflow_buf, char *, overflow->buf);
|
|
XP_MEMMOVE(overflow_buf + over_len, buf, len);
|
|
XP_UNLOCK_BLOCK(overflow->buf);
|
|
overflow->len = new_size;
|
|
return 1;
|
|
|
|
}
|
|
|
|
/*
|
|
* If we are holding buffered data for this document from a
|
|
* previous parse attempt,
|
|
* Glomb it onto the beginning in a new buffer.
|
|
*
|
|
* We hold data if we might have a partial MDL tag element.
|
|
* We hold data if we might have a partial ampersand escape.
|
|
* We hold data if we might have a two character newline to skip.
|
|
*/
|
|
if (doc_data->hold)
|
|
{
|
|
if ((doc_data->hold_len + len) > doc_data->hold_size)
|
|
{
|
|
/*
|
|
* Grow the hold buffer if itis not big enough to hold
|
|
* the combined buffers.
|
|
*/
|
|
#ifdef XP_WIN16
|
|
/*
|
|
* On the 32K limit, our hold_buf is already max size
|
|
*/
|
|
#else
|
|
buff = XP_REALLOC_BLOCK(doc_data->hold_buf,
|
|
((doc_data->hold_size + HOLD_BUF_UNIT + len) * sizeof(char)));
|
|
if (buff == NULL)
|
|
{
|
|
return(MK_OUT_OF_MEMORY);
|
|
}
|
|
doc_data->hold_buf = buff;
|
|
doc_data->hold_size += (HOLD_BUF_UNIT + len);
|
|
#endif /* XP_WIN16 */
|
|
}
|
|
|
|
/*
|
|
* Lock down the hold buffer so we can do pointer magic
|
|
* on it.
|
|
*/
|
|
XP_LOCK_BLOCK(hold_buf, char *, (doc_data->hold_buf));
|
|
|
|
/*
|
|
* Append the new buffer to the old buffer
|
|
* Make it look like the merged chunk is what
|
|
* was passed to us originally.
|
|
*/
|
|
#ifdef XP_WIN16
|
|
if ((doc_data->hold_len + len) > doc_data->hold_size)
|
|
{
|
|
extra = doc_data->hold_len + len - doc_data->hold_size;
|
|
extra_ptr = (char *)(buf + (len - extra));
|
|
|
|
XP_BCOPY(buf, (hold_buf + doc_data->hold_len), (len - extra));
|
|
buf = hold_buf;
|
|
len = doc_data->hold_len + len - extra;
|
|
doc_data->hold_len = len;
|
|
}
|
|
else
|
|
{
|
|
XP_BCOPY(buf, (hold_buf + doc_data->hold_len), len);
|
|
buf = hold_buf;
|
|
len = len + doc_data->hold_len;
|
|
doc_data->hold_len = len;
|
|
}
|
|
#else
|
|
XP_BCOPY(buf, (hold_buf + doc_data->hold_len), len);
|
|
buf = hold_buf;
|
|
len = len + doc_data->hold_len;
|
|
doc_data->hold_len = len;
|
|
#endif /* XP_WIN16 */
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* We always want the hold buffer to be locked as we enter the
|
|
* following while loop
|
|
*/
|
|
doc_data->hold_len = 0;
|
|
XP_LOCK_BLOCK(hold_buf, char *, (doc_data->hold_buf));
|
|
}
|
|
|
|
doc_data->hold = 0;
|
|
|
|
/*
|
|
* Loop until we get a partial something to hold,
|
|
* or we have drained the buffer.
|
|
*/
|
|
while ((!doc_data->hold)&&(!doc_data->overflow_depth)&&(len != 0))
|
|
{
|
|
char *tptr;
|
|
char *tptr2;
|
|
intn is_comment;
|
|
|
|
/*
|
|
* The P_PLAIN_TEXT tag element is very special, and
|
|
* if we just got one, all other text is just dumped
|
|
* out of the parser as plain text.
|
|
*/
|
|
if (doc_data->brute_tag == P_PLAIN_TEXT)
|
|
{
|
|
tag = pa_CreateTextTag(doc_data, buf, len);
|
|
if (tag == NULL)
|
|
{
|
|
return(MK_OUT_OF_MEMORY);
|
|
}
|
|
ret = doc_data->output_tag(data_object, tag, PA_PARSED);
|
|
if (ret < 0)
|
|
{
|
|
return(ret);
|
|
}
|
|
buf = NULL;
|
|
len = 0;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* In the case where we just removed a comment, or just opened
|
|
* a preformatting tag, we want to remove any newline that
|
|
* appears right after that tag element because we assume
|
|
* it was really "part of the element", I.E. WE are guessing
|
|
* that the user WANTS us to remove it, a dangerous guess.
|
|
*
|
|
* Depending on what platform generated the file, a newline
|
|
* could be \n, \r, or \r\n. Since it can span 2 characters, we
|
|
* may actually have to hold until the next buffer to
|
|
* throw away the newline.
|
|
*/
|
|
if (doc_data->lose_newline != FALSE)
|
|
{
|
|
if (*buf == '\n')
|
|
{
|
|
buf++;
|
|
len--;
|
|
if (len == 0)
|
|
{
|
|
buf = NULL;
|
|
}
|
|
if (doc_data->no_newline_count == 0)
|
|
doc_data->newline_count++;
|
|
doc_data->comment_bytes++;
|
|
}
|
|
else if ((*buf == '\r')&&(len == 1))
|
|
{
|
|
doc_data->hold = 1;
|
|
/*
|
|
* Grow the hold buffer if it is not big enough to hold
|
|
* the rest of this buffer.
|
|
*/
|
|
#ifdef XP_WIN16
|
|
/*
|
|
* On the 32K limit, our hold_buf is already max size
|
|
*/
|
|
#else
|
|
if (len > doc_data->hold_size)
|
|
{
|
|
XP_ASSERT(0); /* I don't think we can ever reach
|
|
this code since len is always equal to 1 and
|
|
hold_size is never zero. */
|
|
XP_UNLOCK_BLOCK((doc_data->hold_buf));
|
|
buff = XP_REALLOC_BLOCK(doc_data->hold_buf,
|
|
((doc_data->hold_size + HOLD_BUF_UNIT + len) *
|
|
sizeof(char)));
|
|
if (buff == NULL)
|
|
{
|
|
return(MK_OUT_OF_MEMORY);
|
|
}
|
|
doc_data->hold_buf = buff;
|
|
XP_LOCK_BLOCK(hold_buf, char *,
|
|
(doc_data->hold_buf));
|
|
doc_data->hold_size += (HOLD_BUF_UNIT + len);
|
|
}
|
|
#endif /* XP_WIN16 */
|
|
|
|
XP_BCOPY(buf, hold_buf, len);
|
|
doc_data->hold_len = len;
|
|
continue;
|
|
}
|
|
/* Check for Dos or Mac style EOL (need at least 2 chars in buf)*/
|
|
else if (*buf == '\r')
|
|
{
|
|
if ((*((char *)(buf + 1)) == '\n'))
|
|
{
|
|
/* DOS style EOL */
|
|
buf += 2;
|
|
len -= 2;
|
|
if (len == 0)
|
|
{
|
|
buf = NULL;
|
|
}
|
|
if (doc_data->no_newline_count == 0)
|
|
doc_data->newline_count++;
|
|
doc_data->comment_bytes += 2;
|
|
}
|
|
else
|
|
{
|
|
/* Mac style EOL */
|
|
buf++;
|
|
len--;
|
|
if (len == 0)
|
|
{
|
|
buf = NULL;
|
|
}
|
|
if (doc_data->no_newline_count == 0)
|
|
doc_data->newline_count++;
|
|
doc_data->comment_bytes++;
|
|
}
|
|
}
|
|
doc_data->lose_newline = FALSE;
|
|
}
|
|
|
|
/*
|
|
* Find the start of any MDL tags in this buffer.
|
|
* Returns NULL if there are none.
|
|
*/
|
|
tptr = pa_FindMDLTag(doc_data, buf, len, &is_comment);
|
|
|
|
/*
|
|
* Some portion of the start of the buffer is text.
|
|
*/
|
|
if (tptr != buf)
|
|
{
|
|
int32 text_len;
|
|
int32 new_len;
|
|
INTL_CharSetInfo c;
|
|
|
|
/*
|
|
* Find the length of the text.
|
|
*/
|
|
if (tptr == NULL)
|
|
{
|
|
text_len = len;
|
|
}
|
|
else
|
|
{
|
|
text_len = (int32)(tptr - buf);
|
|
}
|
|
|
|
/*
|
|
* Expand any ampersand escapes. We might need
|
|
* to hold a partial escape. Ampersand escapes
|
|
* are NOT expanded if we are inside one of the
|
|
* following elements.
|
|
*/
|
|
if ((doc_data->brute_tag == P_PLAIN_PIECE)||
|
|
(doc_data->brute_tag == P_SERVER)||
|
|
(doc_data->brute_tag == P_SCRIPT)||
|
|
(doc_data->brute_tag == P_STYLE))
|
|
{
|
|
tptr2 = NULL;
|
|
new_len = text_len;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If we have an MDL tag right after this, we can't have
|
|
* partial escapes because the tag is a guaranteed
|
|
* terminator. Thus force expansion is set to true.
|
|
*/
|
|
c = LO_GetDocumentCharacterSetInfo(doc_data->window_id);
|
|
if (tptr != NULL)
|
|
{
|
|
tptr2 = pa_ExpandEscapes(buf, text_len,
|
|
&new_len, TRUE, INTL_GetCSIWinCSID(c));
|
|
}
|
|
else
|
|
{
|
|
tptr2 = pa_ExpandEscapes(buf, text_len,
|
|
&new_len, FALSE,INTL_GetCSIWinCSID(c));
|
|
}
|
|
|
|
/*
|
|
* Lump loss of escaped bytes into comment_bytes so that
|
|
* dead reckoning in layscrip.c doesn't come up short
|
|
*/
|
|
if (text_len > new_len)
|
|
doc_data->comment_bytes += text_len - new_len;
|
|
|
|
}
|
|
|
|
/*
|
|
* Create and parse the text into a layout element.
|
|
*/
|
|
tag = pa_CreateTextTag(doc_data, buf, new_len);
|
|
if (tag == NULL)
|
|
{
|
|
return(MK_OUT_OF_MEMORY);
|
|
}
|
|
ret = doc_data->output_tag(data_object, tag, PA_PARSED);
|
|
if (ret < 0)
|
|
{
|
|
return(ret);
|
|
}
|
|
|
|
/*
|
|
* Check if we are holding a partial ampersand escape.
|
|
*/
|
|
if (tptr2 != NULL)
|
|
{
|
|
text_len = (int32)(tptr2 - buf);
|
|
tptr = tptr2;
|
|
is_comment = COMMENT_MAYBE;
|
|
}
|
|
|
|
/*
|
|
* Move up pointers so beginning of tag now heads the buffer.
|
|
*/
|
|
buf = tptr;
|
|
if (buf == NULL)
|
|
{
|
|
len = 0;
|
|
}
|
|
else
|
|
{
|
|
len = len - text_len;
|
|
}
|
|
|
|
if (doc_data->overflow_depth)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* If we got a maybe, we need to save this
|
|
* remnant for later.
|
|
*/
|
|
if ((is_comment == COMMENT_MAYBE)&&(buf != NULL))
|
|
{
|
|
doc_data->hold = 1;
|
|
/*
|
|
* Grow the hold buffer if it is not big enough to hold
|
|
* the rest of this buffer.
|
|
*/
|
|
#ifdef XP_WIN16
|
|
/*
|
|
* On the 32K limit, our hold_buf is already max size
|
|
*/
|
|
#else
|
|
if (len > doc_data->hold_size)
|
|
{
|
|
XP_UNLOCK_BLOCK((doc_data->hold_buf));
|
|
buff = XP_REALLOC_BLOCK(doc_data->hold_buf,
|
|
((doc_data->hold_size + HOLD_BUF_UNIT + len) *
|
|
sizeof(char)));
|
|
if (buff == NULL)
|
|
{
|
|
return(MK_OUT_OF_MEMORY);
|
|
}
|
|
doc_data->hold_buf = buff;
|
|
XP_LOCK_BLOCK(hold_buf, char *,
|
|
(doc_data->hold_buf));
|
|
doc_data->hold_size += (HOLD_BUF_UNIT + len);
|
|
}
|
|
#endif /* XP_WIN16 */
|
|
|
|
XP_BCOPY(buf, hold_buf, len);
|
|
doc_data->hold_len = len;
|
|
}
|
|
/*
|
|
* else we either have the start of an MDL tag, or
|
|
* buf == NULL. Find the end of the tag if we
|
|
* have it in the buffer, otherwise return NULL.
|
|
*/
|
|
else
|
|
{
|
|
if ((is_comment == COMMENT_YES) || (is_comment == COMMENT_UNCOMMENT))
|
|
{
|
|
tptr = pa_FindMDLEndComment(doc_data, buf, len);
|
|
}
|
|
else if (is_comment == COMMENT_PROCESS)
|
|
{
|
|
tptr = pa_FindMDLEndProcessInstruction(doc_data, buf, len);
|
|
}
|
|
else
|
|
{
|
|
tptr = pa_FindMDLEndTag(doc_data, buf, len);
|
|
}
|
|
|
|
/* Skip over comment opening delimiter and JavaScript entity */
|
|
if ((tptr != NULL) && (is_comment == COMMENT_UNCOMMENT))
|
|
{
|
|
int32 skip_len = 0;
|
|
|
|
/* pa_isolate_javascript_expression() stores a null
|
|
character after the JS expresion. */
|
|
while (*buf) {
|
|
buf++;
|
|
skip_len++;
|
|
}
|
|
buf += 2; /* Skip over }; JS entity delimiter */
|
|
skip_len += 2;
|
|
|
|
/*
|
|
* Lump loss of escaped bytes into comment_bytes so that
|
|
* dead reckoning in layscrip.c doesn't come up short
|
|
*/
|
|
doc_data->comment_bytes += skip_len;
|
|
len = len - skip_len;
|
|
|
|
/* Erase the ending comment delimiter */
|
|
XP_MEMMOVE(tptr-2, tptr+1, len - (tptr - buf - 3));
|
|
len -= 3;
|
|
doc_data->comment_bytes += 3;
|
|
|
|
/* Now start parsing all over again, this time with the comment
|
|
delimiters eliminated. */
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Got the end of the MDL comment,
|
|
* discard the comment
|
|
*/
|
|
if ((tptr != NULL)&&((is_comment == COMMENT_YES)||
|
|
(is_comment == COMMENT_PROCESS)))
|
|
{
|
|
int32 comment_len;
|
|
|
|
comment_len = (int32)(tptr - buf) + 1;
|
|
|
|
/*
|
|
* If we are inside one of the "special"
|
|
* tags that ignore all tags except their
|
|
* own endtags (e.g. P_TITLE, P_PLAIN_PIECE)
|
|
* we need to output this comment
|
|
* as normal text.
|
|
*/
|
|
if (doc_data->brute_tag != P_UNKNOWN)
|
|
{
|
|
tag = pa_CreateTextTag(doc_data, buf, comment_len);
|
|
if (tag == NULL)
|
|
{
|
|
return(MK_OUT_OF_MEMORY);
|
|
}
|
|
ret = doc_data->output_tag(data_object, tag, PA_PARSED);
|
|
if (ret < 0)
|
|
{
|
|
return(ret);
|
|
}
|
|
}
|
|
else if (doc_data->window_id && EDT_IS_EDITOR(doc_data->window_id))
|
|
{
|
|
/*
|
|
* The Editor wants to see comments.
|
|
*/
|
|
tag = pa_CreateMDLTag(doc_data, buf, comment_len);
|
|
if (tag == NULL)
|
|
{
|
|
return(MK_OUT_OF_MEMORY);
|
|
}
|
|
ret = doc_data->output_tag(data_object, tag, PA_PARSED);
|
|
if (ret < 0)
|
|
{
|
|
return(ret);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Apply lose_newline heuristic after
|
|
* discarding a comment.
|
|
*/
|
|
doc_data->lose_newline = TRUE;
|
|
doc_data->comment_bytes += comment_len;
|
|
}
|
|
|
|
/*
|
|
* Move the buffer forward.
|
|
*/
|
|
len = len - comment_len;
|
|
if (len == 0)
|
|
{
|
|
buf = NULL;
|
|
}
|
|
else
|
|
{
|
|
buf = tptr;
|
|
buf++;
|
|
}
|
|
}
|
|
/*
|
|
* Else got the end of the MDL tag!
|
|
*/
|
|
else if (tptr != NULL)
|
|
{
|
|
int32 text_len;
|
|
|
|
/*
|
|
* Create and format the tag(s)
|
|
*/
|
|
text_len = (int32)(tptr - buf) + 1;
|
|
tag = pa_CreateMDLTag(doc_data, buf, text_len);
|
|
|
|
/*
|
|
* If we are inside one of the "special"
|
|
* tags that ignore all tags except their
|
|
* own endtags (e.g. P_TITLE, P_PLAIN_PIECE)
|
|
* we check here to see if this is the
|
|
* proper end tag, if not, we turn it
|
|
* back into normal text.
|
|
*/
|
|
if (doc_data->brute_tag != P_UNKNOWN)
|
|
{
|
|
if ((tag == NULL)||
|
|
(tag->is_end == FALSE)||
|
|
(doc_data->brute_tag != tag->type))
|
|
{
|
|
PA_FreeTag(tag);
|
|
/*
|
|
* Strip only the '<' which made us think this
|
|
* was an HTML tag.
|
|
*/
|
|
text_len = 1;
|
|
tptr = buf;
|
|
tag = pa_CreateTextTag(doc_data, buf, text_len);
|
|
if (tag == NULL)
|
|
{
|
|
return(MK_OUT_OF_MEMORY);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
doc_data->brute_tag = P_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* These tags are special in that, after opening one
|
|
* of them, all other tags are ignored until the matching
|
|
* closing tag.
|
|
*/
|
|
if ((tag != NULL)&&(tag->is_end == FALSE)&&
|
|
((tag->type == P_TITLE)||
|
|
(tag->type == P_TEXTAREA)||
|
|
(tag->type == P_PLAIN_PIECE)||
|
|
(tag->type == P_PLAIN_TEXT)||
|
|
(tag->type == P_SERVER)||
|
|
(tag->type == P_SCRIPT)||
|
|
(tag->type == P_STYLE)))
|
|
{
|
|
doc_data->brute_tag = tag->type;
|
|
}
|
|
|
|
/*
|
|
* These tags are special in that, after opening one
|
|
* of them, the lose_newline heuristic is applied.
|
|
*/
|
|
if ((tag != NULL)&&(tag->is_end == FALSE)&&
|
|
((tag->type == P_TITLE)||
|
|
(tag->type == P_TEXTAREA)||
|
|
(tag->type == P_PLAIN_PIECE)||
|
|
(tag->type == P_LISTING_TEXT)||
|
|
(tag->type == P_PREFORMAT)))
|
|
{
|
|
doc_data->lose_newline = TRUE;
|
|
}
|
|
|
|
ret = doc_data->output_tag(data_object, tag, PA_PARSED);
|
|
if (ret < 0)
|
|
{
|
|
return(ret);
|
|
}
|
|
|
|
/*
|
|
* Move the buffer forward.
|
|
*/
|
|
len = len - text_len;
|
|
if (len == 0)
|
|
{
|
|
buf = NULL;
|
|
}
|
|
else
|
|
{
|
|
buf = tptr;
|
|
buf++;
|
|
}
|
|
|
|
if (doc_data->overflow_depth)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
}
|
|
/*
|
|
* We couldn't find the end of the MDL tag.
|
|
* Hold the start if we have one.
|
|
*/
|
|
else if (buf != NULL)
|
|
{
|
|
doc_data->hold = 1;
|
|
/*
|
|
* Grow the hold buffer if it is not big enough to hold
|
|
* the rest of this buffer.
|
|
*/
|
|
#ifdef XP_WIN16
|
|
/*
|
|
* On the 32K limit, our hold_buf is already max size
|
|
*/
|
|
#else
|
|
if (len > doc_data->hold_size)
|
|
{
|
|
XP_UNLOCK_BLOCK((doc_data->hold_buf));
|
|
buff = XP_REALLOC_BLOCK(doc_data->hold_buf,
|
|
((doc_data->hold_size + HOLD_BUF_UNIT + len) *
|
|
sizeof(char)));
|
|
if (buff == NULL)
|
|
{
|
|
return(MK_OUT_OF_MEMORY);
|
|
}
|
|
doc_data->hold_buf = buff;
|
|
XP_LOCK_BLOCK(hold_buf, char *,
|
|
(doc_data->hold_buf));
|
|
doc_data->hold_size += (HOLD_BUF_UNIT + len);
|
|
}
|
|
#endif /* XP_WIN16 */
|
|
|
|
XP_BCOPY(buf, hold_buf, len);
|
|
doc_data->hold_len = len;
|
|
}
|
|
} /* end of else on COMMENT_MAYBE */
|
|
} /* end of while */
|
|
|
|
/*
|
|
* If we got here because we entered overflow mode all of the
|
|
* left over data should go into the overflow buffer and the
|
|
* hold buffer should be left empty
|
|
*/
|
|
if (doc_data->overflow_depth && len > 0)
|
|
{
|
|
pa_Overflow *overflow = doc_data->overflow_stack; /* check for NULL? */
|
|
char * overflow_buf;
|
|
int32 over_len = overflow->len;
|
|
int32 over_size = overflow->size;
|
|
int32 new_size = over_len + len;
|
|
|
|
XP_UNLOCK_BLOCK((doc_data->hold_buf));
|
|
|
|
if (over_size < new_size)
|
|
{
|
|
buff = XP_REALLOC_BLOCK(overflow->buf,
|
|
new_size * sizeof(char));
|
|
if (buff == NULL)
|
|
{
|
|
return(MK_OUT_OF_MEMORY);
|
|
}
|
|
overflow->buf = buff;
|
|
overflow->size = new_size;
|
|
}
|
|
|
|
/*
|
|
* We could be processing data that was passed in from the
|
|
* overflow_buf so we need to do an over-lap safe copy here
|
|
*/
|
|
XP_LOCK_BLOCK(overflow_buf, char *, overflow->buf);
|
|
XP_MEMMOVE(overflow_buf + over_len, buf, len);
|
|
XP_UNLOCK_BLOCK(overflow->buf);
|
|
overflow->len = new_size;
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Unlock the hold buffer, and clear it if we
|
|
* aren't holding anything this time around.
|
|
*/
|
|
if ((!doc_data->hold)&&(doc_data->hold_buf != NULL))
|
|
{
|
|
hold_buf = NULL;
|
|
doc_data->hold_len = 0;
|
|
XP_UNLOCK_BLOCK((doc_data->hold_buf));
|
|
}
|
|
else if (doc_data->hold_buf != NULL)
|
|
{
|
|
XP_UNLOCK_BLOCK((doc_data->hold_buf));
|
|
}
|
|
|
|
#ifdef XP_WIN16
|
|
if (extra_ptr != NULL)
|
|
{
|
|
if ((doc_data->hold_len + extra) > doc_data->hold_size)
|
|
{
|
|
char minibuf[1];
|
|
intn ret;
|
|
|
|
XP_LOCK_BLOCK(hold_buf, char *, (doc_data->hold_buf));
|
|
minibuf[0] = *hold_buf;
|
|
XP_BCOPY((char *)(hold_buf + 1), hold_buf, (doc_data->hold_len - 1));
|
|
doc_data->hold_len--;
|
|
XP_UNLOCK_BLOCK((doc_data->hold_buf));
|
|
|
|
tag = pa_CreateTextTag(doc_data, minibuf, 1);
|
|
if (tag == NULL)
|
|
{
|
|
return(MK_OUT_OF_MEMORY);
|
|
}
|
|
ret = doc_data->output_tag(data_object, tag, PA_PARSED);
|
|
if (ret < 0)
|
|
{
|
|
return(ret);
|
|
}
|
|
|
|
ret = PA_ParseBlock(stream, extra_ptr, extra);
|
|
return(ret);
|
|
}
|
|
else
|
|
{
|
|
XP_LOCK_BLOCK(hold_buf, char *, (doc_data->hold_buf));
|
|
XP_BCOPY(extra_ptr, (char *)(hold_buf + doc_data->hold_len), extra);
|
|
doc_data->hold_len += extra;
|
|
XP_UNLOCK_BLOCK((doc_data->hold_buf));
|
|
}
|
|
}
|
|
#endif /* XP_WIN16 */
|
|
|
|
return(1);
|
|
}
|
|
|
|
|
|
/*************************************
|
|
* Function: PA_MDLComplete
|
|
*
|
|
* Description: This is a very important entry point to the parser,
|
|
* but it will never be called directly. It will be
|
|
* placed into the stream returned by PA_BeginParseMDL()
|
|
* and be called from there. It tells the parser that the
|
|
* passed document is done parsing, there is no new data.
|
|
*
|
|
* Params: The data_object created and placed in the stream class
|
|
* in PA_BeginParseMDL(). This contains document specific
|
|
* parse state information.
|
|
*
|
|
* Returns: nothing.
|
|
*************************************/
|
|
void
|
|
PA_MDLComplete(NET_StreamClass *stream)
|
|
{
|
|
void *data_object=stream->data_object;
|
|
pa_DocData *doc_data;
|
|
PA_Tag *tag;
|
|
Bool is_main_context_stream;
|
|
|
|
/*
|
|
* This MDL document is complete
|
|
*/
|
|
doc_data = (pa_DocData *)data_object;
|
|
|
|
XP_ASSERT(doc_data->overflow_depth == 0);
|
|
|
|
/*
|
|
* If we were holding some data we hadn't parsed yet, we need
|
|
* to flush it through now. Since we couldn't resolve whatever
|
|
* we were waiting for, just push it through as plain text.
|
|
*/
|
|
if (doc_data->hold)
|
|
{
|
|
char *tptr;
|
|
|
|
/*
|
|
* Lock down the hold buffer so we can do pointer magic
|
|
* on it.
|
|
*/
|
|
XP_LOCK_BLOCK(tptr, char *, (doc_data->hold_buf));
|
|
|
|
/*
|
|
* Due to NCSA Mosaic, there are many incorrectly
|
|
* commented documents out there, that have the <!-- comment
|
|
* start, but expect a different end such as --!> or just >
|
|
* If we have finished this document, and the hold buffer
|
|
* has a starting comment, this is probably what happened,
|
|
* So terminate the starting comment with the next > and
|
|
* parse on.
|
|
* Of necessity this may involve recursion for multiple comments
|
|
*/
|
|
if ((doc_data->hold_len > 4)&&(XP_STRNCMP(tptr, "<!--", 4) == 0))
|
|
{
|
|
char *nothing;
|
|
NET_StreamClass *newstream;
|
|
|
|
/*
|
|
* Break the comment
|
|
*/
|
|
tptr[2] = 'C';
|
|
XP_UNLOCK_BLOCK((doc_data->hold_buf));
|
|
|
|
/*
|
|
* Reparse this, then call yourself.
|
|
*/
|
|
nothing = (char*) XP_ALLOC(1);
|
|
if (nothing == NULL)
|
|
{
|
|
XP_FREE_BLOCK((doc_data->hold_buf));
|
|
return;
|
|
}
|
|
nothing[0] = '\0';
|
|
newstream = doc_data->parser_stream;
|
|
PA_HoldDocData(doc_data);
|
|
(void)PA_ParseBlock(newstream, nothing, 0);
|
|
XP_FREE(nothing);
|
|
PA_DropDocData(newstream);
|
|
return;
|
|
}
|
|
#ifndef LENIENT_END_TAG
|
|
/*
|
|
* Also due to allowing '>' in quoted attributes, if they forget
|
|
* to close a quote, and there is no other in the entire document,
|
|
* we could have held the whole thing looking for the close quote,
|
|
* and never found the tag. Skip this malformed tag by dropping
|
|
* its starting '<' and parsing on.
|
|
* Of necessity this may involve recursion for multiple errors.
|
|
*/
|
|
else if ((doc_data->hold_len > 3)&&(*tptr == '<'))
|
|
{
|
|
char *nothing;
|
|
PA_Tag *tmp_tag;
|
|
NET_StreamClass *newstream;
|
|
|
|
/*
|
|
* Push out the '<'
|
|
*/
|
|
nothing = (char*) XP_ALLOC(1);
|
|
if (nothing == NULL)
|
|
{
|
|
XP_FREE_BLOCK((doc_data->hold_buf));
|
|
return;
|
|
}
|
|
nothing[0] = '<';
|
|
tmp_tag = pa_CreateTextTag(doc_data, nothing, 1);
|
|
doc_data->output_tag(data_object, tmp_tag, PA_PARSED);
|
|
|
|
/*
|
|
* Remove the '<' an move up the hold buffer.
|
|
*/
|
|
XP_BCOPY((char *)(tptr + 1), tptr, (doc_data->hold_len - 1));
|
|
doc_data->hold_len--;
|
|
XP_UNLOCK_BLOCK((doc_data->hold_buf));
|
|
|
|
/*
|
|
* reparse the rest.
|
|
*/
|
|
nothing[0] = '\0';
|
|
newstream = doc_data->parser_stream;
|
|
PA_HoldDocData(doc_data);
|
|
(void)PA_ParseBlock(newstream, nothing, 0);
|
|
XP_FREE(nothing);
|
|
PA_DropDocData(newstream);
|
|
return;
|
|
}
|
|
#endif /* LENIENT_END_TAG */
|
|
|
|
tag = pa_CreateTextTag(doc_data, tptr, doc_data->hold_len);
|
|
doc_data->output_tag(data_object, tag, PA_PARSED);
|
|
|
|
XP_UNLOCK_BLOCK((doc_data->hold_buf));
|
|
XP_FREE_BLOCK((doc_data->hold_buf));
|
|
doc_data->hold_len = 0;
|
|
doc_data->hold_buf = NULL;
|
|
doc_data->hold = 0;
|
|
}
|
|
else if (doc_data->hold_buf != NULL)
|
|
{
|
|
XP_FREE_BLOCK((doc_data->hold_buf));
|
|
doc_data->hold_buf = NULL;
|
|
doc_data->hold_size = 0;
|
|
}
|
|
|
|
doc_data->output_tag(data_object, NULL, PA_COMPLETE);
|
|
is_main_context_stream = pa_RemoveDocData(doc_data);
|
|
|
|
/*
|
|
* free up all the data allocated when this stream was initiated.
|
|
*/
|
|
if (doc_data->url_struct != NULL)
|
|
{
|
|
NET_FreeURLStruct(doc_data->url_struct);
|
|
}
|
|
|
|
if (doc_data->overflow_stack)
|
|
{
|
|
pa_Overflow *overflow;
|
|
|
|
while ((overflow = PA_PopOverflow(doc_data)))
|
|
PA_FreeOverflow(overflow);
|
|
|
|
doc_data->overflow_stack = NULL;
|
|
}
|
|
|
|
if (doc_data->url != NULL)
|
|
{
|
|
XP_FREE(doc_data->url);
|
|
}
|
|
if (is_main_context_stream)
|
|
{
|
|
XP_DELETE(doc_data->parser_stream);
|
|
}
|
|
|
|
XP_DELETE(doc_data);
|
|
}
|
|
|
|
|
|
/*************************************
|
|
* Function: PA_MDLAbort
|
|
*
|
|
* Description: This is a very important entry point to the parser,
|
|
* but it will never be called directly. It will be
|
|
* placed into the stream returned by PA_BeginParseMDL()
|
|
* and be called from there. It tells the parser that the
|
|
* passed document is aborted, there is no new data,
|
|
* throw out everything you have and stop parsing.
|
|
*
|
|
* Params: The data_object created and placed in the stream class
|
|
* in PA_BeginParseMDL(). This contains document specific
|
|
* parse state information. Also passed a character message
|
|
* which is the reason for the abort.
|
|
*
|
|
* Returns: nothing.
|
|
*************************************/
|
|
void
|
|
PA_MDLAbort(NET_StreamClass *stream, int status)
|
|
{
|
|
void *data_object=stream->data_object;
|
|
pa_DocData *doc_data;
|
|
Bool is_main_context_stream;
|
|
/*
|
|
* This MDL document is complete
|
|
*/
|
|
doc_data = (pa_DocData *)data_object;
|
|
|
|
/*
|
|
* If we were holding some data we hadn't parsed yet, we need
|
|
* to throw it out now.
|
|
*/
|
|
if (doc_data->hold)
|
|
{
|
|
XP_UNLOCK_BLOCK((doc_data->hold_buf));
|
|
XP_FREE_BLOCK((doc_data->hold_buf));
|
|
doc_data->hold_len = 0;
|
|
doc_data->hold_buf = NULL;
|
|
doc_data->hold = 0;
|
|
}
|
|
else if (doc_data->hold_buf != NULL)
|
|
{
|
|
XP_FREE_BLOCK((doc_data->hold_buf));
|
|
doc_data->hold_buf = NULL;
|
|
doc_data->hold_size = 0;
|
|
}
|
|
|
|
doc_data->output_tag(data_object, NULL, PA_ABORT);
|
|
is_main_context_stream = pa_RemoveDocData(doc_data);
|
|
|
|
/*
|
|
* free up all the data allocated when this stream was initiated.
|
|
*/
|
|
if (doc_data->url_struct != NULL)
|
|
{
|
|
NET_FreeURLStruct(doc_data->url_struct);
|
|
doc_data->url_struct = NULL;
|
|
}
|
|
if (doc_data->url != NULL)
|
|
{
|
|
XP_FREE(doc_data->url);
|
|
}
|
|
|
|
if (doc_data->overflow_stack)
|
|
{
|
|
pa_Overflow *overflow;
|
|
|
|
while ((overflow = PA_PopOverflow(doc_data)))
|
|
PA_FreeOverflow(overflow);
|
|
}
|
|
doc_data->overflow_depth = 0;
|
|
|
|
if (is_main_context_stream)
|
|
{
|
|
XP_DELETE(doc_data->parser_stream);
|
|
}
|
|
XP_DELETE(doc_data);
|
|
}
|
|
|
|
static pa_DocData *
|
|
pa_FetchDocData(MWContext *window_id)
|
|
{
|
|
pa_DocDataList *dptr;
|
|
pa_DocData *doc_data;
|
|
|
|
dptr = DocDataList;
|
|
while (dptr != NULL)
|
|
{
|
|
if (dptr->window_id == window_id)
|
|
{
|
|
break;
|
|
}
|
|
dptr = dptr->next;
|
|
}
|
|
if (dptr == NULL)
|
|
{
|
|
doc_data = NULL;
|
|
}
|
|
else
|
|
{
|
|
doc_data = dptr->doc_data;
|
|
}
|
|
|
|
return(doc_data);
|
|
}
|
|
|
|
XP_Bool ValidateDocData(MWContext *window_id)
|
|
{
|
|
if (pa_FetchDocData(window_id))
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
static Bool
|
|
pa_RemoveDocData(pa_DocData *target_doc_data)
|
|
{
|
|
pa_DocDataList *dptr;
|
|
pa_DocDataList *doc_data;
|
|
|
|
doc_data = DocDataList;
|
|
dptr = DocDataList;
|
|
while (dptr != NULL)
|
|
{
|
|
if (dptr->doc_data == target_doc_data)
|
|
{
|
|
break;
|
|
}
|
|
doc_data = dptr;
|
|
dptr = dptr->next;
|
|
}
|
|
if (dptr != NULL)
|
|
{
|
|
if (dptr == DocDataList)
|
|
{
|
|
DocDataList = DocDataList->next;
|
|
}
|
|
else
|
|
{
|
|
doc_data->next = dptr->next;
|
|
}
|
|
XP_DELETE(dptr);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static Bool
|
|
pa_StoreDocData(MWContext *window_id, pa_DocData *new_doc_data)
|
|
{
|
|
pa_DocDataList *dptr;
|
|
|
|
dptr = DocDataList;
|
|
while (dptr != NULL)
|
|
{
|
|
if (dptr->window_id == window_id)
|
|
{
|
|
break;
|
|
}
|
|
dptr = dptr->next;
|
|
}
|
|
|
|
if (dptr == NULL)
|
|
{
|
|
dptr = XP_NEW(pa_DocDataList);
|
|
if (dptr == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
dptr->window_id = window_id;
|
|
dptr->next = DocDataList;
|
|
DocDataList = dptr;
|
|
}
|
|
dptr->doc_data = new_doc_data;
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
PA_PushOverflow(pa_DocData* doc_data)
|
|
{
|
|
pa_Overflow *overflow = XP_NEW(pa_Overflow);
|
|
|
|
if (overflow == NULL) return;
|
|
|
|
overflow->buf = XP_ALLOC(HOLD_BUF_UNIT * sizeof(char));
|
|
if (overflow->buf == NULL)
|
|
{
|
|
XP_FREE(overflow);
|
|
return;
|
|
}
|
|
|
|
overflow->buf[0] = 0;
|
|
|
|
overflow->size = HOLD_BUF_UNIT;
|
|
overflow->len = 0;
|
|
|
|
overflow->next = doc_data->overflow_stack;
|
|
doc_data->overflow_stack = overflow;
|
|
}
|
|
|
|
pa_Overflow *
|
|
PA_PopOverflow(pa_DocData* doc_data)
|
|
{
|
|
pa_Overflow *overflow = doc_data->overflow_stack;
|
|
|
|
if (!overflow) return NULL;
|
|
|
|
doc_data->overflow_stack = overflow->next;
|
|
|
|
overflow->next = NULL;
|
|
|
|
return overflow;
|
|
}
|
|
|
|
void
|
|
PA_FreeOverflow(pa_Overflow *overflow)
|
|
{
|
|
XP_FREE(overflow->buf);
|
|
XP_FREE(overflow);
|
|
}
|
|
|
|
int
|
|
PA_GetOverflowDepth(pa_DocData* doc_data)
|
|
{
|
|
return doc_data->overflow_depth;
|
|
}
|
|
|
|
XP_Block
|
|
PA_GetOverflowBuf(pa_DocData* doc_data)
|
|
{
|
|
XP_ASSERT(doc_data->overflow_stack);
|
|
if (!doc_data->overflow_stack) return NULL;
|
|
|
|
return doc_data->overflow_stack->buf;
|
|
}
|
|
|
|
#ifdef PROFILE
|
|
#pragma profile off
|
|
#endif
|
|
|