/* sswf.c++ -- written by Alexis WILKE for Made to Order Software Corp. (c) 2002-2009 */

/*

Copyright (c) 2002-2009 Made to Order Software Corp.

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial
portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/

#define	SSWF_NEED_ASSERT
#include	"sswf.h"

extern "C" {
#include	"sswf_grammar.h"
};


#include	"sswf/libsswf.h"	 /* get the version and the sswf_version() function */



extern	YYLTYPE		yylloc;

#ifdef _MSVC
YYLTYPE			yylloc;
#endif






/***************************************************** DOC
 * NAME
 *	sswf - create an SWF file from a script
 *
 * SYNOPSIS
 *	sswf [-<opt>] file ...
 *
 * DESCRIPTION
 *	The sswf tool will be used to read a script and transform
 *	it in a list of tags in an SWF file.
 *
 *	The script will be written as a set of unordered objects
 *	and a set of display lists. The display lists can be
 *	referenced in the playback sequence or sprites.
 *
 *	Because the SWF format is mainly a sequence of events which
 *	loop, it is important to include a very specific order for
 *	object definitions and the sequence of events. However, in
 *	most cases, the computer can take care of that sorting.
 *
 *	This tool only uses names for all the objects, lists,
 *	sequences, etc. making it easy to reference anything
 *	anywhere.
 *
 * OPTIONS
 *
 * SEE ALSO
 *	sswf(1) manual pages
 *	libsswf.a and libsswf.so
 *	ScriptSWF.html
 */


struct global_t	global;				/* stuff we want to transmit to sub-functions outside this file */
int		errcnt;				/* number of errors found */
int		warning_off;			/* if true, don't emit warnings */
struct node_t *	top_object;			/* the root object from which we can find all the other objects */
const char	no_file[] = "no file";		/* to set the lex_filename when no file is being parsed */
const char *	lex_filename = no_file;		/* the filename currently being read by lex */
int		show_input_filenames;		/* when true, print out the name of all the input files (useful when it is be searched in the -I directories) */
int		show_input_search;		/* if true, show as we search for a file */
int		show_output;			/* if true, print out the resulting data buffer */
int		show_bounds;			/* if true, insert an additional shape + line to draw a rectangle showing the shape boundaries */
int		show_origin;			/* if true, draw two lines to show the origin of the symbol (rotation center if you wish) */
#if DEBUG
int		debug_memory;			/* if true, print out the unfreed memory before to exit */
#endif





