PHPAPI void php_session_start(TSRMLS_D) /* {{{ */
{
	zval **ppid;
	zval **data;
	char *p, *value;
	int nrand;
	int lensess;

	if (PS(use_only_cookies)) {
		PS(apply_trans_sid) = 0;
	} else {
		PS(apply_trans_sid) = PS(use_trans_sid);
	}

	switch (PS(session_status)) {
		case php_session_active:
			php_error(E_NOTICE, "A session had already been started - ignoring session_start()");
			return;
			break;

		case php_session_disabled:
			value = zend_ini_string("session.save_handler", sizeof("session.save_handler"), 0);
			if (!PS(mod) && value) {
				PS(mod) = _php_find_ps_module(value TSRMLS_CC);
				if (!PS(mod)) {
					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot find save handler '%s' - session startup failed", value);
					return;
				}
			}
			value = zend_ini_string("session.serialize_handler", sizeof("session.serialize_handler"), 0);
			if (!PS(serializer) && value) {
				PS(serializer) = _php_find_ps_serializer(value TSRMLS_CC);
				if (!PS(serializer)) {
					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot find serialization handler '%s' - session startup failed", value);
					return;
				}
			}
			PS(session_status) = php_session_none;
			/* fallthrough */

		default:
		case php_session_none:
			PS(define_sid) = 1;
			PS(send_cookie) = 1;
	}

	lensess = strlen(PS(session_name));

	/* Cookies are preferred, because initially
	 * cookie and get variables will be available. */

	if (!PS(id)) {
		if (PS(use_cookies) && zend_hash_find(&EG(symbol_table), "_COOKIE", sizeof("_COOKIE"), (void **) &data) == SUCCESS &&
				Z_TYPE_PP(data) == IS_ARRAY &&
				zend_hash_find(Z_ARRVAL_PP(data), PS(session_name), lensess + 1, (void **) &ppid) == SUCCESS
		) {
			ppid2sid(ppid TSRMLS_CC);
			PS(apply_trans_sid) = 0;
			PS(define_sid) = 0;
		}

		if (!PS(use_only_cookies) && !PS(id) &&
				zend_hash_find(&EG(symbol_table), "_GET", sizeof("_GET"), (void **) &data) == SUCCESS &&
				Z_TYPE_PP(data) == IS_ARRAY &&
				zend_hash_find(Z_ARRVAL_PP(data), PS(session_name), lensess + 1, (void **) &ppid) == SUCCESS
		) {
			ppid2sid(ppid TSRMLS_CC);
		}

		if (!PS(use_only_cookies) && !PS(id) &&
				zend_hash_find(&EG(symbol_table), "_POST", sizeof("_POST"), (void **) &data) == SUCCESS &&
				Z_TYPE_PP(data) == IS_ARRAY &&
				zend_hash_find(Z_ARRVAL_PP(data), PS(session_name), lensess + 1, (void **) &ppid) == SUCCESS
		) {
			ppid2sid(ppid TSRMLS_CC);
		}
	}

	/* Check the REQUEST_URI symbol for a string of the form
	 * '<session-name>=<session-id>' to allow URLs of the form
	 * http://yoursite/<session-name>=<session-id>/script.php */

	if (!PS(use_only_cookies) && !PS(id) && PG(http_globals)[TRACK_VARS_SERVER] &&
			zend_hash_find(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), "REQUEST_URI", sizeof("REQUEST_URI"), (void **) &data) == SUCCESS &&
			Z_TYPE_PP(data) == IS_STRING &&
			(p = strstr(Z_STRVAL_PP(data), PS(session_name))) &&
			p[lensess] == '='
	) {
		char *q;

		p += lensess + 1;
		if ((q = strpbrk(p, "/?\\"))) {
			PS(id) = estrndup(p, q - p);
			PS(send_cookie) = 0;
		}
	}

	/* Check whether the current request was referred to by
	 * an external site which invalidates the previously found id. */

	if (PS(id) &&
			PS(extern_referer_chk)[0] != '\0' &&
			PG(http_globals)[TRACK_VARS_SERVER] &&
			zend_hash_find(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), "HTTP_REFERER", sizeof("HTTP_REFERER"), (void **) &data) == SUCCESS &&
			Z_TYPE_PP(data) == IS_STRING &&
			Z_STRLEN_PP(data) != 0 &&
			strstr(Z_STRVAL_PP(data), PS(extern_referer_chk)) == NULL
	) {
		efree(PS(id));
		PS(id) = NULL;
		PS(send_cookie) = 1;
		if (PS(use_trans_sid) && !PS(use_only_cookies)) {
			PS(apply_trans_sid) = 1;
		}
	}

	php_session_initialize(TSRMLS_C);

	if (!PS(use_cookies) && PS(send_cookie)) {
		if (PS(use_trans_sid) && !PS(use_only_cookies)) {
			PS(apply_trans_sid) = 1;
		}
		PS(send_cookie) = 0;
	}

	php_session_reset_id(TSRMLS_C);

	PS(session_status) = php_session_active;

	php_session_cache_limiter(TSRMLS_C);

	if ((PS(mod_data) || PS(mod_user_implemented)) && PS(gc_probability) > 0) {
		int nrdels = -1;

		nrand = (int) ((float) PS(gc_divisor) * php_combined_lcg(TSRMLS_C));
		if (nrand < PS(gc_probability)) {
			PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &nrdels TSRMLS_CC);
#ifdef SESSION_DEBUG
			if (nrdels != -1) {
				php_error_docref(NULL TSRMLS_CC, E_NOTICE, "purged %d expired session objects", nrdels);
			}
#endif
		}
	}
}


static int exif_scan_FILE_header(image_info_type *ImageInfo TSRMLS_DC)
{
	unsigned char file_header[8];
	int ret = FALSE;

	ImageInfo->FileType = IMAGE_FILETYPE_UNKNOWN;

	if (ImageInfo->FileSize >= 2) {
		php_stream_seek(ImageInfo->infile, 0, SEEK_SET);
		if (php_stream_read(ImageInfo->infile, (char*)file_header, 2) != 2) {
			return FALSE;
		}
		if ((file_header[0]==0xff) && (file_header[1]==M_SOI)) {
			ImageInfo->FileType = IMAGE_FILETYPE_JPEG;
			if (exif_scan_JPEG_header(ImageInfo TSRMLS_CC)) {
				ret = TRUE;
			} else {
				exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid JPEG file");
			}
		} else if (ImageInfo->FileSize >= 8) {
			if (php_stream_read(ImageInfo->infile, (char*)(file_header+2), 6) != 6) {
				return FALSE;
			}
			if (!memcmp(file_header, "II\x2A\x00", 4)) {
				ImageInfo->FileType = IMAGE_FILETYPE_TIFF_II;
				ImageInfo->motorola_intel = 0;
#ifdef EXIF_DEBUG
				exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "File has TIFF/II format");
#endif
				ImageInfo->sections_found |= FOUND_IFD0;
				if (exif_process_IFD_in_TIFF(ImageInfo,
											 php_ifd_get32u(file_header + 4, ImageInfo->motorola_intel),
											 SECTION_IFD0 TSRMLS_CC)) {
					ret = TRUE;
				} else {
					exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid TIFF file");
				}
			} else if (!memcmp(file_header, "MM\x00\x2a", 4)) {
				ImageInfo->FileType = IMAGE_FILETYPE_TIFF_MM;
				ImageInfo->motorola_intel = 1;
#ifdef EXIF_DEBUG
				exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "File has TIFF/MM format");
#endif
				ImageInfo->sections_found |= FOUND_IFD0;
				if (exif_process_IFD_in_TIFF(ImageInfo,
											 php_ifd_get32u(file_header + 4, ImageInfo->motorola_intel),
											 SECTION_IFD0 TSRMLS_CC)) {
					ret = TRUE;
				} else {
					exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid TIFF file");
				}
			} else {
				exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "File not supported");
				return FALSE;
			}
		}
	} else {
		exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "File too small (%d)", ImageInfo->FileSize);
	}
	return ret;
}


PHP_HASH_API void PHP_HAVAL224Final(unsigned char *digest, PHP_HAVAL_CTX * context)
{
	unsigned char bits[10];
	unsigned int index, padLen;

	/* Version, Passes, and Digest Length */
	bits[0] =	(PHP_HASH_HAVAL_VERSION & 0x07) |
				((context->passes & 0x07) << 3) |
				((context->output & 0x03) << 6);
	bits[1] = (context->output >> 2);

	/* Save number of bits */
	Encode(bits + 2, context->count, 8);

	/* Pad out to 118 mod 128.
	 */
	index = (unsigned int) ((context->count[0] >> 3) & 0x7f);
	padLen = (index < 118) ? (118 - index) : (246 - index);
	PHP_HAVALUpdate(context, PADDING, padLen);

	/* Append version, passes, digest length, and message length */
	PHP_HAVALUpdate(context, bits, 10);

	/* Store state in digest */
	context->state[6] +=  context->state[7]        & 0x0000000F;
	context->state[5] += (context->state[7] >>  4) & 0x0000001F;
	context->state[4] += (context->state[7] >>  9) & 0x0000000F;
	context->state[3] += (context->state[7] >> 13) & 0x0000001F;
	context->state[2] += (context->state[7] >> 18) & 0x0000000F;
	context->state[1] += (context->state[7] >> 22) & 0x0000001F;
	context->state[0] += (context->state[7] >> 27) & 0x0000001F;
	Encode(digest, context->state, 28);

	/* Zeroize sensitive information.
	 */
	memset((unsigned char*) context, 0, sizeof(*context));
}


static int preg_do_repl_func(zval *function, char *subject, int *offsets, char **subpat_names, int count, char **result TSRMLS_DC)
{
	zval		*retval_ptr;		/* Function return value */
	zval	   **args[1];			/* Argument to pass to function */
	zval		*subpats;			/* Captured subpatterns */
	int			 result_len;		/* Return value length */
	int			 i;

	MAKE_STD_ZVAL(subpats);
	array_init(subpats);
	for (i = 0; i < count; i++) {
		if (subpat_names[i]) {
			add_assoc_stringl(subpats, subpat_names[i], &subject[offsets[i<<1]] , offsets[(i<<1)+1] - offsets[i<<1], 1);
		}
		add_next_index_stringl(subpats, &subject[offsets[i<<1]], offsets[(i<<1)+1] - offsets[i<<1], 1);
	}
	args[0] = &subpats;

	if (call_user_function_ex(EG(function_table), NULL, function, &retval_ptr, 1, args, 0, NULL TSRMLS_CC) == SUCCESS && retval_ptr) {
		convert_to_string_ex(&retval_ptr);
		*result = estrndup(Z_STRVAL_P(retval_ptr), Z_STRLEN_P(retval_ptr));
		result_len = Z_STRLEN_P(retval_ptr);
		zval_ptr_dtor(&retval_ptr);
	} else {
		if (!EG(exception)) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to call custom replacement function");
		}
		result_len = offsets[1] - offsets[0];
		*result = estrndup(&subject[offsets[0]], result_len);
	}

	zval_ptr_dtor(&subpats);

	return result_len;
}


static inline void add_offset_pair(zval *result, char *str, int len, int offset, char *name)
{
	zval *match_pair;

	ALLOC_ZVAL(match_pair);
	array_init(match_pair);
	INIT_PZVAL(match_pair);

	/* Add (match, offset) to the return value */
	add_next_index_stringl(match_pair, str, len, 1);
	add_next_index_long(match_pair, offset);

	if (name) {
		zval_add_ref(&match_pair);
		zend_hash_update(Z_ARRVAL_P(result), name, strlen(name)+1, &match_pair, sizeof(zval *), NULL);
	}
	zend_hash_next_index_insert(Z_ARRVAL_P(result), &match_pair, sizeof(zval *), NULL);
}


PHP_FUNCTION(xsl_xsltprocessor_register_php_functions)
{
	zval *id;
	xsl_object *intern;
	zval *array_value, **entry, *new_string;
	int  name_len = 0;
	char *name;

	DOM_GET_THIS(id);

	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "a",  &array_value) == SUCCESS) {
		intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC);
		zend_hash_internal_pointer_reset(Z_ARRVAL_P(array_value));

		while (zend_hash_get_current_data(Z_ARRVAL_P(array_value), (void **)&entry) == SUCCESS) {
			SEPARATE_ZVAL(entry);
			convert_to_string_ex(entry);

			MAKE_STD_ZVAL(new_string);
			ZVAL_LONG(new_string,1);

			zend_hash_update(intern->registered_phpfunctions, Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &new_string, sizeof(zval*), NULL);
			zend_hash_move_forward(Z_ARRVAL_P(array_value));
		}
		intern->registerPhpFunctions = 2;

	} else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s",  &name, &name_len) == SUCCESS) {
		intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC);

		MAKE_STD_ZVAL(new_string);
		ZVAL_LONG(new_string,1);
		zend_hash_update(intern->registered_phpfunctions, name, name_len + 1, &new_string, sizeof(zval*), NULL);
		intern->registerPhpFunctions = 2;

	} else {
		intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC);
		intern->registerPhpFunctions = 1;
	}

}


