Exemple #1
0
/* {{{ php_tmpl_load_path */
void php_tmpl_load_path(zval** dest, char* local, int local_len, zval* global) {
	char			*buf;
	int				buf_len;
	register char	*p, *q;

	if(local_len && local[0] == '/') {
		buf = (char*)emalloc(local_len + 1);
		memcpy(buf, local, local_len+1);
		buf_len = local_len;
	} else {
		buf = (char*)emalloc(local_len + ZL(global) + 2);
		memcpy(buf, ZV(global), ZL(global));
		buf[ZL(global)] = '/';
		memcpy(buf + 1 + ZL(global), local, local_len+1);
		buf_len = local_len + 1 + ZL(global);
	}

	while((p = strstr(buf, "//"))) {
		for(q = p+1; *q; q++) *(q-1) = *q;
		*(q-1) = 0;
		--buf_len;
	}

	/* check for `..` in the path */
	/* first, remove path elements to the left of `..` */
	for(p = buf; p <= (buf+buf_len-3); p++) {
		if(memcmp(p, "/..", 3) != 0 || (*(p+3) != '/' && *(p+3) != 0)) continue;
		for(q = p-1; q >= buf && *q != '/'; q--, buf_len--);
		--buf_len;
		if(*q == '/') {
			p += 3;
			while(*p) *(q++) = *(p++);
			*q = 0;
			buf_len -= 3;
			p = buf;
		}
	}
	/* second, clear all `..` in the begining of the path
	   because `/../` = `/` */
	while(buf_len > 2 && memcmp(buf, "/..", 3) == 0) {
		for(p = buf+3; *p; p++) *(p-3) = *p;
		*(p-3) = 0;
		buf_len -= 3;
	}
	/* clear `/` at the end of the path */
	while(buf_len > 1 && buf[buf_len-1] == '/') buf[--buf_len] = 0;
	if(!buf_len) { memcpy(buf, "/", 2); buf_len = 1; }

	for(p=buf; *p; p++) *p = tolower(*p);

	zval_dtor(*dest);
	ZVAL_STRINGL(*dest, buf, buf_len, 0);
}
Exemple #2
0
/* {{{ php_tmpl_parse_check_memory */
inline void php_tmpl_parse_check_memory(t_template* tmpl, HashPosition *dup_tag_pos, t_tmpl_tag* tag, uint tag_mod, zval** iteration, zval** dest, uint* offset) {
zval			**dup_ztag;
t_tmpl_tag		*dup_tag;

	if(NULL == *dup_tag_pos || !zend_hash_num_elements(Z_ARRVAL_P(tmpl->dup_tag))) return;
	/* The next line has been added to avoid skiping of duplicate tags in 
	   some circumstances. This is sort of a dirty fix and needs to be 
	   optimized for speed. */
	zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(tmpl->dup_tag), dup_tag_pos);

	do {
		if(FAILURE == zend_hash_get_current_data_ex(Z_ARRVAL_P(tmpl->dup_tag), (void*)&dup_ztag, dup_tag_pos)) break;
		dup_tag = Z_TMPL_TAG(dup_ztag);

		if(*offset > dup_tag->loff) continue;
		if(TMPL_TAG == tag_mod) {
			if(dup_tag->ctx != tag->ctx && dup_tag->loff < tag->loff) continue;
			if(dup_tag->ctx != tag->ctx || dup_tag->loff >= tag->loff) break;
		} else {
			if(dup_tag->ctx != tag && dup_tag->loff < tag->roff) continue;
			if(dup_tag->ctx != tag || dup_tag->loff > tag->roff) break;
		}

		TMPL_PARSE_DEST_ADD(*offset, dup_tag->loff - *offset);
		*offset = dup_tag->roff;

		if(FAILURE == zend_hash_find(Z_ARRVAL_PP(iteration), ZV(dup_tag->name), ZL(dup_tag->name)+1, (void*)&dup_ztag)) continue;

		TMPL_PARSE_DEST_ADD(Z_STRVAL_PP(dup_ztag)-Z_STRVAL_P(tmpl->original), Z_STRLEN_PP(dup_ztag));

	} while(SUCCESS == zend_hash_move_forward_ex(Z_ARRVAL_P(tmpl->dup_tag), dup_tag_pos));

}
Exemple #3
0
/* {{{ php_tmpl_line_num
	Returns line number in template. Used in error messages. */