#if DEBUG
#define	TYPE_NAME(type)		{ type, #type },
#else
#define	TYPE_NAME(type)		{ #type },
#endif
const struct node_type_names_t	node_type_names[NODE_TYPE_max] =
{
	TYPE_NAME(NODE_TYPE_UNKNOWN)
	TYPE_NAME(NODE_SUBTYPE_UNKNOWN)
	TYPE_NAME(NODE_TYPE_IDENTIFIER)
	TYPE_NAME(NODE_TYPE_INTEGER)
	TYPE_NAME(NODE_TYPE_FLOAT)
	TYPE_NAME(NODE_TYPE_RANGE_INTEGER)
	TYPE_NAME(NODE_TYPE_RANGE_FLOAT)
	TYPE_NAME(NODE_TYPE_STRING)
	TYPE_NAME(NODE_TYPE_DATA)
	TYPE_NAME(NODE_SUBTYPE_DEFINITION)
	TYPE_NAME(NODE_TYPE_DEFINITION)
	TYPE_NAME(NODE_TYPE_OPERATOR)
	TYPE_NAME(NODE_SUBTYPE_ADD)
	TYPE_NAME(NODE_SUBTYPE_AND)
	TYPE_NAME(NODE_SUBTYPE_DIVIDE)
	TYPE_NAME(NODE_SUBTYPE_EQUAL)
	TYPE_NAME(NODE_SUBTYPE_EXPAND)
	TYPE_NAME(NODE_SUBTYPE_FIELD)
	TYPE_NAME(NODE_SUBTYPE_FUNCTION)
	TYPE_NAME(NODE_SUBTYPE_GREATER)
	TYPE_NAME(NODE_SUBTYPE_GREATER_EQUAL)
	TYPE_NAME(NODE_SUBTYPE_GROUP)
	TYPE_NAME(NODE_SUBTYPE_IDENTITY)
	TYPE_NAME(NODE_SUBTYPE_LESS)
	TYPE_NAME(NODE_SUBTYPE_LESS_EQUAL)
	TYPE_NAME(NODE_SUBTYPE_LET)
	TYPE_NAME(NODE_SUBTYPE_LOGICAL_AND)
	TYPE_NAME(NODE_SUBTYPE_LOGICAL_NOT)
	TYPE_NAME(NODE_SUBTYPE_LOGICAL_OR)
	TYPE_NAME(NODE_SUBTYPE_LOGICAL_XOR)
	TYPE_NAME(NODE_SUBTYPE_MAXIMUM)
	TYPE_NAME(NODE_SUBTYPE_MINIMUM)
	TYPE_NAME(NODE_SUBTYPE_MODULO)
	TYPE_NAME(NODE_SUBTYPE_MULTIPLY)
	TYPE_NAME(NODE_SUBTYPE_NEGATE)
	TYPE_NAME(NODE_SUBTYPE_NOT)
	TYPE_NAME(NODE_SUBTYPE_NOT_EQUAL)
	TYPE_NAME(NODE_SUBTYPE_OR)
	TYPE_NAME(NODE_SUBTYPE_POWER)
	TYPE_NAME(NODE_SUBTYPE_RANGE)
	TYPE_NAME(NODE_SUBTYPE_ROTATE_LEFT)
	TYPE_NAME(NODE_SUBTYPE_ROTATE_RIGHT)
	TYPE_NAME(NODE_SUBTYPE_SELECT)
	TYPE_NAME(NODE_SUBTYPE_SHIFT_LEFT)
	TYPE_NAME(NODE_SUBTYPE_SHIFT_RIGHT)
	TYPE_NAME(NODE_SUBTYPE_SHIFT_RIGHT_UNSIGNED)
	TYPE_NAME(NODE_SUBTYPE_SUBSCRIPT)
	TYPE_NAME(NODE_SUBTYPE_SUBTRACT)
	TYPE_NAME(NODE_SUBTYPE_XOR)
	TYPE_NAME(NODE_TYPE_UNIT)
	TYPE_NAME(NODE_SUBTYPE_BC)
	TYPE_NAME(NODE_SUBTYPE_CM)
	TYPE_NAME(NODE_SUBTYPE_DEG)
	TYPE_NAME(NODE_SUBTYPE_FC)
	TYPE_NAME(NODE_SUBTYPE_FPF)
	TYPE_NAME(NODE_SUBTYPE_FPS)
	TYPE_NAME(NODE_SUBTYPE_FRM)
	TYPE_NAME(NODE_SUBTYPE_GRAD)
	TYPE_NAME(NODE_SUBTYPE_IN)
	TYPE_NAME(NODE_SUBTYPE_MIN)
	TYPE_NAME(NODE_SUBTYPE_PR)
	TYPE_NAME(NODE_SUBTYPE_PX)
	TYPE_NAME(NODE_SUBTYPE_RAD)
	TYPE_NAME(NODE_SUBTYPE_RT)
	TYPE_NAME(NODE_SUBTYPE_SEC)
	TYPE_NAME(NODE_SUBTYPE_TW)
	TYPE_NAME(NODE_SUBTYPE_OTHER)
	TYPE_NAME(NODE_TYPE_OBJECT)
	TYPE_NAME(NODE_SUBTYPE_ACTION)
	TYPE_NAME(NODE_SUBTYPE_ACTION_SCRIPT)
	TYPE_NAME(NODE_SUBTYPE_BLOCK)
	TYPE_NAME(NODE_SUBTYPE_BUTTON)
	TYPE_NAME(NODE_SUBTYPE_CATCH)
	TYPE_NAME(NODE_SUBTYPE_COLOR)
	TYPE_NAME(NODE_SUBTYPE_COLOR_TRANSFORM)
	TYPE_NAME(NODE_SUBTYPE_DO_ACTION)
	TYPE_NAME(NODE_SUBTYPE_EDGES)
	TYPE_NAME(NODE_SUBTYPE_EDIT_TEXT)
	TYPE_NAME(NODE_SUBTYPE_END)
	TYPE_NAME(NODE_SUBTYPE_ENVELOPE)
	TYPE_NAME(NODE_SUBTYPE_EXPORT)
	TYPE_NAME(NODE_SUBTYPE_FILL_STYLE)
	TYPE_NAME(NODE_SUBTYPE_FINALLY)
	TYPE_NAME(NODE_SUBTYPE_FONT)
	TYPE_NAME(NODE_SUBTYPE_FOR)
	TYPE_NAME(NODE_SUBTYPE_FRAME_LABEL)
	TYPE_NAME(NODE_SUBTYPE_GLYPH)
	TYPE_NAME(NODE_SUBTYPE_GRADIENT)
	TYPE_NAME(NODE_SUBTYPE_IF)
	TYPE_NAME(NODE_SUBTYPE_IMAGE)
	TYPE_NAME(NODE_SUBTYPE_IMPORT)
	TYPE_NAME(NODE_SUBTYPE_LABEL)
	TYPE_NAME(NODE_SUBTYPE_LINE_STYLE)
	TYPE_NAME(NODE_SUBTYPE_LIST)
	TYPE_NAME(NODE_SUBTYPE_MATRIX)
	TYPE_NAME(NODE_SUBTYPE_METADATA)
	TYPE_NAME(NODE_SUBTYPE_ON_EVENT)
	TYPE_NAME(NODE_SUBTYPE_PLACE_OBJECT)
	TYPE_NAME(NODE_SUBTYPE_POINTS)
	TYPE_NAME(NODE_SUBTYPE_RECT)
	TYPE_NAME(NODE_SUBTYPE_REFERENCE)
	TYPE_NAME(NODE_SUBTYPE_REMOVE)
	TYPE_NAME(NODE_SUBTYPE_REMOVE_ALL)
	TYPE_NAME(NODE_SUBTYPE_REPLACE_OBJECT)
	TYPE_NAME(NODE_SUBTYPE_SCENE_FRAME_DATA)
	TYPE_NAME(NODE_SUBTYPE_SCRIPT_LIMITS)
	TYPE_NAME(NODE_SUBTYPE_SEQUENCE)
	TYPE_NAME(NODE_SUBTYPE_SET_BACKGROUND_COLOR)
	TYPE_NAME(NODE_SUBTYPE_SET_TAB_INDEX)
	TYPE_NAME(NODE_SUBTYPE_SHAPE)
	TYPE_NAME(NODE_SUBTYPE_SHOW_FRAME)
	TYPE_NAME(NODE_SUBTYPE_SOUND)
	TYPE_NAME(NODE_SUBTYPE_SOUND_INFO)
	TYPE_NAME(NODE_SUBTYPE_SPRITE)
	TYPE_NAME(NODE_SUBTYPE_STATE)
	TYPE_NAME(NODE_SUBTYPE_TEXT)
	TYPE_NAME(NODE_SUBTYPE_TEXT_SETUP)
	TYPE_NAME(NODE_SUBTYPE_TRY)
	TYPE_NAME(NODE_SUBTYPE_VARIABLE)
	TYPE_NAME(NODE_SUBTYPE_WITH)
};