xmlXPathNodeSetSort(xmlNodeSetPtr set) {
#ifndef WITH_TIM_SORT
    int i, j, incr, len;
    xmlNodePtr tmp;
#endif

    if (set == NULL)
	return;

#ifndef WITH_TIM_SORT
    /*
     * Use the old Shell's sort implementation to sort the node-set
     * Timsort ought to be quite faster
     */
    len = set->nodeNr;
    for (incr = len / 2; incr > 0; incr /= 2) {
	for (i = incr; i < len; i++) {
	    j = i - incr;
	    while (j >= 0) {
#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
		if (xmlXPathCmpNodesExt(set->nodeTab[j],
			set->nodeTab[j + incr]) == -1)
#else
		if (xmlXPathCmpNodes(set->nodeTab[j],
			set->nodeTab[j + incr]) == -1)
#endif
		{
		    tmp = set->nodeTab[j];
		    set->nodeTab[j] = set->nodeTab[j + incr];
		    set->nodeTab[j + incr] = tmp;
		    j -= incr;
		} else
		    break;
	    }
	}
    }
#else /* WITH_TIM_SORT */
    libxml_domnode_tim_sort(set->nodeTab, set->nodeNr);
#endif /* WITH_TIM_SORT */
}


xmlChar *
xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
    const xmlChar *in;
    xmlChar *ret;
    size_t count = 0;

    if ((ctxt == NULL) || (ctxt->cur == NULL)) return(NULL);
    /*
     * Accelerator for simple ASCII names
     */
    in = ctxt->cur;
    if (((*in >= 0x61) && (*in <= 0x7A)) ||
	((*in >= 0x41) && (*in <= 0x5A)) ||
	(*in == '_') || (*in == ':')) {
	in++;
	while (((*in >= 0x61) && (*in <= 0x7A)) ||
	       ((*in >= 0x41) && (*in <= 0x5A)) ||
	       ((*in >= 0x30) && (*in <= 0x39)) ||
	       (*in == '_') || (*in == '-') ||
	       (*in == ':') || (*in == '.'))
	    in++;
	if ((*in > 0) && (*in < 0x80)) {
	    count = in - ctxt->cur;
            if (count > XML_MAX_NAME_LENGTH) {
                ctxt->cur = in;
                XP_ERRORNULL(XPATH_EXPR_ERROR);
            }
	    ret = xmlStrndup(ctxt->cur, count);
	    ctxt->cur = in;
	    return(ret);
	}
    }


 */
static void
xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
    xmlChar *name;
    xmlChar *prefix;

    SKIP_BLANKS;
    if (CUR != '$') {
	XP_ERROR(XPATH_VARIABLE_REF_ERROR);
    }
    NEXT;
    name = xmlXPathParseQName(ctxt, &prefix);
    if (name == NULL) {
	XP_ERROR(XPATH_VARIABLE_REF_ERROR);
    }
    ctxt->comp->last = -1;
    PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
	           name, prefix);
    SKIP_BLANKS;
    if ((ctxt->context != NULL) && (ctxt->context->flags & XML_XPATH_NOVAR)) {
	XP_ERROR(XPATH_FORBID_VARIABLE_ERROR);


 */
xmlNodePtr
xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur)
{
    if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
    if (cur == NULL) {
        cur = ctxt->context->node;
        if (cur->type == XML_NAMESPACE_DECL)
            return(NULL);
        if (cur->type == XML_ATTRIBUTE_NODE)
            return(cur->parent);
    }
    if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
	return (NULL);
    if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
	cur = cur->prev;
    do {
        if (cur->prev != NULL) {
            for (cur = cur->prev; cur->last != NULL; cur = cur->last) ;
            return (cur);
        }

        cur = cur->parent;
        if (cur == NULL)
            return (NULL);
        if (cur == ctxt->context->doc->children)
            return (NULL);
    } while (xmlXPathIsAncestor(cur, ctxt->context->node));


xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2,
			     int hasNullEntries)
{
    if ((set1 == NULL) && (hasNullEntries == 0)) {
	/*
	* Note that doing a memcpy of the list, namespace nodes are
	* just assigned to set1, since set2 is cleared anyway.
	*/
	set1 = xmlXPathNodeSetCreateSize(set2->nodeNr);
	if (set1 == NULL)
	    return(NULL);
	if (set2->nodeNr != 0) {
	    memcpy(set1->nodeTab, set2->nodeTab,
		set2->nodeNr * sizeof(xmlNodePtr));
	    set1->nodeNr = set2->nodeNr;
	}
    } else {
	int i, j, initNbSet1;
	xmlNodePtr n1, n2;

	if (set1 == NULL)
            set1 = xmlXPathNodeSetCreate(NULL);
        if (set1 == NULL)
            return (NULL);

	initNbSet1 = set1->nodeNr;
	for (i = 0;i < set2->nodeNr;i++) {
	    n2 = set2->nodeTab[i];
	    /*
	    * Skip NULLed entries.
	    */
	    if (n2 == NULL)
		continue;
	    /*
	    * Skip duplicates.
	    */
	    for (j = 0; j < initNbSet1; j++) {
		n1 = set1->nodeTab[j];
		if (n1 == n2) {
		    goto skip_node;
		} else if ((n1->type == XML_NAMESPACE_DECL) &&
		    (n2->type == XML_NAMESPACE_DECL))
		{
		    if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) &&
			(xmlStrEqual(((xmlNsPtr) n1)->prefix,
			((xmlNsPtr) n2)->prefix)))
		    {
			/*
			* Free the namespace node.
			*/
			set2->nodeTab[i] = NULL;
			xmlXPathNodeSetFreeNs((xmlNsPtr) n2);
			goto skip_node;
		    }
		}
	    }
	    /*
	    * grow the nodeTab if needed
	    */
	    if (set1->nodeMax == 0) {
		set1->nodeTab = (xmlNodePtr *) xmlMalloc(
		    XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
		if (set1->nodeTab == NULL) {
		    xmlXPathErrMemory(NULL, "merging nodeset\n");
		    return(NULL);
		}
		memset(set1->nodeTab, 0,
		    XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
		set1->nodeMax = XML_NODESET_DEFAULT;
	    } else if (set1->nodeNr >= set1->nodeMax) {
		xmlNodePtr *temp;

                if (set1->nodeMax >= XPATH_MAX_NODESET_LENGTH) {
                    xmlXPathErrMemory(NULL, "merging nodeset hit limit\n");
                    return(NULL);
                }
		temp = (xmlNodePtr *) xmlRealloc(
		    set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr));
		if (temp == NULL) {
		    xmlXPathErrMemory(NULL, "merging nodeset\n");
		    return(NULL);
		}
		set1->nodeTab = temp;
		set1->nodeMax *= 2;
	    }
	    if (n2->type == XML_NAMESPACE_DECL) {
		xmlNsPtr ns = (xmlNsPtr) n2;

		set1->nodeTab[set1->nodeNr++] =
		    xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
	    } else
		set1->nodeTab[set1->nodeNr++] = n2;
skip_node:
	    {}
	}
    }
    set2->nodeNr = 0;
    return(set1);
}


valuePush(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr value)
{
    if ((ctxt == NULL) || (value == NULL)) return(-1);
    if (ctxt->valueNr >= ctxt->valueMax) {
        xmlXPathObjectPtr *tmp;

        if (ctxt->valueMax >= XPATH_MAX_STACK_DEPTH) {
            xmlXPathErrMemory(NULL, "XPath stack depth limit reached\n");
            ctxt->error = XPATH_MEMORY_ERROR;
            return (0);
        }
        tmp = (xmlXPathObjectPtr *) xmlRealloc(ctxt->valueTab,
                                             2 * ctxt->valueMax *
                                             sizeof(ctxt->valueTab[0]));
        if (tmp == NULL) {
            xmlXPathErrMemory(NULL, "pushing value\n");
            ctxt->error = XPATH_MEMORY_ERROR;
            return (0);
        }
        ctxt->valueMax *= 2;
	ctxt->valueTab = tmp;
    }
    ctxt->valueTab[ctxt->valueNr] = value;
    ctxt->value = value;
    return (ctxt->valueNr++);
}


 */
static xmlNodePtr
xmlXPathNextDescendantOrSelfElemParent(xmlNodePtr cur,
				       xmlNodePtr contextNode)
{
    if (cur == NULL) {
	if (contextNode == NULL)
	    return(NULL);
	switch (contextNode->type) {
	    case XML_ELEMENT_NODE:
	    case XML_XINCLUDE_START:
	    case XML_DOCUMENT_FRAG_NODE:
	    case XML_DOCUMENT_NODE:
#ifdef LIBXML_DOCB_ENABLED
	    case XML_DOCB_DOCUMENT_NODE:
#endif
	    case XML_HTML_DOCUMENT_NODE:
		return(contextNode);
	    default:
		return(NULL);
	}
	return(NULL);
    } else {
	xmlNodePtr start = cur;

	while (cur != NULL) {
	    switch (cur->type) {
		case XML_ELEMENT_NODE:
		/* TODO: OK to have XInclude here? */
		case XML_XINCLUDE_START:
		case XML_DOCUMENT_FRAG_NODE:
		    if (cur != start)
			return(cur);
		    if (cur->children != NULL) {
			cur = cur->children;
			continue;
		    }
		    break;
		/* Not sure if we need those here. */
		case XML_DOCUMENT_NODE:
#ifdef LIBXML_DOCB_ENABLED
		case XML_DOCB_DOCUMENT_NODE:
#endif
		case XML_HTML_DOCUMENT_NODE:
		    if (cur != start)
			return(cur);
		    return(xmlDocGetRootElement((xmlDocPtr) cur));
		default:
		    break;
	    }

next_sibling:
	    if ((cur == NULL) || (cur == contextNode))
		return(NULL);
	    if (cur->next != NULL) {
		cur = cur->next;
	    } else {
		cur = cur->parent;
		goto next_sibling;
	    }
	}
    }


static PHP_MSHUTDOWN_FUNCTION(zip)
{
#ifdef PHP_ZIP_USE_OO
	zend_hash_destroy(&zip_prop_handlers);
	php_unregister_url_stream_wrapper("zip" TSRMLS_CC);
#endif
	return SUCCESS;
}


static int php_zip_extract_file(struct zip * za, char *dest, char *file, int file_len TSRMLS_DC)
{
	php_stream_statbuf ssb;
	struct zip_file *zf;
	struct zip_stat sb;
	char b[8192];
	int n, len, ret;
	php_stream *stream;
	char *fullpath;
	char *file_dirname_fullpath;
	char file_dirname[MAXPATHLEN];
	size_t dir_len;
	char *file_basename;
	size_t file_basename_len;
	int is_dir_only = 0;
	char *path_cleaned;
	size_t path_cleaned_len;
	cwd_state new_state;

	new_state.cwd = (char*)malloc(1);
	new_state.cwd[0] = '\0';
	new_state.cwd_length = 0;

	/* Clean/normlize the path and then transform any path (absolute or relative)
		 to a path relative to cwd (../../mydir/foo.txt > mydir/foo.txt)
	 */
	virtual_file_ex(&new_state, file, NULL, CWD_EXPAND TSRMLS_CC);
	path_cleaned =  php_zip_make_relative_path(new_state.cwd, new_state.cwd_length);
	if(!path_cleaned) {
		return 0;
	}
	path_cleaned_len = strlen(path_cleaned);

	if (path_cleaned_len >= MAXPATHLEN || zip_stat(za, file, 0, &sb) != 0) {
		return 0;
	}

	/* it is a directory only, see #40228 */
	if (path_cleaned_len > 1 && IS_SLASH(path_cleaned[path_cleaned_len - 1])) {
		len = spprintf(&file_dirname_fullpath, 0, "%s/%s", dest, path_cleaned);
		is_dir_only = 1;
	} else {
		memcpy(file_dirname, path_cleaned, path_cleaned_len);
		dir_len = php_dirname(file_dirname, path_cleaned_len);

		if (dir_len <= 0 || (dir_len == 1 && file_dirname[0] == '.')) {
			len = spprintf(&file_dirname_fullpath, 0, "%s", dest);
		} else {
			len = spprintf(&file_dirname_fullpath, 0, "%s/%s", dest, file_dirname);
		}

		php_basename(path_cleaned, path_cleaned_len, NULL, 0, &file_basename, (size_t *)&file_basename_len TSRMLS_CC);

		if (ZIP_OPENBASEDIR_CHECKPATH(file_dirname_fullpath)) {
			efree(file_dirname_fullpath);
			efree(file_basename);
			free(new_state.cwd);
			return 0;
		}
	}

	/* let see if the path already exists */
	if (php_stream_stat_path_ex(file_dirname_fullpath, PHP_STREAM_URL_STAT_QUIET, &ssb, NULL) < 0) {

#if defined(PHP_WIN32) && (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 1)
		char *e;
		e = file_dirname_fullpath;
		while (*e) {
			   if (*e == '/') {
					   *e = DEFAULT_SLASH;
			   }
			   e++;
		}
#endif

		ret = php_stream_mkdir(file_dirname_fullpath, 0777,  PHP_STREAM_MKDIR_RECURSIVE|REPORT_ERRORS, NULL);
		if (!ret) {
			efree(file_dirname_fullpath);
			if (!is_dir_only) {
				efree(file_basename);
				free(new_state.cwd);
			}
			return 0;
		}
	}

	/* it is a standalone directory, job done */
	if (is_dir_only) {
		efree(file_dirname_fullpath);
		free(new_state.cwd);
		return 1;
	}

	len = spprintf(&fullpath, 0, "%s/%s", file_dirname_fullpath, file_basename);
	if (!len) {
		efree(file_dirname_fullpath);
		efree(file_basename);
		free(new_state.cwd);
		return 0;
	} else if (len > MAXPATHLEN) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Full extraction path exceed MAXPATHLEN (%i)", MAXPATHLEN);
		efree(file_dirname_fullpath);
		efree(file_basename);
		free(new_state.cwd);
		return 0;
	}

	/* check again the full path, not sure if it
	 * is required, does a file can have a different
	 * safemode status as its parent folder?
	 */
	if (ZIP_OPENBASEDIR_CHECKPATH(fullpath)) {
		efree(fullpath);
		efree(file_dirname_fullpath);
		efree(file_basename);
		free(new_state.cwd);
		return 0;
	}

#if PHP_API_VERSION < 20100412
	stream = php_stream_open_wrapper(fullpath, "w+b", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL);
#else
	stream = php_stream_open_wrapper(fullpath, "w+b", REPORT_ERRORS, NULL);
#endif

	if (stream == NULL) {
		n = -1;
		goto done;
	}

	zf = zip_fopen(za, file, 0);
	if (zf == NULL) {
		n = -1;
		php_stream_close(stream);
		goto done;
	}

	n = 0;

	while ((n=zip_fread(zf, b, sizeof(b))) > 0) {
		php_stream_write(stream, b, n);
	}

	php_stream_close(stream);
	n = zip_fclose(zf);

done:
	efree(fullpath);
	efree(file_basename);
	efree(file_dirname_fullpath);
	free(new_state.cwd);

	if (n<0) {
		return 0;
	} else {
		return 1;
	}
}


