/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * vsdump: test program to dump and parse content of vsd file
 *
 * Copyright (C) 2006-2007	Valek Filippov (frob@df.ru)
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 3 or later of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
 *
 */
#include <glib.h>
#include <glib/gstdio.h>
#include <gsf/gsf-utils.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <iconv.h>
#include "vsdump.h"
#include "vsd_parse_blocks.h"
#include "names.h"

/* define DEBUG_BLOCK */

/*--------------------------------------------------*/
/* vsd_get_slice
 * Takes stream, pointers to offset and argnum
 * Modifies priority and argnum, returns name or function.
 * priority 0 means 'literal'
 * priority 1 means operation with priority 1 (<,>,<=,>=,==,!=)
 * priority 2 means operation with priority 2 (+ or -)
 * priority 3 means operation with priority 3 (* or /)
 * priority 4 means operation with priority 4 (^)
 * priority 5 means negative sign
 * priority 6 means 'braces'
 * priority 7 means function with argnum args what need to be took in braces
 * priority 8 means end
 */
static char*
vsd_get_slice(GByteArray *stream, int *offset, guint32 *argnum, guint8 *priority, int version){

	guint8	len=0, idx, i;
	char		*formula=NULL;
	gchar		*tmp1=NULL, *tmp2=NULL, *tmp3=NULL;
	guint32	skip=0;
	gchar*	const	names76[24][30] =
	 {{"PinX","PinY","Width","Height","LocPinX","LocPinY","Angle","FlipX","FlipY","ResizeMode"},
	  {"LineWeight","LineColor","LinePattern","Rounding","EndArrowSize","BeginArrow","EndArrow","LineCap","BeginArrowSize"},
	  {"FillForegnd","FillBkgnd","FillPattern","ShdwForegnd","ShdwBkgnd","ShdwPattern"},
	  {"BeginX","BeginY","EndX","EndY"},
	  {"TheData","TheText","EventDblClick","EventXFMod","EventDrop",NULL,"EventTextOverflow"},
	  {"LayerMember"},{},{"EnableLineProps","EnableFillProps","EnableTextProps","HideForApply"},
	  {"ImageOffsetX","ImageOffsetY","ImageWidth","ImageHeight"},
	  {"PageWidth","PageHeight","ShdwOffsetX","ShdwOffsetY","PageScale","DrawingScale","DrawingSizeType","DrawingScaleType",NULL,
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,"InhibitSnap"},
	  {"LeftMargin","RightMargin","TopMargin","BottomMargin","VerticalAlign","TextBkgnd","DefaultTabStop","TextMaxDepth","TextGeometry","TextContainer","TextDirection"},
	  {"TxtPinX","TxtPinY","TxtWidth","TxtHeight","TxtLocPinX","TxtLocPinY","TxtAngle","TxtFlipX","TxtFlipY"},{},
	  {"AlignLeft","AlignCenter","AlignRight","AlignTop","AlignMiddle","AlignBottom"},
	  {"LockWidth","LockHeight","LockMoveX","LockMoveY","LockAspect","LockDelete","LockBegin","LockEnd","LockRotate","LockCrop","LockVtxEdit","LockTextEdit","LockFormat",
	  "LockGroup","LockCalcWH","LockSelect"},
		{"HelpTopic","Copyright"},
		{"NoObjHandles","NonPrinting","NoCtlHandles","NoAlignBox","UpdateAlignBox","HideText",NULL,"ShapeTabStop","DynFeedback","GlueType","WalkPreference",
		"BegTrigger","EndTrigger","ObjType",NULL,NULL,"Comment","IsDropSource","NoLiveDynamics"},
		{"XRulerDensity","YRulerDensity","XRulerSpacing","YRulerSpacing","XRulerOrigin","YRulerOrigin","XGridDensity","YGridDensity","XGridSpacing",
		"YGridSpacing","XGridOrigin","YGridOrigin"},{},
		{"OutputFormat","LockPreview","Metric",NULL,NULL,NULL,NULL,NULL,NULL,"PreviewQuality","PreviewScope"},
		{"Gamma","Contrast","Brightness","Sharpen","Blur","Denoise","Transparency","CompressionLevel","CompressionType"},
		{"SelectMode","DisplayMode","IsDropTarget","IsSnapTarget","IsTextEditTarget","DontMoveChildren"},
		{"ShapePermeableX","ShapePermeableY","ShapePermeablePlace",NULL,NULL,NULL,NULL,NULL,"ShapeFixedCode","ShapePlowCode","ShapeRouteStyle",
		"ShapePlaceStyle","ConFixedCode","ConLineJumpCode","ConLineJumpStyle","ShapePlaceDepth","ConLineJumpDirX","ConLineJumpDirY"},
		{"ResizePage","EnableGrid","DynamicsOff","CtrlAsInput",NULL,NULL,NULL,NULL,"PlaceStyle","RouteStyle","PlaceDepth","PlowCode","LineJumpCode",
		"LineJumpStyle","PageLineJumpDirX","PageLineJumpDirY","LineToNodeX","LineToNodeY","BlockSizeX","BlockSizeY","AvenueSizeX","AvenueSizeY",
		"LineToLineX","LineToLineY","LineJumpFactorX","LineJumpFactorY","LineAdjustFrom","LineAdjustTo"}};	


	*priority = 0; /* 'defaults' */ 
	*argnum = 0;
		
	/* надо ещё расставлять приоритеты операций */
	switch(stream->data[*offset]){
	case 0xFE: /* the end */
		formula = NULL;
		len = 0;
		skip = 1;
		*priority = 8; 
		break;
	case 0xF0: /* FIXME I seen it only in the Nortel stencils (ver 6) */
		formula = NULL;
		len = 0;
		skip = 1;
		*priority = 0; 
		break;
	case 0xc0: /* FIXME I seen it only in the Nortel stencils (ver 6) */
		formula = malloc(5);
		formula = "_c0_";
		len = 0;
		skip = 1;
		*priority = 0; 
		break;

	case 0x60: /* string */
		len = stream->data[*offset+1];
		skip = len + 3;
		if(11 == version) { skip = len*2 + 4 ;}
		 
		tmp1 = malloc(len + 1);
		if(11 == version) {
		// 	for(i=0;i<len+1;i++){
		//		memcpy(tmp1+i, stream->data + *offset + 2 + i*2, 1);
			iconv_t iconv_tr = iconv_open("UTF-8", "UCS-2");
			size_t len1, len2,len3;
			char *iconv_tr_str, *iconv_tr_str1, *iconv_tr_str2;
			len1 = 2*len + 2;
			len2 = 6*len1;
			iconv_tr_str = malloc(len2);
			iconv_tr_str2 = memset(iconv_tr_str, 0, len2);
			iconv_tr_str1 = stream->data +*offset + 2;
			len3 = iconv(iconv_tr, &iconv_tr_str1,&len1, &iconv_tr_str, &len2);
			formula = g_strdup_printf("\"%s\"", iconv_tr_str2);
			iconv_close(iconv_tr);
			free(iconv_tr_str2);
		}else{
			memcpy(tmp1, stream->data + *offset + 2, len+1); 
			formula = g_strdup_printf("\"%s\"", tmp1);
		}
		free(tmp1);
		break;
	case 0x61: /* one byte */
		len = 0;
		skip = 2;
		formula = malloc(4);
		sprintf(formula, "%d", stream->data[*offset + 1]);
		break;
	case 0x14: /* percent */
		len = 0;
		skip = 1;
		formula = malloc(2);
		sprintf(formula, "%%");
		break;
	case 0x62: /* two bytes LE GUINT16 */
		formula = malloc(16);
		sprintf(formula, "%d", GSF_LE_GET_GUINT16(stream->data + *offset + 1));
		len = 0;
		skip = 3;
		break;	
	case 0x75: /* name from name75 array */
		idx = stream->data[*offset+1];
		formula = malloc(strlen(name75[idx]) + 1);
		strcpy(formula, name75[idx]);
		len = 0; 
		skip = 5;
		*argnum = 0;
		break;
	case 0x70:	
	case 0x72:
		len = 0;
		skip = 8;
		idx = stream->data[*offset + 5]; /* to select Obj_name */
		if(0x70 == stream->data[*offset]){
			skip = 12;
			idx = stream->data[*offset + 9];
		}
		switch(idx){
			case 1:
				if(0x70 == stream->data[*offset]){
					tmp1 = g_strdup_printf("Sheet.%d!", GSF_LE_GET_GUINT32(stream->data+*offset+1));
				}else{
					tmp1 = g_strdup_printf("Sheet.");
				}
				break;
			case 3:
				tmp1 = g_strdup_printf("Char.");
				break;
			case 4:
				tmp1 = g_strdup_printf("Para.");
				break;
			case 5:
				tmp1 = g_strdup_printf("Tabs.");
				break;
			case 6:
				tmp1 = g_strdup_printf("Scratch.");
				break;
			case 7:
				tmp1 = g_strdup_printf("Connections.");
				break;
			case 9:
				tmp1 = g_strdup_printf("Controls.");
				break;
			case 0xf0:
				tmp1 = g_strdup_printf("Actions.");
				break;			
			case 0xf1:
				tmp1 = g_strdup_printf("Layer.");
				break;			
			case 0xf2:
				tmp1 = g_strdup_printf("User.");
				break;			
			case 0xf3:
				tmp1 = g_strdup_printf("Prop.");
				break;			
			case 0xf4:
				tmp1 = g_strdup_printf("Hyperlink.");
				break;			
			default:
				if((9 < idx) && (0xf0 > idx) ){			
					tmp1 = g_strdup_printf("Geometry%d.", (idx - 9) );
				}else{
					tmp1 = g_strdup_printf("UNKNOWN_%x.", idx);
				}
		}
		idx = stream->data[*offset + 6]; /* to select Var_name FIXME! not quite right thing. Var_name seems to be Obj_name dependant*/
		if(0x70 == stream->data[*offset]) idx = stream->data[*offset + 10];
		if( (0x70 == stream->data[*offset]) && (1 == stream->data[*offset+5]) ){ /* will use name75 */
			formula = g_strdup_printf("%s%s", tmp1, name75[idx]);
		}else{
			switch(idx){
				case 0:
					tmp2 = g_strdup_printf("%sX", tmp1);
					free(tmp1);
					break;
				case 1:
					tmp2 = g_strdup_printf("%sY", tmp1);
					free(tmp1);
					break;
				case 2:
					tmp2 = g_strdup_printf("%sA", tmp1);
					free(tmp1);
					break;
				case 7:
					tmp2 = g_strdup_printf("%sSize", tmp1);
					free(tmp1);
					break;
				default:
					tmp2 = g_strdup_printf("%s{%d?}", tmp1, idx);
					free(tmp1);
			}
			if(7 != idx || 6 != idx){
				idx = GSF_LE_GET_GUINT32(stream->data + *offset + 1);
				if(0x70 == stream->data[*offset]) idx = 1;
				formula = g_strdup_printf("%s%d", tmp2, idx);
			}else{
				formula = g_strdup_printf("%s", tmp2);
			}
			free(tmp2);
		}			
		break;
	case 0x74:
		len = 0;
		skip = 9;
		idx=stream->data[*offset + 1];
		formula = malloc(15+strlen(name75[idx]));
		switch(GSF_LE_GET_GUINT32(stream->data + *offset + 5)){
			case 0:
				sprintf(formula,"ThePage!%s", name75[idx]);
				break;
			default:
				sprintf(formula,"Sheet.%d!%s", GSF_LE_GET_GUINT32(stream->data + *offset + 5), name75[idx]);
		}			
		break;
	case 0x76: /* FIXME. Weird name. Workaround for info collection is here */
		len = 0;
		skip = 18;
		tmp1 = g_strdup_printf("%s", names76[stream->data[*offset + 11] - 1][stream->data[*offset + 16]]);
		switch(stream->data[*offset+2]){
		 case 1:
		 	tmp2 = g_strdup_printf("GetRef");
		 	break;
		 case 2:
		 	tmp2 = g_strdup_printf("GetVal");
		 	break;
		 default:
 		 	tmp2 = g_strdup_printf("UNKNOWN_76");
		 	break;
		}
		if(NULL!=tmp1){
			formula = g_strdup_printf("%s(%s)", tmp2, tmp1);
			free(tmp1);
		}else{
			formula = g_strdup_printf("%s(UNKNOWN_%x UNKNOWN_%x)", tmp2, stream->data[*offset + 11], stream->data[*offset + 16]);
		}		
		free(tmp2);
		break;
	case 0x7a: /* functions w/o args or with fixed num of args */
		idx = stream->data[*offset+1];
		formula = malloc(strlen(namefunc[idx]) + 1);
		strcpy(formula, namefunc[idx]);
		len = 0; 
		skip = 5;
		if(0xe == idx|| 0x33 == idx || 0x63 == idx || ( 0x84 < idx && 0x8b > idx) || 0xe0 ==idx){
		 *argnum = 1;
		 *priority = 7;
		}
		if( 0x38 == idx || 0x39 == idx || 0x83 == idx || 0x84 == idx || 0x70 == idx){
		 *argnum = 3;
		 *priority = 7;
		}
		if( 0x2f ==  idx || 0x30 == idx ){
		 *argnum = 4;
		 *priority = 7;
		}

		g_warning("Function 0x7a");
		break;
	case 0x80:
		idx = stream->data[*offset+1];
		formula = malloc(strlen(namefunc[idx]) + 1);
		strcpy(formula, namefunc[idx]);
		len = 0; 
		skip = 5;
		/* not quite elegant, but chiper than 2nd rare array with argnums */
		*argnum = 1;
		if(0x1 == idx || 0x2 == idx || 0x4 == idx || 0xb == idx || 0x10 == idx ||
		   0x12 == idx || (0x16 < idx && 0x1d > idx) || (0x1f < idx && 0x23 > idx) ||
			0x2e == idx || 0x36 == idx || (0x3d < idx && 0x41 > idx) ||
			(0x42 < idx && 0x4a > idx) || 0x62 == idx || 0x66 == idx || 0x68 == idx ||
			0x74 == idx || 0x75 == idx || 0x78 == idx || 0x79 == idx || 0x7c == idx ||
			0x7e == idx ) *argnum = 2;
				
		if(0x13 == idx || 0x31 == idx || (0x49 < idx && 0x4f > idx) ||
			0x7b == idx || 0xbc == idx || 0xc6 == idx) *argnum = 0; /* 9 cases */
		if(0x3a == idx || 0x3b == idx || 0x16 == idx || 0x64 == idx) *argnum = 3; /* 4 cases */
		if(0x42 == idx || 0x81 == idx || 0xc4 == idx) *argnum = 4; /* 3 cases */
		if(0x61 == idx || 0x65 == idx || 0x67 == idx || 0x6e == idx || 0x6f == idx) *argnum = 5;
		if(0x34 == idx || 0x9d == idx || 0x9e == idx) *argnum = 6;  /* 3 cases */
		if(0x35 == idx) *argnum = 7;
		if( 0x6a == idx || 0x6c == idx) *argnum = 8;
		if( 0x76 == idx || 0x77 == idx) *argnum = 9;
		if( 0x69 == idx || 0x6b == idx) *argnum = 10;
		*priority = 7;
		g_warning("Function 0x80");
		break;
	case 0x7b: /* just 5 cases atm */
		idx = stream->data[*offset+5];
		formula = malloc(strlen(namefunc[idx]) + 1);
		strcpy(formula, namefunc[idx]);
		len = 0; 
		skip = 9;
		*argnum = GSF_LE_GET_GUINT32(stream->data+*offset+1);
		*priority = 7;
		g_warning("Function 0x7b");
		break;
	case 0x81:
		idx = stream->data[*offset+5];
		formula = malloc(strlen(namefunc[idx]) + 1);
		strcpy(formula, namefunc[idx]);
		len = 0; 
		skip = 9;
		*argnum = GSF_LE_GET_GUINT32(stream->data+*offset+1);
		*priority = 7;
		g_warning("Function 0x81");
		break;
	case 0x8a: /* yet another implementation of NURBS() */
		for(i=0;i<stream->data[*offset+13];i++){
			tmp1 = g_strdup_printf(",%f,%f,%f,%2.0f",
				GSF_LE_GET_DOUBLE(stream->data+*offset+17+i*32),
				GSF_LE_GET_DOUBLE(stream->data+*offset+25+i*32),
				GSF_LE_GET_DOUBLE(stream->data+*offset+33+i*32),
				GSF_LE_GET_DOUBLE(stream->data+*offset+41+i*32));
			if(tmp3){
				tmp2 = g_strdup_printf("%s",tmp3);
				free(tmp3);
			}
			if(tmp2){
				tmp3 = g_strdup_printf("%s%s", tmp2, tmp1);
				free(tmp2);
			}else{
				tmp3 = g_strdup_printf("%s", tmp1);
			}
			free(tmp1);														
		} 		  
		formula =  g_strdup_printf("NURBS(%f,%d,%d,%d%s)",
				GSF_LE_GET_DOUBLE(stream->data+*offset+1),
				GSF_LE_GET_GUINT16(stream->data+*offset+9),
				stream->data[*offset+11],stream->data[*offset+12], tmp3);
		if(tmp3) free(tmp3);
		len = 0;
		skip = stream->data[*offset+13]*32 + 16;
		g_warning("Formula: %s. Knots: %d. Skip: %x", formula, stream->data[*offset+13], skip);
		break;		
	case 0x90: /* end of 'val_if_false' option of IF() */
		len = 0;
		skip = 1;
		*argnum = 3;
		*priority = 7;
		formula = malloc(3);
		sprintf(formula, "IF");
		break;
	case 0x91: /* AND() */
		len = 0;
		skip = 5;
		*argnum = (GSF_LE_GET_GUINT32(stream->data+*offset+1)+1)/2;
		*priority = 7;
		formula = malloc(4);
		sprintf(formula, "AND");
		break;
	case 0x92: /* OR() */
		len = 0;
		skip = 5;
		*argnum = (GSF_LE_GET_GUINT32(stream->data+*offset+1)+1)/2;
		*priority = 7;
		formula = malloc(3);
		sprintf(formula, "OR");
		break;
	case 0x93: /* end of 'val_if_true' option of IF() */
		len = 0;
		skip = 1;	
		break;
	case 0xa0: /* separator for AND() and IF() args */
	case 0xa1: /* separator for OR() args */
	case 0xa2: /* separator for IF() args */
	case 0xa3: /* separator for IF() args */
		len = 0;
		skip = 5;	
		break;
	case 0x13: /* negative sign */
		len = 0;
		skip = 1;
		formula = NULL; /* will use argnum to attach '-' */
		*priority = 5;
		break;
	case 0xe4: /* braces */
		len = 0 ;
		skip = 1;
		*priority = 6;
		break;	
	default:
		if(0x19 < stream->data[*offset] && 0x60 > stream->data[*offset]){
			formula = malloc(17);
			sprintf(formula, "%g", GSF_LE_GET_DOUBLE(stream->data + *offset + 1));
			len = 0;
			skip = 9;
		}
		
		if(0x2 < stream->data[*offset] && 0xF > stream->data[*offset]){
			len = 0;
			skip = 1;
			*argnum = 2;
			*priority = 1; /* no reason to use 2, 3 and 4? */
			switch(stream->data[*offset]){
			case 0x3: /* + */
				formula = g_strdup_printf("+");
				break;
			case 0x4: /* - */
				formula = g_strdup_printf("-");
				break;
			case 0x5: /* * */
				formula = g_strdup_printf("*");
				break;
			case 0x6: /* / */
				formula = g_strdup_printf("/");
				break;
			case 0x7: /* ^ */
				formula = g_strdup_printf("^");
				break;
			case 0x8: /* & */
				formula = g_strdup_printf("&");
				break;
			case 0x9: /* <  */
				formula = g_strdup_printf("<");
				break;
			case 0xa: /* <= */
				formula = g_strdup_printf("<=");
				break;
			case 0xb: /* =  */
				formula = g_strdup_printf("=");
				break;
			case 0xc: /* >= */					
				formula = g_strdup_printf(">=");
				break;
			case 0xd: /* >  */
				formula = g_strdup_printf(">");
				break;
			case 0xe: /* != */
				formula = g_strdup_printf("!=");
				break;
			default:
			g_warning("Something really wrong happens in vsd_get_slice");
			}					
		}
			
	} /* outer switch */
	g_warning("Formula: %s", formula);
	*offset = *offset + skip;
	
	return formula;
}