#if DEBUG
extern "C" {
void assert(int cond, const char *format, ...)
{
	va_list		ap;

	if(cond) return;	/* no prob. */

	va_start(ap, format);
	vfprintf(stderr, format, ap);
	va_end(ap);
	fprintf(stderr, ".\n");

	abort();
}
};
//#else
//#ifdef _MSVC
//extern "C" {
//void assert(int cond, const char *format, ...)
//{
//}
//};
//#endif
#endif






#if DEBUG
void check_type_names(void)
{
	int		idx;

	assert(sizeof(node_type_names) / sizeof(struct node_type_names_t) == NODE_TYPE_max, "the size of the node_type_names table doesn't match the NODE_TYPE_max enumeration counter");
	for(idx = 0; idx < NODE_TYPE_max; idx++) {
		assert(node_type_names[idx].type == idx, "the type at offset %d doesn't correspond to the expected type (%d)", idx, node_type_names[idx].type);
	}
}
#endif




void init_top_object(void)
{
	lex_filename = "*root object*";
	top_object = node_alloc(NODE_TYPE_OBJECT, NODE_SUBTYPE_BLOCK, 0);
	top_object->left = node_alloc(NODE_TYPE_STRING, NODE_SUBTYPE_UNKNOWN, 0);
	top_object->left->string = sswf_strdup("root");
	top_object->left->parent = top_object;
}




#if DEBUG
void clean_top_objects(void)
{
	//node_print(top_object);		// print out the entire tree of nodes
	node_free(top_object);		// remove all the user objects
	top_object = NULL;
	node_done_object();		// remove the all_objects too
}
#endif




void init_internal_variables(void)
{
	set_var("pi", TO_STR(M_PI));
	set_var("e", TO_STR(M_E));
}



int parse(void)
{
	struct node_t	*result;
	int		ec;

	yylloc.first_line = 1;		/* start from the beginning each time */
	ec = yyparse(&result);
	if(ec != 0) {
		return 1;
	}

	/* ensure that the top-most nodes have a proper parent field */
	node_link_parent(top_object, result);

	if(top_object->left == NULL) {
		top_object->left = result;
	}
	else {
		node_link_tail(top_object->left, result);
	}

	return 0;
}





static void check_flag(struct node_t *seq, const char *name, unsigned long flag, int def_value)
{
	struct node_t			result, *var;
	struct node_exec_status_t	status;
	int				ec, v;

	memset(&status, 0, sizeof(status));
	status.unit = NODE_UNIT_UNKNOWN;
	status.obj = seq;

	var = node_search_list(seq, name);
	if(var == NULL) {
		return;
	}

	node_init(&result, NODE_TYPE_UNKNOWN, NODE_TYPE_UNKNOWN);
	ec = node_eval(var->right, &result, &status);
	if(ec == 0) {
		v = def_value;
		switch(result.type) {
		case NODE_TYPE_INTEGER:
			v = result.integer != 0;
			break;

		case NODE_TYPE_FLOAT:
			v = result.floating_point != 0;
			break;

		case NODE_TYPE_STRING:
			v = !(result.string == NULL || result.string[0] == '\0');
			break;

		default:
fprintf(stderr, "ERROR: the protect variable needs to be set to a boolean value.");
			errcnt++;
			break;

		}
		if(v) {
			global.f_flags |= flag;
		}
		else {
			global.f_flags &= ~flag;
		}
	}

	node_clean(&result, 0);
}