int php_zip_glob(char *pattern, int pattern_len, long flags, zval *return_value TSRMLS_DC) /* {{{ */
{
#ifdef HAVE_GLOB
	char cwd[MAXPATHLEN];
	int cwd_skip = 0;
#ifdef ZTS
	char work_pattern[MAXPATHLEN];
	char *result;
#endif
	glob_t globbuf;
	int n;
	int ret;

	if (pattern_len >= MAXPATHLEN) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Pattern exceeds the maximum allowed length of %d characters", MAXPATHLEN);
		return -1;
	}

	if ((GLOB_AVAILABLE_FLAGS & flags) != flags) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "At least one of the passed flags is invalid or not supported on this platform");
		return -1;
	}

#ifdef ZTS
	if (!IS_ABSOLUTE_PATH(pattern, pattern_len)) {
		result = VCWD_GETCWD(cwd, MAXPATHLEN);
		if (!result) {
			cwd[0] = '\0';
		}
#ifdef PHP_WIN32
		if (IS_SLASH(*pattern)) {
			cwd[2] = '\0';
		}
#endif
		cwd_skip = strlen(cwd)+1;

		snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, pattern);
		pattern = work_pattern;
	}
#endif

	globbuf.gl_offs = 0;
	if (0 != (ret = glob(pattern, flags & GLOB_FLAGMASK, NULL, &globbuf))) {
#ifdef GLOB_NOMATCH
		if (GLOB_NOMATCH == ret) {
			/* Some glob implementation simply return no data if no matches
			   were found, others return the GLOB_NOMATCH error code.
			   We don't want to treat GLOB_NOMATCH as an error condition
			   so that PHP glob() behaves the same on both types of
			   implementations and so that 'foreach (glob() as ...'
			   can be used for simple glob() calls without further error
			   checking.
			*/
			array_init(return_value);
			return 0;
		}
#endif
		return 0;
	}

	/* now catch the FreeBSD style of "no matches" */
	if (!globbuf.gl_pathc || !globbuf.gl_pathv) {
		array_init(return_value);
		return 0;
	}

	/* we assume that any glob pattern will match files from one directory only
	   so checking the dirname of the first match should be sufficient */
	strncpy(cwd, globbuf.gl_pathv[0], MAXPATHLEN);
	if (ZIP_OPENBASEDIR_CHECKPATH(cwd)) {
		return -1;
	}

	array_init(return_value);
	for (n = 0; n < globbuf.gl_pathc; n++) {
		/* we need to do this everytime since GLOB_ONLYDIR does not guarantee that
		 * all directories will be filtered. GNU libc documentation states the
		 * following:
		 * If the information about the type of the file is easily available
		 * non-directories will be rejected but no extra work will be done to
		 * determine the information for each file. I.e., the caller must still be
		 * able to filter directories out.
		 */
		if (flags & GLOB_ONLYDIR) {
			struct stat s;

			if (0 != VCWD_STAT(globbuf.gl_pathv[n], &s)) {
				continue;
			}

			if (S_IFDIR != (s.st_mode & S_IFMT)) {
				continue;
			}
		}
		add_next_index_string(return_value, globbuf.gl_pathv[n]+cwd_skip, 1);
	}

	globfree(&globbuf);
	return globbuf.gl_pathc;
#else
	php_error_docref(NULL TSRMLS_CC, E_ERROR, "Glob support is not available");
	return 0;
#endif  /* HAVE_GLOB */
}