VsdBlock* 
vsd_parse_blocks(GByteArray *stream, int *offset, int version)
{
	guint32		len = 0, i = 0, j = 0, argnum=0, tokens=0, check=0, hex = 0;
	guint8		priority = 0, alarm = 0;
	VsdBlock		*bl;
	char			*tmp=NULL, *tmp2=NULL;
	char 			**stack[256];

	bl = g_malloc(sizeof(VsdBlock));
		
	len = GSF_LE_GET_GUINT32(stream->data + *offset); /* length of block */
	
	if(2 != (stream->data[*offset + 4])){ /* only type 2 blocks are supported at the moment*/

#ifdef DEBUG_BLOCK
		g_warning("Block type: %x. Len: %x", stream->data[*offset + 4], len);
#endif /* DEBUG_BLOCK */
		
		*offset= *offset + len;
		return NULL;
	}
	
	bl->idx = stream->data[*offset + 5];	

#ifdef DEBUG_BLOCK
	g_warning("Block T/L/I: %x %x %x", stream->data[*offset + 4], len, bl->idx);
#endif /* DEBUG_BLOCK */	

	check = i;
	i = *offset + 6;
	while(i < (*offset + len) ){
        
		tmp = vsd_get_slice(stream, &i, &argnum, &priority, version);
		switch(priority){
			case 0: /* 'tmp' contains 'literal' to put into stack */
				if(NULL == tmp){
					tokens--;
					g_print("Place of problem?\n");
					hex = 0;
					for(j = 0; j < len; j++){
						g_print("%02x ", stream->data[check+j]);
						hex++;
						if(16 == hex){
						 g_print("\n");
						 hex = 0;
						}
					}
					g_print("\n");
                    /*try to skip this block */
                    *offset+= stream->data[*offset];
					return 0;
				}				
				stack[tokens] = g_malloc(4);
				*stack[tokens] = g_malloc(strlen(tmp)+1);
				sprintf(*stack[tokens],"%s",tmp);
				free(tmp);
				break;
			case 1: /* comparisons and operations */
				stack[tokens] = g_malloc(4);
				*stack[tokens] = g_strjoin(tmp,*stack[tokens-2], *stack[tokens - 1], NULL);
				free(*stack[tokens-1]);
				/* free(stack[tokens-1]);*/
				free(*stack[tokens-2]);				
				free(tmp);
				stack[tokens-2] = stack[tokens];
				tokens-= 2;
				break;  
			case 5: /* negative sign */
				tmp2 = g_strdup_printf("-%s", *stack[tokens - 1]);
				stack[tokens - 1] = &tmp2;
				free(tmp);
				tokens-= 1;
				break;  
			case 6: /* braces */
				tmp = g_strdup_printf("(%s)", *stack[tokens - 1]);
				free(*stack[tokens - 1]);
				*stack[tokens-1] = g_strdup_printf("%s", tmp);
				free(tmp);
				tokens-= 1;
				break;
			case 7: /* function */
				stack[tokens] = g_malloc(4);
g_warning("Tokens: %d. tmp: %s. Argnum: %d.", tokens, tmp, argnum);
				if(!argnum){
g_warning("2 Tokens: %d. tmp: %s. Argnum: %d.", tokens, tmp, argnum);				
					*stack[tokens] = g_malloc(strlen(tmp)+5);
					sprintf(*stack[tokens], "%s()", tmp);
					free(tmp);
					break;
				}
				if(argnum > tokens){
					g_warning("MORE ARGS THAN TOKENS!!!");
					alarm = 1;					
					sprintf(*stack[tokens], "%s()", tmp);
					free(tmp);
					break;
				}
				*stack[tokens] = g_malloc(strlen(tmp)+2+strlen(*stack[tokens-argnum]));
				sprintf(*stack[tokens], "%s(%s", tmp, *stack[tokens-argnum]);
				free(tmp);
				j = 1;
				while (j < argnum){
					tmp = g_strjoin(";",*stack[tokens], *stack[tokens - argnum + j], NULL);
					free(*stack[tokens]);
					free(*stack[tokens - argnum + j]);
					*stack[tokens] = g_malloc(strlen(tmp)+1);
					sprintf(*stack[tokens],"%s",tmp);
					free(tmp);
					j++;
				}
				tmp = g_malloc(strlen(*stack[tokens])+2);
				sprintf(tmp, "%s)", *stack[tokens]);
				free(*stack[tokens]);
				tokens = tokens - argnum;
				*stack[tokens] = g_malloc(strlen(tmp)+1);
				sprintf(*stack[tokens],"%s",tmp);
				free(tmp);
				break;		  
			case 8: /* end */
				tokens--;
				break;
			default:
				g_warning("Something really wrong happens in vsd_parse_blocks");
		}
		tokens++;
		if(alarm) break;
		check = i;
	}
	*offset = *offset + len; 	
 	if(stack[0]){
		bl->formula = *stack[0];
		return bl;	
	}
		 
	return NULL;
}