int evaluate(void)
{
	struct node_t			result, fr_result, mv_result, *seq, **l;
	struct node_t			*frame_rate_var, *minimum_version, *maximum_version, *version;
	struct node_exec_status_t	status;
	int				ec, cnt;

	if(global.f_show_steps > 0) {
		printf("+ Evaluating the scripts.\n");
	}

	/* put objects in their respective parent object list */
	ec = node_setup_objects();
	if(ec != 0) {
		fprintf(stderr, "ERROR: error #%d occured while setting up the objects.\n", ec);
		errcnt++;
		return ec;
	}

	/* search for the main sequence */
	seq = node_search_list(top_object, "main");
	if(seq == NULL) {
		/* search the first sequence object instead */
		l = top_object->list;
		cnt = top_object->cnt;
		while(cnt > 0) {
			seq = *l++;
			if(seq->type == NODE_TYPE_OBJECT && seq->sub_type == NODE_SUBTYPE_SEQUENCE) {
				break;
			}
			cnt--;
		}
		if(cnt == 0) {
			/* didn't find any sequence! */
			fprintf(stderr, "ERROR: can't find a sequence to evaluate as an SWF binary file.\n");
			errcnt++;
			return 1;
		}
		if(!warning_off && seq->left != NULL) {
fprintf(stderr, "WARNING: using sequence \"%s\" as the main sequence.\n", seq->left->string);
		}
	}
	else if(seq->type != NODE_TYPE_OBJECT || seq->sub_type != NODE_SUBTYPE_SEQUENCE) {
		fprintf(stderr, "ERROR: the object named \"main\" is not a sequence. (%d/%d)\n", seq->type, seq->sub_type);
		errcnt++;
		return 1;
	}

	/* ready to evaluate, let's go! */
	memset(&status, 0, sizeof(status));
	status.unit = NODE_UNIT_UNKNOWN;
	status.obj = seq;
#if 0
printf("%%%%%% node_execute()\n");
sswf_info_serial();
#endif
	node_init(&result, NODE_TYPE_UNKNOWN, NODE_TYPE_UNKNOWN);
	ec = node_execute(seq, &result, &status);
	if(ec != 0) {
		fprintf(stderr, "ERROR: error #%d occured while executing the nodes; the process will stop.\n", abs(ec));
		errcnt++;
		node_clean(&result, 0);
		return ec;
	}
	if(result.type != NODE_TYPE_DATA) {
		fprintf(stderr, "ERROR: unexpected result for a sequence object (Type: %d).\n", result.type);
		errcnt++;
		node_clean(&result, 0);
		return 1;
	}

	if(global.f_show_steps > 0) {
		printf("+ The evaluation is finished.\n");
	}

	/* everything worked now we can nearly save the result in a .swf file! */
#if 0
printf("%%%%%% define frame rate, protect, compress...\n");
sswf_info_serial();
#endif
	global.f_frame_rate = 30.0;
	frame_rate_var = node_search_list(seq, "frame_rate");
	if(frame_rate_var == NULL) {
		frame_rate_var = node_search_list(seq, "framerate");
	}
	if(frame_rate_var != NULL) {
		status.unit = NODE_UNIT_SPEED;		/* by default we want FPS here */
		node_init(&fr_result, NODE_TYPE_UNKNOWN, NODE_TYPE_UNKNOWN);
		ec = node_eval(frame_rate_var->right, &fr_result, &status);
		if(ec != 0) {
			node_clean(&result, 0);
			return ec;
		}
		if(fr_result.type == NODE_TYPE_INTEGER) {
			global.f_frame_rate = (double) fr_result.integer;
		}
		else if(fr_result.type == NODE_TYPE_FLOAT) {
			global.f_frame_rate = fr_result.floating_point;
		}
		else if(fr_result.type == NODE_TYPE_STRING) {
			if(strcasecmp(fr_result.string, "PAL") == 0) {
				global.f_frame_rate = 25.0;
			}
			else if(strcasecmp(fr_result.string, "NTSC") == 0) {
				global.f_frame_rate = 30.0;
			}
			else {
fprintf(stderr, "ERROR: unknown frame rate (%s).", fr_result.string);
				errcnt++;
			}
		}
		else {
fprintf(stderr, "ERROR: invalid frame rate value; an integer, a floating point or a string was expected.");
			errcnt++;
		}
		node_clean(&fr_result, 0);
	}

	global.f_version = 0;
	version = node_search_list(seq, "version");
	if(version != NULL) {
		status.unit = NODE_UNIT_SIZE;		/* there isn't really any unit for this one */
		node_init(&mv_result, NODE_TYPE_UNKNOWN, NODE_TYPE_UNKNOWN);
		ec = node_eval(version->right, &mv_result, &status);
		if(ec != 0) {
			node_clean(&result, 0);
			return ec;
		}
		if(mv_result.type == NODE_TYPE_INTEGER) {
			global.f_version = mv_result.integer;
		}
		else if(mv_result.type == NODE_TYPE_FLOAT) {
			global.f_version = (int) rint(mv_result.floating_point);
		}
		else {
fprintf(stderr, "ERROR: invalid version value; an integer or a a floating point was expected.");
			errcnt++;
		}
		node_clean(&mv_result, 0);
		if(global.f_version < 0) {
fprintf(stderr, "ERROR: invalid version; it has to be 1 or more.\n");
			errcnt++;
		}
	}


	global.f_minimum_version = 0;
	minimum_version = node_search_list(seq, "minimum_version");
	if(minimum_version == NULL) {
		minimum_version = node_search_list(seq, "minimumversion");
	}
	if(minimum_version != NULL) {
		status.unit = NODE_UNIT_SIZE;		/* there isn't really any unit for this one */
		node_init(&mv_result, NODE_TYPE_UNKNOWN, NODE_TYPE_UNKNOWN);
		ec = node_eval(minimum_version->right, &mv_result, &status);
		if(ec != 0) {
			node_clean(&result, 0);
			return ec;
		}
		if(mv_result.type == NODE_TYPE_INTEGER) {
			global.f_minimum_version = mv_result.integer;
		}
		else if(mv_result.type == NODE_TYPE_FLOAT) {
			global.f_minimum_version = (int) rint(mv_result.floating_point);
		}
		else {
fprintf(stderr, "ERROR: invalid minimum version value; an integer or a a floating point was expected.");
			errcnt++;
		}
		node_clean(&mv_result, 0);
		if(global.f_minimum_version < 0) {
fprintf(stderr, "ERROR: invalid minimum version; it has to be 1 or more.\n");
			errcnt++;
		}
	}

	global.f_maximum_version = 0;
	maximum_version = node_search_list(seq, "maximum_version");
	if(maximum_version == NULL) {
		maximum_version = node_search_list(seq, "maximumversion");
	}
	if(maximum_version != NULL) {
		status.unit = NODE_UNIT_SIZE;		/* there isn't really any unit for this one */
		node_init(&mv_result, NODE_TYPE_UNKNOWN, NODE_TYPE_UNKNOWN);
		ec = node_eval(maximum_version->right, &mv_result, &status);
		if(ec != 0) {
			node_clean(&result, 0);
			return ec;
		}
		if(mv_result.type == NODE_TYPE_INTEGER) {
			global.f_maximum_version = mv_result.integer;
		}
		else if(mv_result.type == NODE_TYPE_FLOAT) {
			global.f_maximum_version = (int) rint(mv_result.floating_point);
		}
		else {
fprintf(stderr, "ERROR: invalid maximum version value; an integer or a a floating point was expected.");
			errcnt++;
		}
		node_clean(&mv_result, 0);
		if(global.f_maximum_version < 0) {
fprintf(stderr, "ERROR: invalid maximum version; it has to be 1 or more.\n");
			errcnt++;
		}
	}


	if(global.f_minimum_version != 0 && global.f_maximum_version != 0
	&& global.f_minimum_version > global.f_maximum_version) {
fprintf(stderr, "ERROR: the minimum version cannot be larger than the maximum version.\n");
		errcnt++;
	}
	if(global.f_version != 0) {
		if((global.f_minimum_version != 0 && global.f_version < global.f_minimum_version)
		     || (global.f_maximum_version != 0 && global.f_version > global.f_maximum_version)) {
fprintf(stderr, "ERROR: version must be defined between the minimum and maximum versions.\n");
			errcnt++;
		}
	}



	check_flag(seq, "protect",  GLOBAL_FLAG_PROTECT,  true);

	/*
	 * Try with and without the '_'; we expect the users to have
	 * only one such flag anyway.
	 * For security reasons, this flag is assumed to be false
	 * by default. It anyway has no effect on older movies
	 * (version 7 or less).
	 */
	check_flag(seq, "usenetwork",  GLOBAL_FLAG_USE_NETWORK,  false);
	check_flag(seq, "use_network",  GLOBAL_FLAG_USE_NETWORK,  false);

	/*
	 * The default for the compression flag should be true if the
	 * movie is to be saved in a V6 Flash movie binary file
	 */
	check_flag(seq, "compress", GLOBAL_FLAG_COMPRESS, false);

	if(show_output) {
		node_print_data(result.data);
	}

	if(global.f_show_steps) {
		printf("+ Save the resulting SWF file.\n");
	}

	/* we don't want to save if any error occured */
	if(errcnt > 0) {
		ec = 1;
	}
	else {
#if 0
printf("%%%%%% save_data()\n");
sswf_info_serial();
#endif
		ec = save_data(result.data, &global);
	}

	node_clean(&result, 0);

#if 0
printf("%%%%%% done evaluation...\n");
sswf_info_serial();
#endif

	return ec;
}