ulong php_tmpl_line_num(t_template* tmpl, char* ptr) {
ulong	line_num;
char	*p;

	p = ZV(tmpl->original);
	if(ptr < p || ptr > (p + ZL(tmpl->original))) 
		return 0;

	for(line_num = 1; p < ptr; p++) 
		if(*p == '\n') line_num++;

	return line_num;
}
Exemple #4
0
/* {{{ php_tmpl_pre_parse_search */
inline void php_tmpl_pre_parse_search(t_template* tmpl, ulong_pair** point, const short typ, uchar* buf, ulong len) {
register ulong	i, j, k, shift;
register ulong	cmp;
ulong			bmBc[256];

	if(ZL(tmpl->original) < (int)len) return;

	/* Using "Tuned Boyer-Moore" searching algorythm */
	/* Preprocessing */
	for(i=0; i < 256; ++i) bmBc[i] = len;
	for(i=0; i < len-1; ++i) bmBc[buf[i]] = len - i - 1;

	shift = bmBc[ buf[len-1] ];
	bmBc[ buf[len-1] ] = 0;
	memset(ZV(tmpl->original) + ZL(tmpl->original), buf[len-1], len);
	/* Searching */
	j = 0;
	while(j < (ulong)ZL(tmpl->original)) {
		k = bmBc[ ZV(tmpl->original)[j + len - 1] ];
		while(k != 0) {
			j += k; k = bmBc[ ZV(tmpl->original)[j + len - 1] ];
			j += k; k = bmBc[ ZV(tmpl->original)[j + len - 1] ];
			j += k; k = bmBc[ ZV(tmpl->original)[j + len - 1] ];
		}
		if(/* memcmp(buf, ZV(tmpl->original) + j, len-1) == 0 && */ j < (ulong)ZL(tmpl->original) 
			&& (((tmpl->config_start || tmpl->config_end) && (j < tmpl->config_start || j > tmpl->config_end)) ||
			(!tmpl->config_start && !tmpl->config_end))) {

			for(cmp = 0; cmp < len; cmp++)
				if(tolower(buf[cmp]) != tolower(ZV(tmpl->original)[j+cmp])) break;
			if(cmp == len) {
				PAIR_CHECK(*point);
				(*point)[++((*point)[0].r)].l = j;
				(*point)[(*point)[0].r].r = typ;
			}
		}
		j += shift;
	}
}
Exemple #5
0
/* {{{ php_tmpl_set */
int php_tmpl_set(t_template* tmpl, zval* path, zval** data) {
zval		**iteration, *cp_data, **ztag;
t_tmpl_tag	*tag;
char		*p;

	if(FAILURE == zend_hash_find(Z_ARRVAL_P(tmpl->tags), ZV(path), ZL(path)+1, (void*)&ztag)) {
		/* php_error(E_NOTICE, "Can't set value for tag/context \"%s\" which doesn't exist", ZV(path)); */
		return FAILURE;
	}
	tag = Z_TMPL_TAG(ztag);

	if(TMPL_TAG == tag->typ) {
		if((iteration = (zval**)php_tmpl_get_iteration(tmpl, path, TMPL_ITERATION_CURRENT)) == NULL) {
			return FAILURE;
		}
	} else {
		for(p = ZV(path)+ZL(path); p >= ZV(path) && *p != '/'; p--);
		*(p > ZV(path) ? p++ : ++p) = 0;
		ZL(path) = strlen(ZV(path));
		if((iteration = (zval**)php_tmpl_get_iteration(tmpl, path, TMPL_ITERATION_CURRENT)) == NULL) {
			return FAILURE;
		}
	}

	convert_to_string_ex(data); 
	MAKE_STD_ZVAL(cp_data); 
	ZVAL_STRINGL(cp_data, Z_STRVAL_PP(data), Z_STRLEN_PP(data), 1);

	if(SUCCESS == zend_hash_find(Z_ARRVAL_PP(iteration), ZV(tag->name), ZL(tag->name)+1, (void*)&ztag)) {
		if(IS_ARRAY == Z_TYPE_PP(ztag)) {
			/* MEMORY LEAK CAUSED BY THE NEXT LINE !!! */
			zend_hash_del(Z_ARRVAL_PP(iteration), ZV(tag->name), ZL(tag->name)+1);
		} else {
			tmpl->size -= (Z_STRLEN_PP(ztag) * tag->tag_num);
		}
	}
	zend_hash_update(Z_ARRVAL_PP(iteration), ZV(tag->name), ZL(tag->name)+1, (void*)&cp_data, sizeof(zval**), NULL);
	tmpl->size += (ZL(cp_data) * tag->tag_num);

	return SUCCESS;
}
Exemple #6
0
short int php_tmpl_pre_parse_config(t_template* tmpl) {
zval		*zparam;

char	*nam, *val;
uint	nam_len, val_len;

uchar	quote;
register char	*p;
register char	*start;

	nam = (char*)emalloc(TMPL_MAX_TAG_LEN); nam_len = 0;
	val = (char*)emalloc(TMPL_MAX_TAG_LEN); val_len = 0;
	MAKE_STD_ZVAL(zparam); array_init(zparam);

	sprintf(nam, "<%s", TMPL_CONFIG_TAG_NAME);
	nam_len = strlen(nam);

	if(!(p = strstrl_ex(ZV(tmpl->original), ZL(tmpl->original), nam, nam_len))) {
		TMPL_PRE_PARSE_CONFIG_CLEANUP;
		return SUCCESS;
	}

	start = p;
	p += nam_len;

	while(1) {

		/* skip delimiters and check for end of the tag */
		while(*p && '>' != *p && IS_DELIM(*p)) p++;
		if(!(*p) || '>' == *p) {	/* end of tag */
			if('>' == *p) {			/* hide this tag from result output */
				for(++p; *p;) *(start++) = *(++p);
				*start = 0;
				ZL(tmpl->original) -= (p-start);
			}
			break;
		}

		/* get parameter name */
		for(nam_len=0; *p && nam_len < TMPL_MAX_TAG_LEN && !IS_DELIM(*p) && '=' != *p; p++) nam[nam_len++] = *p;
		if(!(*p)) break; else nam[nam_len] = 0;

		if('=' != *p) {
			php_error(E_ERROR, "Invalid configuration tag parameter in template (line:%d)", php_tmpl_line_num(tmpl, p));
			TMPL_PRE_PARSE_CONFIG_CLEANUP;
			return FAILURE;
		}

		/* check if the value is quoted and get the value */
		p++;
		quote = IS_QUOTE(*p) ? *(p++) : 0;

		for(val_len=0; *p && val_len < TMPL_MAX_TAG_LEN && quote ? quote != *p : !IS_DELIM(*p); p++) val[val_len++] = *p;
		if(!(*p)) break; else val[val_len] = 0;

		if(quote && quote != *p) {
			php_error(E_ERROR, "Invalid parameter value in configuration tag in template (line:%d)", php_tmpl_line_num(tmpl, p));
			TMPL_PRE_PARSE_CONFIG_CLEANUP;
			return FAILURE;
		}
		if(quote) p++;

		add_assoc_stringl(zparam, nam, val, val_len, 1);
	}

	php_tmpl_process_param_array(tmpl, zparam);

	TMPL_PRE_PARSE_CONFIG_CLEANUP;
	return SUCCESS;
}
Exemple #7
0
int php_tmpl_parse(zval** dest, t_template*	tmpl, zval* path, HashPosition* pos, zval** data) {
uint			tag_num;
int				i;
zval			**ztag;
t_tmpl_tag		*tag, *ctx;
char			*buf;
ulong			buf_alloc;
zval			*new_path;
zval			**tag_data;
HashPosition	cur_pos, saved_pos, dup_tag_pos;
uint			offset;
unsigned short	need_skip;

zval		**iteration;

char		*key_tag_key;
uint		key_tag_len;
ulong		key_tag_index;

	/* Initialize variables */
	buf_alloc = TMPL_MAX_TAG_LEN;
	buf = (char*)emalloc(buf_alloc);
	MAKE_STD_ZVAL(new_path); ZVAL_STRINGL(new_path, buf, 0, 0);
	dup_tag_pos = NULL;

	/* Get the context's info from tags array */
	if(!pos) {	/* This is not a recursion call. Look for the context's last non-empty iteration */
		zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(tmpl->tags), &cur_pos);
		i = 0;	/* we'll set this flag when found the context */

		do {
			if(HASH_KEY_IS_STRING != zend_hash_get_current_key_ex(Z_ARRVAL_P(tmpl->tags), &key_tag_key, &key_tag_len, &key_tag_index, 0, &cur_pos)) break;
			if(SUCCESS != zend_hash_get_current_data_ex(Z_ARRVAL_P(tmpl->tags), (void**)&ztag, &cur_pos)) break;
			tag = ctx = (t_tmpl_tag*)Z_STRVAL_PP(ztag);
			if(TMPL_CONTEXT != ctx->typ) continue;

			/* get out of the loop if we're inside of the context we need */
			if((uint)ZL(path) == key_tag_len-1 && !memcmp(ZV(path), key_tag_key, ZL(path))) {
				i = 1;
				break;
			}

		} while(SUCCESS == zend_hash_move_forward_ex(Z_ARRVAL_P(tmpl->tags), &cur_pos));

		if(!i) { TMPL_PARSE_CLEANUP; return FAILURE; }
		tag_data = php_tmpl_get_iteration(tmpl, path, TMPL_ITERATION_PARENT);
	} else {
		cur_pos = *pos;
		tag = ctx = (t_tmpl_tag*)Z_STRVAL_PP((zval**)(cur_pos->pData));
		tag_data = data;
	}

	saved_pos = cur_pos;

	/* Check all iterations in the opened context */
	zend_hash_internal_pointer_reset(Z_ARRVAL_PP(tag_data));
	do {
		if(FAILURE == zend_hash_get_current_data(Z_ARRVAL_PP(tag_data), (void*)&iteration)) break;
		/* Uncomment the following line to avoid parsing of empty iterations */
		/* if(pos && !zend_hash_num_elements(Z_ARRVAL_PP(iteration))) break; */

		/* Initialize the offset from the template's content begining */
		offset = (1 == ZL(ctx->name) && '/' == ZV(ctx->name)[0]) ? 0 : ctx->loff + ZL(tmpl->ctx_ol) + ZL(ctx->name) + ZL(tmpl->ctx_or);

		/* We only need tags which are inside of the context */
		cur_pos = saved_pos;
		for(tag_num = 0; tag_num < ctx->tag_num; tag_num++) {
			if(FAILURE == zend_hash_move_forward_ex(Z_ARRVAL_P(tmpl->tags), &cur_pos)) break;
			if(FAILURE == zend_hash_get_current_data_ex(Z_ARRVAL_P(tmpl->tags), (void**)&ztag, &cur_pos)) break;
			tag = (t_tmpl_tag*)Z_STRVAL_PP(ztag);

			if(NULL == dup_tag_pos && zend_hash_num_elements(Z_ARRVAL_P(tmpl->dup_tag))) {
				zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(tmpl->dup_tag), &dup_tag_pos);
				do {
					if(FAILURE == zend_hash_get_current_data_ex(Z_ARRVAL_P(tmpl->dup_tag), (void**)&ztag, &dup_tag_pos)) break;
				} while(Z_TMPL_TAG(ztag)->loff < offset && SUCCESS == zend_hash_move_forward_ex(Z_ARRVAL_P(tmpl->dup_tag), &dup_tag_pos));
			}

			php_tmpl_parse_check_memory(tmpl, &dup_tag_pos, tag, TMPL_TAG, iteration, dest, &offset);

			TMPL_PARSE_DEST_ADD(offset, tag->loff - offset);
			offset = tag->roff;

			need_skip = (FAILURE == zend_hash_find(Z_ARRVAL_PP(iteration), ZV(tag->name), ZL(tag->name)+1, (void*)&ztag));

			if(!need_skip) {

				if(TMPL_CONTEXT == tag->typ && IS_ARRAY == Z_TYPE_PP(ztag)) {	/* Processing a context */

					/* Make a recursive call */
					if(buf_alloc <= (unsigned)(ZL(path)+1+ZL(tag->name)+1)) {
						while(buf_alloc <= (unsigned)(ZL(path)+1+ZL(tag->name)+1)) buf_alloc <<= 1;
						(char*)ZV(new_path) = erealloc(ZV(new_path), buf_alloc);
					}
					sprintf(ZV(new_path), (1 == ZL(path) && '/' == ZV(path)[0]) ? "%s%s" : "%s/%s", ZV(path), ZV(tag->name));
					ZL(new_path) = ZL(path) + ZL(tag->name) + ((1 == ZL(path) && '/' == ZV(path)[0]) ? 0 : 1);
					php_tmpl_parse(dest, tmpl, new_path, &cur_pos, ztag);

				} else {	/* Processing a tag */

					TMPL_PARSE_DEST_ADD(Z_STRVAL_PP(ztag)-Z_STRVAL_P(tmpl->original), Z_STRLEN_PP(ztag));
					if(TMPL_CONTEXT == tag->typ) need_skip = 1;

				}
			}

			if(need_skip && TMPL_CONTEXT == tag->typ) {
				for(i=0; i < (int)tag->tag_num; i++) {
					if(FAILURE == zend_hash_move_forward_ex(Z_ARRVAL_P(tmpl->tags), &cur_pos)) break;
					if(FAILURE == zend_hash_get_current_data_ex(Z_ARRVAL_P(tmpl->tags), (void**)&ztag, &cur_pos)) break;
					i -= (TMPL_CONTEXT == Z_TMPL_TAG(ztag)->typ) ? Z_TMPL_TAG(ztag)->tag_num : 0;
				}
			}
		}

		php_tmpl_parse_check_memory(tmpl, &dup_tag_pos, ctx, TMPL_CONTEXT, iteration, dest, &offset);

		if(1 != ZL(path) || '/' != ZV(path)[0]) {
			TMPL_PARSE_DEST_ADD(offset, ctx->roff - offset - ZL(tmpl->ctx_cl) - ZL(ctx->name)*(ZL(tmpl->ctx_cr)?1:0) - ZL(tmpl->ctx_cr));
		} else {
			TMPL_PARSE_DEST_ADD(offset, ZL(tmpl->original)-offset);
		}

	} while(SUCCESS == zend_hash_move_forward(Z_ARRVAL_PP(tag_data)));

	if(pos) *pos = cur_pos;

	TMPL_PARSE_CLEANUP;
	return SUCCESS;
}
Exemple #8
0
short int php_tmpl_pre_parse(t_template* tmpl) {
ulong_pair		*point;
register uchar	*p;

uchar			*buf;
uint			buf_len, buf_alloc, buf_this;

uint			i, j, len, close_idx;
t_tmpl_tag		*tag, *context;
zval			**ztag;

	if(FAILURE == php_tmpl_pre_parse_config(tmpl)) return FAILURE;

	/* Initialize variables */
	PAIR_INIT(point);
	buf_alloc = TMPL_MAX_TAG_LEN+4;
	buf = (uchar*)emalloc(buf_alloc);
	/* Searching algorythm will require larger buffer */
	(char*)ZV(tmpl->original) = erealloc(ZV(tmpl->original), 
		ZL(tmpl->original) + MAX(
			ZL(tmpl->tag_left), MAX(
				ZL(tmpl->tag_right), MAX(
					ZL(tmpl->ctx_ol), MAX(
						ZL(tmpl->ctx_or), MAX(
							ZL(tmpl->ctx_cl), ZL(tmpl->ctx_cr)
						)
					)
				)
			)
		)
	);

	/* Obtain positions of all tags and contexts */
	php_tmpl_pre_parse_search(tmpl, &point, TMPL_TAG, ZV(tmpl->tag_left), ZL(tmpl->tag_left));
	php_tmpl_pre_parse_search(tmpl, &point, TMPL_TAG_END, ZV(tmpl->tag_right), ZL(tmpl->tag_right));
	php_tmpl_pre_parse_search(tmpl, &point, TMPL_CONTEXT_OPEN_LEFT, ZV(tmpl->ctx_ol), ZL(tmpl->ctx_ol));
	php_tmpl_pre_parse_search(tmpl, &point, TMPL_CONTEXT_OPEN_RIGHT, ZV(tmpl->ctx_or), ZL(tmpl->ctx_or));
	php_tmpl_pre_parse_search(tmpl, &point, TMPL_CONTEXT_CLOSE_LEFT, ZV(tmpl->ctx_cl), ZL(tmpl->ctx_cl));
	if(ZL(tmpl->ctx_cr)) php_tmpl_pre_parse_search(tmpl, &point, TMPL_CONTEXT_CLOSE_RIGHT, ZV(tmpl->ctx_cr), ZL(tmpl->ctx_cr));

	if(0 == point[0].r) { TMPL_PRE_PARSE_CLEANUP; return SUCCESS; }

	qsort(&point[1], point[0].r, sizeof(ulong_pair), ULONG_PAIR_COMPARE);

	strcpy(buf, "/"); buf_len = buf_this = 1;

	/* Add root context */
	TAG_INIT(tag);
 	tag->loff = 0;
	tag->roff = ZL(tmpl->original); 
	tag->typ = TMPL_CONTEXT;
	ZVAL_STRINGL(tag->name, "/", 1, 1);
	add_assoc_stringl(tmpl->tags, buf, (char*)tag, sizeof(t_tmpl_tag), 0);
	context = tag;
	context->size = ZL(tmpl->original);

	/* Pre parse template */
	for(i=1; i <= point[0].r; i++) {
		switch(point[i].r) {
		case TMPL_TAG:
			p = ZV(tmpl->original) + point[i].l + ZL(tmpl->tag_left);

			TMPL_PRE_PARSE_GET_LEN(TMPL_TAG_END, ZL(tmpl->tag_left));

			if(buf_alloc < buf_len+len+1) {
				while(buf_alloc < buf_len+len+1) buf_alloc += TMPL_MAX_TAG_LEN;
				buf = (char*)erealloc(buf, buf_alloc);
			}
			if(buf_len > 1) buf[buf_len++] = '/';
			for(j=0; j < len; j++) buf[buf_len++] = tolower(*(p-len+j));
			buf[buf_len] = 0;

			TAG_INIT(tag);
			tag->loff = point[i].l;
			tag->roff = point[close_idx].l + ZL(tmpl->tag_right);
			tag->size = (tag->roff - tag->loff);
			tag->typ = TMPL_TAG;
			tag->tag_num = 1;
			tag->ctx = context;
			ZVAL_STRINGL(tag->name, buf+buf_len-len, len, 1);

			if(FAILURE == zend_hash_find(Z_ARRVAL_P(tmpl->tags), buf, buf_len+1, (void*)&ztag)) {
				/* There's no the tag defined in the current context. Creating one */
				add_assoc_stringl(tmpl->tags, buf, (char*)tag, sizeof(t_tmpl_tag), 0);
				context->tag_num++;
			} else {	/* add another instance of the tag in the same context */
				(Z_TMPL_TAG(ztag)->tag_num)++;
				add_next_index_stringl(tmpl->dup_tag, (char*)tag, sizeof(t_tmpl_tag), 0);
			}
			context->size -= tag->size;

			while(buf_len > 1 && buf[buf_len-1] != '/') buf[--buf_len] = 0;
			if(buf_len > 1) buf[--buf_len] = 0;

			i = close_idx;
			break;

		case TMPL_CONTEXT_OPEN_LEFT:
			p = ZV(tmpl->original) + point[i].l + ZL(tmpl->ctx_ol);

			TMPL_PRE_PARSE_GET_LEN(TMPL_CONTEXT_OPEN_RIGHT, ZL(tmpl->ctx_ol));

			if(buf_alloc < buf_len+len+1) {
				while(buf_alloc < buf_len+len+1) buf_alloc += TMPL_MAX_TAG_LEN;
				buf = (char*)erealloc(buf, buf_alloc);
			}
			if(buf_len > 1) buf[buf_len++] = '/';
			buf_this = buf_len;
			for(j=0; j < len; j++) buf[buf_len++] = tolower(*(p-len+j));
			buf[buf_len] = 0;

			if(SUCCESS == zend_hash_find(Z_ARRVAL_P(tmpl->tags), buf, buf_len+1, (void*)&ztag)) {
				php_error(E_ERROR, "Duplicate context \"%s\" in template (line: %d)", buf, php_tmpl_line_num(tmpl, p));
				TMPL_PRE_PARSE_CLEANUP;
				return FAILURE;
			}

			TAG_INIT(tag);
			tag->loff = point[i].l;
			tag->typ = TMPL_CONTEXT;
			tag->ctx = context;
			ZVAL_STRINGL(tag->name, buf+buf_len-len, len, 1);
			add_assoc_stringl(tmpl->tags, buf, (char*)tag, sizeof(t_tmpl_tag), 0);
			context->tag_num++;
			context = tag;

			i = close_idx;
			break;

		case TMPL_CONTEXT_CLOSE_LEFT:
			p = ZV(tmpl->original) + point[i].l + ZL(tmpl->ctx_cl);

			if(ZL(tmpl->ctx_cr)) {
	
				TMPL_PRE_PARSE_GET_LEN(TMPL_CONTEXT_CLOSE_RIGHT, ZL(tmpl->ctx_cl));

				for(j=0; j < len; j++)
					if(buf[buf_this+j] != tolower(*(p-len+j))) break;
				if(j < len) continue;

			}

			tag = context;
			tag->roff = ZL(tmpl->ctx_cr) ? point[close_idx].l + ZL(tmpl->ctx_cr) : point[i].l + ZL(tmpl->ctx_cl);
			tag->size += (tag->roff - tag->loff);

			while(buf_len > 1 && buf[buf_len-1] != '/') buf[--buf_len] = 0;
			if(buf_len > 1) buf[--buf_len] = 0;
			buf_this = buf_len;
			while(buf_this > 1 && buf[buf_this-1] != '/') --buf_this;
			if(FAILURE == zend_hash_find(Z_ARRVAL_P(tmpl->tags), buf, buf_len+1, (void*)&ztag)) {
				php_error(E_ERROR, "Can't find parent context in template. You should not see this message");
				TMPL_PRE_PARSE_CLEANUP;
				return FAILURE;
			}
			context = (t_tmpl_tag*)Z_STRVAL_PP(ztag);
			context->size -= tag->size;

			if(ZL(tmpl->ctx_cr)) i = close_idx;
			break;
		}
	}
	if(buf_len != 1) {
		php_error(E_ERROR, "Can't continue with an unterminated context \"%s\" in template (line:%d)", buf, php_tmpl_line_num(tmpl, ZV(tmpl->original) + context->loff));
		TMPL_PRE_PARSE_CLEANUP;
		return FAILURE;
	}
	tmpl->size = context->size;

	TMPL_PRE_PARSE_CLEANUP;
	return SUCCESS;
}
inline void
ReformHermitianMatrix
( UpperOrLower uplo,
        DistMatrix<R,MC,MR>& A,
  const DistMatrix<R,VR,STAR>& w,
  const DistMatrix<R,MC,MR>& Z,
  const RealFunctor& f )
{
#ifndef RELEASE
    PushCallStack("hermitian_function::ReformHermitianMatrix");
#endif
    const Grid& g = A.Grid();

    DistMatrix<R,MC,MR> ZL(g), ZR(g),
                        Z0(g), Z1(g), Z2(g);
    DistMatrix<R,VR,STAR> wT(g),  w0(g),
                          wB(g),  w1(g),
                                  w2(g);

    DistMatrix<R,MC,  STAR> Z1_MC_STAR(g);
    DistMatrix<R,VR,  STAR> Z1_VR_STAR(g);
    DistMatrix<R,STAR,MR  > Z1Trans_STAR_MR(g);
    DistMatrix<R,STAR,STAR> w1_STAR_STAR(g);

    if( uplo == LOWER )
        MakeTrapezoidal( LEFT, UPPER, 1, A );
    else
        MakeTrapezoidal( LEFT, LOWER, -1, A );
    LockedPartitionRight( Z, ZL, ZR, 0 );
    LockedPartitionDown
    ( w, wT,
         wB, 0 );
    while( ZL.Width() < Z.Width() )
    {
        LockedRepartitionRight
        ( ZL, /**/ ZR,
          Z0, /**/ Z1, Z2 );
        LockedRepartitionDown
        ( wT,  w0,
         /**/ /**/
               w1,
          wB,  w2 );

        Z1_MC_STAR.AlignWith( A );
        Z1_VR_STAR.AlignWith( A );
        Z1Trans_STAR_MR.AlignWith( A );
        //--------------------------------------------------------------------//
        Z1_MC_STAR = Z1;
        Z1_VR_STAR = Z1_MC_STAR;
        w1_STAR_STAR = w1;

        // Scale Z1[VR,* ] with the modified eigenvalues
        const int width = Z1_VR_STAR.Width();
        const int localHeight = Z1_VR_STAR.LocalHeight();
        for( int j=0; j<width; ++j )
        {
            const R omega = f(w1_STAR_STAR.GetLocalEntry(j,0));
            R* buffer = Z1_VR_STAR.LocalBuffer(0,j);
            for( int iLocal=0; iLocal<localHeight; ++iLocal )
                buffer[iLocal] *= omega;
        }

        Z1Trans_STAR_MR.TransposeFrom( Z1_VR_STAR );
        internal::LocalTrrk( uplo, (R)1, Z1_MC_STAR, Z1Trans_STAR_MR, (R)1, A );
        //--------------------------------------------------------------------//
        Z1Trans_STAR_MR.FreeAlignments();
        Z1_VR_STAR.FreeAlignments();
        Z1_MC_STAR.FreeAlignments();

        SlideLockedPartitionDown
        ( wT,  w0,
               w1,
         /**/ /**/
          wB,  w2 );
        SlideLockedPartitionRight
        ( ZL,     /**/ ZR,
          Z0, Z1, /**/ Z2 );
    }
#ifndef RELEASE
    PopCallStack();
#endif
}
inline void
ReformNormalMatrix
(       DistMatrix<Complex<R>,MC,MR  >& A,
  const DistMatrix<R,         VR,STAR>& w,
  const DistMatrix<Complex<R>,MC,MR  >& Z,
  const ComplexFunctor& f )
{
#ifndef RELEASE
    PushCallStack("hermitian_function::ReformNormalMatrix");
#endif
    const Grid& g = A.Grid();
    typedef Complex<R> C;

    DistMatrix<C,MC,MR> ZL(g), ZR(g),
                        Z0(g), Z1(g), Z2(g);
    DistMatrix<R,VR,STAR> wT(g),  w0(g),
                          wB(g),  w1(g),
                                  w2(g);

    DistMatrix<C,MC,  STAR> Z1_MC_STAR(g);
    DistMatrix<C,VR,  STAR> Z1_VR_STAR(g);
    DistMatrix<C,STAR,MR  > Z1Adj_STAR_MR(g);
    DistMatrix<R,STAR,STAR> w1_STAR_STAR(g);

    Zero( A );
    LockedPartitionRight( Z, ZL, ZR, 0 );
    LockedPartitionDown
    ( w, wT,
         wB, 0 );
    while( ZL.Width() < Z.Width() )
    {
        LockedRepartitionRight
        ( ZL, /**/ ZR,
          Z0, /**/ Z1, Z2 );
        LockedRepartitionDown
        ( wT,  w0,
         /**/ /**/
               w1,
          wB,  w2 );

        Z1_MC_STAR.AlignWith( A );
        Z1_VR_STAR.AlignWith( A );
        Z1Adj_STAR_MR.AlignWith( A );
        //--------------------------------------------------------------------//
        Z1_MC_STAR = Z1;
        Z1_VR_STAR = Z1_MC_STAR;
        w1_STAR_STAR = w1;

        // Scale Z1[VR,* ] with the modified eigenvalues
        const int width = Z1_VR_STAR.Width();
        const int localHeight = Z1_VR_STAR.LocalHeight();
        for( int j=0; j<width; ++j )
        {
            const C conjOmega = Conj(f(w1_STAR_STAR.GetLocalEntry(j,0)));
            C* buffer = Z1_VR_STAR.LocalBuffer(0,j);
            for( int iLocal=0; iLocal<localHeight; ++iLocal )
                buffer[iLocal] *= conjOmega;
        }

        Z1Adj_STAR_MR.AdjointFrom( Z1_VR_STAR );
        internal::LocalGemm
        ( NORMAL, NORMAL, (C)1, Z1_MC_STAR, Z1Adj_STAR_MR, (C)1, A );
        //--------------------------------------------------------------------//
        Z1Adj_STAR_MR.FreeAlignments();
        Z1_VR_STAR.FreeAlignments();
        Z1_MC_STAR.FreeAlignments();

        SlideLockedPartitionDown
        ( wT,  w0,
               w1,
         /**/ /**/
          wB,  w2 );
        SlideLockedPartitionRight
        ( ZL,     /**/ ZR,
          Z0, Z1, /**/ Z2 );
    }
#ifndef RELEASE
    PopCallStack();
#endif
}
inline void
HermitianFromEVD
( UpperOrLower uplo,
        DistMatrix<F>& A,
  const DistMatrix<BASE(F),VR,STAR>& w,
  const DistMatrix<F>& Z )
{
#ifndef RELEASE
    CallStackEntry entry("HermitianFromEVD");
#endif
    const Grid& g = A.Grid();
    typedef BASE(F) R;

    DistMatrix<F> ZL(g), ZR(g),
                  Z0(g), Z1(g), Z2(g);
    DistMatrix<R,VR,STAR> wT(g),  w0(g),
                          wB(g),  w1(g),
                                  w2(g);

    DistMatrix<F,MC,  STAR> Z1_MC_STAR(g);
    DistMatrix<F,VR,  STAR> Z1_VR_STAR(g);
    DistMatrix<F,STAR,MR  > Z1Adj_STAR_MR(g);
    DistMatrix<R,STAR,STAR> w1_STAR_STAR(g);

    A.ResizeTo( Z.Height(), Z.Height() );
    if( uplo == LOWER )
        MakeTrapezoidal( UPPER, A, 1 );
    else
        MakeTrapezoidal( LOWER, A, -1 );
    LockedPartitionRight( Z, ZL, ZR, 0 );
    LockedPartitionDown
    ( w, wT,
         wB, 0 );
    while( ZL.Width() < Z.Width() )
    {
        LockedRepartitionRight
        ( ZL, /**/ ZR,
          Z0, /**/ Z1, Z2 );
        LockedRepartitionDown
        ( wT,  w0,
         /**/ /**/
               w1,
          wB,  w2 );

        Z1_MC_STAR.AlignWith( A );
        Z1_VR_STAR.AlignWith( A );
        Z1Adj_STAR_MR.AlignWith( A );
        //--------------------------------------------------------------------//
        Z1_MC_STAR = Z1;
        Z1_VR_STAR = Z1_MC_STAR;
        w1_STAR_STAR = w1;

        DiagonalScale( RIGHT, NORMAL, w1_STAR_STAR, Z1_VR_STAR );

        Z1Adj_STAR_MR.AdjointFrom( Z1_VR_STAR );
        LocalTrrk( uplo, F(1), Z1_MC_STAR, Z1Adj_STAR_MR, F(1), A );
        //--------------------------------------------------------------------//

        SlideLockedPartitionDown
        ( wT,  w0,
               w1,
         /**/ /**/
          wB,  w2 );
        SlideLockedPartitionRight
        ( ZL,     /**/ ZR,
          Z0, Z1, /**/ Z2 );
    }
}