/* {{{ date_period_it_rewind */
static void date_period_it_rewind(zend_object_iterator *iter TSRMLS_DC)
{
	date_period_it   *iterator = (date_period_it *)iter;

	iterator->current_index = 0;
	if (iterator->object->current) {
		timelib_time_dtor(iterator->object->current);
	}
	iterator->object->current = timelib_time_clone(iterator->object->start);


/* {{{ proto int strtotime(string time [, int now ])
   Convert string representation of date and time to a timestamp */
PHP_FUNCTION(strtotime)
{
	char *times, *initial_ts;
	int   time_len, error1, error2;
	struct timelib_error_container *error;
	long  preset_ts = 0, ts;

	timelib_time *t, *now;
	timelib_tzinfo *tzi;

	tzi = get_timezone_info(TSRMLS_C);

	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "sl", &times, &time_len, &preset_ts) != FAILURE) {
		/* We have an initial timestamp */
		now = timelib_time_ctor();

		initial_ts = emalloc(25);
		snprintf(initial_ts, 24, "@%ld UTC", preset_ts);
		t = timelib_strtotime(initial_ts, strlen(initial_ts), NULL, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper); /* we ignore the error here, as this should never fail */
		timelib_update_ts(t, tzi);
		now->tz_info = tzi;
		now->zone_type = TIMELIB_ZONETYPE_ID;
		timelib_unixtime2local(now, t->sse);
		timelib_time_dtor(t);
		efree(initial_ts);
	} else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &times, &time_len, &preset_ts) != FAILURE) {
		/* We have no initial timestamp */
		now = timelib_time_ctor();
		now->tz_info = tzi;
		now->zone_type = TIMELIB_ZONETYPE_ID;
		timelib_unixtime2local(now, (timelib_sll) time(NULL));
	} else {
		RETURN_FALSE;
	}

	if (!time_len) {
		timelib_time_dtor(now);
		RETURN_FALSE;
	}

	t = timelib_strtotime(times, time_len, &error, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
	error1 = error->error_count;
	timelib_error_container_dtor(error);
	timelib_fill_holes(t, now, TIMELIB_NO_CLONE);
	timelib_update_ts(t, tzi);
	ts = timelib_date_to_int(t, &error2);

	timelib_time_dtor(now);
	timelib_time_dtor(t);

	if (error1 || error2) {
		RETURN_FALSE;
	} else {
		RETURN_LONG(ts);


static void _php_date_tzinfo_dtor(void *tzinfo)
{
	timelib_tzinfo **tzi = (timelib_tzinfo **)tzinfo;


   Get end date.
*/
PHP_METHOD(DatePeriod, getEndDate)
{
        php_period_obj   *dpobj;
        php_date_obj     *dateobj;

        if (zend_parse_parameters_none() == FAILURE) {
                return;
        }

        dpobj = (php_period_obj *)zend_object_store_get_object(getThis() TSRMLS_CC);

        php_date_instantiate(dpobj->start_ce, return_value TSRMLS_CC);
        dateobj = (php_date_obj *)zend_object_store_get_object(return_value TSRMLS_CC);
        dateobj->time = timelib_time_ctor();
        *dateobj->time = *dpobj->end;
        if (dpobj->end->tz_abbr) {
                dateobj->time->tz_abbr = strdup(dpobj->end->tz_abbr);
        }
        if (dpobj->end->tz_info) {
                dateobj->time->tz_info = dpobj->end->tz_info;


   Sets the ISO date.
*/
PHP_FUNCTION(date_isodate_set)
{
	zval *object;
	long  y, w, d = 1;

	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll|l", &object, date_ce_date, &y, &w, &d) == FAILURE) {
		RETURN_FALSE;
	}

	php_date_isodate_set(object, y, w, d, return_value TSRMLS_CC);


   Creates new DateTimeImmutable object from an existing mutable DateTime object.
*/
PHP_METHOD(DateTimeImmutable, createFromMutable)
{
	zval *datetime_object = NULL;
	php_date_obj *new_obj = NULL;
	php_date_obj *old_obj = NULL;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O!", &datetime_object, date_ce_date) == FAILURE) {
		return;
	}

	php_date_instantiate(date_ce_immutable, return_value TSRMLS_CC);
	old_obj = (php_date_obj *) zend_object_store_get_object(datetime_object TSRMLS_CC);
	new_obj = (php_date_obj *) zend_object_store_get_object(return_value TSRMLS_CC);

	new_obj->time = timelib_time_ctor();
	*new_obj->time = *old_obj->time;
	if (old_obj->time->tz_abbr) {
		new_obj->time->tz_abbr = strdup(old_obj->time->tz_abbr);
	}
	if (old_obj->time->tz_info) {
		new_obj->time->tz_info = old_obj->time->tz_info;


/* {{{ date_period_read_property */
static zval *date_period_read_property(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC)
{
	zval *zv;
	if (type != BP_VAR_IS && type != BP_VAR_R) {
		php_error_docref(NULL TSRMLS_CC, E_ERROR, "Retrieval of DatePeriod properties for modification is unsupported");
	}

	Z_OBJPROP_P(object); /* build properties hash table */

	zv = std_object_handlers.read_property(object, member, type, key TSRMLS_CC);
	if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJ_HANDLER_P(zv, clone_obj)) {
		/* defensive copy */
		zend_object_value zov = Z_OBJ_HANDLER_P(zv, clone_obj)(zv TSRMLS_CC);
		MAKE_STD_ZVAL(zv);
		Z_TYPE_P(zv) = IS_OBJECT;
		Z_OBJVAL_P(zv) = zov;
	}


}

static zend_object_value date_object_clone_timezone(zval *this_ptr TSRMLS_DC)
{
	php_timezone_obj *new_obj = NULL;
	php_timezone_obj *old_obj = (php_timezone_obj *) zend_object_store_get_object(this_ptr TSRMLS_CC);
	zend_object_value new_ov = date_object_new_timezone_ex(old_obj->std.ce, &new_obj TSRMLS_CC);

	zend_objects_clone_members(&new_obj->std, new_ov, &old_obj->std, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC);
	if (!old_obj->initialized) {
		return new_ov;
	}

	new_obj->type = old_obj->type;
	new_obj->initialized = 1;
	switch (new_obj->type) {
		case TIMELIB_ZONETYPE_ID:
			new_obj->tzi.tz = old_obj->tzi.tz;
			break;
		case TIMELIB_ZONETYPE_OFFSET:
			new_obj->tzi.utc_offset = old_obj->tzi.utc_offset;
			break;
		case TIMELIB_ZONETYPE_ABBR:
			new_obj->tzi.z.utc_offset = old_obj->tzi.z.utc_offset;
			new_obj->tzi.z.dst        = old_obj->tzi.z.dst;
			new_obj->tzi.z.abbr       = strdup(old_obj->tzi.z.abbr);
			break;
	}


/* }}} */

static int check_id_allowed(char *id, long what) /* {{{ */
{
	if (what & PHP_DATE_TIMEZONE_GROUP_AFRICA     && strncasecmp(id, "Africa/",      7) == 0) return 1;
	if (what & PHP_DATE_TIMEZONE_GROUP_AMERICA    && strncasecmp(id, "America/",     8) == 0) return 1;
	if (what & PHP_DATE_TIMEZONE_GROUP_ANTARCTICA && strncasecmp(id, "Antarctica/", 11) == 0) return 1;
	if (what & PHP_DATE_TIMEZONE_GROUP_ARCTIC     && strncasecmp(id, "Arctic/",      7) == 0) return 1;
	if (what & PHP_DATE_TIMEZONE_GROUP_ASIA       && strncasecmp(id, "Asia/",        5) == 0) return 1;
	if (what & PHP_DATE_TIMEZONE_GROUP_ATLANTIC   && strncasecmp(id, "Atlantic/",    9) == 0) return 1;
	if (what & PHP_DATE_TIMEZONE_GROUP_AUSTRALIA  && strncasecmp(id, "Australia/",  10) == 0) return 1;
	if (what & PHP_DATE_TIMEZONE_GROUP_EUROPE     && strncasecmp(id, "Europe/",      7) == 0) return 1;
	if (what & PHP_DATE_TIMEZONE_GROUP_INDIAN     && strncasecmp(id, "Indian/",      7) == 0) return 1;
	if (what & PHP_DATE_TIMEZONE_GROUP_PACIFIC    && strncasecmp(id, "Pacific/",     8) == 0) return 1;
	if (what & PHP_DATE_TIMEZONE_GROUP_UTC        && strncasecmp(id, "UTC",          3) == 0) return 1;


/* {{{ proto string gmstrftime(string format [, int timestamp])
   Format a GMT/UCT time/date according to locale settings */
PHP_FUNCTION(gmstrftime)
{


/* {{{ date_period_it_dtor */
static void date_period_it_dtor(zend_object_iterator *iter TSRMLS_DC)
{
	date_period_it *iterator = (date_period_it *)iter;

	date_period_it_invalidate_current(iter TSRMLS_CC);

	zval_ptr_dtor(&iterator->date_period_zval);


/* }}} */

static void php_date_timestamp_set(zval *object, long timestamp, zval *return_value TSRMLS_DC)
{
	php_date_obj *dateobj;

	dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
	DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
	timelib_unixtime2local(dateobj->time, (timelib_sll)timestamp);


/* }}} */

static int date_interval_initialize(timelib_rel_time **rt, /*const*/ char *format, int format_length TSRMLS_DC)
{
	timelib_time     *b = NULL, *e = NULL;
	timelib_rel_time *p = NULL;
	int               r = 0;
	int               retval = 0;
	struct timelib_error_container *errors;

	timelib_strtointerval(format, format_length, &b, &e, &p, &r, &errors);

	if (errors->error_count > 0) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown or bad format (%s)", format);
		retval = FAILURE;
	} else {
		if(p) {
			*rt = p;
			retval = SUCCESS;
		} else {
			if(b && e) {
				timelib_update_ts(b, NULL);
				timelib_update_ts(e, NULL);
				*rt = timelib_diff(b, e);
				retval = SUCCESS;
			} else {
				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse interval (%s)", format);
				retval = FAILURE;
			}
		}
	}
	timelib_error_container_dtor(errors);


/* {{{ date_period_it_current_data */
static void date_period_it_current_data(zend_object_iterator *iter, zval ***data TSRMLS_DC)
{
	date_period_it *iterator = (date_period_it *)iter;
	php_period_obj *object   = iterator->object;
	timelib_time   *it_time = object->current;
	php_date_obj   *newdateobj;

	/* Create new object */
	MAKE_STD_ZVAL(iterator->current);
	php_date_instantiate(object->start_ce, iterator->current TSRMLS_CC);
	newdateobj = (php_date_obj *) zend_object_store_get_object(iterator->current TSRMLS_CC);
	newdateobj->time = timelib_time_ctor();
	*newdateobj->time = *it_time;
	if (it_time->tz_abbr) {
		newdateobj->time->tz_abbr = strdup(it_time->tz_abbr);
	}
	if (it_time->tz_info) {
		newdateobj->time->tz_info = it_time->tz_info;
	}


static PHP_GINIT_FUNCTION(spl)
{
	spl_globals->autoload_extensions     = NULL;
	spl_globals->autoload_extensions_len = 0;
	spl_globals->autoload_functions      = NULL;
	spl_globals->autoload_running        = 0;
}


PHP_RINIT_FUNCTION(spl) /* {{{ */
{
	SPL_G(autoload_extensions) = NULL;
	SPL_G(autoload_extensions_len) = 0;
	SPL_G(autoload_functions) = NULL;
	SPL_G(hash_mask_init) = 0;
	return SUCCESS;


PHP_MINIT_FUNCTION(imagetypes)
{
	REGISTER_LONG_CONSTANT("IMAGETYPE_GIF",     IMAGE_FILETYPE_GIF,     CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("IMAGETYPE_JPEG",    IMAGE_FILETYPE_JPEG,    CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("IMAGETYPE_PNG",     IMAGE_FILETYPE_PNG,     CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("IMAGETYPE_SWF",     IMAGE_FILETYPE_SWF,     CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("IMAGETYPE_PSD",     IMAGE_FILETYPE_PSD,     CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("IMAGETYPE_BMP",     IMAGE_FILETYPE_BMP,     CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("IMAGETYPE_TIFF_II", IMAGE_FILETYPE_TIFF_II, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("IMAGETYPE_TIFF_MM", IMAGE_FILETYPE_TIFF_MM, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("IMAGETYPE_JPC",     IMAGE_FILETYPE_JPC,     CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("IMAGETYPE_JP2",     IMAGE_FILETYPE_JP2,     CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("IMAGETYPE_JPX",     IMAGE_FILETYPE_JPX,     CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("IMAGETYPE_JB2",     IMAGE_FILETYPE_JB2,     CONST_CS | CONST_PERSISTENT);
#if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB)
	REGISTER_LONG_CONSTANT("IMAGETYPE_SWC",     IMAGE_FILETYPE_SWC,     CONST_CS | CONST_PERSISTENT);
#endif
	REGISTER_LONG_CONSTANT("IMAGETYPE_IFF",     IMAGE_FILETYPE_IFF,     CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("IMAGETYPE_WBMP",    IMAGE_FILETYPE_WBMP,    CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("IMAGETYPE_JPEG2000",IMAGE_FILETYPE_JPC,     CONST_CS | CONST_PERSISTENT); /* keep alias */
	REGISTER_LONG_CONSTANT("IMAGETYPE_XBM",     IMAGE_FILETYPE_XBM,     CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("IMAGETYPE_ICO",     IMAGE_FILETYPE_ICO,     CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("IMAGETYPE_UNKNOWN", IMAGE_FILETYPE_UNKNOWN, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("IMAGETYPE_COUNT",   IMAGE_FILETYPE_COUNT,   CONST_CS | CONST_PERSISTENT);
	return SUCCESS;
}


PHP_FUNCTION(image_type_to_mime_type)
{
	long p_image_type;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &p_image_type) == FAILURE) {
		return;
	}

	ZVAL_STRING(return_value, (char*)php_image_type_to_mime_type(p_image_type), 1);
}


static struct gfxinfo *php_handle_iff(php_stream * stream TSRMLS_DC)
{
	struct gfxinfo * result;
	unsigned char a[10];
	int chunkId;
	int size;
	short width, height, bits;

	if (php_stream_read(stream, a, 8) != 8) {
		return NULL;
	}
	if (strncmp(a+4, "ILBM", 4) && strncmp(a+4, "PBM ", 4)) {
		return NULL;
	}

	/* loop chunks to find BMHD chunk */
	do {
		if (php_stream_read(stream, a, 8) != 8) {
			return NULL;
		}
		chunkId = php_ifd_get32s(a+0, 1);
		size    = php_ifd_get32s(a+4, 1);
		if (size < 0) {
			return NULL;
		}
		if ((size & 1) == 1) {
			size++;
		}
		if (chunkId == 0x424d4844) { /* BMHD chunk */
			if (size < 9 || php_stream_read(stream, a, 9) != 9) {
				return NULL;
			}
			width  = php_ifd_get16s(a+0, 1);
			height = php_ifd_get16s(a+2, 1);
			bits   = a[8] & 0xff;
			if (width > 0 && height > 0 && bits > 0 && bits < 33) {
				result = (struct gfxinfo *) ecalloc(1, sizeof(struct gfxinfo));
				result->width    = width;
				result->height   = height;
				result->bits     = bits;
				result->channels = 0;
				return result;
			}
		} else {
			if (php_stream_seek(stream, size, SEEK_CUR)) {
				return NULL;
			}
		}
	} while (1);
}


static struct gfxinfo *php_handle_swc(php_stream * stream TSRMLS_DC)
{
	struct gfxinfo *result = NULL;

	long bits;
	unsigned char a[64];
	unsigned long len=64, szlength;
	int factor=1,maxfactor=16;
	int slength, status=0;
	char *b, *buf=NULL, *bufz=NULL;

	b = ecalloc (1, len + 1);

	if (php_stream_seek(stream, 5, SEEK_CUR))
		return NULL;

	if (php_stream_read(stream, a, sizeof(a)) != sizeof(a))
		return NULL;

	if (uncompress(b, &len, a, sizeof(a)) != Z_OK) {
		/* failed to decompress the file, will try reading the rest of the file */
		if (php_stream_seek(stream, 8, SEEK_SET))
			return NULL;

		slength = php_stream_copy_to_mem(stream, &bufz, PHP_STREAM_COPY_ALL, 0);

		/*
		 * zlib::uncompress() wants to know the output data length
		 * if none was given as a parameter
		 * we try from input length * 2 up to input length * 2^8
		 * doubling it whenever it wasn't big enough
		 * that should be eneugh for all real life cases
		*/

		do {
			szlength=slength*(1<<factor++);
			buf = (char *) erealloc(buf,szlength);
			status = uncompress(buf, &szlength, bufz, slength);
		} while ((status==Z_BUF_ERROR)&&(factor<maxfactor));

		if (bufz) {
			pefree(bufz, 0);
		}

		if (status == Z_OK) {
			 memcpy(b, buf, len);
		}

		if (buf) {
			efree(buf);
		}
	}

	if (!status) {
		result = (struct gfxinfo *) ecalloc (1, sizeof (struct gfxinfo));
		bits = php_swf_get_bits (b, 0, 5);
		result->width = (php_swf_get_bits (b, 5 + bits, bits) -
			php_swf_get_bits (b, 5, bits)) / 20;
		result->height = (php_swf_get_bits (b, 5 + (3 * bits), bits) -
			php_swf_get_bits (b, 5 + (2 * bits), bits)) / 20;
	} else {
		result = NULL;
	}

	efree (b);
	return result;
}


PHPAPI char * php_image_type_to_mime_type(int image_type)
{
	switch( image_type) {
		case IMAGE_FILETYPE_GIF:
			return "image/gif";
		case IMAGE_FILETYPE_JPEG:
			return "image/jpeg";
		case IMAGE_FILETYPE_PNG:
			return "image/png";
		case IMAGE_FILETYPE_SWF:
		case IMAGE_FILETYPE_SWC:
			return "application/x-shockwave-flash";
		case IMAGE_FILETYPE_PSD:
			return "image/psd";
		case IMAGE_FILETYPE_BMP:
			return "image/x-ms-bmp";
		case IMAGE_FILETYPE_TIFF_II:
		case IMAGE_FILETYPE_TIFF_MM:
			return "image/tiff";
		case IMAGE_FILETYPE_IFF:
			return "image/iff";
		case IMAGE_FILETYPE_WBMP:
			return "image/vnd.wap.wbmp";
		case IMAGE_FILETYPE_JPC:
			return "application/octet-stream";
		case IMAGE_FILETYPE_JP2:
			return "image/jp2";
		case IMAGE_FILETYPE_XBM:
			return "image/xbm";
		case IMAGE_FILETYPE_ICO:
			return "image/vnd.microsoft.icon";
		default:
		case IMAGE_FILETYPE_UNKNOWN:
			return "application/octet-stream"; /* suppose binary format */
	}
}


uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap)
{
    uint32_t val;
    rap &= 127;
    switch (rap) {
    case BCR_LNKST:
    case BCR_LED1:
    case BCR_LED2:
    case BCR_LED3:
        val = s->bcr[rap] & ~0x8000;
        val |= (val & 0x017f & s->lnkst) ? 0x8000 : 0;
        break;
    default:
        val = rap < 32 ? s->bcr[rap] : 0;
        break;
    }
#ifdef PCNET_DEBUG_BCR
    printf("pcnet_bcr_readw rap=%d val=0x%04x\n", rap, val);
#endif
    return val;
}


uint32_t pcnet_ioport_readl(void *opaque, uint32_t addr)
{
    PCNetState *s = opaque;
    uint32_t val = -1;
    pcnet_poll_timer(s);
    if (BCR_DWIO(s)) {
        switch (addr & 0x0f) {
        case 0x00: /* RDP */
            val = pcnet_csr_readw(s, s->rap);
            break;
        case 0x04:
            val = s->rap;
            break;
        case 0x08:
            pcnet_s_reset(s);
            val = 0;
            break;
        case 0x0c:
            val = pcnet_bcr_readw(s, s->rap);
            break;
        }
    }
    pcnet_update_irq(s);
#ifdef PCNET_DEBUG_IO
    printf("pcnet_ioport_readl addr=0x%08x val=0x%08x\n", addr, val);
#endif
    return val;
}


static inline void pcnet_rmd_store(PCNetState *s, struct pcnet_RMD *rmd,
                                   hwaddr addr)
{
    if (!BCR_SSIZE32(s)) {
        struct {
            uint32_t rbadr;
            int16_t buf_length;
            int16_t msg_length;
        } rda;
        rda.rbadr = cpu_to_le32((rmd->rbadr & 0xffffff) |
                                ((rmd->status & 0xff00) << 16));
        rda.buf_length = cpu_to_le16(rmd->buf_length);
        rda.msg_length = cpu_to_le16(rmd->msg_length);
        s->phys_mem_write(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0);
    } else {
        struct {
            uint32_t rbadr;
            int16_t buf_length;
            int16_t status;
            uint32_t msg_length;
            uint32_t res;
        } rda;
        rda.rbadr = cpu_to_le32(rmd->rbadr);
        rda.buf_length = cpu_to_le16(rmd->buf_length);
        rda.status = cpu_to_le16(rmd->status);
        rda.msg_length = cpu_to_le32(rmd->msg_length);
        rda.res = cpu_to_le32(rmd->res);
        if (BCR_SWSTYLE(s) == 3) {
            uint32_t tmp = rda.rbadr;
            rda.rbadr = rda.msg_length;
            rda.msg_length = tmp;
        }
        s->phys_mem_write(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0);
    }
}


static inline void pcnet_tmd_store(PCNetState *s, const struct pcnet_TMD *tmd,
                                   hwaddr addr)
{
    if (!BCR_SSIZE32(s)) {
        struct {
            uint32_t tbadr;
            int16_t length;
            int16_t status;
        } xda;
        xda.tbadr = cpu_to_le32((tmd->tbadr & 0xffffff) |
                                ((tmd->status & 0xff00) << 16));
        xda.length = cpu_to_le16(tmd->length);
        xda.status = cpu_to_le16(tmd->misc >> 16);
        s->phys_mem_write(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0);
    } else {
        struct {
            uint32_t tbadr;
            int16_t length;
            int16_t status;
            uint32_t misc;
            uint32_t res;
        } xda;
        xda.tbadr = cpu_to_le32(tmd->tbadr);
        xda.length = cpu_to_le16(tmd->length);
        xda.status = cpu_to_le16(tmd->status);
        xda.misc = cpu_to_le32(tmd->misc);
        xda.res = cpu_to_le32(tmd->res);
        if (BCR_SWSTYLE(s) == 3) {
            uint32_t tmp = xda.tbadr;
            xda.tbadr = xda.misc;
            xda.misc = tmp;
        }
        s->phys_mem_write(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0);
    }
}


pixops_medialib_scale     (guchar          *dest_buf,
                           int              dest_width,
                           int              dest_height,
                           int              dest_rowstride,
                           int              dest_channels,
                           int              dest_has_alpha,
                           const guchar    *src_buf,
                           int              src_width,
                           int              src_height,
                           int              src_rowstride,
                           int              src_channels,
                           int              src_has_alpha,
                           int              dest_x,
                           int              dest_y,
                           int              dest_region_width,
                           int              dest_region_height,
                           double           offset_x,
                           double           offset_y,
                           double           scale_x,
                           double           scale_y,
                           PixopsInterpType interp_type)
{
  if (scale_x == 0 || scale_y == 0)
    return;

  if (!medialib_initialized)
    _pixops_use_medialib ();

  /*
   * We no longer support mediaLib 2.1 because it has a core dumping problem
   * in the mlib_ImageZoomTranslateTable function that has been corrected in
   * 2.2.  Although the mediaLib_zoom function could be used, it does not
   * work properly if the source and destination images have different
   * values for "has_alpha" or "num_channels".  The complicated if-logic
   * required to support both versions is not worth supporting
   * mediaLib 2.1 moving forward.
   */
  if (!use_medialib)
    {
      _pixops_scale_real (dest_buf + (gsize)dest_y * dest_rowstride + (gsize)dest_x *
			  dest_channels, dest_x - offset_x, dest_y - offset_y,
			  dest_x + dest_region_width - offset_x,
			  dest_y + dest_region_height - offset_y,
			  dest_rowstride, dest_channels, dest_has_alpha,
			  src_buf, src_width, src_height, src_rowstride,
			  src_channels, src_has_alpha, scale_x, scale_y,
			  interp_type);
    }
  else
    {
      mlInterp ml_interp;
      mlib_image img_orig_src, img_src, img_dest;
      double ml_offset_x, ml_offset_y;
      guchar *tmp_buf = NULL;

      mlib_ImageSetStruct (&img_orig_src, MLIB_BYTE, src_channels, src_width,
			   src_height, src_rowstride, src_buf);

      if (dest_x == 0 && dest_y == 0 &&
          dest_width == dest_region_width &&
          dest_height == dest_region_height)
        {
          mlib_ImageSetStruct (&img_dest, MLIB_BYTE, dest_channels,
			       dest_width, dest_height, dest_rowstride,
			       dest_buf);
        }
      else
        {
	  mlib_u8 *data = dest_buf + (gsize)dest_y * dest_rowstride +
				     (gsize)dest_x * dest_channels;

          mlib_ImageSetStruct (&img_dest, MLIB_BYTE, dest_channels,
			       dest_region_width, dest_region_height,
			       dest_rowstride, data);
        }

      ml_offset_x = floor (offset_x) - dest_x;
      ml_offset_y = floor (offset_y) - dest_y;

     /*
      * Note that zoomTranslate and zoomTranslateTable are faster
      * than zoomTranslateBlend and zoomTranslateTableBlend.  However
      * the faster functions only work in the following case:
      *
      *   if (src_channels == dest_channels &&
      *       (!src_alpha && interp_table != PIXOPS_INTERP_NEAREST))
      *
      * We use the faster versions if we can.
      *
      * Note when the interp_type is BILINEAR and the interpolation
      * table will be size 2x2 (when both x/y scale factors > 1.0),
      * then we do not bother building the interpolation table.   In
      * this case we can just use MLIB_BILINEAR, which is faster than
      * using a specified interpolation table.
      */
      img_src = img_orig_src;

      if (!src_has_alpha)
        {
          if (src_channels > dest_channels)
            {
              int channels  = 3;
              int rowstride = (channels * src_width + 3) & ~3;

              tmp_buf = g_malloc_n (src_rowstride, src_height);

              if (src_buf != NULL)
                {
                  src_channels  = channels;
                  src_rowstride = rowstride;

                  mlib_ImageSetStruct (&img_src, MLIB_BYTE, src_channels,
				       src_width, src_height, src_rowstride,
				       tmp_buf);
                  mlib_ImageChannelExtract (&img_src, &img_orig_src, 0xE);
                }
            }
        }

      if (interp_type == PIXOPS_INTERP_NEAREST)
        {
          if (src_channels == dest_channels)
            {
              mlib_ImageZoomTranslate (&img_dest,
                                       &img_src,
                                       scale_x,
                                       scale_y,
                                       ml_offset_x,
                                       ml_offset_y,
                                       MLIB_NEAREST,
                                       MLIB_EDGE_SRC_EXTEND_INDEF);
            }
          else
            {
              mlib_ImageZoomTranslateBlend (&img_dest,
                                            &img_src,
                                            scale_x,
                                            scale_y,
                                            ml_offset_x,
                                            ml_offset_y,
                                            MLIB_NEAREST,
                                            MLIB_EDGE_SRC_EXTEND_INDEF,
                                            MLIB_BLEND_GTK_SRC,
                                            1.0,
                                            1);
            }
        }
      else if (src_channels == dest_channels && !src_has_alpha)
        {
          if (interp_type == PIXOPS_INTERP_BILINEAR &&
              scale_x > 1.0 && scale_y > 1.0)
            {
               mlib_ImageZoomTranslate (&img_dest,
                                        &img_src,
                                        scale_x,
                                        scale_y,
                                        ml_offset_x,
                                        ml_offset_y,
                                        MLIB_BILINEAR,
                                        MLIB_EDGE_SRC_EXTEND_INDEF);
            }
          else
            {
              medialib_get_interpolation (&ml_interp, interp_type,
                                          scale_x, scale_y, 1.0);

              if (ml_interp.interp_table != NULL)
                {
                  mlib_ImageZoomTranslateTable (&img_dest,
                                                &img_src,
                                                scale_x,
                                                scale_y,
                                                ml_offset_x + ml_interp.tx,
                                                ml_offset_y + ml_interp.ty,
                                                ml_interp.interp_table,
                                                MLIB_EDGE_SRC_EXTEND_INDEF);

	          mlib_ImageInterpTableDelete (ml_interp.interp_table);
                }
              else
                {
                  /* Should not happen. */
                  mlib_filter  ml_filter;

                  switch (interp_type)
                    {
                    case PIXOPS_INTERP_BILINEAR:
                      ml_filter = MLIB_BILINEAR;
                      break;

                    case PIXOPS_INTERP_TILES:
                      ml_filter = MLIB_BILINEAR;
                      break;

                    case PIXOPS_INTERP_HYPER:
                      ml_filter = MLIB_BICUBIC;
                      break;
                    }

                  mlib_ImageZoomTranslate (&img_dest,
                                           &img_src,
                                           scale_x,
                                           scale_y,
                                           ml_offset_x,
                                           ml_offset_y,
                                           ml_filter,
                                           MLIB_EDGE_SRC_EXTEND_INDEF);
                }
            }
        }

      /* Deal with case where src_channels != dest_channels || src_has_alpha */
      else if (interp_type == PIXOPS_INTERP_BILINEAR &&
               scale_x > 1.0 && scale_y > 1.0)
        {
          mlib_ImageZoomTranslateBlend (&img_dest,
                                        &img_src,
                                        scale_x,
                                        scale_y,
                                        ml_offset_x,
                                        ml_offset_y,
                                        MLIB_BILINEAR,
                                        MLIB_EDGE_SRC_EXTEND_INDEF,
                                        MLIB_BLEND_GTK_SRC,
                                        1.0,
                                        1);
        }
      else
        {
          medialib_get_interpolation (&ml_interp, interp_type,
                                      scale_x, scale_y, 1.0);

          if (ml_interp.interp_table != NULL)
            {
              mlib_ImageZoomTranslateTableBlend (&img_dest,
                                                 &img_src,
                                                 scale_x,
                                                 scale_y,
                                                 ml_offset_x + ml_interp.tx,
                                                 ml_offset_y + ml_interp.ty,
                                                 ml_interp.interp_table,
                                                 MLIB_EDGE_SRC_EXTEND_INDEF,
                                                 MLIB_BLEND_GTK_SRC,
                                                 1);
              mlib_ImageInterpTableDelete (ml_interp.interp_table);
            }
          else
            {
              mlib_filter  ml_filter;

              switch (interp_type)
                {
                case PIXOPS_INTERP_BILINEAR:
                  ml_filter = MLIB_BILINEAR;
                  break;

                case PIXOPS_INTERP_TILES:
                  ml_filter = MLIB_BILINEAR;
                  break;

                case PIXOPS_INTERP_HYPER:
                  ml_filter = MLIB_BICUBIC;
                  break;
                }

              mlib_ImageZoomTranslate (&img_dest,
                                       &img_src,
                                       scale_x,
                                       scale_y,
                                       ml_offset_x,
                                       ml_offset_y,
                                       ml_filter,
                                       MLIB_EDGE_SRC_EXTEND_INDEF);
            }
        }

      if (tmp_buf != NULL)
        g_free (tmp_buf);
    }
}


bilinear_box_make_weights (PixopsFilterDimension *dim,
			   double                 scale)
{
  int n = ceil (1/scale + 3.0);
  double *pixel_weights;
  double w;
  int offset, i;

  pixel_weights = g_malloc_n (sizeof (double) * SUBSAMPLE, n);
  if (pixel_weights == NULL)
    return FALSE;

  dim->offset = -1.0;
  dim->n = n;
  dim->weights = pixel_weights;

  for (offset = 0; offset < SUBSAMPLE; offset++)
    {
      double x = (double)offset / SUBSAMPLE;
      double a = x + 1 / scale;

      for (i = 0; i < n; i++)
        {
          w  = linear_box_half (0.5 + i - a, 0.5 + i - x);
          w += linear_box_half (1.5 + x - i, 1.5 + a - i);

          *(pixel_weights++) = w * scale;
        }
    }

  return TRUE;
}


static IOBuffer *io_buffer_free_segment(IOBuffer *buffer,
					guint count,
                                        GError **err)
{
	g_return_val_if_fail(buffer != NULL, NULL);
	g_return_val_if_fail(buffer->data != NULL, NULL);
	if (count == buffer->size) {
		g_free(buffer->data);
		buffer->data = NULL;
		buffer->size = 0;
	} else {
		guchar *new_buf;
		guint new_size;

		new_size = buffer->size - count;
		new_buf = g_try_malloc(new_size);
		if (!new_buf) {
			g_set_error_literal(err, GDK_PIXBUF_ERROR,
                                            GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
                                            _("Cannot allocate temporary IOBuffer data"));
			g_free(buffer->data);
			g_free(buffer);
			return NULL;
		}

		g_memmove(new_buf, &buffer->data[count], new_size);
		g_free(buffer->data);
		buffer->data = new_buf;
		buffer->size = new_size;
	}
	return buffer;
}


static guint parse_rle_data_pseudocolor(TGAContext *ctx)
{
	guint rle_num, raw_num;
	guchar *s, tag;
	guint n;

	g_return_val_if_fail(ctx->in->size > 0, 0);
	s = ctx->in->data;

	for (n = 0; n < ctx->in->size; ) {
		tag = *s;
		s++, n++;
		if (tag & 0x80) {
			if (n == ctx->in->size) {
				return --n;
			} else {
				rle_num = (tag & 0x7f) + 1;
				write_rle_data(ctx, colormap_get_color (ctx->cmap, *s), &rle_num);
				s++, n++;
				if (ctx->pbuf_bytes_done == ctx->pbuf_bytes) {
					ctx->done = TRUE;
					return n;
				}
			}
		} else {
			raw_num = tag + 1;
			if (n + raw_num >= ctx->in->size) {
				return --n;
			} else {
				for (; raw_num; raw_num--) {
                                        const TGAColor *color = colormap_get_color (ctx->cmap, *s);
					*ctx->pptr++ = color->r;
					*ctx->pptr++ = color->g;
					*ctx->pptr++ = color->b;
					if (ctx->pbuf->n_channels == 4)
						*ctx->pptr++ = color->a;
					s++, n++;
					ctx->pbuf_bytes_done += ctx->pbuf->n_channels;
					if (ctx->pbuf_bytes_done == ctx->pbuf_bytes) {
						ctx->done = TRUE;
						return n;
					}
				}
			}
		}
	}

	if (ctx->pbuf_bytes_done == ctx->pbuf_bytes)
		ctx->done = TRUE;

	return n;
}


colormap_set_color (TGAColormap    *cmap,
                    guint           id,
                    const TGAColor *color)
{
  if (id >= cmap->n_colors)
    return;

  cmap->colors[id] = *color;
}


static void rds_conn_message_info(struct socket *sock, unsigned int len,
				  struct rds_info_iterator *iter,
				  struct rds_info_lengths *lens,
				  int want_send)
{
	struct hlist_head *head;
	struct list_head *list;
	struct rds_connection *conn;
	struct rds_message *rm;
	unsigned int total = 0;
	unsigned long flags;
	size_t i;

	len /= sizeof(struct rds_info_message);

	rcu_read_lock();

	for (i = 0, head = rds_conn_hash; i < ARRAY_SIZE(rds_conn_hash);
	     i++, head++) {
		hlist_for_each_entry_rcu(conn, head, c_hash_node) {
			if (want_send)
				list = &conn->c_send_queue;
			else
				list = &conn->c_retrans;

			spin_lock_irqsave(&conn->c_lock, flags);

			/* XXX too lazy to maintain counts.. */
			list_for_each_entry(rm, list, m_conn_item) {
				total++;
				if (total <= len)
					rds_inc_info_copy(&rm->m_inc, iter,
							  conn->c_laddr,
							  conn->c_faddr, 0);
			}

			spin_unlock_irqrestore(&conn->c_lock, flags);
		}
	}
	rcu_read_unlock();

	lens->nr = total;
	lens->each = sizeof(struct rds_info_message);
}


static OPJ_BOOL opj_j2k_read_mco (      opj_j2k_t *p_j2k,
                                                                    OPJ_BYTE * p_header_data,
                                                                    OPJ_UINT32 p_header_size,
                                                                    opj_event_mgr_t * p_manager
                                    )
{
        OPJ_UINT32 l_tmp, i;
        OPJ_UINT32 l_nb_stages;
        opj_tcp_t * l_tcp;
        opj_tccp_t * l_tccp;
        opj_image_t * l_image;

        /* preconditions */
        assert(p_header_data != 00);
        assert(p_j2k != 00);
        assert(p_manager != 00);

        l_image = p_j2k->m_private_image;
        l_tcp = p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_TPH ?
                        &p_j2k->m_cp.tcps[p_j2k->m_current_tile_number] :
                        p_j2k->m_specific_param.m_decoder.m_default_tcp;

        if (p_header_size < 1) {
                opj_event_msg(p_manager, EVT_ERROR, "Error reading MCO marker\n");
                return OPJ_FALSE;
        }

        opj_read_bytes(p_header_data,&l_nb_stages,1);                           /* Nmco : only one tranform stage*/
        ++p_header_data;

        if (l_nb_stages > 1) {
                opj_event_msg(p_manager, EVT_WARNING, "Cannot take in charge multiple transformation stages.\n");
                return OPJ_TRUE;
        }

        if (p_header_size != l_nb_stages + 1) {
                opj_event_msg(p_manager, EVT_WARNING, "Error reading MCO marker\n");
                return OPJ_FALSE;
        }

        l_tccp = l_tcp->tccps;

        for (i=0;i<l_image->numcomps;++i) {
                l_tccp->m_dc_level_shift = 0;
                ++l_tccp;
        }

        if (l_tcp->m_mct_decoding_matrix) {
                opj_free(l_tcp->m_mct_decoding_matrix);
                l_tcp->m_mct_decoding_matrix = 00;
        }

        for (i=0;i<l_nb_stages;++i) {
                opj_read_bytes(p_header_data,&l_tmp,1);
                ++p_header_data;

                if (! opj_j2k_add_mct(l_tcp,p_j2k->m_private_image,l_tmp)) {
                        return OPJ_FALSE;
                }
        }

        return OPJ_TRUE;
}


opj_j2k_t* opj_j2k_create_compress(void)
{
        opj_j2k_t *l_j2k = (opj_j2k_t*) opj_calloc(1,sizeof(opj_j2k_t));
        if (!l_j2k) {
                return NULL;
        }

        l_j2k->m_is_decoder = 0;
        l_j2k->m_cp.m_is_decoder = 0;

        l_j2k->m_specific_param.m_encoder.m_header_tile_data = (OPJ_BYTE *) opj_malloc(OPJ_J2K_DEFAULT_HEADER_SIZE);
        if (! l_j2k->m_specific_param.m_encoder.m_header_tile_data) {
                opj_j2k_destroy(l_j2k);
                return NULL;
        }

        l_j2k->m_specific_param.m_encoder.m_header_tile_data_size = OPJ_J2K_DEFAULT_HEADER_SIZE;

        /* validation list creation*/
        l_j2k->m_validation_list = opj_procedure_list_create();
        if (! l_j2k->m_validation_list) {
                opj_j2k_destroy(l_j2k);
                return NULL;
        }

        /* execution list creation*/
        l_j2k->m_procedure_list = opj_procedure_list_create();
        if (! l_j2k->m_procedure_list) {
                opj_j2k_destroy(l_j2k);
                return NULL;
        }

        return l_j2k;
}


static void opj_j2k_tcp_destroy (opj_tcp_t *p_tcp)
{
	if (p_tcp == 00) {
		return;
	}

	if (p_tcp->ppt_markers != 00) {
		OPJ_UINT32 i;
		for (i = 0U; i < p_tcp->ppt_markers_count; ++i) {
			if (p_tcp->ppt_markers[i].m_data != NULL) {
				opj_free(p_tcp->ppt_markers[i].m_data);
			}
		}
		p_tcp->ppt_markers_count = 0U;
		opj_free(p_tcp->ppt_markers);
		p_tcp->ppt_markers = NULL;
	}

	if (p_tcp->ppt_buffer != 00) {
		opj_free(p_tcp->ppt_buffer);
		p_tcp->ppt_buffer = 00;
	}

	if (p_tcp->tccps != 00) {
		opj_free(p_tcp->tccps);
		p_tcp->tccps = 00;
	}

	if (p_tcp->m_mct_coding_matrix != 00) {
		opj_free(p_tcp->m_mct_coding_matrix);
		p_tcp->m_mct_coding_matrix = 00;
	}

	if (p_tcp->m_mct_decoding_matrix != 00) {
		opj_free(p_tcp->m_mct_decoding_matrix);
		p_tcp->m_mct_decoding_matrix = 00;
	}

	if (p_tcp->m_mcc_records) {
		opj_free(p_tcp->m_mcc_records);
		p_tcp->m_mcc_records = 00;
		p_tcp->m_nb_max_mcc_records = 0;
		p_tcp->m_nb_mcc_records = 0;
	}

	if (p_tcp->m_mct_records) {
		opj_mct_data_t * l_mct_data = p_tcp->m_mct_records;
		OPJ_UINT32 i;

		for (i=0;i<p_tcp->m_nb_mct_records;++i) {
			if (l_mct_data->m_data) {
				opj_free(l_mct_data->m_data);
				l_mct_data->m_data = 00;
			}

			++l_mct_data;
		}

		opj_free(p_tcp->m_mct_records);
		p_tcp->m_mct_records = 00;
	}

	if (p_tcp->mct_norms != 00) {
		opj_free(p_tcp->mct_norms);
		p_tcp->mct_norms = 00;
	}

	opj_j2k_tcp_data_destroy(p_tcp);

}


OPJ_BOOL opj_j2k_decode_tile (  opj_j2k_t * p_j2k,
                                                        OPJ_UINT32 p_tile_index,
                                                        OPJ_BYTE * p_data,
                                                        OPJ_UINT32 p_data_size,
                                                        opj_stream_private_t *p_stream,
                                                        opj_event_mgr_t * p_manager )
{
        OPJ_UINT32 l_current_marker;
        OPJ_BYTE l_data [2];
        opj_tcp_t * l_tcp;

        /* preconditions */
        assert(p_stream != 00);
        assert(p_j2k != 00);
        assert(p_manager != 00);

        if ( !(p_j2k->m_specific_param.m_decoder.m_state & 0x0080/*FIXME J2K_DEC_STATE_DATA*/)
                || (p_tile_index != p_j2k->m_current_tile_number) ) {
                return OPJ_FALSE;
        }

        l_tcp = &(p_j2k->m_cp.tcps[p_tile_index]);
        if (! l_tcp->m_data) {
                opj_j2k_tcp_destroy(l_tcp);
                return OPJ_FALSE;
        }

        if (! opj_tcd_decode_tile(      p_j2k->m_tcd,
                                                                l_tcp->m_data,
                                                                l_tcp->m_data_size,
                                                                p_tile_index,
                                                                p_j2k->cstr_index, p_manager) ) {
                opj_j2k_tcp_destroy(l_tcp);
                p_j2k->m_specific_param.m_decoder.m_state |= 0x8000;/*FIXME J2K_DEC_STATE_ERR;*/
                opj_event_msg(p_manager, EVT_ERROR, "Failed to decode.\n");
                return OPJ_FALSE;
        }

        if (! opj_tcd_update_tile_data(p_j2k->m_tcd,p_data,p_data_size)) {
                return OPJ_FALSE;
        }

        /* To avoid to destroy the tcp which can be useful when we try to decode a tile decoded before (cf j2k_random_tile_access)
         * we destroy just the data which will be re-read in read_tile_header*/
        /*opj_j2k_tcp_destroy(l_tcp);
        p_j2k->m_tcd->tcp = 0;*/
        opj_j2k_tcp_data_destroy(l_tcp);

        p_j2k->m_specific_param.m_decoder.m_can_decode = 0;
        p_j2k->m_specific_param.m_decoder.m_state &= (~ (0x0080u));/* FIXME J2K_DEC_STATE_DATA);*/

        if(opj_stream_get_number_byte_left(p_stream) == 0
            && p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_NEOC){
            return OPJ_TRUE;
        }

        if (p_j2k->m_specific_param.m_decoder.m_state != 0x0100){ /*FIXME J2K_DEC_STATE_EOC)*/
                if (opj_stream_read_data(p_stream,l_data,2,p_manager) != 2) {
                        opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
                        return OPJ_FALSE;
                }

                opj_read_bytes(l_data,&l_current_marker,2);

                if (l_current_marker == J2K_MS_EOC) {
                        p_j2k->m_current_tile_number = 0;
                        p_j2k->m_specific_param.m_decoder.m_state =  0x0100;/*FIXME J2K_DEC_STATE_EOC;*/
                }
                else if (l_current_marker != J2K_MS_SOT)
                {
                        if(opj_stream_get_number_byte_left(p_stream) == 0) {
                            p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_NEOC;
                            opj_event_msg(p_manager, EVT_WARNING, "Stream does not end with EOC\n");
                            return OPJ_TRUE;
                        }
                        opj_event_msg(p_manager, EVT_ERROR, "Stream too short, expected SOT\n");
                        return OPJ_FALSE;
                }
        }

        return OPJ_TRUE;
}


static OPJ_BOOL opj_j2k_post_write_tile (      opj_j2k_t * p_j2k,
                                                                opj_stream_private_t *p_stream,
                                                                opj_event_mgr_t * p_manager )
{
        OPJ_UINT32 l_nb_bytes_written;
        OPJ_BYTE * l_current_data = 00;
        OPJ_UINT32 l_tile_size = 0;
        OPJ_UINT32 l_available_data;

        /* preconditions */
        assert(p_j2k->m_specific_param.m_encoder.m_encoded_tile_data);

        l_tile_size = p_j2k->m_specific_param.m_encoder.m_encoded_tile_size;
        l_available_data = l_tile_size;
        l_current_data = p_j2k->m_specific_param.m_encoder.m_encoded_tile_data;

        l_nb_bytes_written = 0;
        if (! opj_j2k_write_first_tile_part(p_j2k,l_current_data,&l_nb_bytes_written,l_available_data,p_stream,p_manager)) {
                return OPJ_FALSE;
        }
        l_current_data += l_nb_bytes_written;
        l_available_data -= l_nb_bytes_written;

        l_nb_bytes_written = 0;
        if (! opj_j2k_write_all_tile_parts(p_j2k,l_current_data,&l_nb_bytes_written,l_available_data,p_stream,p_manager)) {
                return OPJ_FALSE;
        }

        l_available_data -= l_nb_bytes_written;
        l_nb_bytes_written = l_tile_size - l_available_data;

        if ( opj_stream_write_data(     p_stream,
                                                                p_j2k->m_specific_param.m_encoder.m_encoded_tile_data,
                                                                l_nb_bytes_written,p_manager) != l_nb_bytes_written) {
                return OPJ_FALSE;
        }

        ++p_j2k->m_current_tile_number;

        return OPJ_TRUE;
}


static OPJ_BOOL opj_j2k_merge_ppt(opj_tcp_t *p_tcp, opj_event_mgr_t * p_manager)
{
	OPJ_UINT32 i, l_ppt_data_size;
	/* preconditions */
	assert(p_tcp != 00);
	assert(p_manager != 00);
	assert(p_tcp->ppt_buffer == NULL);

	if (p_tcp->ppt == 0U) {
		return OPJ_TRUE;
	}

	l_ppt_data_size = 0U;
	for (i = 0U; i < p_tcp->ppt_markers_count; ++i) {
		l_ppt_data_size += p_tcp->ppt_markers[i].m_data_size; /* can't overflow, max 256 markers of max 65536 bytes */
	}

	p_tcp->ppt_buffer = (OPJ_BYTE *) opj_malloc(l_ppt_data_size);
	if (p_tcp->ppt_buffer == 00) {
		opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to read PPT marker\n");
		return OPJ_FALSE;
	}
	p_tcp->ppt_len = l_ppt_data_size;
	l_ppt_data_size = 0U;
	for (i = 0U; i < p_tcp->ppt_markers_count; ++i) {
		if (p_tcp->ppt_markers[i].m_data != NULL) { /* standard doesn't seem to require contiguous Zppt */
			memcpy(p_tcp->ppt_buffer + l_ppt_data_size, p_tcp->ppt_markers[i].m_data, p_tcp->ppt_markers[i].m_data_size);
			l_ppt_data_size += p_tcp->ppt_markers[i].m_data_size; /* can't overflow, max 256 markers of max 65536 bytes */

			opj_free(p_tcp->ppt_markers[i].m_data);
			p_tcp->ppt_markers[i].m_data = NULL;
			p_tcp->ppt_markers[i].m_data_size = 0U;
		}
	}

	p_tcp->ppt_markers_count = 0U;
	opj_free(p_tcp->ppt_markers);
	p_tcp->ppt_markers = NULL;

	p_tcp->ppt_data = p_tcp->ppt_buffer;
	p_tcp->ppt_data_size = p_tcp->ppt_len;
	return OPJ_TRUE;
}


OPJ_BOOL opj_j2k_encode(opj_j2k_t * p_j2k,
                        opj_stream_private_t *p_stream,
                        opj_event_mgr_t * p_manager )
{
        OPJ_UINT32 i, j;
        OPJ_UINT32 l_nb_tiles;
        OPJ_UINT32 l_max_tile_size = 0, l_current_tile_size;
        OPJ_BYTE * l_current_data = 00;
        opj_tcd_t* p_tcd = 00;

        /* preconditions */
        assert(p_j2k != 00);
        assert(p_stream != 00);
        assert(p_manager != 00);

        p_tcd = p_j2k->m_tcd;

        l_nb_tiles = p_j2k->m_cp.th * p_j2k->m_cp.tw;
        for (i=0;i<l_nb_tiles;++i) {
                if (! opj_j2k_pre_write_tile(p_j2k,i,p_stream,p_manager)) {
                        if (l_current_data) {
                                opj_free(l_current_data);
                        }
                        return OPJ_FALSE;
                }

                /* if we only have one tile, then simply set tile component data equal to image component data */
                /* otherwise, allocate the data */
                for (j=0;j<p_j2k->m_tcd->image->numcomps;++j) {
                        opj_tcd_tilecomp_t* l_tilec = p_tcd->tcd_image->tiles->comps + j;
                        if (l_nb_tiles == 1) {
												        opj_image_comp_t * l_img_comp = p_tcd->image->comps + j;
												        l_tilec->data  =  l_img_comp->data;
												        l_tilec->ownsData = OPJ_FALSE;
                        } else {
												        if(! opj_alloc_tile_component_data(l_tilec)) {
												                opj_event_msg(p_manager, EVT_ERROR, "Error allocating tile component data." );
												                if (l_current_data) {
												                        opj_free(l_current_data);
												                }
												                return OPJ_FALSE;
												        }
                        }
                }
                l_current_tile_size = opj_tcd_get_encoded_tile_size(p_j2k->m_tcd);
                if (l_nb_tiles > 1) {
                        if (l_current_tile_size > l_max_tile_size) {
												        OPJ_BYTE *l_new_current_data = (OPJ_BYTE *) opj_realloc(l_current_data, l_current_tile_size);
												        if (! l_new_current_data) {
												                if (l_current_data) {
												                        opj_free(l_current_data);
												                }
												                opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to encode all tiles\n");
												                return OPJ_FALSE;
																}
																l_current_data = l_new_current_data;
																l_max_tile_size = l_current_tile_size;
                        }

                        /* copy image data (32 bit) to l_current_data as contiguous, all-component, zero offset buffer */
                        /* 32 bit components @ 8 bit precision get converted to 8 bit */
                        /* 32 bit components @ 16 bit precision get converted to 16 bit */
                        opj_j2k_get_tile_data(p_j2k->m_tcd,l_current_data);

                        /* now copy this data into the tile component */
                        if (! opj_tcd_copy_tile_data(p_j2k->m_tcd,l_current_data,l_current_tile_size)) {
																opj_event_msg(p_manager, EVT_ERROR, "Size mismatch between tile data and sent data." );
																return OPJ_FALSE;
                        }
                }

                if (! opj_j2k_post_write_tile (p_j2k,p_stream,p_manager)) {
                        return OPJ_FALSE;
                }
        }

        if (l_current_data) {
                opj_free(l_current_data);
        }
        return OPJ_TRUE;
}


static OPJ_BOOL opj_j2k_read_tlm (  opj_j2k_t *p_j2k,
                                    OPJ_BYTE * p_header_data,
                                    OPJ_UINT32 p_header_size,
                                    opj_event_mgr_t * p_manager
                                    )
{
        OPJ_UINT32 l_Ztlm, l_Stlm, l_ST, l_SP, l_tot_num_tp_remaining, l_quotient, l_Ptlm_size;
        /* preconditions */
        assert(p_header_data != 00);
        assert(p_j2k != 00);
        assert(p_manager != 00);

        if (p_header_size < 2) {
                opj_event_msg(p_manager, EVT_ERROR, "Error reading TLM marker\n");
                return OPJ_FALSE;
        }
        p_header_size -= 2;

        opj_read_bytes(p_header_data,&l_Ztlm,1);                                /* Ztlm */
        ++p_header_data;
        opj_read_bytes(p_header_data,&l_Stlm,1);                                /* Stlm */
        ++p_header_data;

        l_ST = ((l_Stlm >> 4) & 0x3);
        l_SP = (l_Stlm >> 6) & 0x1;

        l_Ptlm_size = (l_SP + 1) * 2;
        l_quotient = l_Ptlm_size + l_ST;

        l_tot_num_tp_remaining = p_header_size % l_quotient;

        if (l_tot_num_tp_remaining != 0) {
                opj_event_msg(p_manager, EVT_ERROR, "Error reading TLM marker\n");
                return OPJ_FALSE;
        }
        /* FIXME Do not care of this at the moment since only local variables are set here */
        /*
        for
                (i = 0; i < l_tot_num_tp; ++i)
        {
                opj_read_bytes(p_header_data,&l_Ttlm_i,l_ST);                           // Ttlm_i
                p_header_data += l_ST;
                opj_read_bytes(p_header_data,&l_Ptlm_i,l_Ptlm_size);            // Ptlm_i
                p_header_data += l_Ptlm_size;
        }*/
        return OPJ_TRUE;
}


static OPJ_BOOL opj_j2k_write_cod(     opj_j2k_t *p_j2k,
                                                        opj_stream_private_t *p_stream,
                                                        opj_event_mgr_t * p_manager )
{
        opj_cp_t *l_cp = 00;
        opj_tcp_t *l_tcp = 00;
        OPJ_UINT32 l_code_size,l_remaining_size;
        OPJ_BYTE * l_current_data = 00;

        /* preconditions */
        assert(p_j2k != 00);
        assert(p_manager != 00);
        assert(p_stream != 00);

        l_cp = &(p_j2k->m_cp);
        l_tcp = &l_cp->tcps[p_j2k->m_current_tile_number];
        l_code_size = 9 + opj_j2k_get_SPCod_SPCoc_size(p_j2k,p_j2k->m_current_tile_number,0);
        l_remaining_size = l_code_size;

        if (l_code_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
                OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_code_size);
                if (! new_header_tile_data) {
                        opj_free(p_j2k->m_specific_param.m_encoder.m_header_tile_data);
                        p_j2k->m_specific_param.m_encoder.m_header_tile_data = NULL;
                        p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = 0;
                        opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to write COD marker\n");
                        return OPJ_FALSE;
                }
                p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
                p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_code_size;
        }

        l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;

        opj_write_bytes(l_current_data,J2K_MS_COD,2);           /* COD */
        l_current_data += 2;

        opj_write_bytes(l_current_data,l_code_size-2,2);        /* L_COD */
        l_current_data += 2;

        opj_write_bytes(l_current_data,l_tcp->csty,1);          /* Scod */
        ++l_current_data;

        opj_write_bytes(l_current_data,l_tcp->prg,1);           /* SGcod (A) */
        ++l_current_data;

        opj_write_bytes(l_current_data,l_tcp->numlayers,2);     /* SGcod (B) */
        l_current_data+=2;

        opj_write_bytes(l_current_data,l_tcp->mct,1);           /* SGcod (C) */
        ++l_current_data;

        l_remaining_size -= 9;

        if (! opj_j2k_write_SPCod_SPCoc(p_j2k,p_j2k->m_current_tile_number,0,l_current_data,&l_remaining_size,p_manager)) {
                opj_event_msg(p_manager, EVT_ERROR, "Error writing COD marker\n");
                return OPJ_FALSE;
        }

        if (l_remaining_size != 0) {
                opj_event_msg(p_manager, EVT_ERROR, "Error writing COD marker\n");
                return OPJ_FALSE;
        }

        if (opj_stream_write_data(p_stream,p_j2k->m_specific_param.m_encoder.m_header_tile_data,l_code_size,p_manager) != l_code_size) {
                return OPJ_FALSE;
        }

        return OPJ_TRUE;
}


OPJ_BOOL opj_j2k_set_decode_area(       opj_j2k_t *p_j2k,
                                                                    opj_image_t* p_image,
                                                                    OPJ_INT32 p_start_x, OPJ_INT32 p_start_y,
                                                                    OPJ_INT32 p_end_x, OPJ_INT32 p_end_y,
                                                                    opj_event_mgr_t * p_manager )
{
        opj_cp_t * l_cp = &(p_j2k->m_cp);
        opj_image_t * l_image = p_j2k->m_private_image;

        OPJ_UINT32 it_comp;
        OPJ_INT32 l_comp_x1, l_comp_y1;
        opj_image_comp_t* l_img_comp = NULL;

        /* Check if we are read the main header */
        if (p_j2k->m_specific_param.m_decoder.m_state != J2K_STATE_TPHSOT) { /* FIXME J2K_DEC_STATE_TPHSOT)*/
                opj_event_msg(p_manager, EVT_ERROR, "Need to decode the main header before begin to decode the remaining codestream");
                return OPJ_FALSE;
        }

        if ( !p_start_x && !p_start_y && !p_end_x && !p_end_y){
                opj_event_msg(p_manager, EVT_INFO, "No decoded area parameters, set the decoded area to the whole image\n");

                p_j2k->m_specific_param.m_decoder.m_start_tile_x = 0;
                p_j2k->m_specific_param.m_decoder.m_start_tile_y = 0;
                p_j2k->m_specific_param.m_decoder.m_end_tile_x = l_cp->tw;
                p_j2k->m_specific_param.m_decoder.m_end_tile_y = l_cp->th;

                return OPJ_TRUE;
        }

        /* ----- */
        /* Check if the positions provided by the user are correct */

        /* Left */
        assert(p_start_x >= 0 );
        assert(p_start_y >= 0 );

        if ((OPJ_UINT32)p_start_x > l_image->x1 ) {
                opj_event_msg(p_manager, EVT_ERROR,
                        "Left position of the decoded area (region_x0=%d) is outside the image area (Xsiz=%d).\n",
                        p_start_x, l_image->x1);
                return OPJ_FALSE;
        }
        else if ((OPJ_UINT32)p_start_x < l_image->x0){
                opj_event_msg(p_manager, EVT_WARNING,
                                "Left position of the decoded area (region_x0=%d) is outside the image area (XOsiz=%d).\n",
                                p_start_x, l_image->x0);
                p_j2k->m_specific_param.m_decoder.m_start_tile_x = 0;
                p_image->x0 = l_image->x0;
        }
        else {
                p_j2k->m_specific_param.m_decoder.m_start_tile_x = ((OPJ_UINT32)p_start_x - l_cp->tx0) / l_cp->tdx;
                p_image->x0 = (OPJ_UINT32)p_start_x;
        }

        /* Up */
        if ((OPJ_UINT32)p_start_y > l_image->y1){
                opj_event_msg(p_manager, EVT_ERROR,
                                "Up position of the decoded area (region_y0=%d) is outside the image area (Ysiz=%d).\n",
                                p_start_y, l_image->y1);
                return OPJ_FALSE;
        }
        else if ((OPJ_UINT32)p_start_y < l_image->y0){
                opj_event_msg(p_manager, EVT_WARNING,
                                "Up position of the decoded area (region_y0=%d) is outside the image area (YOsiz=%d).\n",
                                p_start_y, l_image->y0);
                p_j2k->m_specific_param.m_decoder.m_start_tile_y = 0;
                p_image->y0 = l_image->y0;
        }
        else {
                p_j2k->m_specific_param.m_decoder.m_start_tile_y = ((OPJ_UINT32)p_start_y - l_cp->ty0) / l_cp->tdy;
                p_image->y0 = (OPJ_UINT32)p_start_y;
        }

        /* Right */
        assert((OPJ_UINT32)p_end_x > 0);
        assert((OPJ_UINT32)p_end_y > 0);
        if ((OPJ_UINT32)p_end_x < l_image->x0) {
                opj_event_msg(p_manager, EVT_ERROR,
                        "Right position of the decoded area (region_x1=%d) is outside the image area (XOsiz=%d).\n",
                        p_end_x, l_image->x0);
                return OPJ_FALSE;
        }
        else if ((OPJ_UINT32)p_end_x > l_image->x1) {
                opj_event_msg(p_manager, EVT_WARNING,
                        "Right position of the decoded area (region_x1=%d) is outside the image area (Xsiz=%d).\n",
                        p_end_x, l_image->x1);
                p_j2k->m_specific_param.m_decoder.m_end_tile_x = l_cp->tw;
                p_image->x1 = l_image->x1;
        }
        else {
                p_j2k->m_specific_param.m_decoder.m_end_tile_x = (OPJ_UINT32)opj_int_ceildiv(p_end_x - (OPJ_INT32)l_cp->tx0, (OPJ_INT32)l_cp->tdx);
                p_image->x1 = (OPJ_UINT32)p_end_x;
        }

        /* Bottom */
        if ((OPJ_UINT32)p_end_y < l_image->y0) {
                opj_event_msg(p_manager, EVT_ERROR,
                        "Bottom position of the decoded area (region_y1=%d) is outside the image area (YOsiz=%d).\n",
                        p_end_y, l_image->y0);
                return OPJ_FALSE;
        }
        if ((OPJ_UINT32)p_end_y > l_image->y1){
                opj_event_msg(p_manager, EVT_WARNING,
                        "Bottom position of the decoded area (region_y1=%d) is outside the image area (Ysiz=%d).\n",
                        p_end_y, l_image->y1);
                p_j2k->m_specific_param.m_decoder.m_end_tile_y = l_cp->th;
                p_image->y1 = l_image->y1;
        }
        else{
                p_j2k->m_specific_param.m_decoder.m_end_tile_y = (OPJ_UINT32)opj_int_ceildiv(p_end_y - (OPJ_INT32)l_cp->ty0, (OPJ_INT32)l_cp->tdy);
                p_image->y1 = (OPJ_UINT32)p_end_y;
        }
        /* ----- */

        p_j2k->m_specific_param.m_decoder.m_discard_tiles = 1;

        l_img_comp = p_image->comps;
        for (it_comp=0; it_comp < p_image->numcomps; ++it_comp)
        {
                OPJ_INT32 l_h,l_w;

                l_img_comp->x0 = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)p_image->x0, (OPJ_INT32)l_img_comp->dx);
                l_img_comp->y0 = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)p_image->y0, (OPJ_INT32)l_img_comp->dy);
                l_comp_x1 = opj_int_ceildiv((OPJ_INT32)p_image->x1, (OPJ_INT32)l_img_comp->dx);
                l_comp_y1 = opj_int_ceildiv((OPJ_INT32)p_image->y1, (OPJ_INT32)l_img_comp->dy);

                l_w = opj_int_ceildivpow2(l_comp_x1, (OPJ_INT32)l_img_comp->factor)
                                - opj_int_ceildivpow2((OPJ_INT32)l_img_comp->x0, (OPJ_INT32)l_img_comp->factor);
                if (l_w < 0){
                        opj_event_msg(p_manager, EVT_ERROR,
                                "Size x of the decoded component image is incorrect (comp[%d].w=%d).\n",
                                it_comp, l_w);
                        return OPJ_FALSE;
                }
                l_img_comp->w = (OPJ_UINT32)l_w;

                l_h = opj_int_ceildivpow2(l_comp_y1, (OPJ_INT32)l_img_comp->factor)
                                - opj_int_ceildivpow2((OPJ_INT32)l_img_comp->y0, (OPJ_INT32)l_img_comp->factor);
                if (l_h < 0){
                        opj_event_msg(p_manager, EVT_ERROR,
                                "Size y of the decoded component image is incorrect (comp[%d].h=%d).\n",
                                it_comp, l_h);
                        return OPJ_FALSE;
                }
                l_img_comp->h = (OPJ_UINT32)l_h;

                l_img_comp++;
        }

        opj_event_msg( p_manager, EVT_INFO,"Setting decoding area to %d,%d,%d,%d\n",
                        p_image->x0, p_image->y0, p_image->x1, p_image->y1);

        return OPJ_TRUE;
}


static opj_codestream_index_t* opj_j2k_create_cstr_index(void)
{
        opj_codestream_index_t* cstr_index = (opj_codestream_index_t*)
                        opj_calloc(1,sizeof(opj_codestream_index_t));
        if (!cstr_index)
                return NULL;

        cstr_index->maxmarknum = 100;
        cstr_index->marknum = 0;
        cstr_index->marker = (opj_marker_info_t*)
                        opj_calloc(cstr_index->maxmarknum, sizeof(opj_marker_info_t));
        if (!cstr_index-> marker)
                return NULL;

        cstr_index->tile_index = NULL;

        return cstr_index;
}


static OPJ_BOOL opj_j2k_decode_tiles ( opj_j2k_t *p_j2k,
                                                            opj_stream_private_t *p_stream,
                                                            opj_event_mgr_t * p_manager)
{
        OPJ_BOOL l_go_on = OPJ_TRUE;
        OPJ_UINT32 l_current_tile_no;
        OPJ_UINT32 l_data_size,l_max_data_size;
        OPJ_INT32 l_tile_x0,l_tile_y0,l_tile_x1,l_tile_y1;
        OPJ_UINT32 l_nb_comps;
        OPJ_BYTE * l_current_data;
        OPJ_UINT32 nr_tiles = 0;

        l_current_data = (OPJ_BYTE*)opj_malloc(1000);
        if (! l_current_data) {
                opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to decode tiles\n");
                return OPJ_FALSE;
        }
        l_max_data_size = 1000;

		for (;;) {
                if (! opj_j2k_read_tile_header( p_j2k,
                                        &l_current_tile_no,
                                        &l_data_size,
                                        &l_tile_x0, &l_tile_y0,
                                        &l_tile_x1, &l_tile_y1,
                                        &l_nb_comps,
                                        &l_go_on,
                                        p_stream,
                                        p_manager)) {
                        opj_free(l_current_data);
                        return OPJ_FALSE;
                }

                if (! l_go_on) {
                        break;
                }

                if (l_data_size > l_max_data_size) {
                        OPJ_BYTE *l_new_current_data = (OPJ_BYTE *) opj_realloc(l_current_data, l_data_size);
                        if (! l_new_current_data) {
                                opj_free(l_current_data);
                                opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to decode tile %d/%d\n", l_current_tile_no +1, p_j2k->m_cp.th * p_j2k->m_cp.tw);
                                return OPJ_FALSE;
                        }
                        l_current_data = l_new_current_data;
                        l_max_data_size = l_data_size;
                }

                if (! opj_j2k_decode_tile(p_j2k,l_current_tile_no,l_current_data,l_data_size,p_stream,p_manager)) {
                        opj_free(l_current_data);
                        opj_event_msg(p_manager, EVT_ERROR, "Failed to decode tile %d/%d\n", l_current_tile_no +1, p_j2k->m_cp.th * p_j2k->m_cp.tw);
                        return OPJ_FALSE;
                }
                opj_event_msg(p_manager, EVT_INFO, "Tile %d/%d has been decoded.\n", l_current_tile_no +1, p_j2k->m_cp.th * p_j2k->m_cp.tw);

                if (! opj_j2k_update_image_data(p_j2k->m_tcd,l_current_data, p_j2k->m_output_image)) {
                        opj_free(l_current_data);
                        return OPJ_FALSE;
                }
                opj_event_msg(p_manager, EVT_INFO, "Image data has been updated with tile %d.\n\n", l_current_tile_no + 1);

                if(opj_stream_get_number_byte_left(p_stream) == 0
                    && p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_NEOC)
                    break;
                if(++nr_tiles ==  p_j2k->m_cp.th * p_j2k->m_cp.tw)
                    break;
        }

        opj_free(l_current_data);

        return OPJ_TRUE;
}


OPJ_BOOL opj_j2k_set_decoded_resolution_factor(opj_j2k_t *p_j2k,
                                               OPJ_UINT32 res_factor,
                                               opj_event_mgr_t * p_manager)
{
        OPJ_UINT32 it_comp;

        p_j2k->m_cp.m_specific_param.m_dec.m_reduce = res_factor;

        if (p_j2k->m_private_image) {
                if (p_j2k->m_private_image->comps) {
                        if (p_j2k->m_specific_param.m_decoder.m_default_tcp) {
                                if (p_j2k->m_specific_param.m_decoder.m_default_tcp->tccps) {
                                        for (it_comp = 0 ; it_comp < p_j2k->m_private_image->numcomps; it_comp++) {
                                                OPJ_UINT32 max_res = p_j2k->m_specific_param.m_decoder.m_default_tcp->tccps[it_comp].numresolutions;
                                                if ( res_factor >= max_res){
                                                        opj_event_msg(p_manager, EVT_ERROR, "Resolution factor is greater than the maximum resolution in the component.\n");
                                                        return OPJ_FALSE;
                                                }
                                                p_j2k->m_private_image->comps[it_comp].factor = res_factor;
                                        }
                                        return OPJ_TRUE;
                                }
                        }
                }
        }

        return OPJ_FALSE;
}


static int opj_j2k_initialise_4K_poc(opj_poc_t *POC, int numres){
    POC[0].tile  = 1;
    POC[0].resno0  = 0;
    POC[0].compno0 = 0;
    POC[0].layno1  = 1;
    POC[0].resno1  = (OPJ_UINT32)(numres-1);
    POC[0].compno1 = 3;
    POC[0].prg1 = OPJ_CPRL;
    POC[1].tile  = 1;
    POC[1].resno0  = (OPJ_UINT32)(numres-1);
    POC[1].compno0 = 0;
    POC[1].layno1  = 1;
    POC[1].resno1  = (OPJ_UINT32)numres;
    POC[1].compno1 = 3;
    POC[1].prg1 = OPJ_CPRL;
    return 2;
}


static int whiteheat_probe(struct usb_serial *serial,
				const struct usb_device_id *id)
{
	struct usb_host_interface *iface_desc;
	struct usb_endpoint_descriptor *endpoint;
	size_t num_bulk_in = 0;
	size_t num_bulk_out = 0;
	size_t min_num_bulk;
	unsigned int i;

	iface_desc = serial->interface->cur_altsetting;

	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
		endpoint = &iface_desc->endpoint[i].desc;
		if (usb_endpoint_is_bulk_in(endpoint))
			++num_bulk_in;
		if (usb_endpoint_is_bulk_out(endpoint))
			++num_bulk_out;
	}

	min_num_bulk = COMMAND_PORT + 1;
	if (num_bulk_in < min_num_bulk || num_bulk_out < min_num_bulk)
		return -ENODEV;

	return 0;
}


static int whiteheat_port_remove(struct usb_serial_port *port)
{
	struct whiteheat_private *info;

	info = usb_get_serial_port_data(port);
	kfree(info);

	return 0;
}


static void mpage_put_bnr_to_bhs(struct mpage_da_data *mpd, sector_t logical,
				 struct buffer_head *exbh)
{
	struct inode *inode = mpd->inode;
	struct address_space *mapping = inode->i_mapping;
	int blocks = exbh->b_size >> inode->i_blkbits;
	sector_t pblock = exbh->b_blocknr, cur_logical;
	struct buffer_head *head, *bh;
	pgoff_t index, end;
	struct pagevec pvec;
	int nr_pages, i;

	index = logical >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
	end = (logical + blocks - 1) >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
	cur_logical = index << (PAGE_CACHE_SHIFT - inode->i_blkbits);

	pagevec_init(&pvec, 0);

	while (index <= end) {
		/* XXX: optimize tail */
		nr_pages = pagevec_lookup(&pvec, mapping, index, PAGEVEC_SIZE);
		if (nr_pages == 0)
			break;
		for (i = 0; i < nr_pages; i++) {
			struct page *page = pvec.pages[i];

			index = page->index;
			if (index > end)
				break;
			index++;

			BUG_ON(!PageLocked(page));
			BUG_ON(PageWriteback(page));
			BUG_ON(!page_has_buffers(page));

			bh = page_buffers(page);
			head = bh;

			/* skip blocks out of the range */
			do {
				if (cur_logical >= logical)
					break;
				cur_logical++;
			} while ((bh = bh->b_this_page) != head);

			do {
				if (cur_logical >= logical + blocks)
					break;

				if (buffer_delay(bh) ||
						buffer_unwritten(bh)) {

					BUG_ON(bh->b_bdev != inode->i_sb->s_bdev);

					if (buffer_delay(bh)) {
						clear_buffer_delay(bh);
						bh->b_blocknr = pblock;
					} else {
						/*
						 * unwritten already should have
						 * blocknr assigned. Verify that
						 */
						clear_buffer_unwritten(bh);
						BUG_ON(bh->b_blocknr != pblock);
					}

				} else if (buffer_mapped(bh))
					BUG_ON(bh->b_blocknr != pblock);

				if (buffer_uninit(exbh))
					set_buffer_uninit(bh);
				cur_logical++;
				pblock++;
			} while ((bh = bh->b_this_page) != head);
		}
		pagevec_release(&pvec);
	}
}


static int ext4_set_bh_endio(struct buffer_head *bh, struct inode *inode)
{
	ext4_io_end_t *io_end;
	struct page *page = bh->b_page;
	loff_t offset = (sector_t)page->index << PAGE_CACHE_SHIFT;
	size_t size = bh->b_size;

retry:
	io_end = ext4_init_io_end(inode, GFP_ATOMIC);
	if (!io_end) {
		if (printk_ratelimit())
			printk(KERN_WARNING "%s: allocation fail\n", __func__);
		schedule();
		goto retry;
	}
	io_end->offset = offset;
	io_end->size = size;
	/*
	 * We need to hold a reference to the page to make sure it
	 * doesn't get evicted before ext4_end_io_work() has a chance
	 * to convert the extent from written to unwritten.
	 */
	io_end->page = page;
	get_page(io_end->page);

	bh->b_private = io_end;
	bh->b_end_io = ext4_end_io_buffer_write;
	return 0;
}


static int noalloc_get_block_write(struct inode *inode, sector_t iblock,
				   struct buffer_head *bh_result, int create)
{
	int ret = 0;
	unsigned max_blocks = bh_result->b_size >> inode->i_blkbits;

	BUG_ON(bh_result->b_size != inode->i_sb->s_blocksize);

	/*
	 * we don't want to do block allocation in writepage
	 * so call get_block_wrap with create = 0
	 */
	ret = ext4_get_blocks(NULL, inode, iblock, max_blocks, bh_result, 0);
	if (ret > 0) {
		bh_result->b_size = (ret << inode->i_blkbits);
		ret = 0;
	}
	return ret;
}


static ext4_io_end_t *ext4_init_io_end (struct inode *inode, gfp_t flags)
{
	ext4_io_end_t *io = NULL;

	io = kmalloc(sizeof(*io), flags);

	if (io) {
		igrab(inode);
		io->inode = inode;
		io->flag = 0;
		io->offset = 0;
		io->size = 0;
		io->page = NULL;
		INIT_WORK(&io->work, ext4_end_io_work);
		INIT_LIST_HEAD(&io->list);
	}

	return io;
}