void set_var(const char *name, const char *value)
{
	const char	*str;
	char		*end;
	struct node_t	*n, *var;
	double		v;

/* skip leading spaces so we know if the value is only composed of spaces */
	str = value;
	while(isspace(*value)) {
		value++;
	}

	lex_filename = "*command line variable*";

/* create a variable */
	var = node_alloc(NODE_TYPE_OBJECT, NODE_SUBTYPE_VARIABLE, 0);

/* set the variable name */
	n = node_alloc(NODE_TYPE_IDENTIFIER, NODE_SUBTYPE_UNKNOWN, 0);
	n->string = sswf_strdup(name);
	node_link_left(var, n);

/* create a node for the variable value */
	n = node_alloc(NODE_TYPE_INTEGER, NODE_SUBTYPE_UNKNOWN, 0);
	if(*value == '\0') {
		/* by default we assume 1 */
		n->integer = 1;
	}
	else {
		v = strtod(value, &end);
		if(v == 0.0 && value == end) {
			end = 0;
		}
		else {
			while(isspace(*end)) {
				end++;
			}
			if(*end != '\0') {
				end = 0;
			}
		}
		if(end == 0) {	// failed
			n->type = NODE_TYPE_STRING;
			n->string = sswf_strdup(str);
		}
		else {
			if(v == floor(v)) {
				n->integer = (long) v;
			}
			else {
				n->type = NODE_TYPE_FLOAT;
				n->floating_point = v;
			}
		}
	}
	node_link_right(var, n);
	node_link_object(var);

	/* make the variable public */
	node_link_parent(top_object, var);
	if(top_object->left == NULL) {
		top_object->left = var;
	}
	else {
		node_link_tail(top_object->left, var);
	}
}





void usage(void)
{
fprintf(stderr, "sswf v" TO_STR(SSWF_VERSION) " -- Copyright (c) 2002-2009 Made to Order Software Corp.\n");
fprintf(stderr, "\n");
fprintf(stderr, "Usage: sswf [-opt] [input-file ...]\n");
fprintf(stderr, "  where the available options are:\n");
fprintf(stderr, "\n");
fprintf(stderr, "     -c or --compress\n");
fprintf(stderr, "                 creates a compressed movie (v6 only)\n");
fprintf(stderr, "\n");
fprintf(stderr, "     -D<varname>=<value>\n");
fprintf(stderr, "                 defines a named variable\n");
fprintf(stderr, "\n");
fprintf(stderr, "     -e <name> or --encoding=<name>\n");
fprintf(stderr, "                 define the character encoding to use in the output\n");
fprintf(stderr, "\n");
fprintf(stderr, "     -h or --help\n");
fprintf(stderr, "                 print out this help screen\n");
fprintf(stderr, "\n");
fprintf(stderr, "     -I <path>\n");
fprintf(stderr, "                 specify one or more paths to search for input files\n");
fprintf(stderr, "\n");
fprintf(stderr, "     --lib-version\n");
fprintf(stderr, "                 output the version of the library and exit\n");
fprintf(stderr, "\n");
fprintf(stderr, "     -o <filename> or --output=<filename>\n");
fprintf(stderr, "                 defines the name of the output filename (default: \"a.swf\")\n");
fprintf(stderr, "\n");
fprintf(stderr, "     --no-network\n");
fprintf(stderr, "                 forbid local movies from using the network (since version 8)\n");
fprintf(stderr, "\n");
fprintf(stderr, "     --protect\n");
fprintf(stderr, "                 creates a protected movie (this is the default)\n");
fprintf(stderr, "\n");
fprintf(stderr, "     --show-bounds\n");
fprintf(stderr, "                 draw each shape boundaries with a red rectangle\n");
fprintf(stderr, "\n");
fprintf(stderr, "     --show-input-filenames\n");
fprintf(stderr, "                 show files which are read as input\n");
fprintf(stderr, "\n");
fprintf(stderr, "     --show-input-search\n");
fprintf(stderr, "                 show where each file is being searched\n");
fprintf(stderr, "\n");
fprintf(stderr, "     --show-origin\n");
fprintf(stderr, "                 draw a veritcal and horizontal set of lines to show the origin of all shapes\n");
fprintf(stderr, "\n");
fprintf(stderr, "     --show-output\n");
fprintf(stderr, "                 print the parsed data before to generate the output file\n");
fprintf(stderr, "\n");
fprintf(stderr, "     --show-step[=<level>]\n");
fprintf(stderr, "                 print out the current working step\n");
fprintf(stderr, "\n");
fprintf(stderr, "     --uncompress\n");
fprintf(stderr, "                 creates an uncompressed movie (default)\n");
fprintf(stderr, "\n");
fprintf(stderr, "     --unprotect\n");
fprintf(stderr, "                 creates an unprotected movie\n");
fprintf(stderr, "\n");
fprintf(stderr, "     -n or --use-network\n");
fprintf(stderr, "                 allow local movies to use network connections\n");
fprintf(stderr, "\n");
fprintf(stderr, "     --version\n");
fprintf(stderr, "                 output the version and exit\n");
#if DEBUG
fprintf(stderr, "\n");
fprintf(stderr, "     --debug-memory[=<level>]\n");
fprintf(stderr, "                 test memory extensively to ensure proper behavior\n");
#endif
}




int commandline(int argc, char *argv[])
{
	int		i, j, len, files_only, parsed;
	char		*file, *name, *value, *end, c;

	parsed = 0;
	files_only = 0;
	i = 1;
	while(i < argc) {
		// if we have an option, check it
		// note that "-" is not an option but a filename (i.e. stdin)
		if(argv[i][0] == '-' && argv[i][1] != '\0' && !files_only) {
			file = NULL;
			if(argv[i][1] == '-') {
				if(argv[i][2] == '\0') {
					files_only = 1;
				}
				else if(strcmp(argv[i] + 2, "compress") == 0) {
					global.f_flags |= GLOBAL_FLAG_COMPRESS;
				}
#if DEBUG
				else if(strncmp(argv[i] + 2, "debug-memory", 12) == 0) {
					j = 2 + 12;
					if(argv[i][j] == '=') {
						j++;
					}
					debug_memory = atol(argv[i] + j);
					if(debug_memory <= 0) {
						debug_memory = 1;
					}
				}
#endif
				else if(strncmp(argv[i] + 2, "encoding", 8) == 0) {
					if(argv[i][10] == '=') {
						global.f_output_encoding = argv[i] + 11;
					}
					else if(argv[i][10] != '\0') {
						fprintf(stderr, "ERROR: invalid flag '%s'\n", argv[i]);
						return -1;
					}
					else {
						i++;
						if(i >= argc) {
							fprintf(stderr, "ERROR: invalid use of the --encoding flag; a filename was expected.\n");
							return -1;
						}
						global.f_output_encoding = argv[i];
					}
				}
				else if(strcmp(argv[i] + 2, "help") == 0) {
					return -1;
				}
				else if(strcmp(argv[i] + 2, "lib-version") == 0) {
					printf("%s\n", sswf::sswf_version());
					exit(0);
				}
				else if(strcmp(argv[i] + 2, "no-network") == 0) {
					global.f_flags &= ~GLOBAL_FLAG_USE_NETWORK;
				}
				else if(strncmp(argv[i] + 2, "output", 6) == 0) {
					if(argv[i][8] == '=') {
						file = argv[i] + 9;
					}
					else if(argv[i][8] != '\0') {
						fprintf(stderr, "ERROR: invalid flag '%s'\n", argv[i]);
						return -1;
					}
					else {
						i++;
						if(i >= argc) {
							fprintf(stderr, "ERROR: invalid use of the --output flag; a filename was expected.\n");
							return -1;
						}
						file = argv[i];
					}
				}
				else if(strcmp(argv[i] + 2, "protect") == 0) {
					global.f_flags |= GLOBAL_FLAG_PROTECT;
				}
				else if(strcmp(argv[i] + 2, "show-input-filenames") == 0) {
					show_input_filenames = 1;
				}
				else if(strcmp(argv[i] + 2, "show-input-search") == 0) {
					show_input_search = 1;
				}
				else if(strcmp(argv[i] + 2, "show-output") == 0) {
					show_output = 1;
				}
				else if(strcmp(argv[i] + 2, "show-bounds") == 0) {
					show_bounds = 1;
				}
				else if(strcmp(argv[i] + 2, "show-origin") == 0) {
					show_origin = 1;
				}
				else if(strncmp(argv[i] + 2, "show-step", 9) == 0) {
					if(argv[i][11] == '=') {
						global.f_show_steps = atol(argv[i] + 12);
					}
					else if(argv[i][11] == '\0') {
						global.f_show_steps = 1;
					}
					else {
						fprintf(stderr, "ERROR: invalid flag '%s'\n", argv[i]);
						return -1;
					}
				}
				else if(strcmp(argv[i] + 2, "uncompress") == 0) {
					global.f_flags &= ~GLOBAL_FLAG_COMPRESS;
				}
				else if(strcmp(argv[i] + 2, "unprotect") == 0) {
					global.f_flags &= ~GLOBAL_FLAG_PROTECT;
				}
				else if(strcmp(argv[i] + 2, "use-network") == 0) {
					global.f_flags |= GLOBAL_FLAG_USE_NETWORK;
				}
				else if(strcmp(argv[i] + 2, "version") == 0) {
					printf(TO_STR(SSWF_VERSION) "\n");
					exit(0);
				}
				else {
					fprintf(stderr, "ERROR: flag '%s' not recognized.\n", argv[i]);
					return -1;
				}
			}
			else {
				len = strlen(argv[i]);
				j = 1;
				while(j < len) {
					switch(argv[i][j]) {
					case 'c':
						global.f_flags |= GLOBAL_FLAG_COMPRESS;
						break;

					case 'e':
						j++;
						if(argv[i][j] == '\0') {
							j = 0;
							i++;
							if(i >= argc) {
								fprintf(stderr, "ERROR: expected the name of an encoding.\n");
								return -1;
							}
							if(argv[i][0] == '\0') {
								fprintf(stderr, "ERROR: -e doesn't like empty encoding names.\n");
								return -1;
							}
						}
						global.f_output_encoding = argv[i] + j;
						j = len;
						break;

					case 'h':
						return -1;

					case 'n':
						global.f_flags |= GLOBAL_FLAG_USE_NETWORK;
						break;

					case 'o':
						j++;
						if(argv[i][j] == '\0') {
							j = 0;
							i++;
							if(i >= argc) {
								fprintf(stderr, "ERROR: invalid use of the -o flag; a filename was expected.\n");
								return -1;
							}
							if(argv[i][0] == '\0') {
								fprintf(stderr, "ERROR: -o doesn't like empty filenames.\n");
								return -1;
							}
						}
						global.f_output_filename = argv[i] + j;
						j = len;
						break;

					case 'D':
						j++;
						if(argv[i][j] == '\0') {
							j = 0;
							i++;
							if(i >= argc) {
								fprintf(stderr, "ERROR: invalid use of the -D flag; a variable was expected.\n");
								return -1;
							}
							if(argv[i][0] == '\0') {
								fprintf(stderr, "ERROR: -D doesn't like empty variable names.\n");
								return -1;
							}
						}

						value = name = argv[i] + j;
						while(*value != '\0' && *value != '=') {
							value++;
						}
						end = value;
						if(*value == '=') {
							value++;
						}
						c = *end;
						*end = '\0';
						set_var(name, value);
						*end = c;
						j = len;

						break;

					case 'I':
						j++;
						if(argv[i][j] == '\0') {
							i++;
							if(i >= argc) {
								// NOTE: most certainly useless...
								sswf_set_default_include(1);
								break;
							}
							else if(argv[i][0] == '-') {
								sswf_set_default_include(1);
								i--;
								break;
							}
							/* use that as a path */
							j = 0;
						}
						else if(argv[i][j] == '-' && argv[i][j + 1] == '\0') {
							sswf_set_default_include(1);
							j = len;
							break;
						}
						sswf_add_include(argv[i] + j);
						j = len;
						break;

					default:
						fprintf(stderr, "ERROR: flag '-%c' not recognized.\n", argv[i][j]);
						return -1;

					}
					j++;
				}
			}
		}
		else {
			file = argv[i];
		}
		if(file != NULL) {
			/* parse this file */
			parsed = 1;		/* mark that some files were parsed otherwise read stdin */
			if(sswf_open_script(file) == 0) {
				if(global.f_show_steps > 0) {
					printf("+ Parsing file \"%s\".\n", lex_filename);
				}
				j = parse();
				sswf_close_script();		/* we're done with this one! */
				if(j != 0) {
					return j;
				}
			}
		}
		i++;
	}

	if(!parsed) {
		if(sswf_open_script("-") == 0) {
			if(global.f_show_steps > 0) {
				printf("+ Parsing standard input.\n");
			}
			j = parse();
			sswf_close_script();		/* we're done with this one! */
			return j;
		}
		return 1;
	}

	return 0;
}


#include <time.h>
int main(int argc, char *argv[])
{
	int		ec;

	/* avoid core dumps because they tend to be hyper large! */
	/* I think this won't work under MS-Windows... */
#ifndef __MINGW32__
#ifndef _MSVC
{
	struct rlimit	l;
	l.rlim_cur = 0;
	l.rlim_max = 0;
	setrlimit(RLIMIT_CORE, &l);
}
#endif
#endif

	global.f_flags = GLOBAL_FLAG_PROTECT;
	global.f_output_filename = "a.swf";	/* name of the destination file */

#if DEBUG
	check_type_names();
#endif
	assert(sizeof(struct data_common_t) == SSWF_MEM_ALIGN(sizeof(struct data_common_t)), "the size of the data_common_t structure needs to be a multiple of sizeof(double)");

#define	REPEAT_TEST	0
#if REPEAT_TEST
	int repeat = 2;
	while(repeat > 0) {
	repeat--;
	printf("INFO: REPEAT TEST ON (%d)\n", repeat);
#endif
	init_top_object();
	init_internal_variables();

	ec = commandline(argc, argv);
	if(ec == -1) {
		usage();
		ec = 1;
	}
	else if(ec == 0) {
		/* now we can evaluate this sswf script! */
		ec = evaluate();
	}

#if DEBUG
	if(debug_memory) {
		clean_top_objects();
		printf("Any more buffers allocated?\n");
		sswf_info();
	}
#endif
#if REPEAT_TEST
	}
#endif

	if(ec == 0 && errcnt != 0) {
		ec = 1;
	}

	exit(ec);
	/*NOTREACHED*/
	return ec;		/* avoid warnings with some compilers */
}

